All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] add tnetv107x input drivers
@ 2010-09-16 19:54 Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller Cyril Chemparathy
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Cyril Chemparathy @ 2010-09-16 19:54 UTC (permalink / raw)
  To: linux-input, davinci-linux-open-source
  Cc: shubhrajyoti, dmitry.torokhov, khilman, Cyril Chemparathy

Texas Instruments' TNETV107X is an ARM1176 based SoC, with on-chip
touchscreen and keypad controllers.  This patch series adds drivers for these
controllers.

Changes between v1 and v2:
  1. Cleaned up platform data needs by reusing matrix keypad pdata
  2. Eliminated calibration code from touch driver
  3. Relocated h/w init code to open and close
  4. Other minor cleanups

Cyril Chemparathy (5):
  input: add driver for tnetv107x on-chip keypad controller
  davinci: add tnetv107x keypad platform device
  davinci: add keypad config for tnetv107x evm board
  input: add driver for tnetv107x touchscreen controller
  davinci: add tnetv107x touchscreen platform device

 arch/arm/mach-davinci/board-tnetv107x-evm.c    |   56 ++++
 arch/arm/mach-davinci/devices-tnetv107x.c      |   50 +++
 arch/arm/mach-davinci/include/mach/tnetv107x.h |    3 +
 drivers/input/keyboard/Kconfig                 |    9 +
 drivers/input/keyboard/Makefile                |    1 +
 drivers/input/keyboard/tnetv107x-keypad.c      |  329 +++++++++++++++++++
 drivers/input/touchscreen/Kconfig              |    9 +
 drivers/input/touchscreen/Makefile             |    1 +
 drivers/input/touchscreen/tnetv107x-ts.c       |  401 ++++++++++++++++++++++++
 9 files changed, 859 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/tnetv107x-keypad.c
 create mode 100644 drivers/input/touchscreen/tnetv107x-ts.c


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

* [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller
  2010-09-16 19:54 [PATCH v2 0/5] add tnetv107x input drivers Cyril Chemparathy
@ 2010-09-16 19:54 ` Cyril Chemparathy
  2010-09-20  0:03   ` Dmitry Torokhov
  2010-09-16 19:54 ` [PATCH v2 2/5] davinci: add tnetv107x keypad platform device Cyril Chemparathy
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Cyril Chemparathy @ 2010-09-16 19:54 UTC (permalink / raw)
  To: linux-input, davinci-linux-open-source
  Cc: shubhrajyoti, dmitry.torokhov, khilman, Cyril Chemparathy

This patch adds support for tnetv107x's on-chip keypad controller.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/input/keyboard/Kconfig            |    9 +
 drivers/input/keyboard/Makefile           |    1 +
 drivers/input/keyboard/tnetv107x-keypad.c |  329 +++++++++++++++++++++++++++++
 3 files changed, 339 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/tnetv107x-keypad.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 9cc488d..df1facb 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -424,6 +424,15 @@ config KEYBOARD_OMAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called omap-keypad.
 
+config KEYBOARD_TNETV107X
+	tristate "TI TNETV107X keypad support"
+	depends on ARCH_DAVINCI_TNETV107X
+	help
+	  Say Y here if you want to use the TNETV107X keypad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tnetv107x-keypad.
+
 config KEYBOARD_TWL4030
 	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
 	depends on TWL4030_CORE
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 504b591..dc04518 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -38,6 +38,7 @@ 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_TNETV107X)	+= tnetv107x-keypad.o
 obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
 obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c
new file mode 100644
index 0000000..c262cb4
--- /dev/null
+++ b/drivers/input/keyboard/tnetv107x-keypad.c
@@ -0,0 +1,329 @@
+/*
+ * Texas Instruments TNETV107X Keypad Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/input/matrix_keypad.h>
+
+#define BITS(x)			(BIT(x) - 1)
+
+#define KEYPAD_ROWS		9
+#define KEYPAD_COLS		9
+
+#define DEBOUNCE_MIN		BIT(10)
+#define DEBOUNCE_MAX		BITS(30)
+
+struct keypad_regs {
+	u32	rev;
+	u32	mode;
+	u32	mask;
+	u32	pol;
+	u32	dclock;
+	u32	rclock;
+	u32	stable_cnt;
+	u32	in_en;
+	u32	out;
+	u32	out_en;
+	u32	in;
+	u32	lock;
+	u32	pres[3];
+};
+
+#define keypad_read(kp, reg)		__raw_readl(&(kp)->regs->reg)
+#define keypad_write(kp, reg, val)	__raw_writel(val, &(kp)->regs->reg)
+
+struct keypad_data {
+	struct input_dev		*input_dev;
+	struct resource			*res;
+	struct keypad_regs __iomem	*regs;
+	struct clk			*clk;
+	struct device			*dev;
+	u32				irq_press;
+	u32				irq_release;
+	int				rows, cols, row_shift;
+	int				debounce_ms, active_low;
+	unsigned short			*keycodes;
+	u32				prev_keys[3];
+};
+
+static irqreturn_t keypad_irq(int irq, void *data)
+{
+	struct keypad_data *kp = (struct keypad_data *)data;
+	int i, bit, val, row, col, code;
+	u32 curr_keys[3];
+	u32 change;
+
+	memset(curr_keys, 0, sizeof(curr_keys));
+	if (irq == kp->irq_press)
+		for (i = 0; i < 3; i++)
+			curr_keys[i] = keypad_read(kp, pres[i]);
+
+	for (i = 0; i < 3; i++) {
+		change = curr_keys[i] ^ kp->prev_keys[i];
+
+		while (change) {
+			bit     = fls(change) - 1;
+			change ^= BIT(bit);
+			val     = curr_keys[i] & BIT(bit);
+			bit    += i * 32;
+			row     = bit / KEYPAD_COLS;
+			col     = bit % KEYPAD_COLS;
+
+			code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
+			input_event(kp->input_dev, EV_MSC, MSC_SCAN, code);
+			input_report_key(kp->input_dev, kp->keycodes[code],
+					 val);
+		}
+	}
+	input_sync(kp->input_dev);
+	memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys));
+
+	if (irq == kp->irq_press)
+		keypad_write(kp, lock, 0); /* Allow hardware updates */
+
+	return IRQ_HANDLED;
+}
+
+static int keypad_start(struct input_dev *dev)
+{
+	struct keypad_data *kp = input_get_drvdata(dev);
+	unsigned long mask, debounce, clk_rate_khz;
+	int error;
+
+	clk_enable(kp->clk);
+	clk_rate_khz = clk_get_rate(kp->clk) / 1000;
+
+	/* Initialize device registers */
+	keypad_write(kp, mode, 0);
+
+	mask  = BITS(kp->rows) << KEYPAD_COLS;
+	mask |= BITS(kp->cols);
+	keypad_write(kp, mask, ~mask);
+
+	keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff);
+	debounce = kp->debounce_ms * clk_rate_khz;
+	debounce = min(debounce, DEBOUNCE_MAX);
+	debounce = max(debounce, DEBOUNCE_MIN);
+	keypad_write(kp, dclock, debounce);
+	keypad_write(kp, rclock, 4 * debounce);
+	keypad_write(kp, stable_cnt, 3);
+	keypad_write(kp, in_en, 1);
+
+	error = request_irq(kp->irq_press, keypad_irq, 0, "key-press", kp);
+	if (error < 0) {
+		dev_err(kp->dev, "Could not allocate keypad press key irq\n");
+		clk_disable(kp->clk);
+		return error;
+	}
+
+	error = request_irq(kp->irq_release, keypad_irq, 0, "key-release", kp);
+	if (error < 0) {
+		dev_err(kp->dev, "Could not allocate keypad release key irq\n");
+		free_irq(kp->irq_press, kp);
+		clk_disable(kp->clk);
+		return error;
+	}
+
+	return 0;
+}
+
+static void keypad_stop(struct input_dev *dev)
+{
+	struct keypad_data *kp = input_get_drvdata(dev);
+
+	free_irq(kp->irq_press, kp);
+	free_irq(kp->irq_release, kp);
+	clk_disable(kp->clk);
+}
+
+static int __devinit keypad_probe(struct platform_device *pdev)
+{
+	const struct matrix_keypad_platform_data *pdata;
+	const struct matrix_keymap_data *keymap_data;
+	struct device *dev = &pdev->dev;
+	struct keypad_data *kp;
+	int error = 0, sz;
+	u32 rev = 0;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(dev, "cannot find device data\n");
+		return -EINVAL;
+	}
+
+	keymap_data = pdata->keymap_data;
+	if (!keymap_data) {
+		dev_err(dev, "cannot find keymap data\n");
+		return -EINVAL;
+	}
+
+	kp = kzalloc(sizeof(struct keypad_data), GFP_KERNEL);
+	if (!kp) {
+		dev_err(dev, "cannot allocate device info\n");
+		return -ENOMEM;
+	}
+
+	kp->dev  = dev;
+	kp->rows = pdata->num_row_gpios;
+	kp->cols = pdata->num_col_gpios;
+	kp->row_shift = get_count_order(kp->cols);
+	platform_set_drvdata(pdev, kp);
+
+	sz = (kp->rows << kp->row_shift) * sizeof(*kp->keycodes);
+	kp->keycodes = kzalloc(sz, GFP_KERNEL);
+	if (!kp->keycodes) {
+		dev_err(dev, "cannot allocate keymap info\n");
+		error = -ENOMEM;
+		goto error;
+	}
+
+	kp->irq_press   = platform_get_irq_byname(pdev, "press");
+	kp->irq_release = platform_get_irq_byname(pdev, "release");
+	if (kp->irq_press < 0 || kp->irq_release < 0) {
+		dev_err(dev, "cannot determine device interrupts\n");
+		error = -ENODEV;
+		goto error;
+	}
+
+	kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!kp->res) {
+		dev_err(dev, "cannot determine register area\n");
+		error = -ENODEV;
+		goto error;
+	}
+
+	if (!request_mem_region(kp->res->start, resource_size(kp->res),
+				pdev->name)) {
+		dev_err(dev, "cannot claim register memory\n");
+		kp->res = NULL;
+		error = -EINVAL;
+		goto error;
+	}
+
+	kp->regs = ioremap(kp->res->start, resource_size(kp->res));
+	if (!kp->regs) {
+		dev_err(dev, "cannot map register memory\n");
+		error = -ENOMEM;
+		goto error;
+	}
+
+	kp->clk = clk_get(dev, NULL);
+	if (!kp->clk) {
+		dev_err(dev, "cannot claim device clock\n");
+		error = -EINVAL;
+		goto error;
+	}
+
+	kp->input_dev = input_allocate_device();
+	if (!kp->input_dev) {
+		dev_err(dev, "cannot allocate input device\n");
+		error = -ENOMEM;
+		goto error;
+	}
+	input_set_drvdata(kp->input_dev, kp);
+
+	kp->input_dev->name	  = pdev->name;
+	kp->input_dev->dev.parent = &pdev->dev;
+	kp->input_dev->open	  = keypad_start;
+	kp->input_dev->close	  = keypad_stop;
+	kp->input_dev->evbit[0]	  = BIT_MASK(EV_KEY);
+	if (!pdata->no_autorepeat)
+		kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
+
+	clk_enable(kp->clk);
+	rev = keypad_read(kp, rev);
+	kp->input_dev->id.bustype = BUS_HOST;
+	kp->input_dev->id.product = ((rev >>  8) & 0x07);
+	kp->input_dev->id.version = ((rev >> 16) & 0xfff);
+	clk_disable(kp->clk);
+
+	kp->input_dev->keycode     = kp->keycodes;
+	kp->input_dev->keycodesize = sizeof(*kp->keycodes);
+	kp->input_dev->keycodemax  = kp->rows << kp->row_shift;
+
+	matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes,
+				   kp->input_dev->keybit);
+
+	input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
+
+	error = input_register_device(kp->input_dev);
+	if (error < 0) {
+		dev_err(dev, "Could not register input device\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	if (kp->input_dev)
+		input_free_device(kp->input_dev);
+	if (kp->clk)
+		clk_put(kp->clk);
+	if (kp->regs)
+		iounmap(kp->regs);
+	if (kp->res)
+		release_mem_region(kp->res->start, resource_size(kp->res));
+	platform_set_drvdata(pdev, NULL);
+	kfree(kp->keycodes);
+	kfree(kp);
+	return error;
+}
+
+static int __devexit keypad_remove(struct platform_device *pdev)
+{
+	struct keypad_data *kp = platform_get_drvdata(pdev);
+
+	input_unregister_device(kp->input_dev);
+	clk_put(kp->clk);
+	iounmap(kp->regs);
+	release_mem_region(kp->res->start, resource_size(kp->res));
+	platform_set_drvdata(pdev, NULL);
+	kfree(kp->keycodes);
+	kfree(kp);
+
+	return 0;
+}
+
+static struct platform_driver keypad_driver = {
+	.probe		= keypad_probe,
+	.remove		= keypad_remove,
+	.driver.name	= "tnetv107x-keypad",
+	.driver.owner	= THIS_MODULE,
+};
+
+static int __init keypad_init(void)
+{
+	return platform_driver_register(&keypad_driver);
+}
+
+static void __exit keypad_exit(void)
+{
+	platform_driver_unregister(&keypad_driver);
+}
+
+module_init(keypad_init);
+module_exit(keypad_exit);
+
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_DESCRIPTION("TNETV107X Keypad Driver");
+MODULE_ALIAS("platform: tnetv107x-keypad");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4


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

* [PATCH v2 2/5] davinci: add tnetv107x keypad platform device
  2010-09-16 19:54 [PATCH v2 0/5] add tnetv107x input drivers Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller Cyril Chemparathy
@ 2010-09-16 19:54 ` Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 3/5] davinci: add keypad config for tnetv107x evm board Cyril Chemparathy
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Cyril Chemparathy @ 2010-09-16 19:54 UTC (permalink / raw)
  To: linux-input, davinci-linux-open-source
  Cc: shubhrajyoti, dmitry.torokhov, khilman, Cyril Chemparathy

This patch adds a platform device definition for tnetv107x's keypad
controller.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 arch/arm/mach-davinci/devices-tnetv107x.c      |   30 ++++++++++++++++++++++++
 arch/arm/mach-davinci/include/mach/tnetv107x.h |    3 ++
 2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c
index 2718a3a..086269f 100644
--- a/arch/arm/mach-davinci/devices-tnetv107x.c
+++ b/arch/arm/mach-davinci/devices-tnetv107x.c
@@ -33,6 +33,7 @@
 #define TNETV107X_WDOG_BASE			0x08086700
 #define TNETV107X_SDIO0_BASE			0x08088700
 #define TNETV107X_SDIO1_BASE			0x08088800
+#define TNETV107X_KEYPAD_BASE			0x08088a00
 #define TNETV107X_ASYNC_EMIF_CNTRL_BASE		0x08200000
 #define TNETV107X_ASYNC_EMIF_DATA_CE0_BASE	0x30000000
 #define TNETV107X_ASYNC_EMIF_DATA_CE1_BASE	0x40000000
@@ -298,6 +299,30 @@ static int __init nand_init(int chipsel, struct davinci_nand_pdata *data)
 	return platform_device_register(pdev);
 }
 
+static struct resource keypad_resources[] = {
+	{
+		.start	= TNETV107X_KEYPAD_BASE,
+		.end	= TNETV107X_KEYPAD_BASE + 0xff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TNETV107X_KEYPAD,
+		.flags	= IORESOURCE_IRQ,
+		.name	= "press",
+	},
+	{
+		.start	= IRQ_TNETV107X_KEYPAD_FREE,
+		.flags	= IORESOURCE_IRQ,
+		.name	= "release",
+	},
+};
+
+static struct platform_device keypad_device = {
+	.name		= "tnetv107x-keypad",
+	.num_resources	= ARRAY_SIZE(keypad_resources),
+	.resource	= keypad_resources,
+};
+
 void __init tnetv107x_devices_init(struct tnetv107x_device_info *info)
 {
 	int i;
@@ -317,4 +342,9 @@ void __init tnetv107x_devices_init(struct tnetv107x_device_info *info)
 	for (i = 0; i < 4; i++)
 		if (info->nand_config[i])
 			nand_init(i, info->nand_config[i]);
+
+	if (info->keypad_config) {
+		keypad_device.dev.platform_data = info->keypad_config;
+		platform_device_register(&keypad_device);
+	}
 }
diff --git a/arch/arm/mach-davinci/include/mach/tnetv107x.h b/arch/arm/mach-davinci/include/mach/tnetv107x.h
index c720647..5a681d8 100644
--- a/arch/arm/mach-davinci/include/mach/tnetv107x.h
+++ b/arch/arm/mach-davinci/include/mach/tnetv107x.h
@@ -33,6 +33,8 @@
 #ifndef __ASSEMBLY__
 
 #include <linux/serial_8250.h>
+#include <linux/input/matrix_keypad.h>
+
 #include <mach/mmc.h>
 #include <mach/nand.h>
 #include <mach/serial.h>
@@ -41,6 +43,7 @@ struct tnetv107x_device_info {
 	struct davinci_uart_config	*serial_config;
 	struct davinci_mmc_config	*mmc_config[2];  /* 2 controllers */
 	struct davinci_nand_pdata	*nand_config[4]; /* 4 chipsels */
+	struct matrix_keypad_platform_data *keypad_config;
 };
 
 extern struct platform_device tnetv107x_wdt_device;
-- 
1.7.0.4


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

* [PATCH v2 3/5] davinci: add keypad config for tnetv107x evm board
  2010-09-16 19:54 [PATCH v2 0/5] add tnetv107x input drivers Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 2/5] davinci: add tnetv107x keypad platform device Cyril Chemparathy
@ 2010-09-16 19:54 ` Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller Cyril Chemparathy
  2010-09-16 19:54 ` [PATCH v2 5/5] davinci: add tnetv107x touchscreen platform device Cyril Chemparathy
  4 siblings, 0 replies; 9+ messages in thread
From: Cyril Chemparathy @ 2010-09-16 19:54 UTC (permalink / raw)
  To: linux-input, davinci-linux-open-source
  Cc: shubhrajyoti, dmitry.torokhov, khilman, Cyril Chemparathy

This patch adds evm board specific keymap definitions and controller
configuration data for on-chip keypad controller on tnetv107x silicon.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 arch/arm/mach-davinci/board-tnetv107x-evm.c |   56 +++++++++++++++++++++++++++
 1 files changed, 56 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c
index fe2a9d9..5afa8fc 100644
--- a/arch/arm/mach-davinci/board-tnetv107x-evm.c
+++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c
@@ -23,6 +23,9 @@
 #include <linux/ratelimit.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 
@@ -141,10 +144,63 @@ static struct davinci_uart_config serial_config __initconst = {
 	.enabled_uarts	= BIT(1),
 };
 
+static const uint32_t keymap[] = {
+	KEY(0, 0, KEY_NUMERIC_1),
+	KEY(0, 1, KEY_NUMERIC_2),
+	KEY(0, 2, KEY_NUMERIC_3),
+	KEY(0, 3, KEY_FN_F1),
+	KEY(0, 4, KEY_MENU),
+
+	KEY(1, 0, KEY_NUMERIC_4),
+	KEY(1, 1, KEY_NUMERIC_5),
+	KEY(1, 2, KEY_NUMERIC_6),
+	KEY(1, 3, KEY_UP),
+	KEY(1, 4, KEY_FN_F2),
+
+	KEY(2, 0, KEY_NUMERIC_7),
+	KEY(2, 1, KEY_NUMERIC_8),
+	KEY(2, 2, KEY_NUMERIC_9),
+	KEY(2, 3, KEY_LEFT),
+	KEY(2, 4, KEY_ENTER),
+
+	KEY(3, 0, KEY_NUMERIC_STAR),
+	KEY(3, 1, KEY_NUMERIC_0),
+	KEY(3, 2, KEY_NUMERIC_POUND),
+	KEY(3, 3, KEY_DOWN),
+	KEY(3, 4, KEY_RIGHT),
+
+	KEY(4, 0, KEY_FN_F3),
+	KEY(4, 1, KEY_FN_F4),
+	KEY(4, 2, KEY_MUTE),
+	KEY(4, 3, KEY_HOME),
+	KEY(4, 4, KEY_BACK),
+
+	KEY(5, 0, KEY_VOLUMEDOWN),
+	KEY(5, 1, KEY_VOLUMEUP),
+	KEY(5, 2, KEY_F1),
+	KEY(5, 3, KEY_F2),
+	KEY(5, 4, KEY_F3),
+};
+
+static const struct matrix_keymap_data keymap_data = {
+	.keymap		= keymap,
+	.keymap_size	= ARRAY_SIZE(keymap),
+};
+
+static struct matrix_keypad_platform_data keypad_config = {
+	.keymap_data	= &keymap_data,
+	.num_row_gpios	= 6,
+	.num_col_gpios	= 5,
+	.debounce_ms	= 0, /* minimum */
+	.active_low	= 0, /* pull up realization */
+	.no_autorepeat	= 0,
+};
+
 static struct tnetv107x_device_info evm_device_info __initconst = {
 	.serial_config		= &serial_config,
 	.mmc_config[1]		= &mmc_config,	/* controller 1 */
 	.nand_config[0]		= &nand_config,	/* chip select 0 */
+	.keypad_config		= &keypad_config,
 };
 
 static __init void tnetv107x_evm_board_init(void)
-- 
1.7.0.4


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

* [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller
  2010-09-16 19:54 [PATCH v2 0/5] add tnetv107x input drivers Cyril Chemparathy
                   ` (2 preceding siblings ...)
  2010-09-16 19:54 ` [PATCH v2 3/5] davinci: add keypad config for tnetv107x evm board Cyril Chemparathy
@ 2010-09-16 19:54 ` Cyril Chemparathy
  2010-09-20  0:16   ` Dmitry Torokhov
  2010-09-20  5:38   ` Datta, Shubhrajyoti
  2010-09-16 19:54 ` [PATCH v2 5/5] davinci: add tnetv107x touchscreen platform device Cyril Chemparathy
  4 siblings, 2 replies; 9+ messages in thread
From: Cyril Chemparathy @ 2010-09-16 19:54 UTC (permalink / raw)
  To: linux-input, davinci-linux-open-source
  Cc: shubhrajyoti, dmitry.torokhov, khilman, Cyril Chemparathy

This patch adds support for tnetv107x's on-chip touchscreen controller.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/input/touchscreen/Kconfig        |    9 +
 drivers/input/touchscreen/Makefile       |    1 +
 drivers/input/touchscreen/tnetv107x-ts.c |  401 ++++++++++++++++++++++++++++++
 3 files changed, 411 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/tnetv107x-ts.c

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0069d97..8d32028 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -328,6 +328,15 @@ config TOUCHSCREEN_MIGOR
 	  To compile this driver as a module, choose M here: the
 	  module will be called migor_ts.
 
+config TOUCHSCREEN_TNETV107X
+	tristate "TI TNETV107X touchscreen support"
+	depends on ARCH_DAVINCI_TNETV107X
+	help
+	  Say Y here if you want to use the TNETV107X touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tnetv107x-ts.
+
 config TOUCHSCREEN_TOUCHRIGHT
 	tristate "Touchright serial touchscreen"
 	select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 28217e1..d41a964 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_QT602240)	+= qt602240_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
new file mode 100644
index 0000000..0d4dbc4
--- /dev/null
+++ b/drivers/input/touchscreen/tnetv107x-ts.c
@@ -0,0 +1,401 @@
+/*
+ * Texas Instruments TNETV107X Touchscreen Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/tnetv107x.h>
+
+#define TSC_PENUP_POLL		(HZ / 5)
+#define IDLE_TIMEOUT		100 /* msec */
+
+/*
+ * The first and last samples of a touch interval are usually garbage and need
+ * to be filtered out with these devices.  The following definitions control
+ * the number of samples skipped.
+ */
+#define TSC_HEAD_SKIP		1
+#define TSC_TAIL_SKIP		1
+#define TSC_SKIP		(TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
+#define TSC_SAMPLES		(TSC_SKIP + 1)
+
+/* Register Offsets */
+struct tsc_regs {
+	u32	rev;
+	u32	tscm;
+	u32	bwcm;
+	u32	swc;
+	u32	adcchnl;
+	u32	adcdata;
+	u32	chval[4];
+};
+
+/* TSC Mode Configuration Register (tscm) bits */
+#define WMODE		BIT(0)
+#define TSKIND		BIT(1)
+#define ZMEASURE_EN	BIT(2)
+#define IDLE		BIT(3)
+#define TSC_EN		BIT(4)
+#define STOP		BIT(5)
+#define ONE_SHOT	BIT(6)
+#define SINGLE		BIT(7)
+#define AVG		BIT(8)
+#define AVGNUM(x)	(((x) & 0x03) <<  9)
+#define PVSTC(x)	(((x) & 0x07) << 11)
+#define PON		BIT(14)
+#define PONBG		BIT(15)
+#define AFERST		BIT(16)
+
+/* ADC DATA Capture Register bits */
+#define DATA_VALID	BIT(16)
+
+/* Register Access Macros */
+#define tsc_read(ts, reg)		__raw_readl(&(ts)->regs->reg)
+#define tsc_write(ts, reg, val)		__raw_writel(val, &(ts)->regs->reg);
+#define tsc_set_bits(ts, reg, val)	\
+	tsc_write(ts, reg, tsc_read(ts, reg) | (val))
+#define tsc_clr_bits(ts, reg, val)	\
+	tsc_write(ts, reg, tsc_read(ts, reg) & ~(val))
+
+struct sample {
+	int x, y, p;
+};
+
+struct tsc_data {
+	struct input_dev		*input_dev;
+	struct resource			*res;
+	struct tsc_regs __iomem		*regs;
+	struct timer_list		timer;
+	spinlock_t			lock;
+	struct clk			*clk;
+	struct device			*dev;
+	int				sample_count;
+	struct sample			samples[TSC_SAMPLES];
+	int				tsc_irq;
+};
+
+static int tsc_read_sample(struct tsc_data *ts, struct sample* sample)
+{
+	int	x, y, z1, z2, t, p = 0;
+	u32	val;
+
+	val = tsc_read(ts, chval[0]);
+	if (val & DATA_VALID)
+		x = val & 0xffff;
+	else
+		return -EINVAL;
+
+	y  = tsc_read(ts, chval[1]) & 0xffff;
+	z1 = tsc_read(ts, chval[2]) & 0xffff;
+	z2 = tsc_read(ts, chval[3]) & 0xffff;
+
+	if (z1) {
+		t = ((600 * x) * (z2 - z1));
+		p = t / (u32) (z1 << 12);
+		if (p < 0)
+			p = 0;
+	}
+
+	sample->x  = x;
+	sample->y  = y;
+	sample->p  = p;
+
+	return 0;
+}
+
+static void tsc_poll(unsigned long data)
+{
+	struct tsc_data *ts = (struct tsc_data *)data;
+	unsigned long flags;
+	int i, val, x, y, p;
+
+	spin_lock_irqsave(&ts->lock, flags);
+
+	if (ts->sample_count >= TSC_SKIP) {
+		input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
+		input_report_key(ts->input_dev, BTN_TOUCH, 0);
+		input_sync(ts->input_dev);
+	} else if (ts->sample_count > 0) {
+		/*
+		 * A touch event lasted less than our skip count.  Salvage and
+		 * report anyway.
+		 */
+		for (i = 0, val = 0; i < ts->sample_count; i++)
+			val += ts->samples[i].x;
+		x = val / ts->sample_count;
+
+		for (i = 0, val = 0; i < ts->sample_count; i++)
+			val += ts->samples[i].y;
+		y = val / ts->sample_count;
+
+		for (i = 0, val = 0; i < ts->sample_count; i++)
+			val += ts->samples[i].p;
+		p = val / ts->sample_count;
+
+		input_report_abs(ts->input_dev, ABS_X, x);
+		input_report_abs(ts->input_dev, ABS_Y, y);
+		input_report_abs(ts->input_dev, ABS_PRESSURE, p);
+		input_report_key(ts->input_dev, BTN_TOUCH, 1);
+		input_sync(ts->input_dev);
+	}
+
+	ts->sample_count = 0;
+
+	spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static irqreturn_t tsc_irq(int irq, void *dev_id)
+{
+	struct tsc_data *ts = (struct tsc_data *)dev_id;
+	struct sample *sample;
+	int index;
+
+	spin_lock(&ts->lock);
+
+	index = ts->sample_count % TSC_SAMPLES;
+	sample = &ts->samples[index];
+	if (tsc_read_sample(ts, sample) >= 0) {
+		++ts->sample_count;
+
+		if (ts->sample_count < TSC_SKIP)
+			goto done;
+
+		index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES;
+		sample = &ts->samples[index];
+
+		input_report_abs(ts->input_dev, ABS_X, sample->x);
+		input_report_abs(ts->input_dev, ABS_Y, sample->y);
+		input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p);
+		if (ts->sample_count == TSC_SKIP)
+			input_report_key(ts->input_dev, BTN_TOUCH, 1);
+		input_sync(ts->input_dev);
+done:
+		mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);
+	}
+
+	spin_unlock(&ts->lock);
+	return IRQ_HANDLED;
+}
+
+static int tsc_start(struct input_dev *dev)
+{
+	struct tsc_data *ts = input_get_drvdata(dev);
+	unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT);
+	u32 val;
+	int error;
+
+	clk_enable(ts->clk);
+
+	/* Go to idle mode, before any initialization */
+	while (time_after(timeout, jiffies)) {
+		if (tsc_read(ts, tscm) & IDLE)
+			break;
+	}
+
+	if (time_before(timeout, jiffies)) {
+		dev_warn(ts->dev, "timeout waiting for idle\n");
+		clk_disable(ts->clk);
+		return -EIO;
+	}
+
+	/* Configure TSC Control register*/
+	val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN);
+	tsc_write(ts, tscm, val);
+
+	/* Bring TSC out of reset: Clear AFE reset bit */
+	val &= ~(AFERST);
+	tsc_write(ts, tscm, val);
+	udelay(10);
+
+	/* Configure all pins for hardware control*/
+	tsc_write(ts, bwcm, 0);
+
+	/* Finally enable the TSC */
+	tsc_set_bits(ts, tscm, TSC_EN);
+
+	error = request_irq(ts->tsc_irq, tsc_irq, 0, "tnetv107x-ts", ts);
+	if (error < 0) {
+		dev_err(ts->dev, "Could not allocate ts irq\n");
+		clk_disable(ts->clk);
+		return error;
+	}
+
+	return 0;
+}
+
+static void tsc_stop(struct input_dev *dev)
+{
+	struct tsc_data *ts = input_get_drvdata(dev);
+
+	tsc_clr_bits(ts, tscm, TSC_EN);
+	del_timer_sync(&ts->timer);
+	free_irq(ts->tsc_irq, ts);
+	clk_disable(ts->clk);
+}
+
+static int __devinit tsc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct tsc_data *ts;
+	int error = 0;
+	u32 rev = 0;
+
+	ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL);
+	if (!ts) {
+		dev_err(dev, "cannot allocate device info\n");
+		return -ENOMEM;
+	}
+
+	ts->dev = dev;
+	spin_lock_init(&ts->lock);
+	setup_timer(&ts->timer, tsc_poll, (unsigned long)ts);
+	platform_set_drvdata(pdev, ts);
+
+	ts->tsc_irq = platform_get_irq(pdev, 0);
+	if (ts->tsc_irq < 0) {
+		dev_err(dev, "cannot determine device interrupt\n");
+		error = -ENODEV;
+		goto error;
+	}
+
+	ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!ts->res) {
+		dev_err(dev, "cannot determine register area\n");
+		error = -ENODEV;
+		goto error;
+	}
+
+	if (!request_mem_region(ts->res->start, resource_size(ts->res),
+				pdev->name)) {
+		dev_err(dev, "cannot claim register memory\n");
+		ts->res = NULL;
+		error = -EINVAL;
+		goto error;
+	}
+
+	ts->regs = ioremap(ts->res->start, resource_size(ts->res));
+	if (!ts->regs) {
+		dev_err(dev, "cannot map register memory\n");
+		error = -ENOMEM;
+		goto error;
+	}
+
+	ts->clk = clk_get(dev, NULL);
+	if (!ts->clk) {
+		dev_err(dev, "cannot claim device clock\n");
+		error = -EINVAL;
+		goto error;
+	}
+
+	ts->input_dev = input_allocate_device();
+	if (!ts->input_dev) {
+		dev_err(dev, "cannot allocate input device\n");
+		error = -ENOMEM;
+		goto error;
+	}
+	input_set_drvdata(ts->input_dev, ts);
+
+	ts->input_dev->name       = pdev->name;
+	ts->input_dev->id.bustype = BUS_HOST;
+	ts->input_dev->dev.parent = &pdev->dev;
+	ts->input_dev->open	  = tsc_start;
+	ts->input_dev->close	  = tsc_stop;
+
+	clk_enable(ts->clk);
+	rev = tsc_read(ts, rev);
+	ts->input_dev->id.product = ((rev >>  8) & 0x07);
+	ts->input_dev->id.version = ((rev >> 16) & 0xfff);
+	clk_disable(ts->clk);
+
+	set_bit(EV_KEY,       ts->input_dev->evbit);
+	set_bit(EV_ABS,       ts->input_dev->evbit);
+	set_bit(BTN_TOUCH,    ts->input_dev->keybit);
+	set_bit(ABS_X,        ts->input_dev->absbit);
+	set_bit(ABS_Y,        ts->input_dev->absbit);
+	set_bit(ABS_PRESSURE, ts->input_dev->absbit);
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0);
+
+	error = input_register_device(ts->input_dev);
+	if (error < 0) {
+		dev_err(dev, "failed input device registration\n");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	if (ts->clk)
+		clk_put(ts->clk);
+	if (ts->regs)
+		iounmap(ts->regs);
+	if (ts->res)
+		release_mem_region(ts->res->start, resource_size(ts->res));
+	if (ts->input_dev)
+		input_free_device(ts->input_dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(ts);
+
+	return error;
+}
+
+static int __devexit tsc_remove(struct platform_device *pdev)
+{
+	struct tsc_data *ts = platform_get_drvdata(pdev);
+
+	input_unregister_device(ts->input_dev);
+	clk_put(ts->clk);
+	iounmap(ts->regs);
+	release_mem_region(ts->res->start, resource_size(ts->res));
+	platform_set_drvdata(pdev, NULL);
+	kfree(ts);
+
+	return 0;
+}
+
+static struct platform_driver tsc_driver = {
+	.probe		= tsc_probe,
+	.remove		= tsc_remove,
+	.driver.name	= "tnetv107x-ts",
+	.driver.owner	= THIS_MODULE,
+};
+
+static int __init tsc_init(void)
+{
+	return platform_driver_register(&tsc_driver);
+}
+
+static void __exit tsc_exit(void)
+{
+	platform_driver_unregister(&tsc_driver);
+}
+
+module_init(tsc_init);
+module_exit(tsc_exit);
+
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
+MODULE_ALIAS("platform: tnetv107x-ts");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4


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

* [PATCH v2 5/5] davinci: add tnetv107x touchscreen platform device
  2010-09-16 19:54 [PATCH v2 0/5] add tnetv107x input drivers Cyril Chemparathy
                   ` (3 preceding siblings ...)
  2010-09-16 19:54 ` [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller Cyril Chemparathy
@ 2010-09-16 19:54 ` Cyril Chemparathy
  4 siblings, 0 replies; 9+ messages in thread
From: Cyril Chemparathy @ 2010-09-16 19:54 UTC (permalink / raw)
  To: linux-input, davinci-linux-open-source
  Cc: shubhrajyoti, dmitry.torokhov, khilman, Cyril Chemparathy

This patch adds a platform device definition for tnetv107x's touchscreen
controller.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 arch/arm/mach-davinci/devices-tnetv107x.c |   20 ++++++++++++++++++++
 1 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c
index 086269f..c9a86d8 100644
--- a/arch/arm/mach-davinci/devices-tnetv107x.c
+++ b/arch/arm/mach-davinci/devices-tnetv107x.c
@@ -31,6 +31,7 @@
 #define TNETV107X_TPTC0_BASE			0x01c10000
 #define TNETV107X_TPTC1_BASE			0x01c10400
 #define TNETV107X_WDOG_BASE			0x08086700
+#define TNETV107X_TSC_BASE			0x08088500
 #define TNETV107X_SDIO0_BASE			0x08088700
 #define TNETV107X_SDIO1_BASE			0x08088800
 #define TNETV107X_KEYPAD_BASE			0x08088a00
@@ -323,12 +324,31 @@ static struct platform_device keypad_device = {
 	.resource	= keypad_resources,
 };
 
+static struct resource tsc_resources[] = {
+	{
+		.start	= TNETV107X_TSC_BASE,
+		.end	= TNETV107X_TSC_BASE + 0xff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TNETV107X_TSC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tsc_device = {
+	.name		= "tnetv107x-ts",
+	.num_resources	= ARRAY_SIZE(tsc_resources),
+	.resource	= tsc_resources,
+};
+
 void __init tnetv107x_devices_init(struct tnetv107x_device_info *info)
 {
 	int i;
 
 	platform_device_register(&edma_device);
 	platform_device_register(&tnetv107x_wdt_device);
+	platform_device_register(&tsc_device);
 
 	if (info->serial_config)
 		davinci_serial_init(info->serial_config);
-- 
1.7.0.4


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

* Re: [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller
  2010-09-16 19:54 ` [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller Cyril Chemparathy
@ 2010-09-20  0:03   ` Dmitry Torokhov
  0 siblings, 0 replies; 9+ messages in thread
From: Dmitry Torokhov @ 2010-09-20  0:03 UTC (permalink / raw)
  To: Cyril Chemparathy
  Cc: linux-input, davinci-linux-open-source, shubhrajyoti, khilman

Hi Cyril,

Thank you for making changes. Some more comments below.

On Thu, Sep 16, 2010 at 03:54:40PM -0400, Cyril Chemparathy wrote:
> This patch adds support for tnetv107x's on-chip keypad controller.
> 
> Signed-off-by: Cyril Chemparathy <cyril@ti.com>
> ---
>  drivers/input/keyboard/Kconfig            |    9 +
>  drivers/input/keyboard/Makefile           |    1 +
>  drivers/input/keyboard/tnetv107x-keypad.c |  329 +++++++++++++++++++++++++++++
>  3 files changed, 339 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/keyboard/tnetv107x-keypad.c
> 
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 9cc488d..df1facb 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -424,6 +424,15 @@ config KEYBOARD_OMAP
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called omap-keypad.
>  
> +config KEYBOARD_TNETV107X
> +	tristate "TI TNETV107X keypad support"
> +	depends on ARCH_DAVINCI_TNETV107X
> +	help
> +	  Say Y here if you want to use the TNETV107X keypad.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called tnetv107x-keypad.
> +
>  config KEYBOARD_TWL4030
>  	tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
>  	depends on TWL4030_CORE
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 504b591..dc04518 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -38,6 +38,7 @@ 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_TNETV107X)	+= tnetv107x-keypad.o
>  obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
>  obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
>  obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
> diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c
> new file mode 100644
> index 0000000..c262cb4
> --- /dev/null
> +++ b/drivers/input/keyboard/tnetv107x-keypad.c
> @@ -0,0 +1,329 @@
> +/*
> + * Texas Instruments TNETV107X Keypad Driver
> + *
> + * Copyright (C) 2010 Texas Instruments
> + *
> + * 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 version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/input.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/input/matrix_keypad.h>
> +
> +#define BITS(x)			(BIT(x) - 1)
> +
> +#define KEYPAD_ROWS		9
> +#define KEYPAD_COLS		9
> +
> +#define DEBOUNCE_MIN		BIT(10)
> +#define DEBOUNCE_MAX		BITS(30)
> +
> +struct keypad_regs {
> +	u32	rev;
> +	u32	mode;
> +	u32	mask;
> +	u32	pol;
> +	u32	dclock;
> +	u32	rclock;
> +	u32	stable_cnt;
> +	u32	in_en;
> +	u32	out;
> +	u32	out_en;
> +	u32	in;
> +	u32	lock;
> +	u32	pres[3];
> +};
> +
> +#define keypad_read(kp, reg)		__raw_readl(&(kp)->regs->reg)
> +#define keypad_write(kp, reg, val)	__raw_writel(val, &(kp)->regs->reg)
> +
> +struct keypad_data {
> +	struct input_dev		*input_dev;
> +	struct resource			*res;
> +	struct keypad_regs __iomem	*regs;
> +	struct clk			*clk;
> +	struct device			*dev;
> +	u32				irq_press;
> +	u32				irq_release;
> +	int				rows, cols, row_shift;
> +	int				debounce_ms, active_low;
> +	unsigned short			*keycodes;
> +	u32				prev_keys[3];
> +};
> +
> +static irqreturn_t keypad_irq(int irq, void *data)
> +{
> +	struct keypad_data *kp = (struct keypad_data *)data;

No need to cast.

> +	int i, bit, val, row, col, code;
> +	u32 curr_keys[3];
> +	u32 change;
> +
> +	memset(curr_keys, 0, sizeof(curr_keys));
> +	if (irq == kp->irq_press)
> +		for (i = 0; i < 3; i++)
> +			curr_keys[i] = keypad_read(kp, pres[i]);
> +
> +	for (i = 0; i < 3; i++) {
> +		change = curr_keys[i] ^ kp->prev_keys[i];
> +
> +		while (change) {
> +			bit     = fls(change) - 1;
> +			change ^= BIT(bit);
> +			val     = curr_keys[i] & BIT(bit);
> +			bit    += i * 32;
> +			row     = bit / KEYPAD_COLS;
> +			col     = bit % KEYPAD_COLS;
> +
> +			code = MATRIX_SCAN_CODE(row, col, kp->row_shift);
> +			input_event(kp->input_dev, EV_MSC, MSC_SCAN, code);
> +			input_report_key(kp->input_dev, kp->keycodes[code],
> +					 val);
> +		}
> +	}
> +	input_sync(kp->input_dev);
> +	memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys));
> +
> +	if (irq == kp->irq_press)
> +		keypad_write(kp, lock, 0); /* Allow hardware updates */
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int keypad_start(struct input_dev *dev)
> +{
> +	struct keypad_data *kp = input_get_drvdata(dev);
> +	unsigned long mask, debounce, clk_rate_khz;
> +	int error;
> +
> +	clk_enable(kp->clk);
> +	clk_rate_khz = clk_get_rate(kp->clk) / 1000;
> +
> +	/* Initialize device registers */
> +	keypad_write(kp, mode, 0);
> +
> +	mask  = BITS(kp->rows) << KEYPAD_COLS;
> +	mask |= BITS(kp->cols);
> +	keypad_write(kp, mask, ~mask);
> +
> +	keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff);
> +	debounce = kp->debounce_ms * clk_rate_khz;
> +	debounce = min(debounce, DEBOUNCE_MAX);
> +	debounce = max(debounce, DEBOUNCE_MIN);

	debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX);

Also I think defining DEBOUNCE_MIN and DEBOUNCE_MAX via BIT() is
confusing (I was expecting to see some bitwise ops); just use a
constant.

> +	keypad_write(kp, dclock, debounce);
> +	keypad_write(kp, rclock, 4 * debounce);
> +	keypad_write(kp, stable_cnt, 3);
> +	keypad_write(kp, in_en, 1);
> +
> +	error = request_irq(kp->irq_press, keypad_irq, 0, "key-press", kp);
> +	if (error < 0) {
> +		dev_err(kp->dev, "Could not allocate keypad press key irq\n");
> +		clk_disable(kp->clk);
> +		return error;
> +	}
> +
> +	error = request_irq(kp->irq_release, keypad_irq, 0, "key-release", kp);
> +	if (error < 0) {
> +		dev_err(kp->dev, "Could not allocate keypad release key irq\n");
> +		free_irq(kp->irq_press, kp);
> +		clk_disable(kp->clk);
> +		return error;
> +	}
> +

Request IRQ should go into keypad_probe(). The split is as follows - if
possible probe() should leave the device fully ready (all needed
resources acquired) but inactive, and open() should just activate the
device.

> +	return 0;
> +}
> +
> +static void keypad_stop(struct input_dev *dev)
> +{
> +	struct keypad_data *kp = input_get_drvdata(dev);
> +
> +	free_irq(kp->irq_press, kp);
> +	free_irq(kp->irq_release, kp);
> +	clk_disable(kp->clk);
> +}
> +
> +static int __devinit keypad_probe(struct platform_device *pdev)
> +{
> +	const struct matrix_keypad_platform_data *pdata;
> +	const struct matrix_keymap_data *keymap_data;
> +	struct device *dev = &pdev->dev;
> +	struct keypad_data *kp;
> +	int error = 0, sz;
> +	u32 rev = 0;
> +
> +	pdata = pdev->dev.platform_data;
> +	if (!pdata) {
> +		dev_err(dev, "cannot find device data\n");
> +		return -EINVAL;
> +	}
> +
> +	keymap_data = pdata->keymap_data;
> +	if (!keymap_data) {
> +		dev_err(dev, "cannot find keymap data\n");
> +		return -EINVAL;
> +	}
> +
> +	kp = kzalloc(sizeof(struct keypad_data), GFP_KERNEL);
> +	if (!kp) {
> +		dev_err(dev, "cannot allocate device info\n");
> +		return -ENOMEM;
> +	}
> +
> +	kp->dev  = dev;
> +	kp->rows = pdata->num_row_gpios;
> +	kp->cols = pdata->num_col_gpios;
> +	kp->row_shift = get_count_order(kp->cols);
> +	platform_set_drvdata(pdev, kp);
> +
> +	sz = (kp->rows << kp->row_shift) * sizeof(*kp->keycodes);
> +	kp->keycodes = kzalloc(sz, GFP_KERNEL);
> +	if (!kp->keycodes) {
> +		dev_err(dev, "cannot allocate keymap info\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}

Why don't you allocate keymap together with the keypad structure:

struct keypad_data {
	...
	u32				prev_keys[3];
	unsigned short			keycodes[];
};

	keymap_size = (pdata->num_row_gpios <<
			get_order(pdata->num_col_gpios)) *
			sizeof(unsigned short);
	kp = kzalloc(sizeof(struct keypad_data) + keymap_size, GFP_KERNEL);
	...

> +
> +	kp->irq_press   = platform_get_irq_byname(pdev, "press");
> +	kp->irq_release = platform_get_irq_byname(pdev, "release");
> +	if (kp->irq_press < 0 || kp->irq_release < 0) {
> +		dev_err(dev, "cannot determine device interrupts\n");
> +		error = -ENODEV;
> +		goto error;
> +	}
> +
> +	kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!kp->res) {
> +		dev_err(dev, "cannot determine register area\n");
> +		error = -ENODEV;
> +		goto error;
> +	}
> +
> +	if (!request_mem_region(kp->res->start, resource_size(kp->res),
> +				pdev->name)) {
> +		dev_err(dev, "cannot claim register memory\n");
> +		kp->res = NULL;
> +		error = -EINVAL;
> +		goto error;
> +	}
> +
> +	kp->regs = ioremap(kp->res->start, resource_size(kp->res));
> +	if (!kp->regs) {
> +		dev_err(dev, "cannot map register memory\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}
> +
> +	kp->clk = clk_get(dev, NULL);
> +	if (!kp->clk) {
> +		dev_err(dev, "cannot claim device clock\n");
> +		error = -EINVAL;
> +		goto error;
> +	}
> +
> +	kp->input_dev = input_allocate_device();
> +	if (!kp->input_dev) {
> +		dev_err(dev, "cannot allocate input device\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}
> +	input_set_drvdata(kp->input_dev, kp);
> +
> +	kp->input_dev->name	  = pdev->name;
> +	kp->input_dev->dev.parent = &pdev->dev;
> +	kp->input_dev->open	  = keypad_start;
> +	kp->input_dev->close	  = keypad_stop;
> +	kp->input_dev->evbit[0]	  = BIT_MASK(EV_KEY);
> +	if (!pdata->no_autorepeat)
> +		kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
> +
> +	clk_enable(kp->clk);
> +	rev = keypad_read(kp, rev);
> +	kp->input_dev->id.bustype = BUS_HOST;
> +	kp->input_dev->id.product = ((rev >>  8) & 0x07);
> +	kp->input_dev->id.version = ((rev >> 16) & 0xfff);
> +	clk_disable(kp->clk);
> +
> +	kp->input_dev->keycode     = kp->keycodes;
> +	kp->input_dev->keycodesize = sizeof(*kp->keycodes);
> +	kp->input_dev->keycodemax  = kp->rows << kp->row_shift;
> +
> +	matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes,
> +				   kp->input_dev->keybit);
> +
> +	input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
> +
> +	error = input_register_device(kp->input_dev);
> +	if (error < 0) {
> +		dev_err(dev, "Could not register input device\n");
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	if (kp->input_dev)
> +		input_free_device(kp->input_dev);
> +	if (kp->clk)
> +		clk_put(kp->clk);
> +	if (kp->regs)
> +		iounmap(kp->regs);
> +	if (kp->res)
> +		release_mem_region(kp->res->start, resource_size(kp->res));

I generally prefer having multiple error path labels instead of checking
the state.

> +	platform_set_drvdata(pdev, NULL);
> +	kfree(kp->keycodes);
> +	kfree(kp);
> +	return error;
> +}
> +
> +static int __devexit keypad_remove(struct platform_device *pdev)
> +{
> +	struct keypad_data *kp = platform_get_drvdata(pdev);
> +
> +	input_unregister_device(kp->input_dev);
> +	clk_put(kp->clk);
> +	iounmap(kp->regs);
> +	release_mem_region(kp->res->start, resource_size(kp->res));
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(kp->keycodes);
> +	kfree(kp);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver keypad_driver = {
> +	.probe		= keypad_probe,
> +	.remove		= keypad_remove,
> +	.driver.name	= "tnetv107x-keypad",
> +	.driver.owner	= THIS_MODULE,
> +};
> +
> +static int __init keypad_init(void)
> +{
> +	return platform_driver_register(&keypad_driver);
> +}
> +
> +static void __exit keypad_exit(void)
> +{
> +	platform_driver_unregister(&keypad_driver);
> +}
> +
> +module_init(keypad_init);
> +module_exit(keypad_exit);
> +
> +MODULE_AUTHOR("Cyril Chemparathy");
> +MODULE_DESCRIPTION("TNETV107X Keypad Driver");
> +MODULE_ALIAS("platform: tnetv107x-keypad");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.0.4
> 

Thanks.

-- 
Dmitry

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

* Re: [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller
  2010-09-16 19:54 ` [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller Cyril Chemparathy
@ 2010-09-20  0:16   ` Dmitry Torokhov
  2010-09-20  5:38   ` Datta, Shubhrajyoti
  1 sibling, 0 replies; 9+ messages in thread
From: Dmitry Torokhov @ 2010-09-20  0:16 UTC (permalink / raw)
  To: Cyril Chemparathy
  Cc: linux-input, davinci-linux-open-source, shubhrajyoti, khilman

Hi Cyril,

On Thu, Sep 16, 2010 at 03:54:43PM -0400, Cyril Chemparathy wrote:
> This patch adds support for tnetv107x's on-chip touchscreen controller.
> 

Similar comments as with the keypad plus some more:
 
> Signed-off-by: Cyril Chemparathy <cyril@ti.com>
> ---
>  drivers/input/touchscreen/Kconfig        |    9 +
>  drivers/input/touchscreen/Makefile       |    1 +
>  drivers/input/touchscreen/tnetv107x-ts.c |  401 ++++++++++++++++++++++++++++++
>  3 files changed, 411 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/touchscreen/tnetv107x-ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 0069d97..8d32028 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -328,6 +328,15 @@ config TOUCHSCREEN_MIGOR
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called migor_ts.
>  
> +config TOUCHSCREEN_TNETV107X
> +	tristate "TI TNETV107X touchscreen support"
> +	depends on ARCH_DAVINCI_TNETV107X
> +	help
> +	  Say Y here if you want to use the TNETV107X touchscreen.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called tnetv107x-ts.
> +
>  config TOUCHSCREEN_TOUCHRIGHT
>  	tristate "Touchright serial touchscreen"
>  	select SERIO
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 28217e1..d41a964 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
>  obj-$(CONFIG_TOUCHSCREEN_QT602240)	+= qt602240_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
> +obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
> diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c
> new file mode 100644
> index 0000000..0d4dbc4
> --- /dev/null
> +++ b/drivers/input/touchscreen/tnetv107x-ts.c
> @@ -0,0 +1,401 @@
> +/*
> + * Texas Instruments TNETV107X Touchscreen Driver
> + *
> + * Copyright (C) 2010 Texas Instruments
> + *
> + * 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 version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/input.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/ctype.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +
> +#include <mach/tnetv107x.h>
> +
> +#define TSC_PENUP_POLL		(HZ / 5)
> +#define IDLE_TIMEOUT		100 /* msec */
> +
> +/*
> + * The first and last samples of a touch interval are usually garbage and need
> + * to be filtered out with these devices.  The following definitions control
> + * the number of samples skipped.
> + */
> +#define TSC_HEAD_SKIP		1
> +#define TSC_TAIL_SKIP		1
> +#define TSC_SKIP		(TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
> +#define TSC_SAMPLES		(TSC_SKIP + 1)
> +
> +/* Register Offsets */
> +struct tsc_regs {
> +	u32	rev;
> +	u32	tscm;
> +	u32	bwcm;
> +	u32	swc;
> +	u32	adcchnl;
> +	u32	adcdata;
> +	u32	chval[4];
> +};
> +
> +/* TSC Mode Configuration Register (tscm) bits */
> +#define WMODE		BIT(0)
> +#define TSKIND		BIT(1)
> +#define ZMEASURE_EN	BIT(2)
> +#define IDLE		BIT(3)
> +#define TSC_EN		BIT(4)
> +#define STOP		BIT(5)
> +#define ONE_SHOT	BIT(6)
> +#define SINGLE		BIT(7)
> +#define AVG		BIT(8)
> +#define AVGNUM(x)	(((x) & 0x03) <<  9)
> +#define PVSTC(x)	(((x) & 0x07) << 11)
> +#define PON		BIT(14)
> +#define PONBG		BIT(15)
> +#define AFERST		BIT(16)
> +
> +/* ADC DATA Capture Register bits */
> +#define DATA_VALID	BIT(16)
> +
> +/* Register Access Macros */
> +#define tsc_read(ts, reg)		__raw_readl(&(ts)->regs->reg)
> +#define tsc_write(ts, reg, val)		__raw_writel(val, &(ts)->regs->reg);
> +#define tsc_set_bits(ts, reg, val)	\
> +	tsc_write(ts, reg, tsc_read(ts, reg) | (val))
> +#define tsc_clr_bits(ts, reg, val)	\
> +	tsc_write(ts, reg, tsc_read(ts, reg) & ~(val))
> +
> +struct sample {
> +	int x, y, p;
> +};
> +
> +struct tsc_data {
> +	struct input_dev		*input_dev;
> +	struct resource			*res;
> +	struct tsc_regs __iomem		*regs;
> +	struct timer_list		timer;
> +	spinlock_t			lock;
> +	struct clk			*clk;
> +	struct device			*dev;
> +	int				sample_count;
> +	struct sample			samples[TSC_SAMPLES];
> +	int				tsc_irq;
> +};
> +
> +static int tsc_read_sample(struct tsc_data *ts, struct sample* sample)
> +{
> +	int	x, y, z1, z2, t, p = 0;
> +	u32	val;
> +
> +	val = tsc_read(ts, chval[0]);
> +	if (val & DATA_VALID)
> +		x = val & 0xffff;
> +	else
> +		return -EINVAL;
> +
> +	y  = tsc_read(ts, chval[1]) & 0xffff;
> +	z1 = tsc_read(ts, chval[2]) & 0xffff;
> +	z2 = tsc_read(ts, chval[3]) & 0xffff;
> +
> +	if (z1) {
> +		t = ((600 * x) * (z2 - z1));
> +		p = t / (u32) (z1 << 12);
> +		if (p < 0)
> +			p = 0;
> +	}
> +
> +	sample->x  = x;
> +	sample->y  = y;
> +	sample->p  = p;
> +
> +	return 0;
> +}
> +
> +static void tsc_poll(unsigned long data)
> +{
> +	struct tsc_data *ts = (struct tsc_data *)data;
> +	unsigned long flags;
> +	int i, val, x, y, p;
> +
> +	spin_lock_irqsave(&ts->lock, flags);
> +
> +	if (ts->sample_count >= TSC_SKIP) {
> +		input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
> +		input_report_key(ts->input_dev, BTN_TOUCH, 0);
> +		input_sync(ts->input_dev);
> +	} else if (ts->sample_count > 0) {
> +		/*
> +		 * A touch event lasted less than our skip count.  Salvage and
> +		 * report anyway.
> +		 */
> +		for (i = 0, val = 0; i < ts->sample_count; i++)
> +			val += ts->samples[i].x;
> +		x = val / ts->sample_count;
> +
> +		for (i = 0, val = 0; i < ts->sample_count; i++)
> +			val += ts->samples[i].y;
> +		y = val / ts->sample_count;
> +
> +		for (i = 0, val = 0; i < ts->sample_count; i++)
> +			val += ts->samples[i].p;
> +		p = val / ts->sample_count;
> +
> +		input_report_abs(ts->input_dev, ABS_X, x);
> +		input_report_abs(ts->input_dev, ABS_Y, y);
> +		input_report_abs(ts->input_dev, ABS_PRESSURE, p);
> +		input_report_key(ts->input_dev, BTN_TOUCH, 1);
> +		input_sync(ts->input_dev);
> +	}
> +
> +	ts->sample_count = 0;
> +
> +	spin_unlock_irqrestore(&ts->lock, flags);
> +}
> +
> +static irqreturn_t tsc_irq(int irq, void *dev_id)
> +{
> +	struct tsc_data *ts = (struct tsc_data *)dev_id;
> +	struct sample *sample;
> +	int index;
> +
> +	spin_lock(&ts->lock);
> +
> +	index = ts->sample_count % TSC_SAMPLES;
> +	sample = &ts->samples[index];
> +	if (tsc_read_sample(ts, sample) >= 0) {
> +		++ts->sample_count;
> +
> +		if (ts->sample_count < TSC_SKIP)
> +			goto done;
> +
> +		index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES;
> +		sample = &ts->samples[index];
> +
> +		input_report_abs(ts->input_dev, ABS_X, sample->x);
> +		input_report_abs(ts->input_dev, ABS_Y, sample->y);
> +		input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p);
> +		if (ts->sample_count == TSC_SKIP)
> +			input_report_key(ts->input_dev, BTN_TOUCH, 1);
> +		input_sync(ts->input_dev);
> +done:
> +		mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);
> +	}
> +
> +	spin_unlock(&ts->lock);
> +	return IRQ_HANDLED;
> +}

Extremely minor nit but I'd prefer:

	if (tsc_read_sample(ts, sample) < 0)
		goto out;

	if (++ts->sample_count >= TSC_SKIP) {
		...
	}

	mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);

out:
	spin_unlock(&ts->lock);
	return IRQ_HANDLED;

> +
> +static int tsc_start(struct input_dev *dev)
> +{
> +	struct tsc_data *ts = input_get_drvdata(dev);
> +	unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT);
> +	u32 val;
> +	int error;
> +
> +	clk_enable(ts->clk);
> +
> +	/* Go to idle mode, before any initialization */
> +	while (time_after(timeout, jiffies)) {
> +		if (tsc_read(ts, tscm) & IDLE)
> +			break;
> +	}
> +
> +	if (time_before(timeout, jiffies)) {
> +		dev_warn(ts->dev, "timeout waiting for idle\n");
> +		clk_disable(ts->clk);
> +		return -EIO;
> +	}
> +
> +	/* Configure TSC Control register*/
> +	val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN);
> +	tsc_write(ts, tscm, val);
> +
> +	/* Bring TSC out of reset: Clear AFE reset bit */
> +	val &= ~(AFERST);
> +	tsc_write(ts, tscm, val);
> +	udelay(10);
> +
> +	/* Configure all pins for hardware control*/
> +	tsc_write(ts, bwcm, 0);
> +
> +	/* Finally enable the TSC */
> +	tsc_set_bits(ts, tscm, TSC_EN);
> +
> +	error = request_irq(ts->tsc_irq, tsc_irq, 0, "tnetv107x-ts", ts);
> +	if (error < 0) {
> +		dev_err(ts->dev, "Could not allocate ts irq\n");
> +		clk_disable(ts->clk);
> +		return error;
> +	}
> +
> +	return 0;
> +}
> +
> +static void tsc_stop(struct input_dev *dev)
> +{
> +	struct tsc_data *ts = input_get_drvdata(dev);
> +
> +	tsc_clr_bits(ts, tscm, TSC_EN);
> +	del_timer_sync(&ts->timer);
> +	free_irq(ts->tsc_irq, ts);
> +	clk_disable(ts->clk);

I think the close sequence should be:

	tsc_clr_bits(ts, tscm, TSC_EN);
	synchronize_irq(ts->tsc_irq);
	/* No new IRQs past this point, shut off the timer */
	del_timer_sync(&ts->timer);

> +}
> +
> +static int __devinit tsc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct tsc_data *ts;
> +	int error = 0;
> +	u32 rev = 0;
> +
> +	ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL);
> +	if (!ts) {
> +		dev_err(dev, "cannot allocate device info\n");
> +		return -ENOMEM;
> +	}
> +
> +	ts->dev = dev;
> +	spin_lock_init(&ts->lock);
> +	setup_timer(&ts->timer, tsc_poll, (unsigned long)ts);
> +	platform_set_drvdata(pdev, ts);
> +
> +	ts->tsc_irq = platform_get_irq(pdev, 0);
> +	if (ts->tsc_irq < 0) {
> +		dev_err(dev, "cannot determine device interrupt\n");
> +		error = -ENODEV;
> +		goto error;
> +	}
> +
> +	ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!ts->res) {
> +		dev_err(dev, "cannot determine register area\n");
> +		error = -ENODEV;
> +		goto error;
> +	}
> +
> +	if (!request_mem_region(ts->res->start, resource_size(ts->res),
> +				pdev->name)) {
> +		dev_err(dev, "cannot claim register memory\n");
> +		ts->res = NULL;
> +		error = -EINVAL;
> +		goto error;
> +	}
> +
> +	ts->regs = ioremap(ts->res->start, resource_size(ts->res));
> +	if (!ts->regs) {
> +		dev_err(dev, "cannot map register memory\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}
> +
> +	ts->clk = clk_get(dev, NULL);
> +	if (!ts->clk) {
> +		dev_err(dev, "cannot claim device clock\n");
> +		error = -EINVAL;
> +		goto error;
> +	}
> +
> +	ts->input_dev = input_allocate_device();
> +	if (!ts->input_dev) {
> +		dev_err(dev, "cannot allocate input device\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}
> +	input_set_drvdata(ts->input_dev, ts);
> +
> +	ts->input_dev->name       = pdev->name;
> +	ts->input_dev->id.bustype = BUS_HOST;
> +	ts->input_dev->dev.parent = &pdev->dev;
> +	ts->input_dev->open	  = tsc_start;
> +	ts->input_dev->close	  = tsc_stop;
> +
> +	clk_enable(ts->clk);
> +	rev = tsc_read(ts, rev);
> +	ts->input_dev->id.product = ((rev >>  8) & 0x07);
> +	ts->input_dev->id.version = ((rev >> 16) & 0xfff);
> +	clk_disable(ts->clk);
> +
> +	set_bit(EV_KEY,       ts->input_dev->evbit);
> +	set_bit(EV_ABS,       ts->input_dev->evbit);
> +	set_bit(BTN_TOUCH,    ts->input_dev->keybit);

__set_bit();

> +	set_bit(ABS_X,        ts->input_dev->absbit);
> +	set_bit(ABS_Y,        ts->input_dev->absbit);
> +	set_bit(ABS_PRESSURE, ts->input_dev->absbit);

input_set_abs_params() already takes care of setting bits.

> +
> +	input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0);
> +	input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0);
> +	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0);
> +
> +	error = input_register_device(ts->input_dev);
> +	if (error < 0) {
> +		dev_err(dev, "failed input device registration\n");
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	if (ts->clk)
> +		clk_put(ts->clk);
> +	if (ts->regs)
> +		iounmap(ts->regs);
> +	if (ts->res)
> +		release_mem_region(ts->res->start, resource_size(ts->res));
> +	if (ts->input_dev)
> +		input_free_device(ts->input_dev);
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(ts);
> +
> +	return error;
> +}
> +
> +static int __devexit tsc_remove(struct platform_device *pdev)
> +{
> +	struct tsc_data *ts = platform_get_drvdata(pdev);
> +
> +	input_unregister_device(ts->input_dev);
> +	clk_put(ts->clk);
> +	iounmap(ts->regs);
> +	release_mem_region(ts->res->start, resource_size(ts->res));
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(ts);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver tsc_driver = {
> +	.probe		= tsc_probe,
> +	.remove		= tsc_remove,
> +	.driver.name	= "tnetv107x-ts",
> +	.driver.owner	= THIS_MODULE,
> +};
> +
> +static int __init tsc_init(void)
> +{
> +	return platform_driver_register(&tsc_driver);
> +}
> +
> +static void __exit tsc_exit(void)
> +{
> +	platform_driver_unregister(&tsc_driver);
> +}
> +
> +module_init(tsc_init);
> +module_exit(tsc_exit);
> +
> +MODULE_AUTHOR("Cyril Chemparathy");
> +MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
> +MODULE_ALIAS("platform: tnetv107x-ts");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.0.4
> 

Thank you.

-- 
Dmitry

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

* RE: [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller
  2010-09-16 19:54 ` [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller Cyril Chemparathy
  2010-09-20  0:16   ` Dmitry Torokhov
@ 2010-09-20  5:38   ` Datta, Shubhrajyoti
  1 sibling, 0 replies; 9+ messages in thread
From: Datta, Shubhrajyoti @ 2010-09-20  5:38 UTC (permalink / raw)
  To: Chemparathy, Cyril, linux-input,
	davinci-linux-open-source@linux.davincidsp.com
  Cc: dmitry.torokhov, khilman

Hi Cyril ,
A few minor points/queries.


> -----Original Message-----
> From: Chemparathy, Cyril
> Sent: Friday, September 17, 2010 1:25 AM
> To: linux-input@vger.kernel.org; davinci-linux-open-
> source@linux.davincidsp.com
> Cc: Datta, Shubhrajyoti; dmitry.torokhov@gmail.com;
> khilman@deeprootsystems.com; Chemparathy, Cyril
> Subject: [PATCH v2 4/5] input: add driver for tnetv107x touchscreen
> controller
> 
> This patch adds support for tnetv107x's on-chip touchscreen controller.
> 
> Signed-off-by: Cyril Chemparathy <cyril@ti.com>
> ---
>  drivers/input/touchscreen/Kconfig        |    9 +
>  drivers/input/touchscreen/Makefile       |    1 +
>  drivers/input/touchscreen/tnetv107x-ts.c |  401
> ++++++++++++++++++++++++++++++
>  3 files changed, 411 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/touchscreen/tnetv107x-ts.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig
> b/drivers/input/touchscreen/Kconfig
> index 0069d97..8d32028 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -328,6 +328,15 @@ config TOUCHSCREEN_MIGOR
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called migor_ts.
> 
> +config TOUCHSCREEN_TNETV107X
> +	tristate "TI TNETV107X touchscreen support"
> +	depends on ARCH_DAVINCI_TNETV107X
> +	help
> +	  Say Y here if you want to use the TNETV107X touchscreen.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called tnetv107x-ts.
> +
>  config TOUCHSCREEN_TOUCHRIGHT
>  	tristate "Touchright serial touchscreen"
>  	select SERIO
> diff --git a/drivers/input/touchscreen/Makefile
> b/drivers/input/touchscreen/Makefile
> index 28217e1..d41a964 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
>  obj-$(CONFIG_TOUCHSCREEN_QT602240)	+= qt602240_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
> +obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
> diff --git a/drivers/input/touchscreen/tnetv107x-ts.c
> b/drivers/input/touchscreen/tnetv107x-ts.c
> new file mode 100644
> index 0000000..0d4dbc4
> --- /dev/null
> +++ b/drivers/input/touchscreen/tnetv107x-ts.c
> @@ -0,0 +1,401 @@
> +/*
> + * Texas Instruments TNETV107X Touchscreen Driver
> + *
> + * Copyright (C) 2010 Texas Instruments
> + *
> + * 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 version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/input.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/ctype.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +
> +#include <mach/tnetv107x.h>
> +
> +#define TSC_PENUP_POLL		(HZ / 5)
> +#define IDLE_TIMEOUT		100 /* msec */
> +
> +/*
> + * The first and last samples of a touch interval are usually garbage and
> need
> + * to be filtered out with these devices.  The following definitions
> control
> + * the number of samples skipped.
> + */
> +#define TSC_HEAD_SKIP		1
> +#define TSC_TAIL_SKIP		1
> +#define TSC_SKIP		(TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
> +#define TSC_SAMPLES		(TSC_SKIP + 1)
> +
> +/* Register Offsets */
> +struct tsc_regs {
> +	u32	rev;
> +	u32	tscm;
> +	u32	bwcm;
> +	u32	swc;
> +	u32	adcchnl;
> +	u32	adcdata;
> +	u32	chval[4];
> +};
> +
> +/* TSC Mode Configuration Register (tscm) bits */
> +#define WMODE		BIT(0)
> +#define TSKIND		BIT(1)
> +#define ZMEASURE_EN	BIT(2)
> +#define IDLE		BIT(3)
> +#define TSC_EN		BIT(4)
> +#define STOP		BIT(5)
> +#define ONE_SHOT	BIT(6)
> +#define SINGLE		BIT(7)
> +#define AVG		BIT(8)
> +#define AVGNUM(x)	(((x) & 0x03) <<  9)
> +#define PVSTC(x)	(((x) & 0x07) << 11)
> +#define PON		BIT(14)
> +#define PONBG		BIT(15)
> +#define AFERST		BIT(16)
> +
> +/* ADC DATA Capture Register bits */
> +#define DATA_VALID	BIT(16)
> +
> +/* Register Access Macros */
> +#define tsc_read(ts, reg)		__raw_readl(&(ts)->regs->reg)
> +#define tsc_write(ts, reg, val)		__raw_writel(val, &(ts)->regs-
> >reg);
> +#define tsc_set_bits(ts, reg, val)	\
> +	tsc_write(ts, reg, tsc_read(ts, reg) | (val))
> +#define tsc_clr_bits(ts, reg, val)	\
> +	tsc_write(ts, reg, tsc_read(ts, reg) & ~(val))
> +
> +struct sample {
> +	int x, y, p;
> +};
> +
> +struct tsc_data {
> +	struct input_dev		*input_dev;
> +	struct resource			*res;
> +	struct tsc_regs __iomem		*regs;
> +	struct timer_list		timer;
> +	spinlock_t			lock;
> +	struct clk			*clk;
> +	struct device			*dev;
> +	int				sample_count;
> +	struct sample			samples[TSC_SAMPLES];
> +	int				tsc_irq;
> +};
> +
> +static int tsc_read_sample(struct tsc_data *ts, struct sample* sample)
> +{
> +	int	x, y, z1, z2, t, p = 0;
> +	u32	val;
> +
> +	val = tsc_read(ts, chval[0]);
> +	if (val & DATA_VALID)
> +		x = val & 0xffff;
> +	else
> +		return -EINVAL;
> +
> +	y  = tsc_read(ts, chval[1]) & 0xffff;
> +	z1 = tsc_read(ts, chval[2]) & 0xffff;
> +	z2 = tsc_read(ts, chval[3]) & 0xffff;
> +
> +	if (z1) {
> +		t = ((600 * x) * (z2 - z1));
> +		p = t / (u32) (z1 << 12);
> +		if (p < 0)
> +			p = 0;
> +	}
> +
> +	sample->x  = x;
> +	sample->y  = y;
> +	sample->p  = p;
> +
> +	return 0;
> +}
> +
> +static void tsc_poll(unsigned long data)
> +{
> +	struct tsc_data *ts = (struct tsc_data *)data;
> +	unsigned long flags;
> +	int i, val, x, y, p;
> +
> +	spin_lock_irqsave(&ts->lock, flags);
> +
> +	if (ts->sample_count >= TSC_SKIP) {
> +		input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
> +		input_report_key(ts->input_dev, BTN_TOUCH, 0);
> +		input_sync(ts->input_dev);
> +	} else if (ts->sample_count > 0) {
> +		/*
> +		 * A touch event lasted less than our skip count.  Salvage and
> +		 * report anyway.
> +		 */
> +		for (i = 0, val = 0; i < ts->sample_count; i++)
> +			val += ts->samples[i].x;
> +		x = val / ts->sample_count;
> +
> +		for (i = 0, val = 0; i < ts->sample_count; i++)
> +			val += ts->samples[i].y;
> +		y = val / ts->sample_count;
> +
> +		for (i = 0, val = 0; i < ts->sample_count; i++)
> +			val += ts->samples[i].p;
> +		p = val / ts->sample_count;
> +
> +		input_report_abs(ts->input_dev, ABS_X, x);
> +		input_report_abs(ts->input_dev, ABS_Y, y);
> +		input_report_abs(ts->input_dev, ABS_PRESSURE, p);
> +		input_report_key(ts->input_dev, BTN_TOUCH, 1);
> +		input_sync(ts->input_dev);
> +	}
> +
> +	ts->sample_count = 0;
> +
> +	spin_unlock_irqrestore(&ts->lock, flags);
> +}
> +
> +static irqreturn_t tsc_irq(int irq, void *dev_id)
> +{
> +	struct tsc_data *ts = (struct tsc_data *)dev_id;
> +	struct sample *sample;
> +	int index;
> +
> +	spin_lock(&ts->lock);
> +
> +	index = ts->sample_count % TSC_SAMPLES;
> +	sample = &ts->samples[index];
> +	if (tsc_read_sample(ts, sample) >= 0) {
> +		++ts->sample_count;
> +
> +		if (ts->sample_count < TSC_SKIP)
> +			goto done;
> +
> +		index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES;
> +		sample = &ts->samples[index];
> +
> +		input_report_abs(ts->input_dev, ABS_X, sample->x);
> +		input_report_abs(ts->input_dev, ABS_Y, sample->y);
> +		input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p);
> +		if (ts->sample_count == TSC_SKIP)
> +			input_report_key(ts->input_dev, BTN_TOUCH, 1);
> +		input_sync(ts->input_dev);
> +done:
> +		mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL);
> +	}
> +
> +	spin_unlock(&ts->lock);
> +	return IRQ_HANDLED;
> +}
> +
> +static int tsc_start(struct input_dev *dev)
> +{
> +	struct tsc_data *ts = input_get_drvdata(dev);
> +	unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT);
> +	u32 val;
> +	int error;
> +
> +	clk_enable(ts->clk);
> +
> +	/* Go to idle mode, before any initialization */
> +	while (time_after(timeout, jiffies)) {
> +		if (tsc_read(ts, tscm) & IDLE)
> +			break;
> +	}
> +
> +	if (time_before(timeout, jiffies)) {
> +		dev_warn(ts->dev, "timeout waiting for idle\n");
> +		clk_disable(ts->clk);
> +		return -EIO;
> +	}
> +
> +	/* Configure TSC Control register*/
> +	val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN);
> +	tsc_write(ts, tscm, val);
> +
> +	/* Bring TSC out of reset: Clear AFE reset bit */
> +	val &= ~(AFERST);
> +	tsc_write(ts, tscm, val);
> +	udelay(10);

A macro may have been helpful.
> +
> +	/* Configure all pins for hardware control*/
> +	tsc_write(ts, bwcm, 0);
> +
> +	/* Finally enable the TSC */
> +	tsc_set_bits(ts, tscm, TSC_EN);
> +
> +	error = request_irq(ts->tsc_irq, tsc_irq, 0, "tnetv107x-ts", ts);
> +	if (error < 0) {
> +		dev_err(ts->dev, "Could not allocate ts irq\n");
> +		clk_disable(ts->clk);
> +		return error;
> +	}

You may want to see threaded irq. You may consider moving to probe as it could be a valid reason for failing probe.
  
> +
> +	return 0;
> +}
> +
> +static void tsc_stop(struct input_dev *dev)
> +{
> +	struct tsc_data *ts = input_get_drvdata(dev);
> +
> +	tsc_clr_bits(ts, tscm, TSC_EN);
> +	del_timer_sync(&ts->timer);
> +	free_irq(ts->tsc_irq, ts);
> +	clk_disable(ts->clk);
> +}
> +
> +static int __devinit tsc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct tsc_data *ts;
> +	int error = 0;
> +	u32 rev = 0;
> +
> +	ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL);
> +	if (!ts) {
> +		dev_err(dev, "cannot allocate device info\n");
> +		return -ENOMEM;
> +	}
> +
> +	ts->dev = dev;
> +	spin_lock_init(&ts->lock);
> +	setup_timer(&ts->timer, tsc_poll, (unsigned long)ts);
> +	platform_set_drvdata(pdev, ts);
> +
> +	ts->tsc_irq = platform_get_irq(pdev, 0);
> +	if (ts->tsc_irq < 0) {
> +		dev_err(dev, "cannot determine device interrupt\n");
> +		error = -ENODEV;
> +		goto error;
> +	}
> +
> +	ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!ts->res) {
> +		dev_err(dev, "cannot determine register area\n");
> +		error = -ENODEV;
> +		goto error;
> +	}
> +
> +	if (!request_mem_region(ts->res->start, resource_size(ts->res),
> +				pdev->name)) {
> +		dev_err(dev, "cannot claim register memory\n");
> +		ts->res = NULL;
> +		error = -EINVAL;
> +		goto error;
> +	}
> +
> +	ts->regs = ioremap(ts->res->start, resource_size(ts->res));
> +	if (!ts->regs) {
> +		dev_err(dev, "cannot map register memory\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}
> +
> +	ts->clk = clk_get(dev, NULL);
> +	if (!ts->clk) {
> +		dev_err(dev, "cannot claim device clock\n");
> +		error = -EINVAL;
> +		goto error;
> +	}
> +
> +	ts->input_dev = input_allocate_device();
> +	if (!ts->input_dev) {
> +		dev_err(dev, "cannot allocate input device\n");
> +		error = -ENOMEM;
> +		goto error;
> +	}
> +	input_set_drvdata(ts->input_dev, ts);
> +
> +	ts->input_dev->name       = pdev->name;
> +	ts->input_dev->id.bustype = BUS_HOST;
> +	ts->input_dev->dev.parent = &pdev->dev;
> +	ts->input_dev->open	  = tsc_start;
> +	ts->input_dev->close	  = tsc_stop;
> +
> +	clk_enable(ts->clk);
> +	rev = tsc_read(ts, rev);
> +	ts->input_dev->id.product = ((rev >>  8) & 0x07);
> +	ts->input_dev->id.version = ((rev >> 16) & 0xfff);
> +	clk_disable(ts->clk);
> +
> +	set_bit(EV_KEY,       ts->input_dev->evbit);
> +	set_bit(EV_ABS,       ts->input_dev->evbit);
> +	set_bit(BTN_TOUCH,    ts->input_dev->keybit);
> +	set_bit(ABS_X,        ts->input_dev->absbit);
> +	set_bit(ABS_Y,        ts->input_dev->absbit);
> +	set_bit(ABS_PRESSURE, ts->input_dev->absbit);
> +
> +	input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0);
> +	input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0);
> +	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0);
> +
> +	error = input_register_device(ts->input_dev);
> +	if (error < 0) {
> +		dev_err(dev, "failed input device registration\n");
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	if (ts->clk)
> +		clk_put(ts->clk);
> +	if (ts->regs)
> +		iounmap(ts->regs);
> +	if (ts->res)
> +		release_mem_region(ts->res->start, resource_size(ts->res));
> +	if (ts->input_dev)
> +		input_free_device(ts->input_dev);
This may  not needed as it already has the check .

> +	platform_set_drvdata(pdev, NULL);
> +	kfree(ts);
> +
> +	return error;
> +}
> +
> +static int __devexit tsc_remove(struct platform_device *pdev)
> +{
> +	struct tsc_data *ts = platform_get_drvdata(pdev);
> +
> +	input_unregister_device(ts->input_dev);
> +	clk_put(ts->clk);
> +	iounmap(ts->regs);
> +	release_mem_region(ts->res->start, resource_size(ts->res));
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(ts);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver tsc_driver = {
> +	.probe		= tsc_probe,
> +	.remove		= tsc_remove,
 You may want to consider __devexit_p

> +	.driver.name	= "tnetv107x-ts",
> +	.driver.owner	= THIS_MODULE,
> +};
> +
> +static int __init tsc_init(void)
> +{
> +	return platform_driver_register(&tsc_driver);
> +}
> +
> +static void __exit tsc_exit(void)
> +{
> +	platform_driver_unregister(&tsc_driver);
> +}
> +
> +module_init(tsc_init);
> +module_exit(tsc_exit);
> +
> +MODULE_AUTHOR("Cyril Chemparathy");
> +MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
> +MODULE_ALIAS("platform: tnetv107x-ts");
> +MODULE_LICENSE("GPL");
> --
> 1.7.0.4


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

end of thread, other threads:[~2010-09-20  5:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-16 19:54 [PATCH v2 0/5] add tnetv107x input drivers Cyril Chemparathy
2010-09-16 19:54 ` [PATCH v2 1/5] input: add driver for tnetv107x on-chip keypad controller Cyril Chemparathy
2010-09-20  0:03   ` Dmitry Torokhov
2010-09-16 19:54 ` [PATCH v2 2/5] davinci: add tnetv107x keypad platform device Cyril Chemparathy
2010-09-16 19:54 ` [PATCH v2 3/5] davinci: add keypad config for tnetv107x evm board Cyril Chemparathy
2010-09-16 19:54 ` [PATCH v2 4/5] input: add driver for tnetv107x touchscreen controller Cyril Chemparathy
2010-09-20  0:16   ` Dmitry Torokhov
2010-09-20  5:38   ` Datta, Shubhrajyoti
2010-09-16 19:54 ` [PATCH v2 5/5] davinci: add tnetv107x touchscreen platform device Cyril Chemparathy

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.