.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * qt2160.c - Atmel AT42QT2160 Touch Sense Controller |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | | - * |
---|
16 | | - * You should have received a copy of the GNU General Public License |
---|
17 | | - * along with this program; if not, write to the Free Software |
---|
18 | | - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
19 | 6 | */ |
---|
20 | 7 | |
---|
21 | 8 | #include <linux/kernel.h> |
---|
.. | .. |
---|
58 | 45 | struct qt2160_led { |
---|
59 | 46 | struct qt2160_data *qt2160; |
---|
60 | 47 | struct led_classdev cdev; |
---|
61 | | - struct work_struct work; |
---|
62 | 48 | char name[32]; |
---|
63 | 49 | int id; |
---|
64 | | - enum led_brightness new_brightness; |
---|
| 50 | + enum led_brightness brightness; |
---|
65 | 51 | }; |
---|
66 | 52 | #endif |
---|
67 | 53 | |
---|
.. | .. |
---|
69 | 55 | struct i2c_client *client; |
---|
70 | 56 | struct input_dev *input; |
---|
71 | 57 | struct delayed_work dwork; |
---|
72 | | - spinlock_t lock; /* Protects canceling/rescheduling of dwork */ |
---|
73 | 58 | unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; |
---|
74 | 59 | u16 key_matrix; |
---|
75 | 60 | #ifdef CONFIG_LEDS_CLASS |
---|
76 | 61 | struct qt2160_led leds[QT2160_NUM_LEDS_X]; |
---|
77 | | - struct mutex led_lock; |
---|
78 | 62 | #endif |
---|
79 | 63 | }; |
---|
80 | 64 | |
---|
.. | .. |
---|
83 | 67 | |
---|
84 | 68 | #ifdef CONFIG_LEDS_CLASS |
---|
85 | 69 | |
---|
86 | | -static void qt2160_led_work(struct work_struct *work) |
---|
87 | | -{ |
---|
88 | | - struct qt2160_led *led = container_of(work, struct qt2160_led, work); |
---|
89 | | - struct qt2160_data *qt2160 = led->qt2160; |
---|
90 | | - struct i2c_client *client = qt2160->client; |
---|
91 | | - int value = led->new_brightness; |
---|
92 | | - u32 drive, pwmen; |
---|
93 | | - |
---|
94 | | - mutex_lock(&qt2160->led_lock); |
---|
95 | | - |
---|
96 | | - drive = qt2160_read(client, QT2160_CMD_DRIVE_X); |
---|
97 | | - pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X); |
---|
98 | | - if (value != LED_OFF) { |
---|
99 | | - drive |= (1 << led->id); |
---|
100 | | - pwmen |= (1 << led->id); |
---|
101 | | - |
---|
102 | | - } else { |
---|
103 | | - drive &= ~(1 << led->id); |
---|
104 | | - pwmen &= ~(1 << led->id); |
---|
105 | | - } |
---|
106 | | - qt2160_write(client, QT2160_CMD_DRIVE_X, drive); |
---|
107 | | - qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen); |
---|
108 | | - |
---|
109 | | - /* |
---|
110 | | - * Changing this register will change the brightness |
---|
111 | | - * of every LED in the qt2160. It's a HW limitation. |
---|
112 | | - */ |
---|
113 | | - if (value != LED_OFF) |
---|
114 | | - qt2160_write(client, QT2160_CMD_PWM_DUTY, value); |
---|
115 | | - |
---|
116 | | - mutex_unlock(&qt2160->led_lock); |
---|
117 | | -} |
---|
118 | | - |
---|
119 | | -static void qt2160_led_set(struct led_classdev *cdev, |
---|
120 | | - enum led_brightness value) |
---|
| 70 | +static int qt2160_led_set(struct led_classdev *cdev, |
---|
| 71 | + enum led_brightness value) |
---|
121 | 72 | { |
---|
122 | 73 | struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev); |
---|
| 74 | + struct qt2160_data *qt2160 = led->qt2160; |
---|
| 75 | + struct i2c_client *client = qt2160->client; |
---|
| 76 | + u32 drive, pwmen; |
---|
123 | 77 | |
---|
124 | | - led->new_brightness = value; |
---|
125 | | - schedule_work(&led->work); |
---|
| 78 | + if (value != led->brightness) { |
---|
| 79 | + drive = qt2160_read(client, QT2160_CMD_DRIVE_X); |
---|
| 80 | + pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X); |
---|
| 81 | + if (value != LED_OFF) { |
---|
| 82 | + drive |= BIT(led->id); |
---|
| 83 | + pwmen |= BIT(led->id); |
---|
| 84 | + |
---|
| 85 | + } else { |
---|
| 86 | + drive &= ~BIT(led->id); |
---|
| 87 | + pwmen &= ~BIT(led->id); |
---|
| 88 | + } |
---|
| 89 | + qt2160_write(client, QT2160_CMD_DRIVE_X, drive); |
---|
| 90 | + qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen); |
---|
| 91 | + |
---|
| 92 | + /* |
---|
| 93 | + * Changing this register will change the brightness |
---|
| 94 | + * of every LED in the qt2160. It's a HW limitation. |
---|
| 95 | + */ |
---|
| 96 | + if (value != LED_OFF) |
---|
| 97 | + qt2160_write(client, QT2160_CMD_PWM_DUTY, value); |
---|
| 98 | + |
---|
| 99 | + led->brightness = value; |
---|
| 100 | + } |
---|
| 101 | + |
---|
| 102 | + return 0; |
---|
126 | 103 | } |
---|
127 | 104 | |
---|
128 | 105 | #endif /* CONFIG_LEDS_CLASS */ |
---|
.. | .. |
---|
221 | 198 | static irqreturn_t qt2160_irq(int irq, void *_qt2160) |
---|
222 | 199 | { |
---|
223 | 200 | struct qt2160_data *qt2160 = _qt2160; |
---|
224 | | - unsigned long flags; |
---|
225 | | - |
---|
226 | | - spin_lock_irqsave(&qt2160->lock, flags); |
---|
227 | 201 | |
---|
228 | 202 | mod_delayed_work(system_wq, &qt2160->dwork, 0); |
---|
229 | | - |
---|
230 | | - spin_unlock_irqrestore(&qt2160->lock, flags); |
---|
231 | 203 | |
---|
232 | 204 | return IRQ_HANDLED; |
---|
233 | 205 | } |
---|
234 | 206 | |
---|
235 | 207 | static void qt2160_schedule_read(struct qt2160_data *qt2160) |
---|
236 | 208 | { |
---|
237 | | - spin_lock_irq(&qt2160->lock); |
---|
238 | 209 | schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); |
---|
239 | | - spin_unlock_irq(&qt2160->lock); |
---|
240 | 210 | } |
---|
241 | 211 | |
---|
242 | 212 | static void qt2160_worker(struct work_struct *work) |
---|
.. | .. |
---|
293 | 263 | int ret; |
---|
294 | 264 | int i; |
---|
295 | 265 | |
---|
296 | | - mutex_init(&qt2160->led_lock); |
---|
297 | | - |
---|
298 | 266 | for (i = 0; i < QT2160_NUM_LEDS_X; i++) { |
---|
299 | 267 | struct qt2160_led *led = &qt2160->leds[i]; |
---|
300 | 268 | |
---|
301 | 269 | snprintf(led->name, sizeof(led->name), "qt2160:x%d", i); |
---|
302 | 270 | led->cdev.name = led->name; |
---|
303 | | - led->cdev.brightness_set = qt2160_led_set; |
---|
| 271 | + led->cdev.brightness_set_blocking = qt2160_led_set; |
---|
304 | 272 | led->cdev.brightness = LED_OFF; |
---|
305 | 273 | led->id = i; |
---|
306 | 274 | led->qt2160 = qt2160; |
---|
307 | | - |
---|
308 | | - INIT_WORK(&led->work, qt2160_led_work); |
---|
309 | 275 | |
---|
310 | 276 | ret = led_classdev_register(&client->dev, &led->cdev); |
---|
311 | 277 | if (ret < 0) |
---|
.. | .. |
---|
324 | 290 | { |
---|
325 | 291 | int i; |
---|
326 | 292 | |
---|
327 | | - for (i = 0; i < QT2160_NUM_LEDS_X; i++) { |
---|
| 293 | + for (i = 0; i < QT2160_NUM_LEDS_X; i++) |
---|
328 | 294 | led_classdev_unregister(&qt2160->leds[i].cdev); |
---|
329 | | - cancel_work_sync(&qt2160->leds[i].work); |
---|
330 | | - } |
---|
331 | 295 | } |
---|
332 | 296 | |
---|
333 | 297 | #else |
---|
.. | .. |
---|
406 | 370 | qt2160->client = client; |
---|
407 | 371 | qt2160->input = input; |
---|
408 | 372 | INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); |
---|
409 | | - spin_lock_init(&qt2160->lock); |
---|
410 | 373 | |
---|
411 | 374 | input->name = "AT42QT2160 Touch Sense Keyboard"; |
---|
412 | 375 | input->id.bustype = BUS_I2C; |
---|