| .. | .. |
|---|
| 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; |
|---|