linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 2.6.29-rc3-git] input:  twl4030_keypad driver
@ 2009-02-06  8:57 David Brownell
  2009-02-06  9:16 ` Gadiyar, Anand
  2009-04-21  6:22 ` David Brownell
  0 siblings, 2 replies; 6+ messages in thread
From: David Brownell @ 2009-02-06  8:57 UTC (permalink / raw)
  To: linux-input; +Cc: OMAP

From: David Brownell <dbrownell@users.sourceforge.net>

Add a driver for the keypad controller on TWL4030 family chips.
These support up to an 8x8 key matrix.  The TWL4030 multifunction
chips are mostly used on OMAP3 (or OMAP 2430) based boards.

Reviewed-by: Trilok Soni <soni.trilok@gmail.com>"
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
Sometime later we may get a more generic/reusable solution
than the KEY() macros used here, which basically came from
the OMAP keypad support.

 drivers/input/keyboard/Kconfig          |   11 
 drivers/input/keyboard/Makefile         |    1 
 drivers/input/keyboard/twl4030_keypad.c |  493 ++++++++++++++++++++++++++++++
 include/linux/i2c/twl4030.h             |   17 -
 4 files changed, 517 insertions(+), 5 deletions(-)

--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -259,6 +259,17 @@ config KEYBOARD_OMAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called omap-keypad.
 
+config KEYBOARD_TWL4030
+	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
+	depends on TWL4030_CORE
+	help
+	  Say Y here if your board use the keypad controller on
+	  TWL4030 family chips.  It's safe to say enable this
+	  even on boards that don't use the keypad controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called twl4030_keypad.
+
 config KEYBOARD_PXA27x
 	tristate "PXA27x/PXA3xx keypad support"
 	depends on PXA27x || PXA3xx
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYBOARD_TOSA)		+= tosakbd.
 obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
 obj-$(CONFIG_KEYBOARD_OMAP)		+= omap-keypad.o
+obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA27x)		+= pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)	+= pxa930_rotary.o
 obj-$(CONFIG_KEYBOARD_AAED2000)		+= aaed2000_kbd.o
--- /dev/null
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -0,0 +1,493 @@
+/*
+ * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Code re-written for 2430SDP by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Manjunatha G K <manjugk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
+
+/*
+ * The TWL4030 family chips include a keypad controller that supports
+ * up to an 8x8 switch matrix.  The controller can issue system wakeup
+ * events, since it uses only the always-on 32KiHz oscillator, and has
+ * an internal state machine that decodes pressed keys, including
+ * multi-key combinations.
+ *
+ * This driver lets boards define what keycodes they wish to report for
+ * which scancodes, as part of the "struct twl4030_keypad_data" used in
+ * the probe() routine.
+ *
+ * See the TPS65950 documentation; that's the general availability
+ * version of the TWL5030 second generation part.
+ */
+#define MAX_ROWS		8	/* TWL4030 hard limit */
+
+struct twl4030_keypad {
+	unsigned	*keymap;
+	unsigned int	keymapsize;
+	u16		kp_state[MAX_ROWS];
+	unsigned	n_rows;
+	unsigned	n_cols;
+	unsigned	irq;
+
+	struct device	*dbg_dev;
+	struct input_dev *input;
+};
+
+#define ROWCOL_MASK	KEY(0xf, 0xf, 0)
+#define KEYNUM_MASK	~PERSISTENT_KEY(0xf, 0xf)
+
+/*----------------------------------------------------------------------*/
+
+/* arbitrary prescaler value 0..7 */
+#define PTV_PRESCALER			4
+
+/* Register Offsets */
+#define KEYP_CTRL			0x00
+#define KEYP_DEB			0x01
+#define KEYP_LONG_KEY			0x02
+#define KEYP_LK_PTV			0x03
+#define KEYP_TIMEOUT_L			0x04
+#define KEYP_TIMEOUT_H			0x05
+#define KEYP_KBC			0x06
+#define KEYP_KBR			0x07
+#define KEYP_SMS			0x08
+#define KEYP_FULL_CODE_7_0		0x09	/* row 0 column status */
+#define KEYP_FULL_CODE_15_8		0x0a	/* ... row 1 ... */
+#define KEYP_FULL_CODE_23_16		0x0b
+#define KEYP_FULL_CODE_31_24		0x0c
+#define KEYP_FULL_CODE_39_32		0x0d
+#define KEYP_FULL_CODE_47_40		0x0e
+#define KEYP_FULL_CODE_55_48		0x0f
+#define KEYP_FULL_CODE_63_56		0x10
+#define KEYP_ISR1			0x11
+#define KEYP_IMR1			0x12
+#define KEYP_ISR2			0x13
+#define KEYP_IMR2			0x14
+#define KEYP_SIR			0x15
+#define KEYP_EDR			0x16	/* edge triggers */
+#define KEYP_SIH_CTRL			0x17
+
+/* KEYP_CTRL_REG Fields */
+#define KEYP_CTRL_SOFT_NRST		BIT(0)
+#define KEYP_CTRL_SOFTMODEN		BIT(1)
+#define KEYP_CTRL_LK_EN			BIT(2)
+#define KEYP_CTRL_TOE_EN		BIT(3)
+#define KEYP_CTRL_TOLE_EN		BIT(4)
+#define KEYP_CTRL_RP_EN			BIT(5)
+#define KEYP_CTRL_KBD_ON		BIT(6)
+
+/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
+#define KEYP_PERIOD_US(t, prescale)	((t) / (31 << (prescale + 1)) - 1)
+
+/* KEYP_LK_PTV_REG Fields */
+#define KEYP_LK_PTV_PTV_SHIFT		5
+
+/* KEYP_{IMR,ISR,SIR} Fields */
+#define KEYP_IMR1_MIS			BIT(3)
+#define KEYP_IMR1_TO			BIT(2)
+#define KEYP_IMR1_LK			BIT(1)
+#define KEYP_IMR1_KP			BIT(0)
+
+/* KEYP_EDR Fields */
+#define KEYP_EDR_KP_FALLING		0x01
+#define KEYP_EDR_KP_RISING		0x02
+#define KEYP_EDR_KP_BOTH		0x03
+#define KEYP_EDR_LK_FALLING		0x04
+#define KEYP_EDR_LK_RISING		0x08
+#define KEYP_EDR_TO_FALLING		0x10
+#define KEYP_EDR_TO_RISING		0x20
+#define KEYP_EDR_MIS_FALLING		0x40
+#define KEYP_EDR_MIS_RISING		0x80
+
+
+/*----------------------------------------------------------------------*/
+
+static int twl4030_kpread(struct twl4030_keypad *kp,
+		u8 *data, u32 reg, u8 num_bytes)
+{
+	int ret;
+
+	ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
+	if (ret < 0) {
+		dev_warn(kp->dbg_dev,
+			"Couldn't read TWL4030: %X - ret %d[%x]\n",
+			 reg, ret, ret);
+		return ret;
+	}
+	return ret;
+}
+
+static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
+{
+	int ret;
+
+	ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
+	if (ret < 0) {
+		dev_warn(kp->dbg_dev,
+			"Could not write TWL4030: %X - ret %d[%x]\n",
+			 reg, ret, ret);
+		return ret;
+	}
+	return ret;
+}
+
+static int twl4030_find_key(struct twl4030_keypad *kp, int col, int row)
+{
+	int i, rc;
+
+	rc = KEY(col, row, 0);
+	for (i = 0; i < kp->keymapsize; i++)
+		if ((kp->keymap[i] & ROWCOL_MASK) == rc)
+			return kp->keymap[i] & (KEYNUM_MASK | KEY_PERSISTENT);
+
+	return -EINVAL;
+}
+
+static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
+{
+	/* If all bits in a row are active for all coloumns then
+	 * we have that row line connected to gnd. Mark this
+	 * key on as if it was on matrix position n_cols (ie
+	 * one higher than the size of the matrix).
+	 */
+	if (col == 0xFF)
+		return 1 << kp->n_cols;
+	else
+		return col & ((1 << kp->n_cols) - 1);
+}
+
+static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
+{
+	u8 new_state[MAX_ROWS];
+	int row;
+	int ret = twl4030_kpread(kp,
+				 new_state, KEYP_FULL_CODE_7_0, kp->n_rows);
+	if (ret >= 0) {
+		for (row = 0; row < kp->n_rows; row++)
+			state[row] = twl4030_col_xlate(kp, new_state[row]);
+	}
+	return ret;
+}
+
+static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
+{
+	int i;
+	u16 check = 0;
+
+	for (i = 0; i < kp->n_rows; i++) {
+		u16 col = key_state[i];
+
+		if ((col & check) && hweight16(col) > 1)
+			return 1;
+		check |= col;
+	}
+
+	return 0;
+}
+
+static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
+{
+	u16 new_state[MAX_ROWS];
+	int col, row;
+
+	if (release_all)
+		memset(new_state, 0, sizeof(new_state));
+	else {
+		/* check for any changes */
+		int ret = twl4030_read_kp_matrix_state(kp, new_state);
+
+		if (ret < 0)	/* panic ... */
+			return;
+		if (twl4030_is_in_ghost_state(kp, new_state))
+			return;
+	}
+
+	/* check for changes and print those */
+	for (row = 0; row < kp->n_rows; row++) {
+		int changed = new_state[row] ^ kp->kp_state[row];
+
+		if (!changed)
+			continue;
+
+		for (col = 0; col < kp->n_cols; col++) {
+			int key;
+
+			if (!(changed & (1 << col)))
+				continue;
+
+			dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
+				(new_state[row] & (1 << col)) ?
+				"press" : "release");
+
+			key = twl4030_find_key(kp, col, row);
+			if (key < 0)
+				dev_warn(kp->dbg_dev,
+					"Spurious key event %d-%d\n",
+					 col, row);
+			else if (key & KEY_PERSISTENT)
+				continue;
+			else
+				input_report_key(kp->input, key,
+						 new_state[row] & (1 << col));
+		}
+		kp->kp_state[row] = new_state[row];
+	}
+	input_sync(kp->input);
+}
+
+/*
+ * Keypad interrupt handler
+ */
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+	struct twl4030_keypad *kp = _kp;
+	u8 reg;
+	int ret;
+
+#ifdef CONFIG_LOCKDEP
+	/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+	 * we don't want and can't tolerate.  Although it might be
+	 * friendlier not to borrow this thread context...
+	 */
+	local_irq_enable();
+#endif
+
+	/* Read & Clear TWL4030 pending interrupt */
+	ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
+
+	/* Release all keys if I2C has gone bad or
+	 * the KEYP has gone to idle state */
+	if ((ret >= 0) && (reg & KEYP_IMR1_KP))
+		twl4030_kp_scan(kp, 0);
+	else
+		twl4030_kp_scan(kp, 1);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Registers keypad device with input subsystem
+ * and configures TWL4030 keypad registers
+ */
+static int __devinit twl4030_kp_probe(struct platform_device *pdev)
+{
+	u8 reg;
+	int i;
+	int ret = 0;
+	struct twl4030_keypad *kp;
+	struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap
+			|| pdata->rows > 8 || pdata->cols > 8) {
+		dev_err(&pdev->dev, "Invalid platform_data\n");
+		return -EINVAL;
+	}
+
+	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, kp);
+
+	/* Get the debug Device */
+	kp->dbg_dev = &pdev->dev;
+
+	kp->input = input_allocate_device();
+	if (!kp->input) {
+		kfree(kp);
+		return -ENOMEM;
+	}
+
+	kp->keymap = pdata->keymap;
+	kp->keymapsize = pdata->keymapsize;
+	kp->n_rows = pdata->rows;
+	kp->n_cols = pdata->cols;
+	kp->irq = platform_get_irq(pdev, 0);
+
+	/* setup input device */
+	__set_bit(EV_KEY, kp->input->evbit);
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (pdata->rep)
+		__set_bit(EV_REP, kp->input->evbit);
+
+	for (i = 0; i < kp->keymapsize; i++)
+		__set_bit(kp->keymap[i] & KEYNUM_MASK,
+				kp->input->keybit);
+
+	kp->input->name		= "TWL4030 Keypad";
+	kp->input->phys		= "twl4030_keypad/input0";
+	kp->input->dev.parent	= &pdev->dev;
+
+	kp->input->id.bustype	= BUS_HOST;
+	kp->input->id.vendor	= 0x0001;
+	kp->input->id.product	= 0x0001;
+	kp->input->id.version	= 0x0003;
+
+	kp->input->keycode	= kp->keymap;
+	kp->input->keycodesize	= sizeof(unsigned int);
+	kp->input->keycodemax	= kp->keymapsize;
+
+	ret = input_register_device(kp->input);
+	if (ret < 0) {
+		dev_err(kp->dbg_dev,
+			"Unable to register twl4030 keypad device\n");
+		goto err2;
+	}
+
+	/* Enable controller, with hardware decoding but not autorepeat */
+	reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
+		| KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_CTRL);
+	if (ret < 0)
+		goto err3;
+
+	/* NOTE:  we could use sih_setup() here to package keypad
+	 * event sources as four different IRQs ... but we don't.
+	 */
+
+	/* Enable TO rising and KP rising and falling edge detection */
+	reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_EDR);
+	if (ret < 0)
+		goto err3;
+
+	/* Set PTV prescaler Field */
+	reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV);
+	if (ret < 0)
+		goto err3;
+
+	/* Set key debounce time to 20 ms */
+	i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+	ret = twl4030_kpwrite_u8(kp, i, KEYP_DEB);
+	if (ret < 0)
+		goto err3;
+
+	/* Set timeout period to 100 ms */
+	i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+	ret = twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L);
+	if (ret < 0)
+		goto err3;
+	ret = twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H);
+	if (ret < 0)
+		goto err3;
+
+	/* Enable Clear-on-Read; disable remembering events that fire
+	 * after the IRQ but before our handler acks (reads) them,
+	 */
+	reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL);
+	if (ret < 0)
+		goto err3;
+
+	/* initialize key state; irqs update it from here on */
+	ret = twl4030_read_kp_matrix_state(kp, kp->kp_state);
+	if (ret < 0)
+		goto err3;
+
+	/*
+	 * This ISR will always execute in kernel thread context because of
+	 * the need to access the TWL4030 over the I2C bus.
+	 *
+	 * NOTE:  we assume this host is wired to TWL4040 INT1, not INT2 ...
+	 */
+	ret = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
+	if (ret < 0) {
+		dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
+			kp->irq);
+		goto err3;
+	} else {
+		/* Enable KP and TO interrupts now. */
+		reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+		ret = twl4030_kpwrite_u8(kp, reg, KEYP_IMR1);
+		if (ret < 0)
+			goto err5;
+	}
+
+	return ret;
+err5:
+	/* mask all events - we don't care about the result */
+	(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
+	free_irq(kp->irq, NULL);
+err3:
+	input_unregister_device(kp->input);
+	kp->input = NULL;
+err2:
+	input_free_device(kp->input);
+	kfree(kp);
+	return -ENODEV;
+}
+
+static int __devexit twl4030_kp_remove(struct platform_device *pdev)
+{
+	struct twl4030_keypad *kp = platform_get_drvdata(pdev);
+
+	free_irq(kp->irq, kp);
+	input_unregister_device(kp->input);
+	kfree(kp);
+
+	return 0;
+}
+
+/*
+ * NOTE: twl4030 are multi-function devices connected via I2C.
+ * So this device is a child of an I2C parent, thus it needs to
+ * support unplug/replug (which most platform devices don't).
+ */
+
+MODULE_ALIAS("platform:twl4030_keypad");
+
+static struct platform_driver twl4030_kp_driver = {
+	.probe		= twl4030_kp_probe,
+	.remove		= __devexit_p(twl4030_kp_remove),
+	.driver		= {
+		.name	= "twl4030_keypad",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init twl4030_kp_init(void)
+{
+	return platform_driver_register(&twl4030_kp_driver);
+}
+module_init(twl4030_kp_init);
+
+static void __exit twl4030_kp_exit(void)
+{
+	platform_driver_unregister(&twl4030_kp_driver);
+}
+module_exit(twl4030_kp_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TWL4030 Keypad Driver");
+MODULE_LICENSE("GPL");
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -255,12 +255,19 @@ struct twl4030_madc_platform_data {
 	int		irq_line;
 };
 
+/* Boards have uniqe mappings of {col, row} --> keycode.
+ * Column and row are 4 bits, but range only from 0..7;
+ * a PERSISTENT_KEY is "always on" and never reported.
+ */
+#define KEY_PERSISTENT		0x00800000
+#define KEY(col, row, keycode)	(((col) << 28) | ((row) << 24) | (keycode))
+#define PERSISTENT_KEY(c, r)	KEY((c), (r), KEY_PERSISTENT)
+
 struct twl4030_keypad_data {
-	int rows;
-	int cols;
-	int *keymap;
-	int irq;
-	unsigned int keymapsize;
+	unsigned rows;
+	unsigned cols;
+	unsigned *keymap;
+	unsigned short keymapsize;
 	unsigned int rep:1;
 };
 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [patch 2.6.29-rc3-git] input:  twl4030_keypad driver
  2009-02-06  8:57 [patch 2.6.29-rc3-git] input: twl4030_keypad driver David Brownell
@ 2009-02-06  9:16 ` Gadiyar, Anand
  2009-02-06 10:33   ` David Brownell
  2009-04-21  6:22 ` David Brownell
  1 sibling, 1 reply; 6+ messages in thread
From: Gadiyar, Anand @ 2009-02-06  9:16 UTC (permalink / raw)
  To: David Brownell, linux-input@vger.kernel.org; +Cc: OMAP

 

> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org 
> [mailto:linux-omap-owner@vger.kernel.org] On Behalf Of David Brownell
> Sent: Friday, February 06, 2009 2:27 PM
> To: linux-input@vger.kernel.org
> Cc: OMAP
> Subject: [patch 2.6.29-rc3-git] input: twl4030_keypad driver
> 
> From: David Brownell <dbrownell@users.sourceforge.net>
> 
> Add a driver for the keypad controller on TWL4030 family chips.
> These support up to an 8x8 key matrix.  The TWL4030 multifunction
> chips are mostly used on OMAP3 (or OMAP 2430) based boards.
> 
> Reviewed-by: Trilok Soni <soni.trilok@gmail.com>"

                                                 ^^^
Cut-paste error? Will it mess up git-shortlog accounting a little
or is it safe to ignore?

- Anand

<snip>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch 2.6.29-rc3-git] input:  twl4030_keypad driver
  2009-02-06  9:16 ` Gadiyar, Anand
@ 2009-02-06 10:33   ` David Brownell
  0 siblings, 0 replies; 6+ messages in thread
From: David Brownell @ 2009-02-06 10:33 UTC (permalink / raw)
  To: Gadiyar, Anand; +Cc: linux-input@vger.kernel.org, OMAP

On Friday 06 February 2009, Gadiyar, Anand wrote:
> 
> > Reviewed-by: Trilok Soni <soni.trilok@gmail.com>"
> 
>                                                  ^^^
> Cut-paste error?

Yes.

> Will it mess up git-shortlog accounting a little 
> or is it safe to ignore?

No idea; but that's trivial for whoever merges this
to address.


--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch 2.6.29-rc3-git] input:  twl4030_keypad driver
  2009-02-06  8:57 [patch 2.6.29-rc3-git] input: twl4030_keypad driver David Brownell
  2009-02-06  9:16 ` Gadiyar, Anand
@ 2009-04-21  6:22 ` David Brownell
  2009-04-24  2:12   ` Dmitry Torokhov
  1 sibling, 1 reply; 6+ messages in thread
From: David Brownell @ 2009-04-21  6:22 UTC (permalink / raw)
  To: linux-input; +Cc: OMAP

On Friday 06 February 2009, David Brownell wrote:
> From: David Brownell <dbrownell@users.sourceforge.net>
> 
> Add a driver for the keypad controller on TWL4030 family chips.

PING?  I was told this was in the input queue, but it's not in

  http://git.kernel.org/?p=linux/kernel/git/dtor/input.git

Here's a current version of the patch.

- Dave

============ CUT HERE
From: David Brownell <dbrownell@users.sourceforge.net>

Add a driver for the keypad controller on TWL4030 family chips.
These support up to an 8x8 key matrix.  The TWL4030 multifunction
chips are mostly used on OMAP3 (or OMAP 2430) based boards.

Reviewed-by: Trilok Soni <soni.trilok@gmail.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
Sometime later we may get a more generic/reusable solution
than the KEY() macros used here, which basically came from
the OMAP keypad support.

 drivers/input/keyboard/Kconfig          |   11 
 drivers/input/keyboard/Makefile         |    1 
 drivers/input/keyboard/twl4030_keypad.c |  493 ++++++++++++++++++++++++++++++
 include/linux/i2c/twl4030.h             |   17 -
 4 files changed, 517 insertions(+), 5 deletions(-)

--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -259,6 +259,17 @@ config KEYBOARD_OMAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called omap-keypad.
 
+config KEYBOARD_TWL4030
+	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
+	depends on TWL4030_CORE
+	help
+	  Say Y here if your board use the keypad controller on
+	  TWL4030 family chips.  It's safe to say enable this
+	  even on boards that don't use the keypad controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called twl4030_keypad.
+
 config KEYBOARD_PXA27x
 	tristate "PXA27x/PXA3xx keypad support"
 	depends on PXA27x || PXA3xx
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYBOARD_TOSA)		+= tosakbd.
 obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
 obj-$(CONFIG_KEYBOARD_OMAP)		+= omap-keypad.o
+obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA27x)		+= pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)	+= pxa930_rotary.o
 obj-$(CONFIG_KEYBOARD_AAED2000)		+= aaed2000_kbd.o
--- /dev/null
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -0,0 +1,493 @@
+/*
+ * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Code re-written for 2430SDP by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Manjunatha G K <manjugk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
+
+/*
+ * The TWL4030 family chips include a keypad controller that supports
+ * up to an 8x8 switch matrix.  The controller can issue system wakeup
+ * events, since it uses only the always-on 32KiHz oscillator, and has
+ * an internal state machine that decodes pressed keys, including
+ * multi-key combinations.
+ *
+ * This driver lets boards define what keycodes they wish to report for
+ * which scancodes, as part of the "struct twl4030_keypad_data" used in
+ * the probe() routine.
+ *
+ * See the TPS65950 documentation; that's the general availability
+ * version of the TWL5030 second generation part.
+ */
+#define MAX_ROWS		8	/* TWL4030 hard limit */
+
+struct twl4030_keypad {
+	unsigned	*keymap;
+	unsigned int	keymapsize;
+	u16		kp_state[MAX_ROWS];
+	unsigned	n_rows;
+	unsigned	n_cols;
+	unsigned	irq;
+
+	struct device	*dbg_dev;
+	struct input_dev *input;
+};
+
+#define ROWCOL_MASK	KEY(0xf, 0xf, 0)
+#define KEYNUM_MASK	~PERSISTENT_KEY(0xf, 0xf)
+
+/*----------------------------------------------------------------------*/
+
+/* arbitrary prescaler value 0..7 */
+#define PTV_PRESCALER			4
+
+/* Register Offsets */
+#define KEYP_CTRL			0x00
+#define KEYP_DEB			0x01
+#define KEYP_LONG_KEY			0x02
+#define KEYP_LK_PTV			0x03
+#define KEYP_TIMEOUT_L			0x04
+#define KEYP_TIMEOUT_H			0x05
+#define KEYP_KBC			0x06
+#define KEYP_KBR			0x07
+#define KEYP_SMS			0x08
+#define KEYP_FULL_CODE_7_0		0x09	/* row 0 column status */
+#define KEYP_FULL_CODE_15_8		0x0a	/* ... row 1 ... */
+#define KEYP_FULL_CODE_23_16		0x0b
+#define KEYP_FULL_CODE_31_24		0x0c
+#define KEYP_FULL_CODE_39_32		0x0d
+#define KEYP_FULL_CODE_47_40		0x0e
+#define KEYP_FULL_CODE_55_48		0x0f
+#define KEYP_FULL_CODE_63_56		0x10
+#define KEYP_ISR1			0x11
+#define KEYP_IMR1			0x12
+#define KEYP_ISR2			0x13
+#define KEYP_IMR2			0x14
+#define KEYP_SIR			0x15
+#define KEYP_EDR			0x16	/* edge triggers */
+#define KEYP_SIH_CTRL			0x17
+
+/* KEYP_CTRL_REG Fields */
+#define KEYP_CTRL_SOFT_NRST		BIT(0)
+#define KEYP_CTRL_SOFTMODEN		BIT(1)
+#define KEYP_CTRL_LK_EN			BIT(2)
+#define KEYP_CTRL_TOE_EN		BIT(3)
+#define KEYP_CTRL_TOLE_EN		BIT(4)
+#define KEYP_CTRL_RP_EN			BIT(5)
+#define KEYP_CTRL_KBD_ON		BIT(6)
+
+/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
+#define KEYP_PERIOD_US(t, prescale)	((t) / (31 << (prescale + 1)) - 1)
+
+/* KEYP_LK_PTV_REG Fields */
+#define KEYP_LK_PTV_PTV_SHIFT		5
+
+/* KEYP_{IMR,ISR,SIR} Fields */
+#define KEYP_IMR1_MIS			BIT(3)
+#define KEYP_IMR1_TO			BIT(2)
+#define KEYP_IMR1_LK			BIT(1)
+#define KEYP_IMR1_KP			BIT(0)
+
+/* KEYP_EDR Fields */
+#define KEYP_EDR_KP_FALLING		0x01
+#define KEYP_EDR_KP_RISING		0x02
+#define KEYP_EDR_KP_BOTH		0x03
+#define KEYP_EDR_LK_FALLING		0x04
+#define KEYP_EDR_LK_RISING		0x08
+#define KEYP_EDR_TO_FALLING		0x10
+#define KEYP_EDR_TO_RISING		0x20
+#define KEYP_EDR_MIS_FALLING		0x40
+#define KEYP_EDR_MIS_RISING		0x80
+
+
+/*----------------------------------------------------------------------*/
+
+static int twl4030_kpread(struct twl4030_keypad *kp,
+		u8 *data, u32 reg, u8 num_bytes)
+{
+	int ret;
+
+	ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
+	if (ret < 0) {
+		dev_warn(kp->dbg_dev,
+			"Couldn't read TWL4030: %X - ret %d[%x]\n",
+			 reg, ret, ret);
+		return ret;
+	}
+	return ret;
+}
+
+static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
+{
+	int ret;
+
+	ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
+	if (ret < 0) {
+		dev_warn(kp->dbg_dev,
+			"Could not write TWL4030: %X - ret %d[%x]\n",
+			 reg, ret, ret);
+		return ret;
+	}
+	return ret;
+}
+
+static int twl4030_find_key(struct twl4030_keypad *kp, int col, int row)
+{
+	int i, rc;
+
+	rc = KEY(col, row, 0);
+	for (i = 0; i < kp->keymapsize; i++)
+		if ((kp->keymap[i] & ROWCOL_MASK) == rc)
+			return kp->keymap[i] & (KEYNUM_MASK | KEY_PERSISTENT);
+
+	return -EINVAL;
+}
+
+static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
+{
+	/* If all bits in a row are active for all coloumns then
+	 * we have that row line connected to gnd. Mark this
+	 * key on as if it was on matrix position n_cols (ie
+	 * one higher than the size of the matrix).
+	 */
+	if (col == 0xFF)
+		return 1 << kp->n_cols;
+	else
+		return col & ((1 << kp->n_cols) - 1);
+}
+
+static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
+{
+	u8 new_state[MAX_ROWS];
+	int row;
+	int ret = twl4030_kpread(kp,
+				 new_state, KEYP_FULL_CODE_7_0, kp->n_rows);
+	if (ret >= 0) {
+		for (row = 0; row < kp->n_rows; row++)
+			state[row] = twl4030_col_xlate(kp, new_state[row]);
+	}
+	return ret;
+}
+
+static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
+{
+	int i;
+	u16 check = 0;
+
+	for (i = 0; i < kp->n_rows; i++) {
+		u16 col = key_state[i];
+
+		if ((col & check) && hweight16(col) > 1)
+			return 1;
+		check |= col;
+	}
+
+	return 0;
+}
+
+static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
+{
+	u16 new_state[MAX_ROWS];
+	int col, row;
+
+	if (release_all)
+		memset(new_state, 0, sizeof(new_state));
+	else {
+		/* check for any changes */
+		int ret = twl4030_read_kp_matrix_state(kp, new_state);
+
+		if (ret < 0)	/* panic ... */
+			return;
+		if (twl4030_is_in_ghost_state(kp, new_state))
+			return;
+	}
+
+	/* check for changes and print those */
+	for (row = 0; row < kp->n_rows; row++) {
+		int changed = new_state[row] ^ kp->kp_state[row];
+
+		if (!changed)
+			continue;
+
+		for (col = 0; col < kp->n_cols; col++) {
+			int key;
+
+			if (!(changed & (1 << col)))
+				continue;
+
+			dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
+				(new_state[row] & (1 << col)) ?
+				"press" : "release");
+
+			key = twl4030_find_key(kp, col, row);
+			if (key < 0)
+				dev_warn(kp->dbg_dev,
+					"Spurious key event %d-%d\n",
+					 col, row);
+			else if (key & KEY_PERSISTENT)
+				continue;
+			else
+				input_report_key(kp->input, key,
+						 new_state[row] & (1 << col));
+		}
+		kp->kp_state[row] = new_state[row];
+	}
+	input_sync(kp->input);
+}
+
+/*
+ * Keypad interrupt handler
+ */
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+	struct twl4030_keypad *kp = _kp;
+	u8 reg;
+	int ret;
+
+#ifdef CONFIG_LOCKDEP
+	/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+	 * we don't want and can't tolerate.  Although it might be
+	 * friendlier not to borrow this thread context...
+	 */
+	local_irq_enable();
+#endif
+
+	/* Read & Clear TWL4030 pending interrupt */
+	ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
+
+	/* Release all keys if I2C has gone bad or
+	 * the KEYP has gone to idle state */
+	if ((ret >= 0) && (reg & KEYP_IMR1_KP))
+		twl4030_kp_scan(kp, 0);
+	else
+		twl4030_kp_scan(kp, 1);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Registers keypad device with input subsystem
+ * and configures TWL4030 keypad registers
+ */
+static int __devinit twl4030_kp_probe(struct platform_device *pdev)
+{
+	u8 reg;
+	int i;
+	int ret = 0;
+	struct twl4030_keypad *kp;
+	struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap
+			|| pdata->rows > 8 || pdata->cols > 8) {
+		dev_err(&pdev->dev, "Invalid platform_data\n");
+		return -EINVAL;
+	}
+
+	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, kp);
+
+	/* Get the debug Device */
+	kp->dbg_dev = &pdev->dev;
+
+	kp->input = input_allocate_device();
+	if (!kp->input) {
+		kfree(kp);
+		return -ENOMEM;
+	}
+
+	kp->keymap = pdata->keymap;
+	kp->keymapsize = pdata->keymapsize;
+	kp->n_rows = pdata->rows;
+	kp->n_cols = pdata->cols;
+	kp->irq = platform_get_irq(pdev, 0);
+
+	/* setup input device */
+	__set_bit(EV_KEY, kp->input->evbit);
+
+	/* Enable auto repeat feature of Linux input subsystem */
+	if (pdata->rep)
+		__set_bit(EV_REP, kp->input->evbit);
+
+	for (i = 0; i < kp->keymapsize; i++)
+		__set_bit(kp->keymap[i] & KEYNUM_MASK,
+				kp->input->keybit);
+
+	kp->input->name		= "TWL4030 Keypad";
+	kp->input->phys		= "twl4030_keypad/input0";
+	kp->input->dev.parent	= &pdev->dev;
+
+	kp->input->id.bustype	= BUS_HOST;
+	kp->input->id.vendor	= 0x0001;
+	kp->input->id.product	= 0x0001;
+	kp->input->id.version	= 0x0003;
+
+	kp->input->keycode	= kp->keymap;
+	kp->input->keycodesize	= sizeof(unsigned int);
+	kp->input->keycodemax	= kp->keymapsize;
+
+	ret = input_register_device(kp->input);
+	if (ret < 0) {
+		dev_err(kp->dbg_dev,
+			"Unable to register twl4030 keypad device\n");
+		goto err2;
+	}
+
+	/* Enable controller, with hardware decoding but not autorepeat */
+	reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
+		| KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_CTRL);
+	if (ret < 0)
+		goto err3;
+
+	/* NOTE:  we could use sih_setup() here to package keypad
+	 * event sources as four different IRQs ... but we don't.
+	 */
+
+	/* Enable TO rising and KP rising and falling edge detection */
+	reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_EDR);
+	if (ret < 0)
+		goto err3;
+
+	/* Set PTV prescaler Field */
+	reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV);
+	if (ret < 0)
+		goto err3;
+
+	/* Set key debounce time to 20 ms */
+	i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+	ret = twl4030_kpwrite_u8(kp, i, KEYP_DEB);
+	if (ret < 0)
+		goto err3;
+
+	/* Set timeout period to 100 ms */
+	i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+	ret = twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L);
+	if (ret < 0)
+		goto err3;
+	ret = twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H);
+	if (ret < 0)
+		goto err3;
+
+	/* Enable Clear-on-Read; disable remembering events that fire
+	 * after the IRQ but before our handler acks (reads) them,
+	 */
+	reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
+	ret = twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL);
+	if (ret < 0)
+		goto err3;
+
+	/* initialize key state; irqs update it from here on */
+	ret = twl4030_read_kp_matrix_state(kp, kp->kp_state);
+	if (ret < 0)
+		goto err3;
+
+	/*
+	 * This ISR will always execute in kernel thread context because of
+	 * the need to access the TWL4030 over the I2C bus.
+	 *
+	 * NOTE:  we assume this host is wired to TWL4040 INT1, not INT2 ...
+	 */
+	ret = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
+	if (ret < 0) {
+		dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
+			kp->irq);
+		goto err3;
+	} else {
+		/* Enable KP and TO interrupts now. */
+		reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+		ret = twl4030_kpwrite_u8(kp, reg, KEYP_IMR1);
+		if (ret < 0)
+			goto err5;
+	}
+
+	return ret;
+err5:
+	/* mask all events - we don't care about the result */
+	(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
+	free_irq(kp->irq, NULL);
+err3:
+	input_unregister_device(kp->input);
+	kp->input = NULL;
+err2:
+	input_free_device(kp->input);
+	kfree(kp);
+	return -ENODEV;
+}
+
+static int __devexit twl4030_kp_remove(struct platform_device *pdev)
+{
+	struct twl4030_keypad *kp = platform_get_drvdata(pdev);
+
+	free_irq(kp->irq, kp);
+	input_unregister_device(kp->input);
+	kfree(kp);
+
+	return 0;
+}
+
+/*
+ * NOTE: twl4030 are multi-function devices connected via I2C.
+ * So this device is a child of an I2C parent, thus it needs to
+ * support unplug/replug (which most platform devices don't).
+ */
+
+MODULE_ALIAS("platform:twl4030_keypad");
+
+static struct platform_driver twl4030_kp_driver = {
+	.probe		= twl4030_kp_probe,
+	.remove		= __devexit_p(twl4030_kp_remove),
+	.driver		= {
+		.name	= "twl4030_keypad",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init twl4030_kp_init(void)
+{
+	return platform_driver_register(&twl4030_kp_driver);
+}
+module_init(twl4030_kp_init);
+
+static void __exit twl4030_kp_exit(void)
+{
+	platform_driver_unregister(&twl4030_kp_driver);
+}
+module_exit(twl4030_kp_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TWL4030 Keypad Driver");
+MODULE_LICENSE("GPL");
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -302,12 +302,19 @@ struct twl4030_madc_platform_data {
 	int		irq_line;
 };
 
+/* Boards have uniqe mappings of {col, row} --> keycode.
+ * Column and row are 4 bits, but range only from 0..7;
+ * a PERSISTENT_KEY is "always on" and never reported.
+ */
+#define KEY_PERSISTENT		0x00800000
+#define KEY(col, row, keycode)	(((col) << 28) | ((row) << 24) | (keycode))
+#define PERSISTENT_KEY(c, r)	KEY((c), (r), KEY_PERSISTENT)
+
 struct twl4030_keypad_data {
-	int rows;
-	int cols;
-	int *keymap;
-	int irq;
-	unsigned int keymapsize;
+	unsigned rows;
+	unsigned cols;
+	unsigned *keymap;
+	unsigned short keymapsize;
 	unsigned int rep:1;
 };
 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch 2.6.29-rc3-git] input:  twl4030_keypad driver
  2009-04-21  6:22 ` David Brownell
@ 2009-04-24  2:12   ` Dmitry Torokhov
  2009-04-24  9:09     ` David Brownell
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry Torokhov @ 2009-04-24  2:12 UTC (permalink / raw)
  To: David Brownell; +Cc: linux-input, OMAP

On Mon, Apr 20, 2009 at 11:22:41PM -0700, David Brownell wrote:
> On Friday 06 February 2009, David Brownell wrote:
> > From: David Brownell <dbrownell@users.sourceforge.net>
> > 
> > Add a driver for the keypad controller on TWL4030 family chips.
> 
> PING?  I was told this was in the input queue, but it's not in
> 
>   http://git.kernel.org/?p=linux/kernel/git/dtor/input.git
> 
> Here's a current version of the patch.
> 

Dave,

It waqs sitting in my local queue, I had some concerns over the keymap
change as it was implemented in the version you sent me. The problem is
that you mangle key codes in your keymap table (encoding row/col data in
them) but input core is not aware of that and when you try using
EVIOCSETKEYCODE it will do wierd things. I was wondering what you would
think about the following patch that should rectify this issue.

Also, I don't think we need the special handling for "persistant" keys.
Just let these keys generate KEY_RESERVED and input core will not
propagate their events.

Thanks!

-- 
Dmitry


Input: twl4030_kepad fixups

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/keyboard/twl4030_keypad.c |   62 ++++++++++---------------------
 include/linux/i2c/twl4030.h             |   18 +++++++--
 2 files changed, 32 insertions(+), 48 deletions(-)


diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
index 987f13c..b761cac 100644
--- a/drivers/input/keyboard/twl4030_keypad.c
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -48,23 +48,18 @@
  * See the TPS65950 documentation; that's the general availability
  * version of the TWL5030 second generation part.
  */
-#define MAX_ROWS		8	/* TWL4030 hard limit */
 
 struct twl4030_keypad {
-	unsigned	*keymap;
-	unsigned int	keymapsize;
-	u16		kp_state[MAX_ROWS];
-	unsigned	n_rows;
-	unsigned	n_cols;
+	unsigned short	keymap[TWL4030_KEYMAP_SIZE];
+	u16		kp_state[TWL4030_MAX_ROWS];
+	u8		n_rows;
+	u8		n_cols;
 	unsigned	irq;
 
 	struct device	*dbg_dev;
 	struct input_dev *input;
 };
 
-#define ROWCOL_MASK	KEY(0xf, 0xf, 0)
-#define KEYNUM_MASK	~PERSISTENT_KEY(0xf, 0xf)
-
 /*----------------------------------------------------------------------*/
 
 /* arbitrary prescaler value 0..7 */
@@ -156,18 +151,6 @@ static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
 	return ret;
 }
 
-static int twl4030_find_key(struct twl4030_keypad *kp, int col, int row)
-{
-	int i, rc;
-
-	rc = KEY(col, row, 0);
-	for (i = 0; i < kp->keymapsize; i++)
-		if ((kp->keymap[i] & ROWCOL_MASK) == rc)
-			return kp->keymap[i] & (KEYNUM_MASK | KEY_PERSISTENT);
-
-	return -EINVAL;
-}
-
 static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
 {
 	/* If all bits in a row are active for all coloumns then
@@ -183,7 +166,7 @@ static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
 
 static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
 {
-	u8 new_state[MAX_ROWS];
+	u8 new_state[TWL4030_MAX_ROWS];
 	int row;
 	int ret = twl4030_kpread(kp,
 				 new_state, KEYP_FULL_CODE_7_0, kp->n_rows);
@@ -213,7 +196,8 @@ static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
 
 static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
 {
-	u16 new_state[MAX_ROWS];
+	struct input_dev *input = kp->input;
+	u16 new_state[TWL4030_MAX_ROWS];
 	int col, row;
 
 	if (release_all)
@@ -246,20 +230,13 @@ static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
 				(new_state[row] & (1 << col)) ?
 				"press" : "release");
 
-			key = twl4030_find_key(kp, col, row);
-			if (key < 0)
-				dev_warn(kp->dbg_dev,
-					"Spurious key event %d-%d\n",
-					 col, row);
-			else if (key & KEY_PERSISTENT)
-				continue;
-			else
-				input_report_key(kp->input, key,
-						 new_state[row] & (1 << col));
+			key = kp->keymap[(row << 3) | col];
+			input_report_key(input, key,
+					 new_state[row] & (1 << col));
 		}
 		kp->kp_state[row] = new_state[row];
 	}
-	input_sync(kp->input);
+	input_sync(input);
 }
 
 /*
@@ -358,8 +335,8 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
 	int i;
 	int error;
 
-	if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap
-			|| pdata->rows > 8 || pdata->cols > 8) {
+	if (!pdata || !pdata->rows || !pdata->cols ||
+	    pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
 		dev_err(&pdev->dev, "Invalid platform_data\n");
 		return -EINVAL;
 	}
@@ -373,11 +350,9 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
 
 	/* Get the debug Device */
 	kp->dbg_dev = &pdev->dev;
-
 	kp->input = input;
 
-	kp->keymap = pdata->keymap;
-	kp->keymapsize = pdata->keymapsize;
+	memcpy(kp->keymap, pdata->keymap, sizeof(pdata->keymap));
 	kp->n_rows = pdata->rows;
 	kp->n_cols = pdata->cols;
 	kp->irq = platform_get_irq(pdev, 0);
@@ -389,8 +364,9 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
 	if (pdata->rep)
 		__set_bit(EV_REP, kp->input->evbit);
 
-	for (i = 0; i < kp->keymapsize; i++)
-		__set_bit(kp->keymap[i] & KEYNUM_MASK, input->keybit);
+	for (i = 0; i < ARRAY_SIZE(kp->keymap); i++)
+		__set_bit(kp->keymap[i], input->keybit);
+	__clear_bit(KEY_RESERVED, input->keybit);
 
 	input->name		= "TWL4030 Keypad";
 	input->phys		= "twl4030_keypad/input0";
@@ -402,8 +378,8 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
 	input->id.version	= 0x0003;
 
 	input->keycode		= kp->keymap;
-	input->keycodesize	= sizeof(unsigned int);
-	input->keycodemax	= kp->keymapsize;
+	input->keycodesize	= sizeof(kp->keymap[0]);
+	input->keycodemax	= ARRAY_SIZE(kp->keymap);
 
 	error = input_register_device(input);
 	if (error) {
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
index 6b9722d..e493d2a 100644
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -25,6 +25,8 @@
 #ifndef __TWL4030_H_
 #define __TWL4030_H_
 
+#include <linux/types.h>
+
 /*
  * Using the twl4030 core we address registers using a pair
  *	{ module id, relative register offset }
@@ -306,16 +308,22 @@ struct twl4030_madc_platform_data {
  * Column and row are 4 bits, but range only from 0..7;
  * a PERSISTENT_KEY is "always on" and never reported.
  */
+
+#define TWL4030_MAX_ROWS	8
+#define TWL4030_MAX_COLS	8
+#define TWL4030_KEYMAP_SIZE	(TWL4030_MAX_ROWS * TWL4030_MAX_COLS)
+
+/*
 #define KEY_PERSISTENT		0x00800000
 #define KEY(col, row, keycode)	(((col) << 28) | ((row) << 24) | (keycode))
 #define PERSISTENT_KEY(c, r)	KEY((c), (r), KEY_PERSISTENT)
+*/
 
 struct twl4030_keypad_data {
-	unsigned rows;
-	unsigned cols;
-	unsigned *keymap;
-	unsigned short keymapsize;
-	unsigned int rep:1;
+	unsigned short keymap[TWL4030_KEYMAP_SIZE];
+	u8 rows;
+	u8 cols;
+	bool rep;
 };
 
 enum twl4030_usb_mode {

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [patch 2.6.29-rc3-git] input:  twl4030_keypad driver
  2009-04-24  2:12   ` Dmitry Torokhov
@ 2009-04-24  9:09     ` David Brownell
  0 siblings, 0 replies; 6+ messages in thread
From: David Brownell @ 2009-04-24  9:09 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, OMAP

On Thursday 23 April 2009, Dmitry Torokhov wrote:
> > 
> 
> Dave,
> 
> It waqs sitting in my local queue, I had some concerns over the keymap
> change as it was implemented in the version you sent me. The problem is
> that you mangle key codes in your keymap table (encoding row/col data in

Well, not really *me* I didn't write any of this.  I just
cleaned it up and sent it along to help get this stuff out
of the OMAP tree, into mainline where it belongs.


> them) but input core is not aware of that and when you try using
> EVIOCSETKEYCODE it will do wierd things. I was wondering what you would
> think about the following patch that should rectify this issue.

It invalidates all the existing keypad tables, which have been
waiting for this driver to merge before they go upstream ... it'd
be simpler just to prevent EVIOCSETKEYCODE calls.  Or provide
update methods that understand the structure of those entries;
conceptually they're a "struct { scancode; keycode; }" though
it's not coded that way

Needing to take 512 bytes per keytable -- vs the keypad-specific
sizes, typically much less even if using a qwerty -- is also a
minor issue.  Keypads with 256 keys are *really* unusual!  Most
current ones are smaller ... the biggest I've seen is 44.


> Also, I don't think we need the special handling for "persistant" keys.
> Just let these keys generate KEY_RESERVED and input core will not
> propagate their events.

I was never sure what to make of that, it seemed like a hack.
Only the "Labrador" boards (since renamed "Zoom1") seem to
need that mechanism.

So I'm not sure whether that would be appropriate.  If it is,
then the keytable construction macros could just change.  But
is the input core aware that it shouldn't remap such things?

- Dave

p.s. a few comments are below.


> 
> Thanks!
> 
> -- 
> Dmitry
> 
> 
> Input: twl4030_kepad fixups
> 
> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
> ---
> 
>  drivers/input/keyboard/twl4030_keypad.c |   62 ++++++++++---------------------
>  include/linux/i2c/twl4030.h             |   18 +++++++--
>  2 files changed, 32 insertions(+), 48 deletions(-)
> 
> 
> diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
> index 987f13c..b761cac 100644
> --- a/drivers/input/keyboard/twl4030_keypad.c
> +++ b/drivers/input/keyboard/twl4030_keypad.c
> @@ -48,23 +48,18 @@
>   * See the TPS65950 documentation; that's the general availability
>   * version of the TWL5030 second generation part.
>   */
> -#define MAX_ROWS		8	/* TWL4030 hard limit */
>  
>  struct twl4030_keypad {
> -	unsigned	*keymap;
> -	unsigned int	keymapsize;
> -	u16		kp_state[MAX_ROWS];
> -	unsigned	n_rows;
> -	unsigned	n_cols;
> +	unsigned short	keymap[TWL4030_KEYMAP_SIZE];

The keypad size is board-specific; not all possible switch
settings are used.


> +	u16		kp_state[TWL4030_MAX_ROWS];

TWL4030_MAX_ROWS makes sense, although the same keypad macros
are used in some other OMAP boards that don't use TWL4030 family
chips, so it's not really TWL-specific.  (OMAP1 boards often use
the "omap-keypad" driver.)


> +	u8		n_rows;
> +	u8		n_cols;

Didn't really need to change those.  This is one of the cases
where the code to read a byte then zero-extend it uses more
space than using a 32-bit unsigned value instead of 8-bit.  :)


>  	unsigned	irq;
>  
>  	struct device	*dbg_dev;
>  	struct input_dev *input;
>  };
>  
> -#define ROWCOL_MASK	KEY(0xf, 0xf, 0)
> -#define KEYNUM_MASK	~PERSISTENT_KEY(0xf, 0xf)

This being a side-effect of changing the key table encoding...

> -
>  /*----------------------------------------------------------------------*/
>  
>  /* arbitrary prescaler value 0..7 */
> @@ -156,18 +151,6 @@ static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
>  	return ret;
>  }
>  
> -static int twl4030_find_key(struct twl4030_keypad *kp, int col, int row)
> -{
> -	int i, rc;
> -
> -	rc = KEY(col, row, 0);
> -	for (i = 0; i < kp->keymapsize; i++)
> -		if ((kp->keymap[i] & ROWCOL_MASK) == rc)
> -			return kp->keymap[i] & (KEYNUM_MASK | KEY_PERSISTENT);
> -
> -	return -EINVAL;
> -}
> -
>  static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
>  {
>  	/* If all bits in a row are active for all coloumns then
> @@ -183,7 +166,7 @@ static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
>  
>  static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
>  {
> -	u8 new_state[MAX_ROWS];
> +	u8 new_state[TWL4030_MAX_ROWS];
>  	int row;
>  	int ret = twl4030_kpread(kp,
>  				 new_state, KEYP_FULL_CODE_7_0, kp->n_rows);
> @@ -213,7 +196,8 @@ static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
>  
>  static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
>  {
> -	u16 new_state[MAX_ROWS];
> +	struct input_dev *input = kp->input;
> +	u16 new_state[TWL4030_MAX_ROWS];
>  	int col, row;
>  
>  	if (release_all)
> @@ -246,20 +230,13 @@ static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
>  				(new_state[row] & (1 << col)) ?
>  				"press" : "release");
>  
> -			key = twl4030_find_key(kp, col, row);
> -			if (key < 0)
> -				dev_warn(kp->dbg_dev,
> -					"Spurious key event %d-%d\n",
> -					 col, row);
> -			else if (key & KEY_PERSISTENT)
> -				continue;
> -			else
> -				input_report_key(kp->input, key,
> -						 new_state[row] & (1 << col));
> +			key = kp->keymap[(row << 3) | col];
> +			input_report_key(input, key,
> +					 new_state[row] & (1 << col));

That being the guts of this patchlet:  using a flat table
lookup instead of a key/value search.


>  		}
>  		kp->kp_state[row] = new_state[row];
>  	}
> -	input_sync(kp->input);
> +	input_sync(input);
>  }
>  
>  /*
> @@ -358,8 +335,8 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
>  	int i;
>  	int error;
>  
> -	if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap
> -			|| pdata->rows > 8 || pdata->cols > 8) {
> +	if (!pdata || !pdata->rows || !pdata->cols ||
> +	    pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
>  		dev_err(&pdev->dev, "Invalid platform_data\n");
>  		return -EINVAL;
>  	}
> @@ -373,11 +350,9 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
>  
>  	/* Get the debug Device */
>  	kp->dbg_dev = &pdev->dev;
> -
>  	kp->input = input;
>  
> -	kp->keymap = pdata->keymap;
> -	kp->keymapsize = pdata->keymapsize;
> +	memcpy(kp->keymap, pdata->keymap, sizeof(pdata->keymap));

Alternatively build a table of the "struct { scancode; keycode; }"
things here ... or update the table construction macros so that's
what they get in the first place (instead of integers with bitfields).


>  	kp->n_rows = pdata->rows;
>  	kp->n_cols = pdata->cols;
>  	kp->irq = platform_get_irq(pdev, 0);
> @@ -389,8 +364,9 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
>  	if (pdata->rep)
>  		__set_bit(EV_REP, kp->input->evbit);
>  
> -	for (i = 0; i < kp->keymapsize; i++)
> -		__set_bit(kp->keymap[i] & KEYNUM_MASK, input->keybit);
> +	for (i = 0; i < ARRAY_SIZE(kp->keymap); i++)
> +		__set_bit(kp->keymap[i], input->keybit);
> +	__clear_bit(KEY_RESERVED, input->keybit);

And I see KEY_RESERVED == 0, which is implicitly relied
on by the way most of that keymap is empty.


>  
>  	input->name		= "TWL4030 Keypad";
>  	input->phys		= "twl4030_keypad/input0";
> @@ -402,8 +378,8 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
>  	input->id.version	= 0x0003;
>  
>  	input->keycode		= kp->keymap;
> -	input->keycodesize	= sizeof(unsigned int);
> -	input->keycodemax	= kp->keymapsize;
> +	input->keycodesize	= sizeof(kp->keymap[0]);
> +	input->keycodemax	= ARRAY_SIZE(kp->keymap);
>  
>  	error = input_register_device(input);
>  	if (error) {
> diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
> index 6b9722d..e493d2a 100644
> --- a/include/linux/i2c/twl4030.h
> +++ b/include/linux/i2c/twl4030.h
> @@ -25,6 +25,8 @@
>  #ifndef __TWL4030_H_
>  #define __TWL4030_H_
>  
> +#include <linux/types.h>
> +
>  /*
>   * Using the twl4030 core we address registers using a pair
>   *	{ module id, relative register offset }
> @@ -306,16 +308,22 @@ struct twl4030_madc_platform_data {
>   * Column and row are 4 bits, but range only from 0..7;
>   * a PERSISTENT_KEY is "always on" and never reported.
>   */
> +
> +#define TWL4030_MAX_ROWS	8
> +#define TWL4030_MAX_COLS	8
> +#define TWL4030_KEYMAP_SIZE	(TWL4030_MAX_ROWS * TWL4030_MAX_COLS)
> +
> +/*
>  #define KEY_PERSISTENT		0x00800000
>  #define KEY(col, row, keycode)	(((col) << 28) | ((row) << 24) | (keycode))
>  #define PERSISTENT_KEY(c, r)	KEY((c), (r), KEY_PERSISTENT)

If your KEY_RESERVED thing checks out,

  PERSISTENT_KEY(c,r) == KEY((c), (r), KEY_RESERVED)

Also, someone had commented that a bunch of other drivers need
basic scancode-to-keycode table support ... so maybe this kind
of stuff should become more standardized, instead of requiring
every driver to re-invent this roundy wheel-ish thing.


> +*/
>  
>  struct twl4030_keypad_data {
> -	unsigned rows;
> -	unsigned cols;
> -	unsigned *keymap;
> -	unsigned short keymapsize;
> -	unsigned int rep:1;
> +	unsigned short keymap[TWL4030_KEYMAP_SIZE];
> +	u8 rows;
> +	u8 cols;
> +	bool rep;
>  };
>  
>  enum twl4030_usb_mode {
> 
> 



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2009-04-24  9:09 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-06  8:57 [patch 2.6.29-rc3-git] input: twl4030_keypad driver David Brownell
2009-02-06  9:16 ` Gadiyar, Anand
2009-02-06 10:33   ` David Brownell
2009-04-21  6:22 ` David Brownell
2009-04-24  2:12   ` Dmitry Torokhov
2009-04-24  9:09     ` David Brownell

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).