diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/keyboard/Kconfig | 13 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/matrix_keypad.c | 453 |
3 files changed, 465 insertions, 2 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index d2df1030675a..a6b989a9dc07 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig | |||
@@ -158,7 +158,16 @@ config KEYBOARD_GPIO | |||
158 | with configuration data saying which GPIOs are used. | 158 | with configuration data saying which GPIOs are used. |
159 | 159 | ||
160 | To compile this driver as a module, choose M here: the | 160 | To compile this driver as a module, choose M here: the |
161 | module will be called gpio-keys. | 161 | module will be called gpio_keys. |
162 | |||
163 | config KEYBOARD_MATRIX | ||
164 | tristate "GPIO driven matrix keypad support" | ||
165 | depends on GENERIC_GPIO | ||
166 | help | ||
167 | Enable support for GPIO driven matrix keypad. | ||
168 | |||
169 | To compile this driver as a module, choose M here: the | ||
170 | module will be called matrix_keypad. | ||
162 | 171 | ||
163 | config KEYBOARD_HIL_OLD | 172 | config KEYBOARD_HIL_OLD |
164 | tristate "HP HIL keyboard support (simple driver)" | 173 | tristate "HP HIL keyboard support (simple driver)" |
@@ -254,7 +263,7 @@ config KEYBOARD_PXA27x | |||
254 | tristate "PXA27x/PXA3xx keypad support" | 263 | tristate "PXA27x/PXA3xx keypad support" |
255 | depends on PXA27x || PXA3xx | 264 | depends on PXA27x || PXA3xx |
256 | help | 265 | help |
257 | Enable support for PXA27x/PXA3xx keypad controller | 266 | Enable support for PXA27x/PXA3xx keypad controller. |
258 | 267 | ||
259 | To compile this driver as a module, choose M here: the | 268 | To compile this driver as a module, choose M here: the |
260 | module will be called pxa27x_keypad. | 269 | module will be called pxa27x_keypad. |
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 632efbc18c44..b5b5eae9724f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile | |||
@@ -20,6 +20,7 @@ obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o | |||
20 | obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o | 20 | obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o |
21 | obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o | 21 | obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o |
22 | obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o | 22 | obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o |
23 | obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o | ||
23 | obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o | 24 | obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o |
24 | obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o | 25 | obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o |
25 | obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o | 26 | obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o |
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c new file mode 100644 index 000000000000..e9b2e7cb05be --- /dev/null +++ b/drivers/input/keyboard/matrix_keypad.c | |||
@@ -0,0 +1,453 @@ | |||
1 | /* | ||
2 | * GPIO driven matrix keyboard driver | ||
3 | * | ||
4 | * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com> | ||
5 | * | ||
6 | * Based on corgikbd.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/jiffies.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/gpio.h> | ||
24 | #include <linux/input/matrix_keypad.h> | ||
25 | |||
26 | struct matrix_keypad { | ||
27 | const struct matrix_keypad_platform_data *pdata; | ||
28 | struct input_dev *input_dev; | ||
29 | unsigned short *keycodes; | ||
30 | |||
31 | uint32_t last_key_state[MATRIX_MAX_COLS]; | ||
32 | struct delayed_work work; | ||
33 | bool scan_pending; | ||
34 | bool stopped; | ||
35 | spinlock_t lock; | ||
36 | }; | ||
37 | |||
38 | /* | ||
39 | * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause | ||
40 | * minmal side effect when scanning other columns, here it is configured to | ||
41 | * be input, and it should work on most platforms. | ||
42 | */ | ||
43 | static void __activate_col(const struct matrix_keypad_platform_data *pdata, | ||
44 | int col, bool on) | ||
45 | { | ||
46 | bool level_on = !pdata->active_low; | ||
47 | |||
48 | if (on) { | ||
49 | gpio_direction_output(pdata->col_gpios[col], level_on); | ||
50 | } else { | ||
51 | gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); | ||
52 | gpio_direction_input(pdata->col_gpios[col]); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | static void activate_col(const struct matrix_keypad_platform_data *pdata, | ||
57 | int col, bool on) | ||
58 | { | ||
59 | __activate_col(pdata, col, on); | ||
60 | |||
61 | if (on && pdata->col_scan_delay_us) | ||
62 | udelay(pdata->col_scan_delay_us); | ||
63 | } | ||
64 | |||
65 | static void activate_all_cols(const struct matrix_keypad_platform_data *pdata, | ||
66 | bool on) | ||
67 | { | ||
68 | int col; | ||
69 | |||
70 | for (col = 0; col < pdata->num_col_gpios; col++) | ||
71 | __activate_col(pdata, col, on); | ||
72 | } | ||
73 | |||
74 | static bool row_asserted(const struct matrix_keypad_platform_data *pdata, | ||
75 | int row) | ||
76 | { | ||
77 | return gpio_get_value_cansleep(pdata->row_gpios[row]) ? | ||
78 | !pdata->active_low : pdata->active_low; | ||
79 | } | ||
80 | |||
81 | static void enable_row_irqs(struct matrix_keypad *keypad) | ||
82 | { | ||
83 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
84 | int i; | ||
85 | |||
86 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
87 | enable_irq(gpio_to_irq(pdata->row_gpios[i])); | ||
88 | } | ||
89 | |||
90 | static void disable_row_irqs(struct matrix_keypad *keypad) | ||
91 | { | ||
92 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
93 | int i; | ||
94 | |||
95 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
96 | disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * This gets the keys from keyboard and reports it to input subsystem | ||
101 | */ | ||
102 | static void matrix_keypad_scan(struct work_struct *work) | ||
103 | { | ||
104 | struct matrix_keypad *keypad = | ||
105 | container_of(work, struct matrix_keypad, work.work); | ||
106 | struct input_dev *input_dev = keypad->input_dev; | ||
107 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
108 | uint32_t new_state[MATRIX_MAX_COLS]; | ||
109 | int row, col, code; | ||
110 | |||
111 | /* de-activate all columns for scanning */ | ||
112 | activate_all_cols(pdata, false); | ||
113 | |||
114 | memset(new_state, 0, sizeof(new_state)); | ||
115 | |||
116 | /* assert each column and read the row status out */ | ||
117 | for (col = 0; col < pdata->num_col_gpios; col++) { | ||
118 | |||
119 | activate_col(pdata, col, true); | ||
120 | |||
121 | for (row = 0; row < pdata->num_row_gpios; row++) | ||
122 | new_state[col] |= | ||
123 | row_asserted(pdata, row) ? (1 << row) : 0; | ||
124 | |||
125 | activate_col(pdata, col, false); | ||
126 | } | ||
127 | |||
128 | for (col = 0; col < pdata->num_col_gpios; col++) { | ||
129 | uint32_t bits_changed; | ||
130 | |||
131 | bits_changed = keypad->last_key_state[col] ^ new_state[col]; | ||
132 | if (bits_changed == 0) | ||
133 | continue; | ||
134 | |||
135 | for (row = 0; row < pdata->num_row_gpios; row++) { | ||
136 | if ((bits_changed & (1 << row)) == 0) | ||
137 | continue; | ||
138 | |||
139 | code = (row << 4) + col; | ||
140 | input_event(input_dev, EV_MSC, MSC_SCAN, code); | ||
141 | input_report_key(input_dev, | ||
142 | keypad->keycodes[code], | ||
143 | new_state[col] & (1 << row)); | ||
144 | } | ||
145 | } | ||
146 | input_sync(input_dev); | ||
147 | |||
148 | memcpy(keypad->last_key_state, new_state, sizeof(new_state)); | ||
149 | |||
150 | activate_all_cols(pdata, true); | ||
151 | |||
152 | /* Enable IRQs again */ | ||
153 | spin_lock_irq(&keypad->lock); | ||
154 | keypad->scan_pending = false; | ||
155 | enable_row_irqs(keypad); | ||
156 | spin_unlock_irq(&keypad->lock); | ||
157 | } | ||
158 | |||
159 | static irqreturn_t matrix_keypad_interrupt(int irq, void *id) | ||
160 | { | ||
161 | struct matrix_keypad *keypad = id; | ||
162 | unsigned long flags; | ||
163 | |||
164 | spin_lock_irqsave(&keypad->lock, flags); | ||
165 | |||
166 | /* | ||
167 | * See if another IRQ beaten us to it and scheduled the | ||
168 | * scan already. In that case we should not try to | ||
169 | * disable IRQs again. | ||
170 | */ | ||
171 | if (unlikely(keypad->scan_pending || keypad->stopped)) | ||
172 | goto out; | ||
173 | |||
174 | disable_row_irqs(keypad); | ||
175 | keypad->scan_pending = true; | ||
176 | schedule_delayed_work(&keypad->work, | ||
177 | msecs_to_jiffies(keypad->pdata->debounce_ms)); | ||
178 | |||
179 | out: | ||
180 | spin_unlock_irqrestore(&keypad->lock, flags); | ||
181 | return IRQ_HANDLED; | ||
182 | } | ||
183 | |||
184 | static int matrix_keypad_start(struct input_dev *dev) | ||
185 | { | ||
186 | struct matrix_keypad *keypad = input_get_drvdata(dev); | ||
187 | |||
188 | keypad->stopped = false; | ||
189 | mb(); | ||
190 | |||
191 | /* | ||
192 | * Schedule an immediate key scan to capture current key state; | ||
193 | * columns will be activated and IRQs be enabled after the scan. | ||
194 | */ | ||
195 | schedule_delayed_work(&keypad->work, 0); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static void matrix_keypad_stop(struct input_dev *dev) | ||
201 | { | ||
202 | struct matrix_keypad *keypad = input_get_drvdata(dev); | ||
203 | |||
204 | keypad->stopped = true; | ||
205 | mb(); | ||
206 | flush_work(&keypad->work.work); | ||
207 | /* | ||
208 | * matrix_keypad_scan() will leave IRQs enabled; | ||
209 | * we should disable them now. | ||
210 | */ | ||
211 | disable_row_irqs(keypad); | ||
212 | } | ||
213 | |||
214 | #ifdef CONFIG_PM | ||
215 | static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state) | ||
216 | { | ||
217 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
218 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
219 | int i; | ||
220 | |||
221 | matrix_keypad_stop(keypad->input_dev); | ||
222 | |||
223 | if (device_may_wakeup(&pdev->dev)) | ||
224 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
225 | enable_irq_wake(gpio_to_irq(pdata->row_gpios[i])); | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int matrix_keypad_resume(struct platform_device *pdev) | ||
231 | { | ||
232 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
233 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
234 | int i; | ||
235 | |||
236 | if (device_may_wakeup(&pdev->dev)) | ||
237 | for (i = 0; i < pdata->num_row_gpios; i++) | ||
238 | disable_irq_wake(gpio_to_irq(pdata->row_gpios[i])); | ||
239 | |||
240 | matrix_keypad_start(keypad->input_dev); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | #else | ||
245 | #define matrix_keypad_suspend NULL | ||
246 | #define matrix_keypad_resume NULL | ||
247 | #endif | ||
248 | |||
249 | static int __devinit init_matrix_gpio(struct platform_device *pdev, | ||
250 | struct matrix_keypad *keypad) | ||
251 | { | ||
252 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
253 | int i, err = -EINVAL; | ||
254 | |||
255 | /* initialized strobe lines as outputs, activated */ | ||
256 | for (i = 0; i < pdata->num_col_gpios; i++) { | ||
257 | err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); | ||
258 | if (err) { | ||
259 | dev_err(&pdev->dev, | ||
260 | "failed to request GPIO%d for COL%d\n", | ||
261 | pdata->col_gpios[i], i); | ||
262 | goto err_free_cols; | ||
263 | } | ||
264 | |||
265 | gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); | ||
266 | } | ||
267 | |||
268 | for (i = 0; i < pdata->num_row_gpios; i++) { | ||
269 | err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); | ||
270 | if (err) { | ||
271 | dev_err(&pdev->dev, | ||
272 | "failed to request GPIO%d for ROW%d\n", | ||
273 | pdata->row_gpios[i], i); | ||
274 | goto err_free_rows; | ||
275 | } | ||
276 | |||
277 | gpio_direction_input(pdata->row_gpios[i]); | ||
278 | } | ||
279 | |||
280 | for (i = 0; i < pdata->num_row_gpios; i++) { | ||
281 | err = request_irq(gpio_to_irq(pdata->row_gpios[i]), | ||
282 | matrix_keypad_interrupt, | ||
283 | IRQF_DISABLED | | ||
284 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
285 | "matrix-keypad", keypad); | ||
286 | if (err) { | ||
287 | dev_err(&pdev->dev, | ||
288 | "Unable to acquire interrupt for GPIO line %i\n", | ||
289 | pdata->row_gpios[i]); | ||
290 | goto err_free_irqs; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | /* initialized as disabled - enabled by input->open */ | ||
295 | disable_row_irqs(keypad); | ||
296 | return 0; | ||
297 | |||
298 | err_free_irqs: | ||
299 | while (--i >= 0) | ||
300 | free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); | ||
301 | i = pdata->num_row_gpios; | ||
302 | err_free_rows: | ||
303 | while (--i >= 0) | ||
304 | gpio_free(pdata->row_gpios[i]); | ||
305 | i = pdata->num_col_gpios; | ||
306 | err_free_cols: | ||
307 | while (--i >= 0) | ||
308 | gpio_free(pdata->col_gpios[i]); | ||
309 | |||
310 | return err; | ||
311 | } | ||
312 | |||
313 | static int __devinit matrix_keypad_probe(struct platform_device *pdev) | ||
314 | { | ||
315 | const struct matrix_keypad_platform_data *pdata; | ||
316 | const struct matrix_keymap_data *keymap_data; | ||
317 | struct matrix_keypad *keypad; | ||
318 | struct input_dev *input_dev; | ||
319 | unsigned short *keycodes; | ||
320 | int i; | ||
321 | int err; | ||
322 | |||
323 | pdata = pdev->dev.platform_data; | ||
324 | if (!pdata) { | ||
325 | dev_err(&pdev->dev, "no platform data defined\n"); | ||
326 | return -EINVAL; | ||
327 | } | ||
328 | |||
329 | keymap_data = pdata->keymap_data; | ||
330 | if (!keymap_data) { | ||
331 | dev_err(&pdev->dev, "no keymap data defined\n"); | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | |||
335 | if (!keymap_data->max_keymap_size) { | ||
336 | dev_err(&pdev->dev, "invalid keymap data supplied\n"); | ||
337 | return -EINVAL; | ||
338 | } | ||
339 | |||
340 | keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); | ||
341 | keycodes = kzalloc(keymap_data->max_keymap_size * | ||
342 | sizeof(keypad->keycodes), | ||
343 | GFP_KERNEL); | ||
344 | input_dev = input_allocate_device(); | ||
345 | if (!keypad || !keycodes || !input_dev) { | ||
346 | err = -ENOMEM; | ||
347 | goto err_free_mem; | ||
348 | } | ||
349 | |||
350 | keypad->input_dev = input_dev; | ||
351 | keypad->pdata = pdata; | ||
352 | keypad->keycodes = keycodes; | ||
353 | keypad->stopped = true; | ||
354 | INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); | ||
355 | spin_lock_init(&keypad->lock); | ||
356 | |||
357 | input_dev->name = pdev->name; | ||
358 | input_dev->id.bustype = BUS_HOST; | ||
359 | input_dev->dev.parent = &pdev->dev; | ||
360 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); | ||
361 | input_dev->open = matrix_keypad_start; | ||
362 | input_dev->close = matrix_keypad_stop; | ||
363 | |||
364 | input_dev->keycode = keycodes; | ||
365 | input_dev->keycodesize = sizeof(*keycodes); | ||
366 | input_dev->keycodemax = keymap_data->max_keymap_size; | ||
367 | |||
368 | for (i = 0; i < keymap_data->keymap_size; i++) { | ||
369 | unsigned int key = keymap_data->keymap[i]; | ||
370 | unsigned int row = KEY_ROW(key); | ||
371 | unsigned int col = KEY_COL(key); | ||
372 | unsigned short code = KEY_VAL(key); | ||
373 | |||
374 | keycodes[(row << 4) + col] = code; | ||
375 | __set_bit(code, input_dev->keybit); | ||
376 | } | ||
377 | __clear_bit(KEY_RESERVED, input_dev->keybit); | ||
378 | |||
379 | input_set_capability(input_dev, EV_MSC, MSC_SCAN); | ||
380 | input_set_drvdata(input_dev, keypad); | ||
381 | |||
382 | err = init_matrix_gpio(pdev, keypad); | ||
383 | if (err) | ||
384 | goto err_free_mem; | ||
385 | |||
386 | err = input_register_device(keypad->input_dev); | ||
387 | if (err) | ||
388 | goto err_free_mem; | ||
389 | |||
390 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
391 | platform_set_drvdata(pdev, keypad); | ||
392 | |||
393 | return 0; | ||
394 | |||
395 | err_free_mem: | ||
396 | input_free_device(input_dev); | ||
397 | kfree(keycodes); | ||
398 | kfree(keypad); | ||
399 | return err; | ||
400 | } | ||
401 | |||
402 | static int __devexit matrix_keypad_remove(struct platform_device *pdev) | ||
403 | { | ||
404 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | ||
405 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | ||
406 | int i; | ||
407 | |||
408 | device_init_wakeup(&pdev->dev, 0); | ||
409 | |||
410 | for (i = 0; i < pdata->num_row_gpios; i++) { | ||
411 | free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); | ||
412 | gpio_free(pdata->row_gpios[i]); | ||
413 | } | ||
414 | |||
415 | for (i = 0; i < pdata->num_col_gpios; i++) | ||
416 | gpio_free(pdata->col_gpios[i]); | ||
417 | |||
418 | input_unregister_device(keypad->input_dev); | ||
419 | platform_set_drvdata(pdev, NULL); | ||
420 | kfree(keypad->keycodes); | ||
421 | kfree(keypad); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static struct platform_driver matrix_keypad_driver = { | ||
427 | .probe = matrix_keypad_probe, | ||
428 | .remove = __devexit_p(matrix_keypad_remove), | ||
429 | .suspend = matrix_keypad_suspend, | ||
430 | .resume = matrix_keypad_resume, | ||
431 | .driver = { | ||
432 | .name = "matrix-keypad", | ||
433 | .owner = THIS_MODULE, | ||
434 | }, | ||
435 | }; | ||
436 | |||
437 | static int __init matrix_keypad_init(void) | ||
438 | { | ||
439 | return platform_driver_register(&matrix_keypad_driver); | ||
440 | } | ||
441 | |||
442 | static void __exit matrix_keypad_exit(void) | ||
443 | { | ||
444 | platform_driver_unregister(&matrix_keypad_driver); | ||
445 | } | ||
446 | |||
447 | module_init(matrix_keypad_init); | ||
448 | module_exit(matrix_keypad_exit); | ||
449 | |||
450 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | ||
451 | MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); | ||
452 | MODULE_LICENSE("GPL v2"); | ||
453 | MODULE_ALIAS("platform:matrix-keypad"); | ||