diff options
author | Luotao Fu <l.fu@pengutronix.de> | 2010-06-10 15:05:23 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-06-10 15:33:59 -0400 |
commit | fb76dd10b91146e9cefbb3cd4e6812c5a95ee43b (patch) | |
tree | a5f944375a2e7965ee61223a02455a20469483a9 | |
parent | 1719ec4136035472d3e83a373908dd1b186dbc0b (diff) |
Input: matrix_keypad - add support for clustered irq
This one adds support of a combined irq source for the whole matrix keypad.
This can be useful if all rows and columns of the keypad are e.g. connected
to a GPIO expander, which only has one interrupt line for all events on
every single GPIO.
Signed-off-by: Luotao Fu <l.fu@pengutronix.de>
Acked-by: Eric Miao <eric.y.miao@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/keyboard/matrix_keypad.c | 108 | ||||
-rw-r--r-- | include/linux/input/matrix_keypad.h | 6 |
2 files changed, 86 insertions, 28 deletions
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index b443e088fd3c..b02e4268e18f 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c | |||
@@ -37,6 +37,7 @@ struct matrix_keypad { | |||
37 | spinlock_t lock; | 37 | spinlock_t lock; |
38 | bool scan_pending; | 38 | bool scan_pending; |
39 | bool stopped; | 39 | bool stopped; |
40 | bool gpio_all_disabled; | ||
40 | }; | 41 | }; |
41 | 42 | ||
42 | /* | 43 | /* |
@@ -87,8 +88,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad) | |||
87 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | 88 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; |
88 | int i; | 89 | int i; |
89 | 90 | ||
90 | for (i = 0; i < pdata->num_row_gpios; i++) | 91 | if (pdata->clustered_irq > 0) |
91 | enable_irq(gpio_to_irq(pdata->row_gpios[i])); | 92 | enable_irq(pdata->clustered_irq); |
93 | else { | ||
94 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
95 | enable_irq(gpio_to_irq(pdata->row_gpios[i])); | ||
96 | } | ||
92 | } | 97 | } |
93 | 98 | ||
94 | static void disable_row_irqs(struct matrix_keypad *keypad) | 99 | static void disable_row_irqs(struct matrix_keypad *keypad) |
@@ -96,8 +101,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad) | |||
96 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | 101 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; |
97 | int i; | 102 | int i; |
98 | 103 | ||
99 | for (i = 0; i < pdata->num_row_gpios; i++) | 104 | if (pdata->clustered_irq > 0) |
100 | disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); | 105 | disable_irq_nosync(pdata->clustered_irq); |
106 | else { | ||
107 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
108 | disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); | ||
109 | } | ||
101 | } | 110 | } |
102 | 111 | ||
103 | /* | 112 | /* |
@@ -216,45 +225,69 @@ static void matrix_keypad_stop(struct input_dev *dev) | |||
216 | } | 225 | } |
217 | 226 | ||
218 | #ifdef CONFIG_PM | 227 | #ifdef CONFIG_PM |
219 | static int matrix_keypad_suspend(struct device *dev) | 228 | static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) |
220 | { | 229 | { |
221 | struct platform_device *pdev = to_platform_device(dev); | ||
222 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
223 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | 230 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; |
231 | unsigned int gpio; | ||
224 | int i; | 232 | int i; |
225 | 233 | ||
226 | matrix_keypad_stop(keypad->input_dev); | 234 | if (pdata->clustered_irq > 0) { |
235 | if (enable_irq_wake(pdata->clustered_irq) == 0) | ||
236 | keypad->gpio_all_disabled = true; | ||
237 | } else { | ||
227 | 238 | ||
228 | if (device_may_wakeup(&pdev->dev)) { | ||
229 | for (i = 0; i < pdata->num_row_gpios; i++) { | 239 | for (i = 0; i < pdata->num_row_gpios; i++) { |
230 | if (!test_bit(i, keypad->disabled_gpios)) { | 240 | if (!test_bit(i, keypad->disabled_gpios)) { |
231 | unsigned int gpio = pdata->row_gpios[i]; | 241 | gpio = pdata->row_gpios[i]; |
232 | 242 | ||
233 | if (enable_irq_wake(gpio_to_irq(gpio)) == 0) | 243 | if (enable_irq_wake(gpio_to_irq(gpio)) == 0) |
234 | __set_bit(i, keypad->disabled_gpios); | 244 | __set_bit(i, keypad->disabled_gpios); |
235 | } | 245 | } |
236 | } | 246 | } |
237 | } | 247 | } |
238 | |||
239 | return 0; | ||
240 | } | 248 | } |
241 | 249 | ||
242 | static int matrix_keypad_resume(struct device *dev) | 250 | static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) |
243 | { | 251 | { |
244 | struct platform_device *pdev = to_platform_device(dev); | ||
245 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
246 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | 252 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; |
253 | unsigned int gpio; | ||
247 | int i; | 254 | int i; |
248 | 255 | ||
249 | if (device_may_wakeup(&pdev->dev)) { | 256 | if (pdata->clustered_irq > 0) { |
257 | if (keypad->gpio_all_disabled) { | ||
258 | disable_irq_wake(pdata->clustered_irq); | ||
259 | keypad->gpio_all_disabled = false; | ||
260 | } | ||
261 | } else { | ||
250 | for (i = 0; i < pdata->num_row_gpios; i++) { | 262 | for (i = 0; i < pdata->num_row_gpios; i++) { |
251 | if (test_and_clear_bit(i, keypad->disabled_gpios)) { | 263 | if (test_and_clear_bit(i, keypad->disabled_gpios)) { |
252 | unsigned int gpio = pdata->row_gpios[i]; | 264 | gpio = pdata->row_gpios[i]; |
253 | |||
254 | disable_irq_wake(gpio_to_irq(gpio)); | 265 | disable_irq_wake(gpio_to_irq(gpio)); |
255 | } | 266 | } |
256 | } | 267 | } |
257 | } | 268 | } |
269 | } | ||
270 | |||
271 | static int matrix_keypad_suspend(struct device *dev) | ||
272 | { | ||
273 | struct platform_device *pdev = to_platform_device(dev); | ||
274 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
275 | |||
276 | matrix_keypad_stop(keypad->input_dev); | ||
277 | |||
278 | if (device_may_wakeup(&pdev->dev)) | ||
279 | matrix_keypad_enable_wakeup(keypad); | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int matrix_keypad_resume(struct device *dev) | ||
285 | { | ||
286 | struct platform_device *pdev = to_platform_device(dev); | ||
287 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
288 | |||
289 | if (device_may_wakeup(&pdev->dev)) | ||
290 | matrix_keypad_disable_wakeup(keypad); | ||
258 | 291 | ||
259 | matrix_keypad_start(keypad->input_dev); | 292 | matrix_keypad_start(keypad->input_dev); |
260 | 293 | ||
@@ -296,17 +329,31 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev, | |||
296 | gpio_direction_input(pdata->row_gpios[i]); | 329 | gpio_direction_input(pdata->row_gpios[i]); |
297 | } | 330 | } |
298 | 331 | ||
299 | for (i = 0; i < pdata->num_row_gpios; i++) { | 332 | if (pdata->clustered_irq > 0) { |
300 | err = request_irq(gpio_to_irq(pdata->row_gpios[i]), | 333 | err = request_irq(pdata->clustered_irq, |
301 | matrix_keypad_interrupt, | 334 | matrix_keypad_interrupt, |
302 | IRQF_DISABLED | | 335 | pdata->clustered_irq_flags, |
303 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
304 | "matrix-keypad", keypad); | 336 | "matrix-keypad", keypad); |
305 | if (err) { | 337 | if (err) { |
306 | dev_err(&pdev->dev, | 338 | dev_err(&pdev->dev, |
307 | "Unable to acquire interrupt for GPIO line %i\n", | 339 | "Unable to acquire clustered interrupt\n"); |
308 | pdata->row_gpios[i]); | 340 | goto err_free_rows; |
309 | goto err_free_irqs; | 341 | } |
342 | } else { | ||
343 | for (i = 0; i < pdata->num_row_gpios; i++) { | ||
344 | err = request_irq(gpio_to_irq(pdata->row_gpios[i]), | ||
345 | matrix_keypad_interrupt, | ||
346 | IRQF_DISABLED | | ||
347 | IRQF_TRIGGER_RISING | | ||
348 | IRQF_TRIGGER_FALLING, | ||
349 | "matrix-keypad", keypad); | ||
350 | if (err) { | ||
351 | dev_err(&pdev->dev, | ||
352 | "Unable to acquire interrupt " | ||
353 | "for GPIO line %i\n", | ||
354 | pdata->row_gpios[i]); | ||
355 | goto err_free_irqs; | ||
356 | } | ||
310 | } | 357 | } |
311 | } | 358 | } |
312 | 359 | ||
@@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev) | |||
418 | 465 | ||
419 | device_init_wakeup(&pdev->dev, 0); | 466 | device_init_wakeup(&pdev->dev, 0); |
420 | 467 | ||
421 | for (i = 0; i < pdata->num_row_gpios; i++) { | 468 | if (pdata->clustered_irq > 0) { |
422 | free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); | 469 | free_irq(pdata->clustered_irq, keypad); |
423 | gpio_free(pdata->row_gpios[i]); | 470 | } else { |
471 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
472 | free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); | ||
424 | } | 473 | } |
425 | 474 | ||
475 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
476 | gpio_free(pdata->row_gpios[i]); | ||
477 | |||
426 | for (i = 0; i < pdata->num_col_gpios; i++) | 478 | for (i = 0; i < pdata->num_col_gpios; i++) |
427 | gpio_free(pdata->col_gpios[i]); | 479 | gpio_free(pdata->col_gpios[i]); |
428 | 480 | ||
diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index c964cd7f436a..80352ad6581a 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h | |||
@@ -41,6 +41,9 @@ struct matrix_keymap_data { | |||
41 | * @col_scan_delay_us: delay, measured in microseconds, that is | 41 | * @col_scan_delay_us: delay, measured in microseconds, that is |
42 | * needed before we can keypad after activating column gpio | 42 | * needed before we can keypad after activating column gpio |
43 | * @debounce_ms: debounce interval in milliseconds | 43 | * @debounce_ms: debounce interval in milliseconds |
44 | * @clustered_irq: may be specified if interrupts of all row/column GPIOs | ||
45 | * are bundled to one single irq | ||
46 | * @clustered_irq_flags: flags that are needed for the clustered irq | ||
44 | * @active_low: gpio polarity | 47 | * @active_low: gpio polarity |
45 | * @wakeup: controls whether the device should be set up as wakeup | 48 | * @wakeup: controls whether the device should be set up as wakeup |
46 | * source | 49 | * source |
@@ -63,6 +66,9 @@ struct matrix_keypad_platform_data { | |||
63 | /* key debounce interval in milli-second */ | 66 | /* key debounce interval in milli-second */ |
64 | unsigned int debounce_ms; | 67 | unsigned int debounce_ms; |
65 | 68 | ||
69 | unsigned int clustered_irq; | ||
70 | unsigned int clustered_irq_flags; | ||
71 | |||
66 | bool active_low; | 72 | bool active_low; |
67 | bool wakeup; | 73 | bool wakeup; |
68 | bool no_autorepeat; | 74 | bool no_autorepeat; |