summaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/Kconfig294
-rw-r--r--drivers/input/keyboard/Makefile33
-rw-r--r--drivers/input/keyboard/atkbd.c32
-rw-r--r--drivers/input/keyboard/gpio_keys.c33
-rw-r--r--drivers/input/keyboard/matrix_keypad.c453
5 files changed, 677 insertions, 168 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 9d8f796c6745..a6b989a9dc07 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -12,6 +12,42 @@ menuconfig INPUT_KEYBOARD
if INPUT_KEYBOARD
+config KEYBOARD_AAED2000
+ tristate "AAED-2000 keyboard"
+ depends on MACH_AAED2000
+ select INPUT_POLLDEV
+ default y
+ help
+ Say Y here to enable the keyboard on the Agilent AAED-2000
+ development board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aaed2000_kbd.
+
+config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+ help
+ Say Y here if you are running Linux on any AMIGA and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amikbd.
+
+config ATARI_KBD_CORE
+ bool
+
+config KEYBOARD_ATARI
+ tristate "Atari keyboard"
+ depends on ATARI
+ select ATARI_KBD_CORE
+ help
+ Say Y here if you are running Linux on any Atari and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atakbd.
+
config KEYBOARD_ATKBD
tristate "AT keyboard" if EMBEDDED || !X86
default y
@@ -68,69 +104,14 @@ config KEYBOARD_ATKBD_RDI_KEYCODES
right-hand column will be interpreted as the key shown in the
left-hand column.
-config KEYBOARD_SUNKBD
- tristate "Sun Type 4 and Type 5 keyboard"
- select SERIO
- help
- Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
- connected either to the Sun keyboard connector or to an serial
- (RS-232) port via a simple adapter.
-
- To compile this driver as a module, choose M here: the
- module will be called sunkbd.
-
-config KEYBOARD_LKKBD
- tristate "DECstation/VAXstation LK201/LK401 keyboard"
- select SERIO
- help
- Say Y here if you want to use a LK201 or LK401 style serial
- keyboard. This keyboard is also useable on PCs if you attach
- it with the inputattach program. The connector pinout is
- described within lkkbd.c.
-
- To compile this driver as a module, choose M here: the
- module will be called lkkbd.
-
-config KEYBOARD_LOCOMO
- tristate "LoCoMo Keyboard Support"
- depends on SHARP_LOCOMO && INPUT_KEYBOARD
- help
- Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
-
- To compile this driver as a module, choose M here: the
- module will be called locomokbd.
-
-config KEYBOARD_XTKBD
- tristate "XT keyboard"
- select SERIO
- help
- Say Y here if you want to use the old IBM PC/XT keyboard (or
- compatible) on your system. This is only possible with a
- parallel port keyboard adapter, you cannot connect it to the
- keyboard port on a PC that runs Linux.
-
- To compile this driver as a module, choose M here: the
- module will be called xtkbd.
-
-config KEYBOARD_NEWTON
- tristate "Newton keyboard"
- select SERIO
- help
- Say Y here if you have a Newton keyboard on a serial port.
-
- To compile this driver as a module, choose M here: the
- module will be called newtonkbd.
-
-config KEYBOARD_STOWAWAY
- tristate "Stowaway keyboard"
- select SERIO
+config KEYBOARD_BFIN
+ tristate "Blackfin BF54x keypad support"
+ depends on (BF54x && !BF544)
help
- Say Y here if you have a Stowaway keyboard on a serial port.
- Stowaway compatible keyboards like Dicota Input-PDA keyboard
- are also supported by this driver.
+ Say Y here if you want to use the BF54x keypad.
To compile this driver as a module, choose M here: the
- module will be called stowaway.
+ module will be called bf54x-keys.
config KEYBOARD_CORGI
tristate "Corgi keyboard"
@@ -143,61 +124,50 @@ config KEYBOARD_CORGI
To compile this driver as a module, choose M here: the
module will be called corgikbd.
-config KEYBOARD_SPITZ
- tristate "Spitz keyboard"
- depends on PXA_SHARPSL
- default y
+config KEYBOARD_LKKBD
+ tristate "DECstation/VAXstation LK201/LK401 keyboard"
+ select SERIO
help
- Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
- SL-C3000 and Sl-C3100 series of PDAs.
+ Say Y here if you want to use a LK201 or LK401 style serial
+ keyboard. This keyboard is also useable on PCs if you attach
+ it with the inputattach program. The connector pinout is
+ described within lkkbd.c.
To compile this driver as a module, choose M here: the
- module will be called spitzkbd.
+ module will be called lkkbd.
-config KEYBOARD_TOSA
- tristate "Tosa keyboard"
- depends on MACH_TOSA
- default y
+config KEYBOARD_EP93XX
+ tristate "EP93xx Matrix Keypad support"
+ depends on ARCH_EP93XX
help
- Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
+ Say Y here to enable the matrix keypad on the Cirrus EP93XX.
To compile this driver as a module, choose M here: the
- module will be called tosakbd.
+ module will be called ep93xx_keypad.
-config KEYBOARD_TOSA_USE_EXT_KEYCODES
- bool "Tosa keyboard: use extended keycodes"
- depends on KEYBOARD_TOSA
- default n
+config KEYBOARD_GPIO
+ tristate "GPIO Buttons"
+ depends on GENERIC_GPIO
help
- Say Y here to enable the tosa keyboard driver to generate extended
- (>= 127) keycodes. Be aware, that they can't be correctly interpreted
- by either console keyboard driver or by Kdrive keybd driver.
-
- Say Y only if you know, what you are doing!
+ This driver implements support for buttons connected
+ to GPIO pins of various CPUs (and some other chips).
-config KEYBOARD_AMIGA
- tristate "Amiga keyboard"
- depends on AMIGA
- help
- Say Y here if you are running Linux on any AMIGA and have a keyboard
- attached.
+ Say Y here if your device has buttons connected
+ directly to such GPIO pins. Your board-specific
+ setup logic must also provide a platform device,
+ with configuration data saying which GPIOs are used.
To compile this driver as a module, choose M here: the
- module will be called amikbd.
+ module will be called gpio_keys.
-config ATARI_KBD_CORE
- bool
-
-config KEYBOARD_ATARI
- tristate "Atari keyboard"
- depends on ATARI
- select ATARI_KBD_CORE
+config KEYBOARD_MATRIX
+ tristate "GPIO driven matrix keypad support"
+ depends on GENERIC_GPIO
help
- Say Y here if you are running Linux on any Atari and have a keyboard
- attached.
+ Enable support for GPIO driven matrix keypad.
To compile this driver as a module, choose M here: the
- module will be called atakbd.
+ module will be called matrix_keypad.
config KEYBOARD_HIL_OLD
tristate "HP HIL keyboard support (simple driver)"
@@ -261,20 +231,39 @@ config KEYBOARD_LM8323
To compile this driver as a module, choose M here: the
module will be called lm8323.
-config KEYBOARD_OMAP
- tristate "TI OMAP keypad support"
- depends on (ARCH_OMAP1 || ARCH_OMAP2)
+config KEYBOARD_LOCOMO
+ tristate "LoCoMo Keyboard Support"
+ depends on SHARP_LOCOMO
help
- Say Y here if you want to use the OMAP keypad.
+ Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
To compile this driver as a module, choose M here: the
- module will be called omap-keypad.
+ module will be called locomokbd.
+
+config KEYBOARD_MAPLE
+ tristate "Maple bus keyboard"
+ depends on SH_DREAMCAST && MAPLE
+ help
+ Say Y here if you have a Dreamcast console running Linux and have
+ a keyboard attached to its Maple bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called maple_keyb.
+
+config KEYBOARD_NEWTON
+ tristate "Newton keyboard"
+ select SERIO
+ help
+ Say Y here if you have a Newton keyboard on a serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called newtonkbd.
config KEYBOARD_PXA27x
tristate "PXA27x/PXA3xx keypad support"
depends on PXA27x || PXA3xx
help
- Enable support for PXA27x/PXA3xx keypad controller
+ Enable support for PXA27x/PXA3xx keypad controller.
To compile this driver as a module, choose M here: the
module will be called pxa27x_keypad.
@@ -288,51 +277,38 @@ config KEYBOARD_PXA930_ROTARY
To compile this driver as a module, choose M here: the
module will be called pxa930_rotary.
-config KEYBOARD_AAED2000
- tristate "AAED-2000 keyboard"
- depends on MACH_AAED2000
- select INPUT_POLLDEV
+config KEYBOARD_SPITZ
+ tristate "Spitz keyboard"
+ depends on PXA_SHARPSL
default y
help
- Say Y here to enable the keyboard on the Agilent AAED-2000
- development board.
-
- To compile this driver as a module, choose M here: the
- module will be called aaed2000_kbd.
-
-config KEYBOARD_GPIO
- tristate "GPIO Buttons"
- depends on GENERIC_GPIO
- help
- This driver implements support for buttons connected
- to GPIO pins of various CPUs (and some other chips).
-
- Say Y here if your device has buttons connected
- directly to such GPIO pins. Your board-specific
- setup logic must also provide a platform device,
- with configuration data saying which GPIOs are used.
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
+ SL-C3000 and Sl-C3100 series of PDAs.
To compile this driver as a module, choose M here: the
- module will be called gpio-keys.
+ module will be called spitzkbd.
-config KEYBOARD_MAPLE
- tristate "Maple bus keyboard"
- depends on SH_DREAMCAST && MAPLE
+config KEYBOARD_STOWAWAY
+ tristate "Stowaway keyboard"
+ select SERIO
help
- Say Y here if you have a Dreamcast console running Linux and have
- a keyboard attached to its Maple bus.
+ Say Y here if you have a Stowaway keyboard on a serial port.
+ Stowaway compatible keyboards like Dicota Input-PDA keyboard
+ are also supported by this driver.
To compile this driver as a module, choose M here: the
- module will be called maple_keyb.
+ module will be called stowaway.
-config KEYBOARD_BFIN
- tristate "Blackfin BF54x keypad support"
- depends on (BF54x && !BF544)
+config KEYBOARD_SUNKBD
+ tristate "Sun Type 4 and Type 5 keyboard"
+ select SERIO
help
- Say Y here if you want to use the BF54x keypad.
+ Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
+ connected either to the Sun keyboard connector or to an serial
+ (RS-232) port via a simple adapter.
To compile this driver as a module, choose M here: the
- module will be called bf54x-keys.
+ module will be called sunkbd.
config KEYBOARD_SH_KEYSC
tristate "SuperH KEYSC keypad support"
@@ -344,13 +320,45 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
-config KEYBOARD_EP93XX
- tristate "EP93xx Matrix Keypad support"
- depends on ARCH_EP93XX
+config KEYBOARD_OMAP
+ tristate "TI OMAP keypad support"
+ depends on (ARCH_OMAP1 || ARCH_OMAP2)
help
- Say Y here to enable the matrix keypad on the Cirrus EP93XX.
+ Say Y here if you want to use the OMAP keypad.
To compile this driver as a module, choose M here: the
- module will be called ep93xx_keypad.
+ module will be called omap-keypad.
+
+config KEYBOARD_TOSA
+ tristate "Tosa keyboard"
+ depends on MACH_TOSA
+ default y
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
+
+ To compile this driver as a module, choose M here: the
+ module will be called tosakbd.
+
+config KEYBOARD_TOSA_USE_EXT_KEYCODES
+ bool "Tosa keyboard: use extended keycodes"
+ depends on KEYBOARD_TOSA
+ help
+ Say Y here to enable the tosa keyboard driver to generate extended
+ (>= 127) keycodes. Be aware, that they can't be correctly interpreted
+ by either console keyboard driver or by Kdrive keybd driver.
+
+ Say Y only if you know, what you are doing!
+
+config KEYBOARD_XTKBD
+ tristate "XT keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use the old IBM PC/XT keyboard (or
+ compatible) on your system. This is only possible with a
+ parallel port keyboard adapter, you cannot connect it to the
+ keyboard port on a PC that runs Linux.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xtkbd.
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 156b647a259b..b5b5eae9724f 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -4,29 +4,30 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
-obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
-obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
-obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
-obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
-obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
-obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
+obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
-obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
-obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
+obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
+obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
+obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
+obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
+obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
+obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
+obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
-obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
-obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
-obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
-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_EP93XX) += ep93xx_keypad.o
+obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
+obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index df3f8aa68115..95fe0452dae4 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -895,6 +895,13 @@ static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = {
};
/*
+ * Amilo Pi 3525 key release for Fn+Volume keys not working
+ */
+static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = {
+ 0x20, 0xa0, 0x2e, 0xae, 0x30, 0xb0, -1U
+};
+
+/*
* Amilo Xi 3650 key release for light touch bar not working
*/
static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = {
@@ -902,6 +909,13 @@ static unsigned int atkbd_amilo_xi3650_forced_release_keys[] = {
};
/*
+ * Soltech TA12 system with broken key release on volume keys and mute key
+ */
+static unsigned int atkdb_soltech_ta12_forced_release_keys[] = {
+ 0xa0, 0xae, 0xb0, -1U
+};
+
+/*
* atkbd_set_keycode_table() initializes keyboard's keycode table
* according to the selected scancode set
*/
@@ -1568,6 +1582,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_amilo_pa1510_forced_release_keys,
},
{
+ .ident = "Fujitsu Amilo Pi 3525",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"),
+ },
+ .callback = atkbd_setup_forced_release,
+ .driver_data = atkbd_amilo_pi3525_forced_release_keys,
+ },
+ {
.ident = "Fujitsu Amilo Xi 3650",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
@@ -1576,6 +1599,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.callback = atkbd_setup_forced_release,
.driver_data = atkbd_amilo_xi3650_forced_release_keys,
},
+ {
+ .ident = "Soltech Corporation TA12",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TA12"),
+ },
+ .callback = atkbd_setup_forced_release,
+ .driver_data = atkdb_soltech_ta12_forced_release_keys,
+ },
{ }
};
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 2157cd7de00c..efed0c9e242e 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -29,7 +29,8 @@
struct gpio_button_data {
struct gpio_keys_button *button;
struct input_dev *input;
- struct delayed_work work;
+ struct timer_list timer;
+ struct work_struct work;
};
struct gpio_keys_drvdata {
@@ -40,7 +41,7 @@ struct gpio_keys_drvdata {
static void gpio_keys_report_event(struct work_struct *work)
{
struct gpio_button_data *bdata =
- container_of(work, struct gpio_button_data, work.work);
+ container_of(work, struct gpio_button_data, work);
struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
@@ -50,17 +51,25 @@ static void gpio_keys_report_event(struct work_struct *work)
input_sync(input);
}
+static void gpio_keys_timer(unsigned long _data)
+{
+ struct gpio_button_data *data = (struct gpio_button_data *)_data;
+
+ schedule_work(&data->work);
+}
+
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
struct gpio_keys_button *button = bdata->button;
- unsigned long delay;
BUG_ON(irq != gpio_to_irq(button->gpio));
- delay = button->debounce_interval ?
- msecs_to_jiffies(button->debounce_interval) : 0;
- schedule_delayed_work(&bdata->work, delay);
+ if (button->debounce_interval)
+ mod_timer(&bdata->timer,
+ jiffies + msecs_to_jiffies(button->debounce_interval));
+ else
+ schedule_work(&bdata->work);
return IRQ_HANDLED;
}
@@ -107,7 +116,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
bdata->input = input;
bdata->button = button;
- INIT_DELAYED_WORK(&bdata->work, gpio_keys_report_event);
+ setup_timer(&bdata->timer,
+ gpio_keys_timer, (unsigned long)bdata);
+ INIT_WORK(&bdata->work, gpio_keys_report_event);
error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
if (error < 0) {
@@ -166,7 +177,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
fail2:
while (--i >= 0) {
free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
- cancel_delayed_work_sync(&ddata->data[i].work);
+ if (pdata->buttons[i].debounce_interval)
+ del_timer_sync(&ddata->data[i].timer);
+ cancel_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
}
@@ -190,7 +203,9 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
for (i = 0; i < pdata->nbuttons; i++) {
int irq = gpio_to_irq(pdata->buttons[i].gpio);
free_irq(irq, &ddata->data[i]);
- cancel_delayed_work_sync(&ddata->data[i].work);
+ if (pdata->buttons[i].debounce_interval)
+ del_timer_sync(&ddata->data[i].timer);
+ cancel_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
}
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
new file mode 100644
index 000000000000..541b981ff075
--- /dev/null
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -0,0 +1,453 @@
+/*
+ * GPIO driven matrix keyboard driver
+ *
+ * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * 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/types.h>
+#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 {
+ const struct matrix_keypad_platform_data *pdata;
+ struct input_dev *input_dev;
+ unsigned short *keycodes;
+ unsigned int row_shift;
+
+ uint32_t last_key_state[MATRIX_MAX_COLS];
+ struct delayed_work work;
+ bool scan_pending;
+ bool stopped;
+ spinlock_t lock;
+};
+
+/*
+ * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
+ * minmal side effect when scanning other columns, here it is configured to
+ * be input, and it should work on most platforms.
+ */
+static void __activate_col(const struct matrix_keypad_platform_data *pdata,
+ int col, bool on)
+{
+ bool 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_col(const struct matrix_keypad_platform_data *pdata,
+ int col, bool on)
+{
+ __activate_col(pdata, col, on);
+
+ if (on && pdata->col_scan_delay_us)
+ udelay(pdata->col_scan_delay_us);
+}
+
+static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
+ bool on)
+{
+ int col;
+
+ for (col = 0; col < pdata->num_col_gpios; col++)
+ __activate_col(pdata, col, on);
+}
+
+static bool row_asserted(const 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)
+{
+ const 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)
+{
+ const 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 input_dev *input_dev = keypad->input_dev;
+ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+ uint32_t new_state[MATRIX_MAX_COLS];
+ int row, col, code;
+
+ /* de-activate all columns for scanning */
+ activate_all_cols(pdata, false);
+
+ 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, true);
+
+ for (row = 0; row < pdata->num_row_gpios; row++)
+ new_state[col] |=
+ row_asserted(pdata, row) ? (1 << row) : 0;
+
+ activate_col(pdata, col, false);
+ }
+
+ 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;
+
+ code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev,
+ keypad->keycodes[code],
+ new_state[col] & (1 << row));
+ }
+ }
+ input_sync(input_dev);
+
+ memcpy(keypad->last_key_state, new_state, sizeof(new_state));
+
+ activate_all_cols(pdata, true);
+
+ /* Enable IRQs again */
+ spin_lock_irq(&keypad->lock);
+ keypad->scan_pending = false;
+ enable_row_irqs(keypad);
+ spin_unlock_irq(&keypad->lock);
+}
+
+static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
+{
+ struct matrix_keypad *keypad = id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&keypad->lock, flags);
+
+ /*
+ * See if another IRQ beaten us to it and scheduled the
+ * scan already. In that case we should not try to
+ * disable IRQs again.
+ */
+ if (unlikely(keypad->scan_pending || keypad->stopped))
+ goto out;
+
+ disable_row_irqs(keypad);
+ keypad->scan_pending = true;
+ schedule_delayed_work(&keypad->work,
+ msecs_to_jiffies(keypad->pdata->debounce_ms));
+
+out:
+ spin_unlock_irqrestore(&keypad->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int matrix_keypad_start(struct input_dev *dev)
+{
+ struct matrix_keypad *keypad = input_get_drvdata(dev);
+
+ keypad->stopped = false;
+ mb();
+
+ /*
+ * Schedule an immediate key scan to capture current key state;
+ * columns will be activated and IRQs be enabled after the scan.
+ */
+ schedule_delayed_work(&keypad->work, 0);
+
+ return 0;
+}
+
+static void matrix_keypad_stop(struct input_dev *dev)
+{
+ struct matrix_keypad *keypad = input_get_drvdata(dev);
+
+ keypad->stopped = true;
+ mb();
+ flush_work(&keypad->work.work);
+ /*
+ * matrix_keypad_scan() will leave IRQs enabled;
+ * we should disable them now.
+ */
+ disable_row_irqs(keypad);
+}
+
+#ifdef CONFIG_PM
+static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+ int i;
+
+ matrix_keypad_stop(keypad->input_dev);
+
+ 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);
+ const 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]));
+
+ matrix_keypad_start(keypad->input_dev);
+
+ return 0;
+}
+#else
+#define matrix_keypad_suspend NULL
+#define matrix_keypad_resume NULL
+#endif
+
+static int __devinit init_matrix_gpio(struct platform_device *pdev,
+ struct matrix_keypad *keypad)
+{
+ const 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) {
+ dev_err(&pdev->dev,
+ "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) {
+ dev_err(&pdev->dev,
+ "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) {
+ dev_err(&pdev->dev,
+ "Unable to acquire interrupt for GPIO line %i\n",
+ pdata->row_gpios[i]);
+ goto err_free_irqs;
+ }
+ }
+
+ /* initialized as disabled - enabled by input->open */
+ disable_row_irqs(keypad);
+ return 0;
+
+err_free_irqs:
+ while (--i >= 0)
+ free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
+ i = pdata->num_row_gpios;
+err_free_rows:
+ while (--i >= 0)
+ gpio_free(pdata->row_gpios[i]);
+ i = pdata->num_col_gpios;
+err_free_cols:
+ while (--i >= 0)
+ gpio_free(pdata->col_gpios[i]);
+
+ return err;
+}
+
+static int __devinit matrix_keypad_probe(struct platform_device *pdev)
+{
+ const struct matrix_keypad_platform_data *pdata;
+ const struct matrix_keymap_data *keymap_data;
+ struct matrix_keypad *keypad;
+ struct input_dev *input_dev;
+ unsigned short *keycodes;
+ unsigned int row_shift;
+ int i;
+ int err;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ keymap_data = pdata->keymap_data;
+ if (!keymap_data) {
+ dev_err(&pdev->dev, "no keymap data defined\n");
+ return -EINVAL;
+ }
+
+ row_shift = get_count_order(pdata->num_col_gpios);
+
+ keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
+ keycodes = kzalloc((pdata->num_row_gpios << row_shift) *
+ sizeof(*keycodes),
+ GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!keypad || !keycodes || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ keypad->input_dev = input_dev;
+ keypad->pdata = pdata;
+ keypad->keycodes = keycodes;
+ keypad->row_shift = row_shift;
+ keypad->stopped = true;
+ INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
+ spin_lock_init(&keypad->lock);
+
+ 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);
+ input_dev->open = matrix_keypad_start;
+ input_dev->close = matrix_keypad_stop;
+
+ input_dev->keycode = keycodes;
+ input_dev->keycodesize = sizeof(*keycodes);
+ input_dev->keycodemax = pdata->num_row_gpios << keypad->row_shift;
+
+ for (i = 0; i < keymap_data->keymap_size; i++) {
+ unsigned int key = keymap_data->keymap[i];
+ unsigned int row = KEY_ROW(key);
+ unsigned int col = KEY_COL(key);
+ unsigned short code = KEY_VAL(key);
+
+ keycodes[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
+ __set_bit(code, input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ input_set_drvdata(input_dev, keypad);
+
+ err = init_matrix_gpio(pdev, keypad);
+ if (err)
+ goto err_free_mem;
+
+ err = input_register_device(keypad->input_dev);
+ if (err)
+ goto err_free_mem;
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(keycodes);
+ kfree(keypad);
+ return err;
+}
+
+static int __devexit matrix_keypad_remove(struct platform_device *pdev)
+{
+ struct matrix_keypad *keypad = platform_get_drvdata(pdev);
+ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+ int i;
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ for (i = 0; i < pdata->num_row_gpios; i++) {
+ free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
+ gpio_free(pdata->row_gpios[i]);
+ }
+
+ for (i = 0; i < pdata->num_col_gpios; i++)
+ gpio_free(pdata->col_gpios[i]);
+
+ input_unregister_device(keypad->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ 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@gmail.com>");
+MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:matrix-keypad");