From: Eric Miao <eric.y.miao@gmail.com>
To: Robert Jarzmik <robert.jarzmik@free.fr>
Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>,
Uli Luckas <u.luckas@road.de>,
linux-arm-kernel@lists.arm.linux.org.uk,
Marek Vasut <marek.vasut@gmail.com>,
Dmitry Torokhov <dmitry.torokhov@gmail.com>,
"linux-input@vger.kernel.org" <linux-input@vger.kernel.org>
Subject: Re: [PATCH] input: add support for generic GPIO-based matrix keypad
Date: Mon, 1 Jun 2009 12:02:40 +0800 [thread overview]
Message-ID: <f17812d70905312102o48940c29l7ba804ae564a2ab7@mail.gmail.com> (raw)
In-Reply-To: <87bpp9qiiy.fsf@free.fr>
[-- Attachment #1: Type: text/plain, Size: 2750 bytes --]
On Mon, Jun 1, 2009 at 1:39 AM, Robert Jarzmik <robert.jarzmik@free.fr> wrote:
> Eric Miao <eric.y.miao@gmail.com> writes:
>
> Hi Eric, some minor cosmetic points ...
>
> <snip>
>
>> +static void activate_all_cols(struct matrix_keypad_platform_data
>> *pdata, int on)
> That function declaration spacing looks suspicious, looks like my mailer (or
> yours) ate a bit out of it, and patch doesn't accept to apply it.
>
> <snip>
>
> +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);
> +}
> That function is called is the worst case 16 times in a row (if I understood
> correctly matrix_keypad_scan()). If delay is 100us, that makes 1.6ms for a
> keystroke with all kernel blocked (udelay).
> I didn't follow the discussion before, so maybe that's the only way. Still,
> that's too bad ...
>
This is AFAIK not necessary. This originally came from the
{corgi,spitz}kbd.c, where it is believed a small delay after
de-activating all columns, and a delay after activating one
specific column are needed (though the delay is suggested
in the {corgi,spitz}kbd.c as 10us). I'll check if it still works
without this delay (I believe it still works).
> <snip>
>
>> +/*
>> + * 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;
>> +
> Nitpicking: extra space on that line.
>
OK.
I've attached the updated one as attachment here.
[-- Attachment #2: 0001-input-add-support-for-generic-GPIO-based-matrix-key.patch --]
[-- Type: text/x-patch, Size: 13064 bytes --]
From d903ce5d97de09901987b20332d37982942a5627 Mon Sep 17 00:00:00 2001
From: Marek Vasut <marek.vasut@gmail.com>
Date: Thu, 7 May 2009 15:49:32 +0800
Subject: [PATCH] input: add support for generic GPIO-based matrix keypad
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-by: Uli Luckas <u.luckas@road.de>
Signed-off-by: Eric Miao <eric.miao@marvell.com>
---
drivers/input/keyboard/Kconfig | 10 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/matrix_keypad.c | 378 ++++++++++++++++++++++++++++++++
include/linux/input/matrix_keypad.h | 34 +++
4 files changed, 423 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..2debc8a
--- /dev/null
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -0,0 +1,378 @@
+/*
+ * drivers/input/keyboard/matrix_keypad.c
+ *
+ * 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/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(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(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;
+
+ 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@gmail.com>");
+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
next prev parent reply other threads:[~2009-06-01 4:02 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-07 8:00 [PATCH] input: add support for generic GPIO-based matrix keypad Eric Miao
2009-05-07 8:41 ` Trilok Soni
2009-05-07 8:48 ` Eric Miao
2009-05-07 8:51 ` Trilok Soni
2009-05-08 1:29 ` Eric Miao
2009-05-08 4:54 ` Trilok Soni
2009-05-08 5:52 ` Arve Hjønnevåg
2009-05-25 3:36 ` Trilok Soni
2009-05-25 7:05 ` Eric Miao
2009-05-27 13:26 ` Uli Luckas
2009-05-31 14:12 ` Eric Miao
2009-05-31 14:20 ` Russell King - ARM Linux
2009-05-31 14:29 ` Eric Miao
2009-05-31 17:38 ` Dmitry Torokhov
2009-06-01 3:43 ` Eric Miao
2009-05-31 17:39 ` Robert Jarzmik
2009-06-01 4:02 ` Eric Miao [this message]
2009-06-02 14:55 ` Uli Luckas
2009-06-02 15:14 ` Uli Luckas
2009-06-03 6:24 ` Eric Miao
2009-06-10 4:10 ` Trilok Soni
2009-07-04 5:54 ` Marek Vasut
2009-07-06 6:02 ` Eric Miao
2009-07-06 10:58 ` Marek Vasut
[not found] ` <f17812d70906070704s10f8eac6ybc353041e2db5a03@mail.gmail.com>
2009-06-12 4:03 ` Dmitry Torokhov
2009-06-12 13:01 ` Trilok Soni
2009-06-12 13:26 ` Eric Miao
2009-06-19 6:54 ` Trilok Soni
2009-06-29 16:26 ` Dmitry Torokhov
2009-06-30 6:43 ` Trilok Soni
2009-07-01 12:14 ` Kim Kyuwon
2009-07-09 9:46 ` Eric Miao
2009-07-09 10:01 ` Trilok Soni
2009-07-17 7:51 ` Paulius Zaleckas
2009-07-17 8:32 ` Trilok Soni
2009-07-17 9:20 ` Paulius Zaleckas
2009-07-20 7:12 ` Trilok Soni
2009-07-20 10:37 ` Eric Miao
2009-07-20 10:43 ` Trilok Soni
2009-07-20 11:43 ` Eric Miao
2009-07-21 8:00 ` Dmitry Torokhov
2009-07-21 8:26 ` Eric Miao
2009-07-21 15:58 ` Dmitry Torokhov
2009-07-27 8:54 ` Eric Miao
2009-06-03 6:06 ` Eric Miao
2009-06-03 18:06 ` Trilok Soni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f17812d70905312102o48940c29l7ba804ae564a2ab7@mail.gmail.com \
--to=eric.y.miao@gmail.com \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-arm-kernel@lists.arm.linux.org.uk \
--cc=linux-input@vger.kernel.org \
--cc=linux@arm.linux.org.uk \
--cc=marek.vasut@gmail.com \
--cc=robert.jarzmik@free.fr \
--cc=u.luckas@road.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).