diff options
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r-- | drivers/input/keyboard/samsung-keypad.c | 173 |
1 files changed, 161 insertions, 12 deletions
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index b746fce2d120..17ba7f9f80f3 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/pm_runtime.h> | 24 | #include <linux/pm_runtime.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/of.h> | ||
27 | #include <linux/of_gpio.h> | ||
26 | #include <linux/sched.h> | 28 | #include <linux/sched.h> |
27 | #include <linux/input/samsung-keypad.h> | 29 | #include <linux/input/samsung-keypad.h> |
28 | 30 | ||
@@ -72,31 +74,26 @@ struct samsung_keypad { | |||
72 | bool stopped; | 74 | bool stopped; |
73 | bool wake_enabled; | 75 | bool wake_enabled; |
74 | int irq; | 76 | int irq; |
77 | enum samsung_keypad_type type; | ||
75 | unsigned int row_shift; | 78 | unsigned int row_shift; |
76 | unsigned int rows; | 79 | unsigned int rows; |
77 | unsigned int cols; | 80 | unsigned int cols; |
78 | unsigned int row_state[SAMSUNG_MAX_COLS]; | 81 | unsigned int row_state[SAMSUNG_MAX_COLS]; |
82 | #ifdef CONFIG_OF | ||
83 | int row_gpios[SAMSUNG_MAX_ROWS]; | ||
84 | int col_gpios[SAMSUNG_MAX_COLS]; | ||
85 | #endif | ||
79 | unsigned short keycodes[]; | 86 | unsigned short keycodes[]; |
80 | }; | 87 | }; |
81 | 88 | ||
82 | static int samsung_keypad_is_s5pv210(struct device *dev) | ||
83 | { | ||
84 | struct platform_device *pdev = to_platform_device(dev); | ||
85 | enum samsung_keypad_type type = | ||
86 | platform_get_device_id(pdev)->driver_data; | ||
87 | |||
88 | return type == KEYPAD_TYPE_S5PV210; | ||
89 | } | ||
90 | |||
91 | static void samsung_keypad_scan(struct samsung_keypad *keypad, | 89 | static void samsung_keypad_scan(struct samsung_keypad *keypad, |
92 | unsigned int *row_state) | 90 | unsigned int *row_state) |
93 | { | 91 | { |
94 | struct device *dev = keypad->input_dev->dev.parent; | ||
95 | unsigned int col; | 92 | unsigned int col; |
96 | unsigned int val; | 93 | unsigned int val; |
97 | 94 | ||
98 | for (col = 0; col < keypad->cols; col++) { | 95 | for (col = 0; col < keypad->cols; col++) { |
99 | if (samsung_keypad_is_s5pv210(dev)) { | 96 | if (keypad->type == KEYPAD_TYPE_S5PV210) { |
100 | val = S5PV210_KEYIFCOLEN_MASK; | 97 | val = S5PV210_KEYIFCOLEN_MASK; |
101 | val &= ~(1 << col) << 8; | 98 | val &= ~(1 << col) << 8; |
102 | } else { | 99 | } else { |
@@ -251,6 +248,126 @@ static void samsung_keypad_close(struct input_dev *input_dev) | |||
251 | samsung_keypad_stop(keypad); | 248 | samsung_keypad_stop(keypad); |
252 | } | 249 | } |
253 | 250 | ||
251 | #ifdef CONFIG_OF | ||
252 | static struct samsung_keypad_platdata *samsung_keypad_parse_dt( | ||
253 | struct device *dev) | ||
254 | { | ||
255 | struct samsung_keypad_platdata *pdata; | ||
256 | struct matrix_keymap_data *keymap_data; | ||
257 | uint32_t *keymap, num_rows = 0, num_cols = 0; | ||
258 | struct device_node *np = dev->of_node, *key_np; | ||
259 | unsigned int key_count = 0; | ||
260 | |||
261 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | ||
262 | if (!pdata) { | ||
263 | dev_err(dev, "could not allocate memory for platform data\n"); | ||
264 | return NULL; | ||
265 | } | ||
266 | |||
267 | of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows); | ||
268 | of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols); | ||
269 | if (!num_rows || !num_cols) { | ||
270 | dev_err(dev, "number of keypad rows/columns not specified\n"); | ||
271 | return NULL; | ||
272 | } | ||
273 | pdata->rows = num_rows; | ||
274 | pdata->cols = num_cols; | ||
275 | |||
276 | keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL); | ||
277 | if (!keymap_data) { | ||
278 | dev_err(dev, "could not allocate memory for keymap data\n"); | ||
279 | return NULL; | ||
280 | } | ||
281 | pdata->keymap_data = keymap_data; | ||
282 | |||
283 | for_each_child_of_node(np, key_np) | ||
284 | key_count++; | ||
285 | |||
286 | keymap_data->keymap_size = key_count; | ||
287 | keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL); | ||
288 | if (!keymap) { | ||
289 | dev_err(dev, "could not allocate memory for keymap\n"); | ||
290 | return NULL; | ||
291 | } | ||
292 | keymap_data->keymap = keymap; | ||
293 | |||
294 | for_each_child_of_node(np, key_np) { | ||
295 | u32 row, col, key_code; | ||
296 | of_property_read_u32(key_np, "keypad,row", &row); | ||
297 | of_property_read_u32(key_np, "keypad,column", &col); | ||
298 | of_property_read_u32(key_np, "linux,code", &key_code); | ||
299 | *keymap++ = KEY(row, col, key_code); | ||
300 | } | ||
301 | |||
302 | if (of_get_property(np, "linux,input-no-autorepeat", NULL)) | ||
303 | pdata->no_autorepeat = true; | ||
304 | if (of_get_property(np, "linux,input-wakeup", NULL)) | ||
305 | pdata->wakeup = true; | ||
306 | |||
307 | return pdata; | ||
308 | } | ||
309 | |||
310 | static void samsung_keypad_parse_dt_gpio(struct device *dev, | ||
311 | struct samsung_keypad *keypad) | ||
312 | { | ||
313 | struct device_node *np = dev->of_node; | ||
314 | int gpio, ret, row, col; | ||
315 | |||
316 | for (row = 0; row < keypad->rows; row++) { | ||
317 | gpio = of_get_named_gpio(np, "row-gpios", row); | ||
318 | keypad->row_gpios[row] = gpio; | ||
319 | if (!gpio_is_valid(gpio)) { | ||
320 | dev_err(dev, "keypad row[%d]: invalid gpio %d\n", | ||
321 | row, gpio); | ||
322 | continue; | ||
323 | } | ||
324 | |||
325 | ret = gpio_request(gpio, "keypad-row"); | ||
326 | if (ret) | ||
327 | dev_err(dev, "keypad row[%d] gpio request failed\n", | ||
328 | row); | ||
329 | } | ||
330 | |||
331 | for (col = 0; col < keypad->cols; col++) { | ||
332 | gpio = of_get_named_gpio(np, "col-gpios", col); | ||
333 | keypad->col_gpios[col] = gpio; | ||
334 | if (!gpio_is_valid(gpio)) { | ||
335 | dev_err(dev, "keypad column[%d]: invalid gpio %d\n", | ||
336 | col, gpio); | ||
337 | continue; | ||
338 | } | ||
339 | |||
340 | ret = gpio_request(gpio, "keypad-col"); | ||
341 | if (ret) | ||
342 | dev_err(dev, "keypad column[%d] gpio request failed\n", | ||
343 | col); | ||
344 | } | ||
345 | } | ||
346 | |||
347 | static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) | ||
348 | { | ||
349 | int cnt; | ||
350 | |||
351 | for (cnt = 0; cnt < keypad->rows; cnt++) | ||
352 | if (gpio_is_valid(keypad->row_gpios[cnt])) | ||
353 | gpio_free(keypad->row_gpios[cnt]); | ||
354 | |||
355 | for (cnt = 0; cnt < keypad->cols; cnt++) | ||
356 | if (gpio_is_valid(keypad->col_gpios[cnt])) | ||
357 | gpio_free(keypad->col_gpios[cnt]); | ||
358 | } | ||
359 | #else | ||
360 | static | ||
361 | struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev) | ||
362 | { | ||
363 | return NULL; | ||
364 | } | ||
365 | |||
366 | static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) | ||
367 | { | ||
368 | } | ||
369 | #endif | ||
370 | |||
254 | static int __devinit samsung_keypad_probe(struct platform_device *pdev) | 371 | static int __devinit samsung_keypad_probe(struct platform_device *pdev) |
255 | { | 372 | { |
256 | const struct samsung_keypad_platdata *pdata; | 373 | const struct samsung_keypad_platdata *pdata; |
@@ -262,7 +379,10 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
262 | unsigned int keymap_size; | 379 | unsigned int keymap_size; |
263 | int error; | 380 | int error; |
264 | 381 | ||
265 | pdata = pdev->dev.platform_data; | 382 | if (pdev->dev.of_node) |
383 | pdata = samsung_keypad_parse_dt(&pdev->dev); | ||
384 | else | ||
385 | pdata = pdev->dev.platform_data; | ||
266 | if (!pdata) { | 386 | if (!pdata) { |
267 | dev_err(&pdev->dev, "no platform data defined\n"); | 387 | dev_err(&pdev->dev, "no platform data defined\n"); |
268 | return -EINVAL; | 388 | return -EINVAL; |
@@ -321,6 +441,16 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
321 | keypad->stopped = true; | 441 | keypad->stopped = true; |
322 | init_waitqueue_head(&keypad->wait); | 442 | init_waitqueue_head(&keypad->wait); |
323 | 443 | ||
444 | if (pdev->dev.of_node) { | ||
445 | #ifdef CONFIG_OF | ||
446 | samsung_keypad_parse_dt_gpio(&pdev->dev, keypad); | ||
447 | keypad->type = of_device_is_compatible(pdev->dev.of_node, | ||
448 | "samsung,s5pv210-keypad"); | ||
449 | #endif | ||
450 | } else { | ||
451 | keypad->type = platform_get_device_id(pdev)->driver_data; | ||
452 | } | ||
453 | |||
324 | input_dev->name = pdev->name; | 454 | input_dev->name = pdev->name; |
325 | input_dev->id.bustype = BUS_HOST; | 455 | input_dev->id.bustype = BUS_HOST; |
326 | input_dev->dev.parent = &pdev->dev; | 456 | input_dev->dev.parent = &pdev->dev; |
@@ -363,6 +493,11 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
363 | if (error) | 493 | if (error) |
364 | goto err_free_irq; | 494 | goto err_free_irq; |
365 | 495 | ||
496 | if (pdev->dev.of_node) { | ||
497 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); | ||
498 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data); | ||
499 | devm_kfree(&pdev->dev, (void *)pdata); | ||
500 | } | ||
366 | return 0; | 501 | return 0; |
367 | 502 | ||
368 | err_free_irq: | 503 | err_free_irq: |
@@ -372,6 +507,7 @@ err_free_irq: | |||
372 | platform_set_drvdata(pdev, NULL); | 507 | platform_set_drvdata(pdev, NULL); |
373 | err_put_clk: | 508 | err_put_clk: |
374 | clk_put(keypad->clk); | 509 | clk_put(keypad->clk); |
510 | samsung_keypad_dt_gpio_free(keypad); | ||
375 | err_unmap_base: | 511 | err_unmap_base: |
376 | iounmap(keypad->base); | 512 | iounmap(keypad->base); |
377 | err_free_mem: | 513 | err_free_mem: |
@@ -398,6 +534,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) | |||
398 | free_irq(keypad->irq, keypad); | 534 | free_irq(keypad->irq, keypad); |
399 | 535 | ||
400 | clk_put(keypad->clk); | 536 | clk_put(keypad->clk); |
537 | samsung_keypad_dt_gpio_free(keypad); | ||
401 | 538 | ||
402 | iounmap(keypad->base); | 539 | iounmap(keypad->base); |
403 | kfree(keypad); | 540 | kfree(keypad); |
@@ -518,6 +655,17 @@ static const struct dev_pm_ops samsung_keypad_pm_ops = { | |||
518 | samsung_keypad_runtime_resume, NULL) | 655 | samsung_keypad_runtime_resume, NULL) |
519 | }; | 656 | }; |
520 | 657 | ||
658 | #ifdef CONFIG_OF | ||
659 | static const struct of_device_id samsung_keypad_dt_match[] = { | ||
660 | { .compatible = "samsung,s3c6410-keypad" }, | ||
661 | { .compatible = "samsung,s5pv210-keypad" }, | ||
662 | {}, | ||
663 | }; | ||
664 | MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); | ||
665 | #else | ||
666 | #define samsung_keypad_dt_match NULL | ||
667 | #endif | ||
668 | |||
521 | static struct platform_device_id samsung_keypad_driver_ids[] = { | 669 | static struct platform_device_id samsung_keypad_driver_ids[] = { |
522 | { | 670 | { |
523 | .name = "samsung-keypad", | 671 | .name = "samsung-keypad", |
@@ -536,6 +684,7 @@ static struct platform_driver samsung_keypad_driver = { | |||
536 | .driver = { | 684 | .driver = { |
537 | .name = "samsung-keypad", | 685 | .name = "samsung-keypad", |
538 | .owner = THIS_MODULE, | 686 | .owner = THIS_MODULE, |
687 | .of_match_table = samsung_keypad_dt_match, | ||
539 | .pm = &samsung_keypad_pm_ops, | 688 | .pm = &samsung_keypad_pm_ops, |
540 | }, | 689 | }, |
541 | .id_table = samsung_keypad_driver_ids, | 690 | .id_table = samsung_keypad_driver_ids, |