All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rabin Vincent <rabin.vincent@stericsson.com>
To: <linux-kernel@vger.kernel.org>
Cc: Samuel Ortiz <sameo@linux.intel.com>,
	<STEricsson_nomadik_linux@list.st.com>,
	<linus.walleij@stericsson.com>, <l.fu@pengutronix.de>,
	Rabin Vincent <rabin.vincent@stericsson.com>
Subject: [PATCHv3 3/3] input: add STMPE keypad driver
Date: Thu, 1 Jul 2010 17:30:48 +0530	[thread overview]
Message-ID: <1277985648-28468-3-git-send-email-rabin.vincent@stericsson.com> (raw)
In-Reply-To: <20100627235515.GB9264@sortiz-mobl>

Add an input driver for the keypad on STMPE I/O expanders.  This driver
uses the common support provided by the STMPE MFD driver.

Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Acked-by: Linus Walleij <linus.walleij@stericsson.com>
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
---
 drivers/input/keyboard/Kconfig        |   10 +
 drivers/input/keyboard/Makefile       |    1 +
 drivers/input/keyboard/stmpe-keypad.c |  386 +++++++++++++++++++++++++++++++++
 3 files changed, 397 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/stmpe-keypad.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index d8fa5d7..699aff9 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -374,6 +374,16 @@ config KEYBOARD_SH_KEYSC
 	  To compile this driver as a module, choose M here: the
 	  module will be called sh_keysc.
 
+config KEYBOARD_STMPE
+	tristate "STMPE keypad support"
+	depends on MFD_STMPE
+	help
+	  Say Y here if you want to use the keypad controller on STMPE I/O
+	  expanders.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called stmpe-keypad.
+
 config KEYBOARD_DAVINCI
 	tristate "TI DaVinci Key Scan"
 	depends on ARCH_DAVINCI_DM365
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 4596d0c..af24b07 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_KEYBOARD_PXA27x)		+= pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)	+= pxa930_rotary.o
 obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
+obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
 obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
new file mode 100644
index 0000000..ab7610c
--- /dev/null
+++ b/drivers/input/keyboard/stmpe-keypad.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/stmpe.h>
+
+/* These are at the same addresses in all STMPE variants */
+#define STMPE_KPC_COL			0x60
+#define STMPE_KPC_ROW_MSB		0x61
+#define STMPE_KPC_ROW_LSB		0x62
+#define STMPE_KPC_CTRL_MSB		0x63
+#define STMPE_KPC_CTRL_LSB		0x64
+#define STMPE_KPC_COMBI_KEY_0		0x65
+#define STMPE_KPC_COMBI_KEY_1		0x66
+#define STMPE_KPC_COMBI_KEY_2		0x67
+#define STMPE_KPC_DATA_BYTE0		0x68
+#define STMPE_KPC_DATA_BYTE1		0x69
+#define STMPE_KPC_DATA_BYTE2		0x6a
+#define STMPE_KPC_DATA_BYTE3		0x6b
+#define STMPE_KPC_DATA_BYTE4		0x6c
+
+#define STMPE_KPC_CTRL_LSB_SCAN		(0x1 << 0)
+#define STMPE_KPC_CTRL_LSB_DEBOUNCE	(0x7f << 1)
+#define STMPE_KPC_CTRL_MSB_SCAN_COUNT	(0xf << 4)
+
+#define STMPE_KPC_ROW_MSB_ROWS		0xff
+
+#define STMPE_KPC_DATA_UP		(0x1 << 7)
+#define STMPE_KPC_DATA_ROW		(0xf << 3)
+#define STMPE_KPC_DATA_COL		(0x7 << 0)
+#define STMPE_KPC_DATA_NOKEY_MASK	0x78
+
+#define STMPE_KEYPAD_MAX_DEBOUNCE	127
+#define STMPE_KEYPAD_MAX_SCAN_COUNT	15
+
+#define STMPE_KEYPAD_MAX_ROWS		8
+#define STMPE_KEYPAD_MAX_COLS		8
+#define STMPE_KEYPAD_ROW_SHIFT		3
+#define STMPE_KEYPAD_KEYMAP_SIZE	\
+	(STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
+
+/**
+ * struct stmpe_keypad_variant - model-specific attributes
+ * @auto_increment: whether the KPC_DATA_BYTE register address
+ *		    auto-increments on multiple read
+ * @num_data: number of data bytes
+ * @num_normal_data: number of normal keys' data bytes
+ * @max_cols: maximum number of columns supported
+ * @max_rows: maximum number of rows supported
+ * @col_gpios: bitmask of gpios which can be used for columns
+ * @row_gpios: bitmask of gpios which can be used for rows
+ */
+struct stmpe_keypad_variant {
+	bool		auto_increment;
+	int		num_data;
+	int		num_normal_data;
+	int		max_cols;
+	int		max_rows;
+	unsigned int	col_gpios;
+	unsigned int	row_gpios;
+};
+
+static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
+	[STMPE1601] = {
+		.auto_increment		= true,
+		.num_data		= 5,
+		.num_normal_data	= 3,
+		.max_cols		= 8,
+		.max_rows		= 8,
+		.col_gpios		= 0x000ff,	/* GPIO 0 - 7 */
+		.row_gpios		= 0x0ff00,	/* GPIO 8 - 15 */
+	},
+	[STMPE2401] = {
+		.auto_increment		= false,
+		.num_data		= 3,
+		.num_normal_data	= 2,
+		.max_cols		= 8,
+		.max_rows		= 12,
+		.col_gpios		= 0x0000ff,	/* GPIO 0 - 7*/
+		.row_gpios		= 0x1fef00,	/* GPIO 8-14, 16-20 */
+	},
+	[STMPE2403] = {
+		.auto_increment		= true,
+		.num_data		= 5,
+		.num_normal_data	= 3,
+		.max_cols		= 8,
+		.max_rows		= 12,
+		.col_gpios		= 0x0000ff,	/* GPIO 0 - 7*/
+		.row_gpios		= 0x1fef00,	/* GPIO 8-14, 16-20 */
+	},
+};
+
+struct stmpe_keypad {
+	struct stmpe *stmpe;
+	struct input_dev *input;
+	const struct stmpe_keypad_variant *variant;
+	const struct stmpe_keypad_platform_data *plat;
+
+	unsigned int rows;
+	unsigned int cols;
+
+	unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE];
+};
+
+static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
+{
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	struct stmpe *stmpe = keypad->stmpe;
+	int ret;
+	int i;
+
+	if (variant->auto_increment)
+		return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
+					variant->num_data, data);
+
+	for (i = 0; i < variant->num_data; i++) {
+		ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
+		if (ret < 0)
+			return ret;
+
+		data[i] = ret;
+	}
+
+	return 0;
+}
+
+static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
+{
+	struct stmpe_keypad *keypad = dev;
+	struct input_dev *input = keypad->input;
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	u8 fifo[variant->num_data];
+	int ret;
+	int i;
+
+	ret = stmpe_keypad_read_data(keypad, fifo);
+	if (ret < 0)
+		return IRQ_NONE;
+
+	for (i = 0; i < variant->num_normal_data; i++) {
+		u8 data = fifo[i];
+		int row = (data & STMPE_KPC_DATA_ROW) >> 3;
+		int col = data & STMPE_KPC_DATA_COL;
+		int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
+		bool up = data & STMPE_KPC_DATA_UP;
+
+		if ((data & STMPE_KPC_DATA_NOKEY_MASK)
+			== STMPE_KPC_DATA_NOKEY_MASK)
+			continue;
+
+		input_event(input, EV_MSC, MSC_SCAN, code);
+		input_report_key(input, keypad->keymap[code], !up);
+		input_sync(input);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
+{
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	unsigned int col_gpios = variant->col_gpios;
+	unsigned int row_gpios = variant->row_gpios;
+	struct stmpe *stmpe = keypad->stmpe;
+	unsigned int pins = 0;
+	int i;
+
+	/*
+	 * Figure out which pins need to be set to the keypad alternate
+	 * function.
+	 *
+	 * {cols,rows}_gpios are bitmasks of which pins on the chip can be used
+	 * for the keypad.
+	 *
+	 * keypad->{cols,rows} are a bitmask of which pins (of the ones useable
+	 * for the keypad) are used on the board.
+	 */
+
+	for (i = 0; i < variant->max_cols; i++) {
+		int num = __ffs(col_gpios);
+
+		if (keypad->cols & (1 << i))
+			pins |= 1 << num;
+
+		col_gpios &= ~(1 << num);
+	}
+
+	for (i = 0; i < variant->max_rows; i++) {
+		int num = __ffs(row_gpios);
+
+		if (keypad->rows & (1 << i))
+			pins |= 1 << num;
+
+		row_gpios &= ~(1 << num);
+	}
+
+	return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
+}
+
+static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
+{
+	const struct stmpe_keypad_platform_data *plat = keypad->plat;
+	const struct stmpe_keypad_variant *variant = keypad->variant;
+	struct stmpe *stmpe = keypad->stmpe;
+	int ret;
+
+	if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
+		return -EINVAL;
+
+	if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
+		return -EINVAL;
+
+	ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+	if (ret < 0)
+		return ret;
+
+	ret = stmpe_keypad_altfunc_init(keypad);
+	if (ret < 0)
+		return ret;
+
+	ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
+	if (ret < 0)
+		return ret;
+
+	ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
+	if (ret < 0)
+		return ret;
+
+	if (variant->max_rows > 8) {
+		ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
+				     STMPE_KPC_ROW_MSB_ROWS,
+				     keypad->rows >> 8);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
+			     STMPE_KPC_CTRL_MSB_SCAN_COUNT,
+			     plat->scan_count << 4);
+	if (ret < 0)
+		return ret;
+
+	return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
+			      STMPE_KPC_CTRL_LSB_SCAN |
+			      STMPE_KPC_CTRL_LSB_DEBOUNCE,
+			      STMPE_KPC_CTRL_LSB_SCAN |
+			      (plat->debounce_ms << 1));
+}
+
+static int __devinit stmpe_keypad_probe(struct platform_device *pdev)
+{
+	struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+	struct stmpe_keypad_platform_data *plat;
+	struct stmpe_keypad *keypad;
+	struct input_dev *input;
+	int ret;
+	int irq;
+	int i;
+
+	plat = stmpe->pdata->keypad;
+	if (!plat)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL);
+	if (!keypad)
+		return -ENOMEM;
+
+	input = input_allocate_device();
+	if (!input) {
+		ret = -ENOMEM;
+		goto out_freekeypad;
+	}
+
+	input->name = "STMPE keypad";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &pdev->dev;
+
+	input_set_capability(input, EV_MSC, MSC_SCAN);
+
+	__set_bit(EV_KEY, input->evbit);
+	if (!plat->no_autorepeat)
+		__set_bit(EV_REP, input->evbit);
+
+	input->keycode = keypad->keymap;
+	input->keycodesize = sizeof(keypad->keymap[0]);
+	input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+	matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT,
+				   input->keycode, input->keybit);
+
+	for (i = 0; i < plat->keymap_data->keymap_size; i++) {
+		unsigned int key = plat->keymap_data->keymap[i];
+
+		keypad->cols |= 1 << KEY_COL(key);
+		keypad->rows |= 1 << KEY_ROW(key);
+	}
+
+	keypad->stmpe = stmpe;
+	keypad->plat = plat;
+	keypad->input = input;
+	keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
+
+	ret = stmpe_keypad_chip_init(keypad);
+	if (ret < 0)
+		goto out_freeinput;
+
+	ret = input_register_device(input);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"unable to register input device: %d\n", ret);
+		goto out_freeinput;
+	}
+
+	ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT,
+				   "stmpe-keypad", keypad);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+		goto out_unregisterinput;
+	}
+
+	platform_set_drvdata(pdev, keypad);
+
+	return 0;
+
+out_unregisterinput:
+	input_unregister_device(input);
+	input = NULL;
+out_freeinput:
+	input_free_device(input);
+out_freekeypad:
+	kfree(keypad);
+	return ret;
+}
+
+static int __devexit stmpe_keypad_remove(struct platform_device *pdev)
+{
+	struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+	struct stmpe *stmpe = keypad->stmpe;
+	int irq = platform_get_irq(pdev, 0);
+
+	stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
+
+	free_irq(irq, keypad);
+	input_unregister_device(keypad->input);
+	platform_set_drvdata(pdev, NULL);
+	kfree(keypad);
+
+	return 0;
+}
+
+static struct platform_driver stmpe_keypad_driver = {
+	.driver.name	= "stmpe-keypad",
+	.driver.owner	= THIS_MODULE,
+	.probe		= stmpe_keypad_probe,
+	.remove		= __devexit_p(stmpe_keypad_remove),
+};
+
+static int __init stmpe_keypad_init(void)
+{
+	return platform_driver_register(&stmpe_keypad_driver);
+}
+module_init(stmpe_keypad_init);
+
+static void __exit stmpe_keypad_exit(void)
+{
+	platform_driver_unregister(&stmpe_keypad_driver);
+}
+module_exit(stmpe_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx keypad driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
-- 
1.7.0


  parent reply	other threads:[~2010-07-01 12:01 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-05-31 12:17 [PATCH 1/3] mfd: add STMPExxxx I/O Expander support Rabin Vincent
2010-05-31 12:17 ` [PATCH 2/3] gpio: add STMPExxxx GPIO driver Rabin Vincent
2010-05-31 12:17 ` [PATCH 3/3] input: add STMPExxxx keypad driver Rabin Vincent
2010-05-31 12:17   ` Rabin Vincent
2010-06-01 22:16   ` Dmitry Torokhov
2010-06-02 13:56     ` Rabin VINCENT
2010-06-02 16:05       ` Dmitry Torokhov
2010-06-18 23:42 ` [PATCH 1/3] mfd: add STMPExxxx I/O Expander support Samuel Ortiz
2010-06-19 13:50   ` Luotao Fu
2010-06-21 13:33   ` Rabin VINCENT
2010-06-21 15:45     ` Luotao Fu
2010-06-22 13:55       ` [PATCHv2 1/3] mfd: add STMPE " Rabin Vincent
2010-06-27 23:55         ` Samuel Ortiz
2010-06-29  3:13           ` Rabin VINCENT
2010-06-29 15:33             ` Samuel Ortiz
2010-07-01 12:00           ` [PATCHv3 " Rabin Vincent
2010-07-01 12:34             ` Luotao Fu
2010-07-02 11:22               ` [PATCHv4 " Rabin Vincent
2010-07-02 15:31                 ` Samuel Ortiz
2010-07-02 11:22               ` [PATCHv4 2/3] gpio: add STMPE GPIO driver Rabin Vincent
2010-07-02 11:22               ` [PATCHv4 3/3] input: add STMPE keypad driver Rabin Vincent
2010-07-02 12:10               ` [RESEND] [PATCH V8] input: STMPE touch controller support Luotao Fu
2010-07-05 14:53                 ` Samuel Ortiz
2010-07-01 12:00           ` [PATCHv3 2/3] gpio: add STMPE GPIO driver Rabin Vincent
2010-07-01 12:29             ` Luotao Fu
2010-07-01 12:00           ` Rabin Vincent [this message]
2010-06-22 13:55       ` [PATCHv2 " Rabin Vincent
2010-06-22 13:55       ` [PATCHv2 3/3] input: add STMPE keypad driver Rabin Vincent
2010-06-22 13:56       ` [PATCH 1/3] mfd: add STMPExxxx I/O Expander support Rabin VINCENT
2010-06-24 11:13         ` mfd: STMPExxxx fixes and touch screen support Luotao Fu
2010-06-24 11:13         ` [PATCH 1/6] gpio/stmpe-gpio: set GPIO alternate function while requesting Luotao Fu
2010-06-24 12:43           ` Rabin VINCENT
2010-06-24 11:13         ` [PATCH 2/6] gpio/stmpe-gpio: fix set direction input Luotao Fu
2010-06-24 12:03           ` Rabin VINCENT
2010-06-24 11:13         ` [PATCH 3/6] mfd/stmpexxx: add touchscreen platform data Luotao Fu
2010-06-24 11:13         ` [PATCH 4/6] mfd/stmpexxx: change touchscreen irq Luotao Fu
2010-06-24 13:09           ` Rabin VINCENT
2010-06-24 13:17             ` Luotao Fu
2010-06-24 11:13         ` [PATCH 5/6] mfd/stmpexxx: fix stmpe811 enable hook Luotao Fu
2010-06-24 12:11           ` Rabin VINCENT
2010-06-24 12:32             ` Luotao Fu
2010-06-24 12:47               ` [PATCH 5/6 V3] " Luotao Fu
2010-06-24 13:05                 ` Rabin VINCENT
2010-06-24 11:13         ` [PATCH 6/6 V4] input: STMPE touch controller support Luotao Fu
2010-06-24 12:27           ` [PATCH 5/6 V2] mfd/stmpexxx: fix stmpe811 enable hook Luotao Fu
2010-06-24 12:35             ` Rabin VINCENT
2010-06-24 12:46               ` Luotao Fu
2010-06-24 12:28           ` [PATCH 6/6 V5] input: STMPE touch controller support Luotao Fu
2010-06-24 14:26             ` [PATCH 5/5] " Luotao Fu
2010-06-24 16:24               ` Dmitry Torokhov
2010-06-24 16:57                 ` Luotao Fu
2010-06-25  8:37                 ` [PATCH 5/5 V7] " Luotao Fu
2010-06-25  9:11                   ` Dmitry Torokhov
2010-06-25  9:32                     ` Luotao Fu
2010-06-27 21:24                     ` Samuel Ortiz
2010-06-25  9:34                   ` [PATCH 5/5 V8] " Luotao Fu
2010-06-24 12:31           ` [PATCH 6/6 V4] " Rabin VINCENT
2010-06-24 12:42             ` Luotao Fu
2010-06-24 13:01               ` Rabin VINCENT
2010-06-24 13:11                 ` Luotao Fu
2010-06-24 13:01               ` Rabin VINCENT

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=1277985648-28468-3-git-send-email-rabin.vincent@stericsson.com \
    --to=rabin.vincent@stericsson.com \
    --cc=STEricsson_nomadik_linux@list.st.com \
    --cc=l.fu@pengutronix.de \
    --cc=linus.walleij@stericsson.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sameo@linux.intel.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.