GPIO driven matrix keypad driver
Subsystems (2)
PATCH 1/1 GPIO driven matrix keypad driver
Hi,
the following patch adds the generic GPIO driven matrix keypad driver. I tested this on two devices I had available - palmtc and palmt3. It should be able to replace corgikbd.c later as well, but I dont have that device. And since this is platform independent driver, any chip that makes it's GPIOs available through gpiolib should be OK with this driver as well. Please consider applying.
Thanks
Marek
From fd96a2a7df64e5d26a757bd91b56bcefdbbc543e Mon Sep 17 00:00:00 2001
From: Marek Vasut <[email protected]>
Date: Sat, 16 Aug 2008 18:53:05 +0200
Subject: [PATCH] Matrix: add GPIO driven matrix keypad driver
--- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/matrix_keypad.c | 346 ++++++++++++++++++++++++++++++++ include/linux/matrix_keypad.h | 39 ++++ 4 files changed, 396 insertions(+), 0 deletions(-) create mode 100644 drivers/input/keyboard/matrix_keypad.c create mode 100644 include/linux/matrix_keypad.h
drivers/input/keyboard/Kconfig +10 -0
@@ -323,4 +323,14 @@ config KEYBOARD_SH_KEYSC To compile this driver as a module, choose M here: the module will be called sh_keysc. + +config KEYBOARD_MATRIX + tristate "GPIO driven matrix keypad support" + depends on GENERIC_GPIO + help + Enable support for GPIO driven matrix keypad + + To compile this driver as a module, choose M here: the + module will be called matrix_keypad. + endif
drivers/input/keyboard/Makefile +1 -0
@@ -27,3 +27,4 @@ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o +obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
drivers/input/keyboard/matrix_keypad.c new file +346 -0
346 lines not on this page · raw message
include/linux/matrix_keypad.h new file +39 -0
@@ -0,0 +1,39 @@ +#ifndef _MATRIX_KEYPAD_H +#define _MATRIX_KEYPAD_H + +#include <linux/input.h> + +struct matrix_keypad_platform_data { + +/* code map for the matrix keys */ +unsigned int *map; +int map_size; + +unsigned int *col_gpio; +int col_gpio_size; +unsigned int *row_gpio; +int row_gpio_size; + +/* line polarities */ +unsigned int col_polarity; +unsigned int row_polarity; + +/* key debounce interval */ +unsigned int scan_delay; + +/* GPIO level change delay */ +unsigned int gpio_delay; + +/* Callbacks */ +int (*suspend)(struct platform_device *dev,pm_message_t state); +int (*resume)(struct platform_device *dev); +}; + +#define KEY (row, col, val) (((row) << 28) | ((col) << 24) | (val & 0xfff)) + +/* key not connected */ +#define KEY_NC 0xfff +/* key to be pressed with shift */ +#define KEY_SFT 0x800 + +#endif /* _MATRIX_KEYPAD_H */
Signature · 1 line
1.5.6
Dmitry Torokhov
maintainerHi Marek,
Quoted 8 lines · Marek Vasut
Hi,
the following patch adds the generic GPIO driven matrix keypad driver. I tested this on two devices I had available - palmtc and palmt3. It should be able to replacecorgikbd.clater as well, but I dont have that device. And since this is platform independent driver, any chip that makes it's GPIOs available through gpiolib should be OK with this driver as well. Please consider applying.
Thank you for your patch, I have a couple of comments:
drivers/input/keyboard/matrix_keypad.c line 37
Quoted 11 lines
drivers/input/keyboard/matrix_keypad.c new file +11
-0+ +struct matrix_keypad { +struct matrix_keypad_platform_data *pdata; +struct input_dev *input_dev; + +struct task_struct *kthread; +unsigned char irqs; + +/* on, off, alt flags */ +unsigned char *flags; +};
It would be great if the driver supported per-device keymaps that coudl be altered from userspace via setkeycode/getkeycode.
Quoted 36 lines
drivers/input/keyboard/matrix_keypad.c new file +36
-0+ +/* +* Lookup the key in our keymap +*/ +static unsigned int matrix_keypad_lookup (int row,int col, +struct matrix_keypad *keypad) +{ +int i; +struct matrix_keypad_platform_data *pdata = keypad->pdata; + +for (i =0 ; i < pdata->map_size; i++) +if ((row == ((pdata->map[i]>>28 ) &0xf )) && + (col == ((pdata->map[i]>>24 ) &0xf ))) +return pdata->map[i] &0xfff ; +return 0xfff ; +} + +/* +* This analyzes the key and reports it to the input subsystem +*/ +static unsigned int matrix_keypad_process (struct matrix_keypad *keypad, +int i,int j) +{ +struct matrix_keypad_platform_data *pdata = keypad->pdata; +int pressed =0 , key =0 , gpio; + + gpio =gpio_get_value (pdata->col_gpio[j]); +if (pdata->col_polarity) + gpio = !gpio; +if (gpio) + pressed++; + key =matrix_keypad_lookup (i, j, keypad); +if (key ==0xfff ) +return pressed; +if (key &KEY_SFT ) +input_report_key (keypad->input_dev,KEY_LEFTSHIFT ,1 );
drivers/input/keyboard/matrix_keypad.c line 83
Quoted 10 lines
drivers/input/keyboard/matrix_keypad.c new file +10
-0+if (gpio) { +input_report_key (keypad->input_dev, key &0x7ff ,1 ); + keypad->flags[key /8 ] |=1 << (key %8 ); + }else if ((keypad->flags[key /8 ] & (1 << (key %8 ))) && !gpio) { +input_report_key (keypad->input_dev, key &0x7ff ,0 ); + keypad->flags[key /8 ] &= ~(1 << (key %8 )); + } +if (key &KEY_SFT ) +input_report_key (keypad->input_dev,KEY_LEFTSHIFT ,0 ); +
This is extremely fragile and will not work on certain keymaps that have upper and lower register switched around.
Quoted 7 lines
drivers/input/keyboard/matrix_keypad.c new file +7
-0+return pressed; +} + +/* +* This gets the keys from keyboard +*/ +static int matrix_keypad_thread (void *arg)
drivers/input/keyboard/matrix_keypad.c line 100
Quoted 10 lines
drivers/input/keyboard/matrix_keypad.c new file +10
-0+{ +int i, j, pressed; +struct matrix_keypad *keypad = arg; +struct matrix_keypad_platform_data *pdata = keypad->pdata; + +set_freezable (); +while (!kthread_should_stop ()) { +if (try_to_freeze ()) +continue ; +
Hmm, a thread per device seems a bit wasteful, I wonder if we could make input-polldev framefork work here...
Quoted 81 lines
drivers/input/keyboard/matrix_keypad.c new file +81
-0+ pressed =0 ; + +/* set all unreadable */ +for (i =0 ; i < pdata->row_gpio_size; i++) +gpio_set_value (pdata->row_gpio[i], pdata->row_polarity); + +/* read the keypad matrix */ +for (i =0 ; i < pdata->row_gpio_size; i++) { +gpio_set_value (pdata->row_gpio[i], + !pdata->row_polarity); +udelay (pdata->gpio_delay); + +for (j =0 ; j < pdata->col_gpio_size; j++) + pressed +=matrix_keypad_process (keypad, i, j); + +gpio_set_value (pdata->row_gpio[i], pdata->row_polarity); +udelay (pdata->gpio_delay); + } + +/* set all readable */ +for (i =0 ; i < pdata->row_gpio_size; i++) +gpio_set_value (pdata->row_gpio[i], + !pdata->row_polarity); + + +/* report to input subsystem */ +input_sync (keypad->input_dev); + +if (pressed) +msleep (pdata->scan_delay); +else { +/* reenable interrupts */ +if (!keypad->irqs) { +for (i =0 ; i < pdata->col_gpio_size; i++) +enable_irq (gpio_to_irq ( + pdata->col_gpio[i])); + keypad->irqs =1 ; + } +schedule (); + } + } +return 0 ; +} + +/* +* Interrupt handler +*/ +static irqreturn_t matrix_keypad_interrupt (int irq,void *id) +{ +struct matrix_keypad *keypad = id; +unsigned long flags; +int i; + +/* disable interrupts as soon as possible */ +local_irq_save (flags); +if (keypad->irqs) { +for (i =0 ; i < keypad->pdata->col_gpio_size; i++) +disable_irq (gpio_to_irq (keypad->pdata->col_gpio[i])); + keypad->irqs =0 ; +wake_up_process (keypad->kthread); + } +local_irq_restore (flags); + +return IRQ_HANDLED ; +} + +#ifdef CONFIG_PM +static int matrix_keypad_suspend (struct platform_device *dev, +pm_message_t state) +{ +struct matrix_keypad *keypad =platform_get_drvdata (dev); + +return keypad->pdata->suspend ? + keypad->pdata->suspend (dev, state) :0 ; +} + +static int matrix_keypad_resume (struct platform_device *dev) +{ +struct matrix_keypad *keypad =platform_get_drvdata (dev); + +return keypad->pdata->resume ? keypad->pdata->resume (dev) :0 ;
drivers/input/keyboard/matrix_keypad.c line 191
Quoted 10 lines
drivers/input/keyboard/matrix_keypad.c new file +10
-0+} +#else +#define matrix_keypad_suspend NULL +#define matrix_keypad_resume NULL +#endif + +/* +* Everything starts here +*/ +static int __init matrix_keypad_probe (struct platform_device *pdev)
__devinit, not __init.
Quoted 14 lines
drivers/input/keyboard/matrix_keypad.c new file +14
-0+{ +struct matrix_keypad *keypad; +struct input_dev *input_dev; +int i, err = -ENOMEM ; + + keypad =kzalloc (sizeof (struct matrix_keypad ),GFP_KERNEL ); + input_dev =input_allocate_device (); +if (!keypad || !input_dev) + goto fail; + +platform_set_drvdata (pdev, keypad); + + keypad->input_dev = input_dev; + keypad->pdata = pdev->dev.platform_data;
drivers/input/keyboard/matrix_keypad.c line 215
Quoted 10 lines
drivers/input/keyboard/matrix_keypad.c new file +10
-0+ keypad->flags =kzalloc (4 *KEY_CNT ,GFP_KERNEL ); +if (!keypad->flags) + goto fail; + + input_dev->name = pdev->name; + input_dev->id.bustype =BUS_HOST ; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0 ] =BIT_MASK (EV_KEY ) |BIT_MASK (EV_REP ) | +BIT_MASK (EV_PWR ) |BIT_MASK (EV_SW );
I don't see where use setting dev->swbit and it does not send/receive EV_PVR either so why do you set these bits?
Quoted 62 lines
drivers/input/keyboard/matrix_keypad.c new file +62
-0+ input_dev->keycodesize =sizeof (unsigned int ); + +for (i =0 ; i < keypad->pdata->map_size; i++) { +if ((keypad->pdata->map[i] &0xfff ) !=0xfff ) +set_bit ((keypad->pdata->map[i] &0xfff ), + input_dev->keybit); +if (((keypad->pdata->map[i] >>12 ) &0xfff ) !=0xfff ) +set_bit ((keypad->pdata->map[i] >>12 ) &0xfff , + input_dev->keybit); + } + + err =input_register_device (keypad->input_dev); +if (err) + goto fail; + +for (i =0 ; i < keypad->pdata->col_gpio_size; i++) { + err =gpio_request (keypad->pdata->col_gpio[i],"KBD_IRQ" ); +if (err) + goto gpio_err; + err =gpio_direction_input (keypad->pdata->col_gpio[i]); +if (err) { +gpio_free (keypad->pdata->col_gpio[i]); + goto gpio_err; + } +if (request_irq (gpio_to_irq (keypad->pdata->col_gpio[i]), + matrix_keypad_interrupt,IRQF_DISABLED | +IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING | +IRQF_SAMPLE_RANDOM , +"matrix-keypad" , keypad)) { +printk (KERN_ERR "Unable to acquire interrupt" +" for GPIO line %i\n" , + keypad->pdata->col_gpio[i]); +gpio_free (keypad->pdata->col_gpio[i]); + goto gpio_err; + } + } + +/* Set strobe lines as outputs, low */ +for (i =0 ; i < keypad->pdata->row_gpio_size; i++) { + err =gpio_request (keypad->pdata->row_gpio[i],"KBD_LINE" ); +if (err) + goto gpio_err2; + err =gpio_direction_output (keypad->pdata->row_gpio[i], + !keypad->pdata->row_polarity); +if (err) { +gpio_free (keypad->pdata->row_gpio[i]); + goto gpio_err2; + } + } + keypad->irqs =1 ; + keypad->kthread =kthread_run (matrix_keypad_thread, keypad,"matrix" ); +if (IS_ERR (keypad->kthread)) + goto thread_err; + +return 0 ; + +thread_err: + i = keypad->pdata->row_gpio_size; +gpio_err2: +for (i = i-1 ; i >=0 ; i--) +gpio_free (keypad->pdata->row_gpio[i]); +for (i =0 ; i < keypad->pdata->col_gpio_size; i++) {
drivers/input/keyboard/matrix_keypad.c line 287
Quoted 10 lines
drivers/input/keyboard/matrix_keypad.c new file +10
-0+free_irq (gpio_to_irq (keypad->pdata->col_gpio[i]), keypad); +gpio_free (keypad->pdata->col_gpio[i]); + } + goto fail; +gpio_err: +for (i = i-1 ; i >=0 ; i--) { +free_irq (gpio_to_irq (keypad->pdata->col_gpio[i]), keypad); +gpio_free (keypad->pdata->col_gpio[i]); + } +fail:input_free_device (input_dev);
Error handling is incomplete.. After device was registered it needs to be unregistered (without calling input_free_device).
drivers/input/keyboard/matrix_keypad.c line 296
Quoted 9 lines
drivers/input/keyboard/matrix_keypad.c new file +9
-0+kfree (keypad); +return err; +} + +/* +* Everything ends here +*/ +static int matrix_keypad_remove (struct platform_device *pdev) +{
This one can be marked __devexit.
Quoted 15 lines
drivers/input/keyboard/matrix_keypad.c new file +15
-0+int i; +struct matrix_keypad *keypad =platform_get_drvdata (pdev); + +kthread_stop (keypad->kthread); + +for (i =0 ; i < keypad->pdata->col_gpio_size; i++) { +free_irq (gpio_to_irq (keypad->pdata->col_gpio[i]), + keypad); +gpio_free (keypad->pdata->col_gpio[i]); + } + +for (i =0 ; i < keypad->pdata->row_gpio_size; i++) +gpio_free (keypad->pdata->row_gpio[i]); + +input_unregister_device (keypad->input_dev);
drivers/input/keyboard/matrix_keypad.c line 321
Quoted 10 lines
drivers/input/keyboard/matrix_keypad.c new file +10
-0+ +kfree (keypad->flags); +kfree (keypad); + +return 0 ; +} + +static struct platform_driver matrix_keypad_driver = { + .probe = matrix_keypad_probe, + .remove = matrix_keypad_remove,
__devexit_p().
Quoted 73 lines
drivers/input/keyboard/matrix_keypad.c new file +25
-0+ .suspend = matrix_keypad_suspend, + .resume = matrix_keypad_resume, + .driver = { + .name = "matrix-keypad", + .owner = THIS_MODULE, + }, +}; + +static int __devinit matrix_keypad_init (void ) +{ +return platform_driver_register (&matrix_keypad_driver); +} + +static void __exit matrix_keypad_exit(void) +{ +platform_driver_unregister (&matrix_keypad_driver); +} + +module_init (matrix_keypad_init); +module_exit (matrix_keypad_exit); + +MODULE_AUTHOR ("Marek Vasut <[email protected]>" ); +MODULE_DESCRIPTION ("GPIO Driven Matrix Keypad Driver" ); +MODULE_LICENSE ("GPL v2" ); +MODULE_ALIAS ("platform:matrix-keypad" );include/linux/matrix_keypad.h new file +39
-0@@ -0,0 +1,39 @@ +#ifndef _MATRIX_KEYPAD_H +#define _MATRIX_KEYPAD_H + +#include <linux/input.h> + +struct matrix_keypad_platform_data { + +/* code map for the matrix keys */ +unsigned int *map; +int map_size; + +unsigned int *col_gpio; +int col_gpio_size; +unsigned int *row_gpio; +int row_gpio_size; + +/* line polarities */ +unsigned int col_polarity; +unsigned int row_polarity; + +/* key debounce interval */ +unsigned int scan_delay; + +/* GPIO level change delay */ +unsigned int gpio_delay; + +/* Callbacks */ +int (*suspend)(struct platform_device *dev,pm_message_t state); +int (*resume)(struct platform_device *dev); +}; + +#define KEY (row, col, val) (((row) << 28) | ((col) << 24) | (val & 0xfff)) + +/* key not connected */ +#define KEY_NC 0xfff +/* key to be pressed with shift */ +#define KEY_SFT 0x800 + +#endif /* _MATRIX_KEYPAD_H */ Signature · 1 line
1.5.6
Signature · 1 line
Dmitry