Hi Dmitry, > > Sounds good, patch updated again, with attachment. > > >From 76776dfc468dc35f0d8394a2331e1a91f390e642 Mon Sep 17 00:00:00 2001 > From: Marek Vasut <marek.vasut@xxxxxxxxx> > Date: Thu, 7 May 2009 15:49:32 +0800 > Subject: [PATCH] input: add support for generic GPIO-based matrix keypad > Could you please review this patch and see if it can be acked as merge window will open in couple of days? Thanks. > Original patch by Marek Vasut, modified by Eric in: > > 1. use delayed work to simplify the debouncing process > > 2. build keycode array for fast lookup > > 3. combine col_polarity/row_polarity into a single active_low > field (are there some cases where the GPIOs are externally > connected with an inverter and thus causing two different > polarity requirement??) > > 4. use a generic bit array based XOR algorithm to detect key > press/release, which should make the column assertion time > shorter and code a bit cleaner > > 5. remove the ALT_FN handling, which is no way generic, ALT_FN > key should be treated as no different from other keys, and > translation can be done by commands like 'loadkeys'. > > 6. explicitly disable row IRQs and flush potential pending work, > and schedule an immediate scan after resuming as suggested > by Uli Luckas > > 7. incorporate review comments from many others > > Patch tested on Littleton/PXA310 (though PXA310 has a dedicate keypad > controller, I have to configure those pins as generic GPIO to use this > driver, works quite well, though ;-), and Sharp Zaurus model SL-C7x0 > and SL-C1000. > > Signed-off-by: Marek Vasut <marek.vasut@xxxxxxxxx> > Reviewed-by: Trilok Soni <soni.trilok@xxxxxxxxx> > Reviewed-by: Uli Luckas <u.luckas@xxxxxxx> > Reviewed-by: Russell King <linux@xxxxxxxxxxxxxxxx> > Reviewed-by: Robert Jarzmik <robert.jarzmik@xxxxxxx> > Signed-off-by: Eric Miao <eric.miao@xxxxxxxxxxx> > --- > drivers/input/keyboard/Kconfig | 10 + > drivers/input/keyboard/Makefile | 1 + > drivers/input/keyboard/matrix_keypad.c | 379 ++++++++++++++++++++++++++++++++ > include/linux/input/matrix_keypad.h | 34 +++ > 4 files changed, 424 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/keyboard/matrix_keypad.c > create mode 100644 include/linux/input/matrix_keypad.h > > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig > index ea2638b..6b9f89c 100644 > --- a/drivers/input/keyboard/Kconfig > +++ b/drivers/input/keyboard/Kconfig > @@ -332,4 +332,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 > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile > index 36351e1..1349408 100644 > --- a/drivers/input/keyboard/Makefile > +++ b/drivers/input/keyboard/Makefile > @@ -28,3 +28,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 > diff --git a/drivers/input/keyboard/matrix_keypad.c > b/drivers/input/keyboard/matrix_keypad.c > new file mode 100644 > index 0000000..2078d64 > --- /dev/null > +++ b/drivers/input/keyboard/matrix_keypad.c > @@ -0,0 +1,379 @@ > +/* > + * drivers/input/keyboard/matrix_keypad.c > + * > + * GPIO driven matrix keyboard driver > + * > + * Copyright (c) 2008 Marek Vasut <marek.vasut@xxxxxxxxx> > + * > + * Based on corgikbd.c > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + */ > + > +#include <linux/delay.h> > +#include <linux/platform_device.h> > +#include <linux/init.h> > +#include <linux/input.h> > +#include <linux/irq.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/module.h> > +#include <linux/gpio.h> > +#include <linux/input/matrix_keypad.h> > + > +struct matrix_keypad { > + struct matrix_keypad_platform_data *pdata; > + struct input_dev *input_dev; > + > + uint32_t last_key_state[MATRIX_MAX_COLS]; > + uint32_t *keycodes; > + struct delayed_work work; > +}; > + > +static int __devinit build_keycodes(struct matrix_keypad *keypad) > +{ > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + struct input_dev *input_dev = keypad->input_dev; > + uint32_t *key; > + int i; > + > + keypad->keycodes = kzalloc(MATRIX_MAX_KEYS * sizeof(int), GFP_KERNEL); > + if (keypad->keycodes == NULL) > + return -ENOMEM; > + > + key = &pdata->key_map[0]; > + for (i = 0; i < pdata->key_map_size; i++, key++) { > + keypad->keycodes[KEY_ROWCOL(*key)] = KEY_VAL(*key); > + set_bit(KEY_VAL(*key), input_dev->keybit); > + } > + return 0; > +} > + > +static unsigned int lookup_keycode(struct matrix_keypad *keypad, > + int row, int col) > +{ > + return keypad->keycodes[(row << 4) + col]; > +} > + > +/* NOTE: normally the GPIO has to be put into HiZ when de-activated to cause > + * minimum side effect when scanning other columns, here it is configured to > + * be input, and it should work on most platforms. > + */ > +static void __activate_col(struct matrix_keypad_platform_data *pdata, > + int col, int on) > +{ > + int level_on = !pdata->active_low; > + > + if (on) > + gpio_direction_output(pdata->col_gpios[col], level_on); > + else { > + gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); > + gpio_direction_input(pdata->col_gpios[col]); > + } > +} > + > +static void activate_all_cols(struct matrix_keypad_platform_data > *pdata, int on) > +{ > + int col; > + > + for (col = 0; col < pdata->num_col_gpios; col++) > + __activate_col(pdata, col, on); > +} > + > +static void activate_col(struct matrix_keypad_platform_data *pdata, > + int col, int on) > +{ > + __activate_col(pdata, col, on); > + > + if (on && pdata->col_scan_delay_us) > + udelay(pdata->col_scan_delay_us); > +} > + > +static int row_asserted(struct matrix_keypad_platform_data *pdata, int row) > +{ > + return gpio_get_value_cansleep(pdata->row_gpios[row]) ? > + !pdata->active_low : pdata->active_low; > +} > + > +static void enable_row_irqs(struct matrix_keypad *keypad) > +{ > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + int i; > + > + for (i = 0; i < pdata->num_row_gpios; i++) > + enable_irq(gpio_to_irq(pdata->row_gpios[i])); > +} > + > +static void disable_row_irqs(struct matrix_keypad *keypad) > +{ > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + int i; > + > + for (i = 0; i < pdata->num_row_gpios; i++) > + disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); > +} > + > +/* > + * This gets the keys from keyboard and reports it to input subsystem > + */ > +static void matrix_keypad_scan(struct work_struct *work) > +{ > + struct matrix_keypad *keypad = > + container_of(work, struct matrix_keypad, work.work); > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + uint32_t new_state[MATRIX_MAX_COLS]; > + int row, col; > + > + /* de-activate all columns for scanning */ > + activate_all_cols(pdata, 0); > + > + memset(new_state, 0, sizeof(new_state)); > + > + /* assert each column and read the row status out */ > + for (col = 0; col < pdata->num_col_gpios; col++) { > + > + activate_col(pdata, col, 1); > + > + for (row = 0; row < pdata->num_row_gpios; row++) > + new_state[col] |= row_asserted(pdata, row) ? > + (1 << row) : 0; > + activate_col(pdata, col, 0); > + } > + > + for (col = 0; col < pdata->num_col_gpios; col++) { > + uint32_t bits_changed; > + > + bits_changed = keypad->last_key_state[col] ^ new_state[col]; > + if (bits_changed == 0) > + continue; > + > + for (row = 0; row < pdata->num_row_gpios; row++) { > + if ((bits_changed & (1 << row)) == 0) > + continue; > + > + input_report_key(keypad->input_dev, > + lookup_keycode(keypad, row, col), > + new_state[col] & (1 << row)); > + } > + } > + input_sync(keypad->input_dev); > + memcpy(keypad->last_key_state, new_state, sizeof(new_state)); > + > + activate_all_cols(pdata, 1); > + enable_row_irqs(keypad); > +} > + > +static irqreturn_t matrix_keypad_interrupt(int irq, void *id) > +{ > + struct matrix_keypad *keypad = id; > + > + disable_row_irqs(keypad); > + schedule_delayed_work(&keypad->work, > + msecs_to_jiffies(keypad->pdata->debounce_ms)); > + return IRQ_HANDLED; > +} > + > +#ifdef CONFIG_PM > +static int matrix_keypad_suspend(struct platform_device *pdev, > + pm_message_t state) > +{ > + struct matrix_keypad *keypad = platform_get_drvdata(pdev); > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + int i; > + > + disable_row_irqs(keypad); > + flush_work(&keypad->work.work); > + > + if (device_may_wakeup(&pdev->dev)) > + for (i = 0; i < pdata->num_row_gpios; i++) > + enable_irq_wake(gpio_to_irq(pdata->row_gpios[i])); > + > + return 0; > +} > + > +static int matrix_keypad_resume(struct platform_device *pdev) > +{ > + struct matrix_keypad *keypad = platform_get_drvdata(pdev); > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + int i; > + > + if (device_may_wakeup(&pdev->dev)) > + for (i = 0; i < pdata->num_row_gpios; i++) > + disable_irq_wake(gpio_to_irq(pdata->row_gpios[i])); > + > + /* Uli Luckas: schedule an immediate key scan as all key state changes > + * were lost while the device was suspended, columns will be activated > + * and IRQs be enabled after the scan. > + */ > + schedule_delayed_work(&keypad->work, 0); > + return 0; > +} > +#else > +#define matrix_keypad_suspend NULL > +#define matrix_keypad_resume NULL > +#endif > + > +static int __devinit init_matrix_gpio(struct matrix_keypad *keypad) > +{ > + struct matrix_keypad_platform_data *pdata = keypad->pdata; > + int i, err = -EINVAL; > + > + /* initialized strobe lines as outputs, activated */ > + for (i = 0; i < pdata->num_col_gpios; i++) { > + err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); > + if (err) { > + pr_err("failed to request GPIO%d for COL%d\n", > + pdata->col_gpios[i], i); > + goto err_free_cols; > + } > + > + gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); > + } > + > + for (i = 0; i < pdata->num_row_gpios; i++) { > + err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); > + if (err) { > + pr_err("failed to request GPIO%d for ROW%d\n", > + pdata->row_gpios[i], i); > + goto err_free_rows; > + } > + > + gpio_direction_input(pdata->row_gpios[i]); > + } > + > + for (i = 0; i < pdata->num_row_gpios; i++) { > + err = request_irq(gpio_to_irq(pdata->row_gpios[i]), > + matrix_keypad_interrupt, IRQF_DISABLED | > + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, > + "matrix-keypad", keypad); > + if (err) { > + pr_err("Unable to acquire interrupt for GPIO line %i\n", > + pdata->row_gpios[i]); > + goto err_free_irqs; > + } > + } > + return 0; > + > +err_free_irqs: > + for (i = i - 1; i >= 0; i--) > + free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); > + > +err_free_rows: > + for (i = i - 1; i >= 0; i--) > + gpio_free(pdata->row_gpios[i]); > + > +err_free_cols: > + for (i = i - 1; i >= 0; i--) > + gpio_free(pdata->col_gpios[i]); > + > + return err; > +} > + > +static int __devinit matrix_keypad_probe(struct platform_device *pdev) > +{ > + struct matrix_keypad_platform_data *pdata; > + struct matrix_keypad *keypad; > + struct input_dev *input_dev; > + int err = -ENOMEM; > + > + pdata = pdev->dev.platform_data; > + if (pdata == NULL) { > + dev_err(&pdev->dev, "no platform data defined\n"); > + return -EINVAL; > + } > + > + keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); > + if (keypad == NULL) > + return -ENOMEM; > + > + input_dev = input_allocate_device(); > + if (!input_dev) > + goto err_free_keypad; > + > + platform_set_drvdata(pdev, keypad); > + > + keypad->input_dev = input_dev; > + keypad->pdata = pdata; > + INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); > + > + 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); > + > + err = build_keycodes(keypad); > + if (err) > + goto err_free_input; > + > + err = input_register_device(keypad->input_dev); > + if (err) > + goto err_free_keycodes; > + > + err = init_matrix_gpio(keypad); > + if (err) > + goto err_unregister; > + > + device_init_wakeup(&pdev->dev, 1); > + return 0; > + > +err_unregister: > + input_unregister_device(input_dev); > +err_free_keycodes: > + kfree(keypad->keycodes); > +err_free_input: > + input_free_device(input_dev); > +err_free_keypad: > + kfree(keypad); > + return err; > +} > + > +static int __devexit matrix_keypad_remove(struct platform_device *pdev) > +{ > + struct matrix_keypad *keypad = platform_get_drvdata(pdev); > + int i; > + > + for (i = 0; i < keypad->pdata->num_row_gpios; i++) { > + free_irq(gpio_to_irq(keypad->pdata->row_gpios[i]), keypad); > + gpio_free(keypad->pdata->row_gpios[i]); > + } > + > + for (i = 0; i < keypad->pdata->num_col_gpios; i++) > + gpio_free(keypad->pdata->col_gpios[i]); > + > + input_unregister_device(keypad->input_dev); > + kfree(keypad->keycodes); > + kfree(keypad); > + return 0; > +} > + > +static struct platform_driver matrix_keypad_driver = { > + .probe = matrix_keypad_probe, > + .remove = __devexit_p(matrix_keypad_remove), > + .suspend = matrix_keypad_suspend, > + .resume = matrix_keypad_resume, > + .driver = { > + .name = "matrix-keypad", > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init 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 <marek.vasut@xxxxxxxxx>"); > +MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:matrix-keypad"); > diff --git a/include/linux/input/matrix_keypad.h > b/include/linux/input/matrix_keypad.h > new file mode 100644 > index 0000000..8b661cb > --- /dev/null > +++ b/include/linux/input/matrix_keypad.h > @@ -0,0 +1,34 @@ > +#ifndef _MATRIX_KEYPAD_H > +#define _MATRIX_KEYPAD_H > + > +#include <linux/input.h> > + > +#define MATRIX_MAX_ROWS 16 > +#define MATRIX_MAX_COLS 16 > +#define MATRIX_MAX_KEYS (MATRIX_MAX_ROWS * MATRIX_MAX_COLS) > + > +struct matrix_keypad_platform_data { > + /* scancode map for the matrix keys */ > + uint32_t *key_map; > + int key_map_size; > + > + unsigned row_gpios[MATRIX_MAX_ROWS]; > + unsigned col_gpios[MATRIX_MAX_COLS]; > + int num_row_gpios; > + int num_col_gpios; > + > + unsigned int active_low; > + unsigned int col_scan_delay_us; > + > + /* key debounce interval in milli-second */ > + unsigned int debounce_ms; > +}; > + > +#define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 28) |\ > + (((col) & (MATRIX_MAX_COLS - 1)) << 24) |\ > + (val & 0xffffff)) > + > +#define KEY_ROWCOL(k) (((k) >> 24) & 0xff) > +#define KEY_VAL(k) ((k) & 0xffffff) > + > +#endif /* _MATRIX_KEYPAD_H */ > -- > 1.6.0.4 > -- ---Trilok Soni http://triloksoni.wordpress.com http://www.linkedin.com/in/triloksoni -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html