Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
bab7614d EM |
2 | /* |
3 | * GPIO driven matrix keyboard driver | |
4 | * | |
5 | * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com> | |
6 | * | |
7 | * Based on corgikbd.c | |
bab7614d EM |
8 | */ |
9 | ||
10 | #include <linux/types.h> | |
11 | #include <linux/delay.h> | |
7aacc42f | 12 | #include <linux/gpio/consumer.h> |
bab7614d | 13 | #include <linux/platform_device.h> |
bab7614d EM |
14 | #include <linux/input.h> |
15 | #include <linux/irq.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/jiffies.h> | |
18 | #include <linux/module.h> | |
19 | #include <linux/gpio.h> | |
20 | #include <linux/input/matrix_keypad.h> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
4a83eecf AC |
22 | #include <linux/of.h> |
23 | #include <linux/of_gpio.h> | |
24 | #include <linux/of_platform.h> | |
bab7614d EM |
25 | |
26 | struct matrix_keypad { | |
27 | const struct matrix_keypad_platform_data *pdata; | |
28 | struct input_dev *input_dev; | |
d82f1c35 | 29 | unsigned int row_shift; |
bab7614d | 30 | |
8cf4b368 DT |
31 | unsigned int row_irqs[MATRIX_MAX_ROWS]; |
32 | unsigned int num_row_irqs; | |
33 | DECLARE_BITMAP(wakeup_enabled_irqs, MATRIX_MAX_ROWS); | |
dd219234 | 34 | |
bab7614d EM |
35 | uint32_t last_key_state[MATRIX_MAX_COLS]; |
36 | struct delayed_work work; | |
dd219234 | 37 | spinlock_t lock; |
bab7614d EM |
38 | bool scan_pending; |
39 | bool stopped; | |
bab7614d EM |
40 | }; |
41 | ||
42 | /* | |
aa0e26bb DR |
43 | * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into |
44 | * HiZ when de-activated to cause minmal side effect when scanning other | |
45 | * columns. In that case it is configured here to be input, otherwise it is | |
46 | * driven with the inactive value. | |
bab7614d EM |
47 | */ |
48 | static void __activate_col(const struct matrix_keypad_platform_data *pdata, | |
49 | int col, bool on) | |
50 | { | |
51 | bool level_on = !pdata->active_low; | |
52 | ||
53 | if (on) { | |
54 | gpio_direction_output(pdata->col_gpios[col], level_on); | |
55 | } else { | |
56 | gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); | |
aa0e26bb DR |
57 | if (!pdata->drive_inactive_cols) |
58 | gpio_direction_input(pdata->col_gpios[col]); | |
bab7614d EM |
59 | } |
60 | } | |
61 | ||
62 | static void activate_col(const struct matrix_keypad_platform_data *pdata, | |
63 | int col, bool on) | |
64 | { | |
65 | __activate_col(pdata, col, on); | |
66 | ||
67 | if (on && pdata->col_scan_delay_us) | |
68 | udelay(pdata->col_scan_delay_us); | |
69 | } | |
70 | ||
71 | static void activate_all_cols(const struct matrix_keypad_platform_data *pdata, | |
72 | bool on) | |
73 | { | |
74 | int col; | |
75 | ||
76 | for (col = 0; col < pdata->num_col_gpios; col++) | |
77 | __activate_col(pdata, col, on); | |
78 | } | |
79 | ||
80 | static bool row_asserted(const struct matrix_keypad_platform_data *pdata, | |
81 | int row) | |
82 | { | |
83 | return gpio_get_value_cansleep(pdata->row_gpios[row]) ? | |
84 | !pdata->active_low : pdata->active_low; | |
85 | } | |
86 | ||
87 | static void enable_row_irqs(struct matrix_keypad *keypad) | |
88 | { | |
bab7614d EM |
89 | int i; |
90 | ||
8cf4b368 DT |
91 | for (i = 0; i < keypad->num_row_irqs; i++) |
92 | enable_irq(keypad->row_irqs[i]); | |
bab7614d EM |
93 | } |
94 | ||
95 | static void disable_row_irqs(struct matrix_keypad *keypad) | |
96 | { | |
bab7614d EM |
97 | int i; |
98 | ||
8cf4b368 DT |
99 | for (i = 0; i < keypad->num_row_irqs; i++) |
100 | disable_irq_nosync(keypad->row_irqs[i]); | |
bab7614d EM |
101 | } |
102 | ||
103 | /* | |
104 | * This gets the keys from keyboard and reports it to input subsystem | |
105 | */ | |
106 | static void matrix_keypad_scan(struct work_struct *work) | |
107 | { | |
108 | struct matrix_keypad *keypad = | |
109 | container_of(work, struct matrix_keypad, work.work); | |
110 | struct input_dev *input_dev = keypad->input_dev; | |
4a83eecf | 111 | const unsigned short *keycodes = input_dev->keycode; |
bab7614d EM |
112 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; |
113 | uint32_t new_state[MATRIX_MAX_COLS]; | |
114 | int row, col, code; | |
115 | ||
116 | /* de-activate all columns for scanning */ | |
117 | activate_all_cols(pdata, false); | |
118 | ||
119 | memset(new_state, 0, sizeof(new_state)); | |
120 | ||
01c84b03 SV |
121 | for (row = 0; row < pdata->num_row_gpios; row++) |
122 | gpio_direction_input(pdata->row_gpios[row]); | |
123 | ||
bab7614d EM |
124 | /* assert each column and read the row status out */ |
125 | for (col = 0; col < pdata->num_col_gpios; col++) { | |
126 | ||
127 | activate_col(pdata, col, true); | |
128 | ||
129 | for (row = 0; row < pdata->num_row_gpios; row++) | |
130 | new_state[col] |= | |
131 | row_asserted(pdata, row) ? (1 << row) : 0; | |
132 | ||
133 | activate_col(pdata, col, false); | |
134 | } | |
135 | ||
136 | for (col = 0; col < pdata->num_col_gpios; col++) { | |
137 | uint32_t bits_changed; | |
138 | ||
139 | bits_changed = keypad->last_key_state[col] ^ new_state[col]; | |
140 | if (bits_changed == 0) | |
141 | continue; | |
142 | ||
143 | for (row = 0; row < pdata->num_row_gpios; row++) { | |
144 | if ((bits_changed & (1 << row)) == 0) | |
145 | continue; | |
146 | ||
d82f1c35 | 147 | code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); |
bab7614d EM |
148 | input_event(input_dev, EV_MSC, MSC_SCAN, code); |
149 | input_report_key(input_dev, | |
4a83eecf | 150 | keycodes[code], |
bab7614d EM |
151 | new_state[col] & (1 << row)); |
152 | } | |
153 | } | |
154 | input_sync(input_dev); | |
155 | ||
156 | memcpy(keypad->last_key_state, new_state, sizeof(new_state)); | |
157 | ||
158 | activate_all_cols(pdata, true); | |
159 | ||
160 | /* Enable IRQs again */ | |
161 | spin_lock_irq(&keypad->lock); | |
162 | keypad->scan_pending = false; | |
163 | enable_row_irqs(keypad); | |
164 | spin_unlock_irq(&keypad->lock); | |
165 | } | |
166 | ||
167 | static irqreturn_t matrix_keypad_interrupt(int irq, void *id) | |
168 | { | |
169 | struct matrix_keypad *keypad = id; | |
170 | unsigned long flags; | |
171 | ||
172 | spin_lock_irqsave(&keypad->lock, flags); | |
173 | ||
174 | /* | |
175 | * See if another IRQ beaten us to it and scheduled the | |
176 | * scan already. In that case we should not try to | |
177 | * disable IRQs again. | |
178 | */ | |
179 | if (unlikely(keypad->scan_pending || keypad->stopped)) | |
180 | goto out; | |
181 | ||
182 | disable_row_irqs(keypad); | |
183 | keypad->scan_pending = true; | |
184 | schedule_delayed_work(&keypad->work, | |
185 | msecs_to_jiffies(keypad->pdata->debounce_ms)); | |
186 | ||
187 | out: | |
188 | spin_unlock_irqrestore(&keypad->lock, flags); | |
189 | return IRQ_HANDLED; | |
190 | } | |
191 | ||
192 | static int matrix_keypad_start(struct input_dev *dev) | |
193 | { | |
194 | struct matrix_keypad *keypad = input_get_drvdata(dev); | |
195 | ||
196 | keypad->stopped = false; | |
197 | mb(); | |
198 | ||
199 | /* | |
200 | * Schedule an immediate key scan to capture current key state; | |
201 | * columns will be activated and IRQs be enabled after the scan. | |
202 | */ | |
203 | schedule_delayed_work(&keypad->work, 0); | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static void matrix_keypad_stop(struct input_dev *dev) | |
209 | { | |
210 | struct matrix_keypad *keypad = input_get_drvdata(dev); | |
211 | ||
ea4f7bd2 | 212 | spin_lock_irq(&keypad->lock); |
bab7614d | 213 | keypad->stopped = true; |
ea4f7bd2 ZB |
214 | spin_unlock_irq(&keypad->lock); |
215 | ||
a342083a | 216 | flush_delayed_work(&keypad->work); |
bab7614d EM |
217 | /* |
218 | * matrix_keypad_scan() will leave IRQs enabled; | |
219 | * we should disable them now. | |
220 | */ | |
221 | disable_row_irqs(keypad); | |
222 | } | |
223 | ||
fb76dd10 | 224 | static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) |
bab7614d | 225 | { |
bab7614d EM |
226 | int i; |
227 | ||
8cf4b368 DT |
228 | for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) |
229 | if (enable_irq_wake(keypad->row_irqs[i]) == 0) | |
230 | __set_bit(i, keypad->wakeup_enabled_irqs); | |
bab7614d EM |
231 | } |
232 | ||
fb76dd10 | 233 | static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) |
bab7614d | 234 | { |
bab7614d EM |
235 | int i; |
236 | ||
8cf4b368 DT |
237 | for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) { |
238 | disable_irq_wake(keypad->row_irqs[i]); | |
239 | __clear_bit(i, keypad->wakeup_enabled_irqs); | |
dd219234 | 240 | } |
fb76dd10 LF |
241 | } |
242 | ||
243 | static int matrix_keypad_suspend(struct device *dev) | |
244 | { | |
245 | struct platform_device *pdev = to_platform_device(dev); | |
246 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | |
247 | ||
248 | matrix_keypad_stop(keypad->input_dev); | |
249 | ||
250 | if (device_may_wakeup(&pdev->dev)) | |
251 | matrix_keypad_enable_wakeup(keypad); | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
256 | static int matrix_keypad_resume(struct device *dev) | |
257 | { | |
258 | struct platform_device *pdev = to_platform_device(dev); | |
259 | struct matrix_keypad *keypad = platform_get_drvdata(pdev); | |
260 | ||
261 | if (device_may_wakeup(&pdev->dev)) | |
262 | matrix_keypad_disable_wakeup(keypad); | |
bab7614d EM |
263 | |
264 | matrix_keypad_start(keypad->input_dev); | |
265 | ||
266 | return 0; | |
267 | } | |
bab7614d | 268 | |
b9425700 JC |
269 | static DEFINE_SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, |
270 | matrix_keypad_suspend, matrix_keypad_resume); | |
0508c19a | 271 | |
5298cc4c BP |
272 | static int matrix_keypad_init_gpio(struct platform_device *pdev, |
273 | struct matrix_keypad *keypad) | |
bab7614d EM |
274 | { |
275 | const struct matrix_keypad_platform_data *pdata = keypad->pdata; | |
a96fb711 | 276 | int i, irq, err; |
bab7614d EM |
277 | |
278 | /* initialized strobe lines as outputs, activated */ | |
279 | for (i = 0; i < pdata->num_col_gpios; i++) { | |
7d0f351d DT |
280 | err = devm_gpio_request(&pdev->dev, |
281 | pdata->col_gpios[i], "matrix_kbd_col"); | |
bab7614d EM |
282 | if (err) { |
283 | dev_err(&pdev->dev, | |
284 | "failed to request GPIO%d for COL%d\n", | |
285 | pdata->col_gpios[i], i); | |
7d0f351d | 286 | return err; |
bab7614d EM |
287 | } |
288 | ||
289 | gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); | |
290 | } | |
291 | ||
292 | for (i = 0; i < pdata->num_row_gpios; i++) { | |
7d0f351d DT |
293 | err = devm_gpio_request(&pdev->dev, |
294 | pdata->row_gpios[i], "matrix_kbd_row"); | |
bab7614d EM |
295 | if (err) { |
296 | dev_err(&pdev->dev, | |
297 | "failed to request GPIO%d for ROW%d\n", | |
298 | pdata->row_gpios[i], i); | |
7d0f351d | 299 | return err; |
bab7614d EM |
300 | } |
301 | ||
302 | gpio_direction_input(pdata->row_gpios[i]); | |
303 | } | |
304 | ||
fb76dd10 | 305 | if (pdata->clustered_irq > 0) { |
7d0f351d DT |
306 | err = devm_request_any_context_irq(&pdev->dev, |
307 | pdata->clustered_irq, | |
bab7614d | 308 | matrix_keypad_interrupt, |
fb76dd10 | 309 | pdata->clustered_irq_flags, |
bab7614d | 310 | "matrix-keypad", keypad); |
24e4d21c | 311 | if (err < 0) { |
bab7614d | 312 | dev_err(&pdev->dev, |
fb76dd10 | 313 | "Unable to acquire clustered interrupt\n"); |
7d0f351d | 314 | return err; |
fb76dd10 | 315 | } |
8cf4b368 DT |
316 | |
317 | keypad->row_irqs[0] = pdata->clustered_irq; | |
318 | keypad->num_row_irqs = 1; | |
fb76dd10 LF |
319 | } else { |
320 | for (i = 0; i < pdata->num_row_gpios; i++) { | |
a96fb711 DT |
321 | irq = gpio_to_irq(pdata->row_gpios[i]); |
322 | if (irq < 0) { | |
323 | err = irq; | |
324 | dev_err(&pdev->dev, | |
325 | "Unable to convert GPIO line %i to irq: %d\n", | |
326 | pdata->row_gpios[i], err); | |
7d0f351d | 327 | return err; |
a96fb711 DT |
328 | } |
329 | ||
7d0f351d DT |
330 | err = devm_request_any_context_irq(&pdev->dev, |
331 | irq, | |
fb76dd10 | 332 | matrix_keypad_interrupt, |
fb76dd10 | 333 | IRQF_TRIGGER_RISING | |
a96fb711 | 334 | IRQF_TRIGGER_FALLING, |
fb76dd10 | 335 | "matrix-keypad", keypad); |
24e4d21c | 336 | if (err < 0) { |
fb76dd10 | 337 | dev_err(&pdev->dev, |
b83643eb | 338 | "Unable to acquire interrupt for GPIO line %i\n", |
fb76dd10 | 339 | pdata->row_gpios[i]); |
7d0f351d | 340 | return err; |
fb76dd10 | 341 | } |
a96fb711 DT |
342 | |
343 | keypad->row_irqs[i] = irq; | |
bab7614d | 344 | } |
8cf4b368 DT |
345 | |
346 | keypad->num_row_irqs = pdata->num_row_gpios; | |
bab7614d EM |
347 | } |
348 | ||
349 | /* initialized as disabled - enabled by input->open */ | |
350 | disable_row_irqs(keypad); | |
b83643eb | 351 | |
7d0f351d | 352 | return 0; |
b83643eb DT |
353 | } |
354 | ||
4a83eecf | 355 | #ifdef CONFIG_OF |
5298cc4c | 356 | static struct matrix_keypad_platform_data * |
4a83eecf AC |
357 | matrix_keypad_parse_dt(struct device *dev) |
358 | { | |
359 | struct matrix_keypad_platform_data *pdata; | |
360 | struct device_node *np = dev->of_node; | |
361 | unsigned int *gpios; | |
d55bda1b | 362 | int ret, i, nrow, ncol; |
4a83eecf AC |
363 | |
364 | if (!np) { | |
365 | dev_err(dev, "device lacks DT data\n"); | |
366 | return ERR_PTR(-ENODEV); | |
367 | } | |
368 | ||
369 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | |
370 | if (!pdata) { | |
371 | dev_err(dev, "could not allocate memory for platform data\n"); | |
372 | return ERR_PTR(-ENOMEM); | |
373 | } | |
374 | ||
f8f7f47d AS |
375 | pdata->num_row_gpios = nrow = gpiod_count(dev, "row"); |
376 | pdata->num_col_gpios = ncol = gpiod_count(dev, "col"); | |
377 | if (nrow < 0 || ncol < 0) { | |
4a83eecf AC |
378 | dev_err(dev, "number of keypad rows/columns not specified\n"); |
379 | return ERR_PTR(-EINVAL); | |
380 | } | |
381 | ||
12c7d0ae | 382 | pdata->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat"); |
aeda5003 DT |
383 | |
384 | pdata->wakeup = of_property_read_bool(np, "wakeup-source") || | |
385 | of_property_read_bool(np, "linux,wakeup"); /* legacy */ | |
386 | ||
12c7d0ae | 387 | pdata->active_low = of_property_read_bool(np, "gpio-activelow"); |
4a83eecf | 388 | |
aa0e26bb DR |
389 | pdata->drive_inactive_cols = |
390 | of_property_read_bool(np, "drive-inactive-cols"); | |
391 | ||
4a83eecf AC |
392 | of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms); |
393 | of_property_read_u32(np, "col-scan-delay-us", | |
394 | &pdata->col_scan_delay_us); | |
395 | ||
a86854d0 KC |
396 | gpios = devm_kcalloc(dev, |
397 | pdata->num_row_gpios + pdata->num_col_gpios, | |
398 | sizeof(unsigned int), | |
4a83eecf AC |
399 | GFP_KERNEL); |
400 | if (!gpios) { | |
401 | dev_err(dev, "could not allocate memory for gpios\n"); | |
402 | return ERR_PTR(-ENOMEM); | |
403 | } | |
404 | ||
d55bda1b CH |
405 | for (i = 0; i < nrow; i++) { |
406 | ret = of_get_named_gpio(np, "row-gpios", i); | |
407 | if (ret < 0) | |
408 | return ERR_PTR(ret); | |
409 | gpios[i] = ret; | |
410 | } | |
4a83eecf | 411 | |
d55bda1b CH |
412 | for (i = 0; i < ncol; i++) { |
413 | ret = of_get_named_gpio(np, "col-gpios", i); | |
414 | if (ret < 0) | |
415 | return ERR_PTR(ret); | |
416 | gpios[nrow + i] = ret; | |
417 | } | |
4a83eecf AC |
418 | |
419 | pdata->row_gpios = gpios; | |
420 | pdata->col_gpios = &gpios[pdata->num_row_gpios]; | |
421 | ||
422 | return pdata; | |
423 | } | |
424 | #else | |
425 | static inline struct matrix_keypad_platform_data * | |
426 | matrix_keypad_parse_dt(struct device *dev) | |
427 | { | |
428 | dev_err(dev, "no platform data defined\n"); | |
429 | ||
430 | return ERR_PTR(-EINVAL); | |
431 | } | |
432 | #endif | |
433 | ||
5298cc4c | 434 | static int matrix_keypad_probe(struct platform_device *pdev) |
bab7614d EM |
435 | { |
436 | const struct matrix_keypad_platform_data *pdata; | |
bab7614d EM |
437 | struct matrix_keypad *keypad; |
438 | struct input_dev *input_dev; | |
bab7614d EM |
439 | int err; |
440 | ||
4a83eecf | 441 | pdata = dev_get_platdata(&pdev->dev); |
bab7614d | 442 | if (!pdata) { |
4a83eecf | 443 | pdata = matrix_keypad_parse_dt(&pdev->dev); |
d55bda1b | 444 | if (IS_ERR(pdata)) |
4a83eecf | 445 | return PTR_ERR(pdata); |
4a83eecf | 446 | } else if (!pdata->keymap_data) { |
bab7614d EM |
447 | dev_err(&pdev->dev, "no keymap data defined\n"); |
448 | return -EINVAL; | |
449 | } | |
450 | ||
7d0f351d DT |
451 | keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); |
452 | if (!keypad) | |
453 | return -ENOMEM; | |
454 | ||
455 | input_dev = devm_input_allocate_device(&pdev->dev); | |
456 | if (!input_dev) | |
457 | return -ENOMEM; | |
bab7614d EM |
458 | |
459 | keypad->input_dev = input_dev; | |
460 | keypad->pdata = pdata; | |
4a83eecf | 461 | keypad->row_shift = get_count_order(pdata->num_col_gpios); |
bab7614d EM |
462 | keypad->stopped = true; |
463 | INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); | |
464 | spin_lock_init(&keypad->lock); | |
465 | ||
466 | input_dev->name = pdev->name; | |
467 | input_dev->id.bustype = BUS_HOST; | |
bab7614d EM |
468 | input_dev->open = matrix_keypad_start; |
469 | input_dev->close = matrix_keypad_stop; | |
470 | ||
4a83eecf | 471 | err = matrix_keypad_build_keymap(pdata->keymap_data, NULL, |
1932811f DT |
472 | pdata->num_row_gpios, |
473 | pdata->num_col_gpios, | |
4a83eecf AC |
474 | NULL, input_dev); |
475 | if (err) { | |
476 | dev_err(&pdev->dev, "failed to build keymap\n"); | |
7d0f351d | 477 | return -ENOMEM; |
4a83eecf | 478 | } |
bab7614d | 479 | |
1932811f DT |
480 | if (!pdata->no_autorepeat) |
481 | __set_bit(EV_REP, input_dev->evbit); | |
bab7614d EM |
482 | input_set_capability(input_dev, EV_MSC, MSC_SCAN); |
483 | input_set_drvdata(input_dev, keypad); | |
484 | ||
b83643eb | 485 | err = matrix_keypad_init_gpio(pdev, keypad); |
bab7614d | 486 | if (err) |
7d0f351d | 487 | return err; |
bab7614d EM |
488 | |
489 | err = input_register_device(keypad->input_dev); | |
490 | if (err) | |
7d0f351d | 491 | return err; |
bab7614d EM |
492 | |
493 | device_init_wakeup(&pdev->dev, pdata->wakeup); | |
494 | platform_set_drvdata(pdev, keypad); | |
495 | ||
496 | return 0; | |
bab7614d EM |
497 | } |
498 | ||
4a83eecf AC |
499 | #ifdef CONFIG_OF |
500 | static const struct of_device_id matrix_keypad_dt_match[] = { | |
501 | { .compatible = "gpio-matrix-keypad" }, | |
502 | { } | |
503 | }; | |
504 | MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match); | |
505 | #endif | |
506 | ||
bab7614d EM |
507 | static struct platform_driver matrix_keypad_driver = { |
508 | .probe = matrix_keypad_probe, | |
bab7614d EM |
509 | .driver = { |
510 | .name = "matrix-keypad", | |
b9425700 | 511 | .pm = pm_sleep_ptr(&matrix_keypad_pm_ops), |
4a83eecf | 512 | .of_match_table = of_match_ptr(matrix_keypad_dt_match), |
bab7614d EM |
513 | }, |
514 | }; | |
5146c84f | 515 | module_platform_driver(matrix_keypad_driver); |
bab7614d EM |
516 | |
517 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | |
518 | MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); | |
519 | MODULE_LICENSE("GPL v2"); | |
520 | MODULE_ALIAS("platform:matrix-keypad"); |