linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] input: add support for TI Touchscreen controller.
@ 2011-07-22  9:13 Patil, Rachna
  2011-08-01  6:21 ` Dmitry Torokhov
       [not found] ` <CAM=Q2cunVNO92ngc0_YaiH8Eikk66xqSUrRU4eLm8xB2n4Y8og@mail.gmail.com>
  0 siblings, 2 replies; 4+ messages in thread
From: Patil, Rachna @ 2011-07-22  9:13 UTC (permalink / raw)
  To: linux-input; +Cc: Dmitry Torokhov, Dmitry Torokhov, Patil, Rachna

This patch adds support for TI's touchscreen
controller for a 4/5/8 wire resistive panel
that is directly fed to the ADC.

This touchscreen controller will be part of
an upcoming TI SoC and has been tested on
an emulation platform.

ChangeLog:
v2: fixed comments from Dimitry
v3: made the driver depend on HAVE_CLK for build

Signed-off-by: Patil, Rachna <rachna@ti.com>
---
 drivers/input/touchscreen/Kconfig     |   12 +
 drivers/input/touchscreen/Makefile    |    1 +
 drivers/input/touchscreen/ti_tscadc.c |  431 +++++++++++++++++++++++++++++++++
 include/linux/input/ti_tscadc.h       |   10 +
 4 files changed, 454 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/ti_tscadc.c
 create mode 100644 include/linux/input/ti_tscadc.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 44589f5..69d367a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -395,6 +395,18 @@ config TOUCHSCREEN_TOUCHWIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called touchwin.
 
+config TOUCHSCREEN_TI_TSCADC
+	tristate "TI Touchscreen Interface"
+	depends on HAVE_CLK
+	help
+	  Say Y here if you have 4/5/8 wire touchscreen controller
+	  to be connected to the ADC controller on your TI SoC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ti_tscadc.
+
 config TOUCHSCREEN_ATMEL_TSADCC
 	tristate "Atmel Touchscreen Interface"
 	depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 51b08b0..86b7ab6 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_TOUCHSCREEN_QT602240)	+= qt602240_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC)     += ti_tscadc.o
 obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c
new file mode 100644
index 0000000..86264fc
--- /dev/null
+++ b/drivers/input/touchscreen/ti_tscadc.c
@@ -0,0 +1,431 @@
+/*
+ * TI Touch Screen driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/input/ti_tscadc.h>
+
+#define TSCADC_REG_IRQEOI		0x020
+#define TSCADC_REG_IRQSTATUS		0x028
+#define TSCADC_REG_IRQENABLE		0x02C
+#define TSCADC_REG_IRQWAKEUP		0x034
+#define TSCADC_REG_CTRL			0x040
+#define TSCADC_REG_ADCFSM		0x044
+#define TSCADC_REG_CLKDIV		0x04C
+#define TSCADC_REG_SE			0x054
+#define TSCADC_REG_IDLECONFIG		0x058
+#define TSCADC_REG_CHARGECONFIG		0x05C
+#define TSCADC_REG_STEPCONFIG1		0x064
+#define TSCADC_REG_STEPDELAY1		0x068
+#define TSCADC_REG_STEPCONFIG2		0x06C
+#define TSCADC_REG_STEPDELAY2		0x070
+#define TSCADC_REG_FIFO0		0x100
+
+/*	Register Bitfields	*/
+#define TSCADC_IRQWKUP_ENB		BIT(0)
+#define TSCADC_STPENB_STEPENB		(7 << 0)
+#define TSCADC_IRQENB_IRQHWPEN		BIT(10)
+#define TSCADC_IRQENB_IRQEOS		BIT(1)
+#define TSCADC_IRQENB_FIFO_OVERFLOW	BIT(3)
+#define TSCADC_IRQENB_PENUP		BIT(9)
+#define TSCADC_STEPCONFIG_MODE_HWSYNC	0x3
+#define TSCADC_STEPCONFIG_2SAMPLES_AVG	BIT(2)
+#define TSCADC_STEPCONFIG_XPP		BIT(5)
+#define TSCADC_STEPCONFIG_XNN		BIT(6)
+#define TSCADC_STEPCONFIG_YPP		BIT(7)
+#define TSCADC_STEPCONFIG_YNN		BIT(8)
+#define TSCADC_STEPCONFIG_XNP		BIT(9)
+#define TSCADC_STEPCONFIG_YPN		BIT(10)
+#define TSCADC_STEPCONFIG_RFP_X		(1 << 12)
+#define TSCADC_STEPCONFIG_RFP_4_Y	(1 << 13)
+#define TSCADC_STEPCONFIG_RFP_5_Y	(1 << 12)
+#define TSCADC_STEPCONFIG_RFP_8_Y	(1 << 13)
+#define TSCADC_STEPCONFIG_INM		(1 << 18)
+#define TSCADC_STEPCONFIG_INP_4		(1 << 20)
+#define TSCADC_STEPCONFIG_INP_5		(1 << 21)
+#define TSCADC_STEPCONFIG_INP_8_X	(3 << 20)
+#define TSCADC_STEPCONFIG_INP_8_Y	(1 << 21)
+#define TSCADC_STEPCONFIG_RFM_4_X	(1 << 23)
+#define TSCADC_STEPCONFIG_RFM_5_X	(1 << 24)
+#define TSCADC_STEPCONFIG_RFM_8_X	(1 << 23)
+#define TSCADC_STEPCONFIG_RFM_Y		(1 << 24)
+#define TSCADC_STEPCONFIG_OPENDLY	(0xf << 0)
+#define TSCADC_STEPCONFIG_SAMPLEDLY	BIT(25)
+#define TSCADC_STEPCHARGE_INM		BIT(18)
+#define TSCADC_STEPCHARGE_RFM		(3 << 23)
+#define TSCADC_CNTRLREG_TSCSSENB	BIT(0)
+#define TSCADC_CNTRLREG_STEPID		BIT(1)
+#define TSCADC_CNTRLREG_STEPCONFIGWRT	BIT(2)
+#define TSCADC_CNTRLREG_TSCENB		BIT(7)
+#define TSCADC_CNTRLREG_4WIRE		(0x1 << 5)
+#define TSCADC_CNTRLREG_5WIRE		(0x1 << 6)
+#define TSCADC_CNTRLREG_8WIRE		(0x3 << 5)
+#define TSCADC_ADCFSM_STEPID		0x10
+#define TSCADC_ADCFSM_FSM		BIT(5)
+
+#define ADC_CLK				3000000
+
+#define MAX_12BIT                       ((1 << 12) - 1)
+
+struct tscadc {
+	struct input_dev	*input;
+	int			wires;
+	struct clk		*clk;
+	int			irq;
+	void __iomem		*tsc_base;
+};
+
+static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg)
+{
+	return readl(ts->tsc_base + reg);
+}
+
+static void tscadc_writel(struct tscadc *tsc, unsigned int reg,
+					unsigned int val)
+{
+	writel(val, tsc->tsc_base + reg);
+}
+
+static void tsc_step_config(struct tscadc *ts_dev)
+{
+	int	stepconfig1, stepconfig2, delay;
+
+	/* Configure the Step registers */
+	stepconfig1 = TSCADC_STEPCONFIG_MODE_HWSYNC |
+			TSCADC_STEPCONFIG_2SAMPLES_AVG | TSCADC_STEPCONFIG_XPP |
+			TSCADC_STEPCONFIG_XNN | TSCADC_STEPCONFIG_INM |
+			TSCADC_STEPCONFIG_RFP_X;
+
+	stepconfig2 = TSCADC_STEPCONFIG_MODE_HWSYNC |
+			TSCADC_STEPCONFIG_2SAMPLES_AVG | TSCADC_STEPCONFIG_YNN |
+			TSCADC_STEPCONFIG_INM | TSCADC_STEPCONFIG_RFM_Y;
+	switch (ts_dev->wires) {
+	case 4:
+		stepconfig1 |= TSCADC_STEPCONFIG_INP_4 |
+				TSCADC_STEPCONFIG_RFM_4_X;
+
+		stepconfig2 |= TSCADC_STEPCONFIG_YPP |
+				TSCADC_STEPCONFIG_RFP_4_Y;
+		break;
+	case 5:
+		stepconfig1 |= TSCADC_STEPCONFIG_YPP |
+				TSCADC_STEPCONFIG_YNN |
+				TSCADC_STEPCONFIG_INP_5 |
+				TSCADC_STEPCONFIG_RFM_5_X;
+
+		stepconfig2 |= TSCADC_STEPCONFIG_XPP |
+				TSCADC_STEPCONFIG_XNP |
+				TSCADC_STEPCONFIG_YPN |
+				TSCADC_STEPCONFIG_RFP_5_Y |
+				TSCADC_STEPCONFIG_INP_5;
+		break;
+	case 8:
+		stepconfig1 |= TSCADC_STEPCONFIG_INP_8_X |
+				TSCADC_STEPCONFIG_RFM_8_X;
+
+		stepconfig2 |= TSCADC_STEPCONFIG_YPP |
+				TSCADC_STEPCONFIG_RFP_8_Y |
+				TSCADC_STEPCONFIG_INP_8_Y;
+		break;
+	}
+	delay = TSCADC_STEPCONFIG_OPENDLY | TSCADC_STEPCONFIG_SAMPLEDLY;
+
+	tscadc_writel(ts_dev, TSCADC_REG_STEPCONFIG1, stepconfig1);
+	tscadc_writel(ts_dev, TSCADC_REG_STEPDELAY1, delay);
+	tscadc_writel(ts_dev, TSCADC_REG_STEPCONFIG2, stepconfig2);
+	tscadc_writel(ts_dev, TSCADC_REG_STEPDELAY2, delay);
+
+	tscadc_writel(ts_dev, TSCADC_REG_SE, TSCADC_STPENB_STEPENB);
+}
+
+static void tsc_idle_config(struct tscadc *ts_config)
+{
+	/* Idle mode touch screen config */
+	unsigned int	 idleconfig;
+
+	idleconfig = TSCADC_STEPCONFIG_YNN |
+				TSCADC_STEPCONFIG_RFP_X |
+				TSCADC_STEPCONFIG_INM ;
+
+	switch (ts_config->wires) {
+	case 4:
+		idleconfig |= TSCADC_STEPCONFIG_INP_4 |
+			TSCADC_STEPCONFIG_RFM_4_X;
+		break;
+	case 5:
+		idleconfig |= TSCADC_STEPCONFIG_INP_5 |
+			TSCADC_STEPCONFIG_RFM_5_X;
+		break;
+	case 8:
+		idleconfig |= TSCADC_STEPCONFIG_INP_4 |
+			TSCADC_STEPCONFIG_RFM_8_X;
+		break;
+	}
+	tscadc_writel(ts_config, TSCADC_REG_IDLECONFIG, idleconfig);
+}
+
+static irqreturn_t tscadc_interrupt(int irq, void *dev)
+{
+	struct tscadc		*ts_dev = (struct tscadc *)dev;
+	struct input_dev	*input_dev = ts_dev->input;
+	unsigned int		status, store, cntrlreg, irqclr = 0;
+	int			absx, absy;
+	int			charge, fsm;
+
+	status = tscadc_readl(ts_dev, TSCADC_REG_IRQSTATUS);
+	tscadc_writel(ts_dev, TSCADC_REG_SE, 0x0);
+
+	/* Pen touch event */
+	if (status & TSCADC_IRQENB_IRQHWPEN)
+		irqclr = TSCADC_IRQENB_IRQHWPEN;
+
+	if (status & TSCADC_IRQENB_PENUP) {
+		/* Pen up event */
+		charge = TSCADC_STEPCHARGE_INM | TSCADC_STEPCHARGE_RFM;
+		tscadc_writel(ts_dev, TSCADC_REG_CHARGECONFIG, charge);
+		input_report_key(input_dev, BTN_TOUCH, 0);
+		input_sync(input_dev);
+		tsc_idle_config(ts_dev);
+		irqclr |= TSCADC_IRQENB_PENUP;
+
+	}
+	if (status & TSCADC_IRQENB_IRQEOS) {
+		/* ADC is done with sampling, ready to read the data */
+		absx = tscadc_readl(ts_dev, TSCADC_REG_FIFO0);
+		absy = tscadc_readl(ts_dev, TSCADC_REG_FIFO0);
+
+		input_report_abs(input_dev, ABS_X, absx);
+		input_report_abs(input_dev, ABS_Y, absy);
+		input_report_key(input_dev, BTN_TOUCH, 1);
+		input_sync(input_dev);
+
+		irqclr |= TSCADC_IRQENB_IRQEOS;
+
+	}
+	if (status & TSCADC_IRQENB_FIFO_OVERFLOW) {
+		/* FIFO overflow condition */
+		cntrlreg = tscadc_readl(ts_dev, TSCADC_REG_CTRL);
+		cntrlreg &= ~TSCADC_CNTRLREG_TSCSSENB;
+		tscadc_writel(ts_dev, TSCADC_REG_CTRL, cntrlreg);
+
+		irqclr |= TSCADC_IRQENB_FIFO_OVERFLOW;
+	}
+
+	tscadc_writel(ts_dev, TSCADC_REG_IRQSTATUS, irqclr);
+	fsm = tscadc_readl(ts_dev, TSCADC_REG_ADCFSM);
+	if ((fsm & TSCADC_ADCFSM_FSM) &&
+			(fsm & TSCADC_ADCFSM_STEPID)) {
+		store = tscadc_readl(ts_dev, TSCADC_REG_CTRL);
+		store |= TSCADC_CNTRLREG_TSCSSENB;
+		tscadc_writel(ts_dev, TSCADC_REG_CTRL, store);
+	}
+
+	 /* check pending interrupts */
+	tscadc_writel(ts_dev, TSCADC_REG_IRQEOI, 0x0);
+
+	tscadc_writel(ts_dev, TSCADC_REG_SE, TSCADC_STPENB_STEPENB);
+	return IRQ_HANDLED;
+}
+
+/*
+* The functions for inserting/removing driver as a module.
+*/
+
+static	int __devinit tscadc_probe(struct platform_device *pdev)
+{
+	struct tscadc			*ts_dev;
+	struct input_dev		*input_dev;
+	int				err;
+	int				clk_value;
+	int				clock_rate, irqenable, ctrl;
+	struct	tsc_data		*pdata = pdev->dev.platform_data;
+	struct resource			*res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined.\n");
+		return -EINVAL;
+	}
+
+	/* Allocate memory for device */
+	ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
+	if (!ts_dev) {
+		dev_err(&pdev->dev, "failed to allocate memory.\n");
+		return -ENOMEM;
+	}
+
+	ts_dev->irq = platform_get_irq(pdev, 0);
+	if (ts_dev->irq < 0) {
+		dev_err(&pdev->dev, "no irq ID is specified.\n");
+		return -ENODEV;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev, "failed to allocate input device.\n");
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+	ts_dev->input = input_dev;
+
+	ts_dev->tsc_base = ioremap(res->start, resource_size(res));
+	if (!ts_dev->tsc_base) {
+		dev_err(&pdev->dev, "failed to map registers.\n");
+		err = -ENOMEM;
+		goto err_release_mem;
+	}
+
+	err = request_irq(ts_dev->irq, tscadc_interrupt, IRQF_DISABLED,
+				pdev->dev.driver->name, ts_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to allocate irq.\n");
+		goto err_unmap_regs;
+	}
+
+	ts_dev->clk = clk_get(&pdev->dev, "tsc_clk");
+	if (IS_ERR(ts_dev->clk)) {
+		dev_err(&pdev->dev, "failed to get ts_clk\n");
+		err = PTR_ERR(ts_dev->clk);
+		goto err_free_irq;
+	}
+	clk_enable(ts_dev->clk);
+
+	clock_rate = clk_get_rate(ts_dev->clk);
+	clk_value = clock_rate / ADC_CLK;
+	if (clk_value < 7) {
+		dev_err(&pdev->dev, "clock input less than min clock requirement\n");
+		goto err_fail;
+	}
+	/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
+	clk_value = clk_value - 1;
+	tscadc_writel(ts_dev, TSCADC_REG_CLKDIV, clk_value);
+
+	 /* Enable wake-up of the SoC using touchscreen */
+	tscadc_writel(ts_dev, TSCADC_REG_IRQWAKEUP, TSCADC_IRQWKUP_ENB);
+
+	ts_dev->wires = pdata->wires;
+
+	/* Set the control register bits */
+	ctrl = TSCADC_CNTRLREG_STEPCONFIGWRT |
+			TSCADC_CNTRLREG_TSCENB |
+			TSCADC_CNTRLREG_STEPID;
+	switch (ts_dev->wires) {
+	case 4:
+		ctrl |= TSCADC_CNTRLREG_4WIRE;
+		break;
+	case 5:
+		ctrl |= TSCADC_CNTRLREG_5WIRE;
+		break;
+	case 8:
+		ctrl |= TSCADC_CNTRLREG_8WIRE;
+		break;
+	}
+	tscadc_writel(ts_dev, TSCADC_REG_CTRL, ctrl);
+
+	/* Set register bits for Idel Config Mode */
+	tsc_idle_config(ts_dev);
+
+	/* IRQ Enable */
+	irqenable = TSCADC_IRQENB_IRQHWPEN |
+			TSCADC_IRQENB_IRQEOS |
+			TSCADC_IRQENB_PENUP | TSCADC_IRQENB_FIFO_OVERFLOW;
+	tscadc_writel(ts_dev, TSCADC_REG_IRQENABLE, irqenable);
+
+	tsc_step_config(ts_dev);
+
+	ctrl |= TSCADC_CNTRLREG_TSCSSENB;
+	tscadc_writel(ts_dev, TSCADC_REG_CTRL, ctrl);
+
+	input_dev->name = "ti-tsc-adcc";
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+	/* register to the input system */
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_fail;
+
+	return 0;
+
+err_fail:
+	clk_disable(ts_dev->clk);
+	clk_put(ts_dev->clk);
+err_free_irq:
+	free_irq(ts_dev->irq, ts_dev);
+err_unmap_regs:
+	iounmap(ts_dev->tsc_base);
+err_release_mem:
+	release_mem_region(res->start, resource_size(res));
+	input_free_device(ts_dev->input);
+err_free_mem:
+	kfree(ts_dev);
+	return err;
+}
+
+static int __devexit tscadc_remove(struct platform_device *pdev)
+{
+	struct tscadc		*ts_dev = dev_get_drvdata(&pdev->dev);
+	struct resource		*res;
+
+	free_irq(ts_dev->irq, ts_dev);
+
+	input_unregister_device(ts_dev->input);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	iounmap(ts_dev->tsc_base);
+	release_mem_region(res->start, resource_size(res));
+
+	clk_disable(ts_dev->clk);
+	clk_put(ts_dev->clk);
+
+	kfree(ts_dev);
+
+	return 0;
+}
+
+static struct platform_driver ti_tsc_driver = {
+	.probe	  = tscadc_probe,
+	.remove	 = __devexit_p(tscadc_remove),
+	.driver	 = {
+		.name   = "tsc",
+	},
+};
+
+static int __init ti_tsc_init(void)
+{
+	return platform_driver_register(&ti_tsc_driver);
+}
+
+static void __exit ti_tsc_exit(void)
+{
+	platform_driver_unregister(&ti_tsc_driver);
+}
+
+module_init(ti_tsc_init);
+module_exit(ti_tsc_exit);
diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h
new file mode 100644
index 0000000..29f87db
--- /dev/null
+++ b/include/linux/input/ti_tscadc.h
@@ -0,0 +1,10 @@
+/**
+ * struct tsc_data	Touchscreen wire configuration
+ * @wires:		Wires refer to application modes
+ *			i.e. 4/5/8 wire touchscreen support
+ *			on the platform
+ */
+
+struct tsc_data {
+	int wires;
+};
-- 
1.6.2.4


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

* Re: [PATCH v3] input: add support for TI Touchscreen controller.
  2011-07-22  9:13 [PATCH v3] input: add support for TI Touchscreen controller Patil, Rachna
@ 2011-08-01  6:21 ` Dmitry Torokhov
       [not found] ` <CAM=Q2cunVNO92ngc0_YaiH8Eikk66xqSUrRU4eLm8xB2n4Y8og@mail.gmail.com>
  1 sibling, 0 replies; 4+ messages in thread
From: Dmitry Torokhov @ 2011-08-01  6:21 UTC (permalink / raw)
  To: Patil, Rachna; +Cc: linux-input

Hi Rachna,

On Fri, Jul 22, 2011 at 02:43:29PM +0530, Patil, Rachna wrote:
> +err_release_mem:
> +	release_mem_region(res->start, resource_size(res));

You call release_mem_region() here and also in tscadc_remove() but I do
not see you calling request_mem_region() in tscadc_probe().

You also need to have MODULE_LICENSE() if you want your driver to load
when compiled as a module and MODULE_ALIAS() to make sure it is
automatically loaded, since name of the device differs from the name of
the module.

> --- /dev/null
> +++ b/include/linux/input/ti_tscadc.h
> @@ -0,0 +1,10 @@
> +/**
> + * struct tsc_data	Touchscreen wire configuration
> + * @wires:		Wires refer to application modes
> + *			i.e. 4/5/8 wire touchscreen support
> + *			on the platform
> + */
> +
> +struct tsc_data {
> +	int wires;
> +};

It is a good idea to guard include files with something like #ifdef
__LINUX_TI_TSCADC_H to allow it be included several times.

Could you please tell me if the patch below still works for you?

Thanks.

-- 
Dmitry


Input: add support for TI Touchscreen controller.

From: Rachna Patil <rachna@ti.com>

This patch adds support for TI's touchscreen controller for a 4/5/8 wire
resistive panel that is directly fed to the ADC.

This touchscreen controller will be part of an upcoming TI SoC and has
been tested on an emulation platform.

Signed-off-by: Patil, Rachna <rachna@ti.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/touchscreen/Kconfig     |   12 +
 drivers/input/touchscreen/Makefile    |    1 
 drivers/input/touchscreen/ti_tscadc.c |  460 +++++++++++++++++++++++++++++++++
 include/linux/input/ti_tscadc.h       |   15 +
 4 files changed, 488 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/ti_tscadc.c
 create mode 100644 include/linux/input/ti_tscadc.h


diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 358be1f..33fc6e4 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -436,6 +436,18 @@ config TOUCHSCREEN_TOUCHWIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called touchwin.
 
+config TOUCHSCREEN_TI_TSCADC
+	tristate "TI Touchscreen Interface"
+	depends on HAVE_CLK
+	help
+	  Say Y here if you have 4/5/8 wire touchscreen controller
+	  to be connected to the ADC controller on your TI SoC.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ti_tscadc.
+
 config TOUCHSCREEN_ATMEL_TSADCC
 	tristate "Atmel Touchscreen Interface"
 	depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index c0727a4..3c0b219 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC)	+= ti_tscadc.o
 obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c
new file mode 100644
index 0000000..8b38510
--- /dev/null
+++ b/drivers/input/touchscreen/ti_tscadc.c
@@ -0,0 +1,460 @@
+/*
+ * TI Touch Screen driver.
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation 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/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/input/ti_tscadc.h>
+
+#define TSCADC_REG_IRQEOI		0x020
+#define TSCADC_REG_IRQSTATUS		0x028
+#define TSCADC_REG_IRQENABLE		0x02C
+#define TSCADC_REG_IRQWAKEUP		0x034
+#define TSCADC_REG_CTRL			0x040
+#define TSCADC_REG_ADCFSM		0x044
+#define TSCADC_REG_CLKDIV		0x04C
+#define TSCADC_REG_SE			0x054
+#define TSCADC_REG_IDLECONFIG		0x058
+#define TSCADC_REG_CHARGECONFIG		0x05C
+#define TSCADC_REG_STEPCONFIG1		0x064
+#define TSCADC_REG_STEPDELAY1		0x068
+#define TSCADC_REG_STEPCONFIG2		0x06C
+#define TSCADC_REG_STEPDELAY2		0x070
+#define TSCADC_REG_FIFO0		0x100
+
+/*	Register Bitfields	*/
+#define TSCADC_IRQWKUP_ENB		BIT(0)
+#define TSCADC_STPENB_STEPENB		(7 << 0)
+#define TSCADC_IRQENB_IRQHWPEN		BIT(10)
+#define TSCADC_IRQENB_IRQEOS		BIT(1)
+#define TSCADC_IRQENB_FIFO_OVERFLOW	BIT(3)
+#define TSCADC_IRQENB_PENUP		BIT(9)
+#define TSCADC_STEPCONFIG_MODE_HWSYNC	0x3
+#define TSCADC_STEPCONFIG_2SAMPLES_AVG	BIT(2)
+#define TSCADC_STEPCONFIG_XPP		BIT(5)
+#define TSCADC_STEPCONFIG_XNN		BIT(6)
+#define TSCADC_STEPCONFIG_YPP		BIT(7)
+#define TSCADC_STEPCONFIG_YNN		BIT(8)
+#define TSCADC_STEPCONFIG_XNP		BIT(9)
+#define TSCADC_STEPCONFIG_YPN		BIT(10)
+#define TSCADC_STEPCONFIG_RFP_X		(1 << 12)
+#define TSCADC_STEPCONFIG_RFP_4_Y	(1 << 13)
+#define TSCADC_STEPCONFIG_RFP_5_Y	(1 << 12)
+#define TSCADC_STEPCONFIG_RFP_8_Y	(1 << 13)
+#define TSCADC_STEPCONFIG_INM		(1 << 18)
+#define TSCADC_STEPCONFIG_INP_4		(1 << 20)
+#define TSCADC_STEPCONFIG_INP_5		(1 << 21)
+#define TSCADC_STEPCONFIG_INP_8_X	(3 << 20)
+#define TSCADC_STEPCONFIG_INP_8_Y	(1 << 21)
+#define TSCADC_STEPCONFIG_RFM_4_X	(1 << 23)
+#define TSCADC_STEPCONFIG_RFM_5_X	(1 << 24)
+#define TSCADC_STEPCONFIG_RFM_8_X	(1 << 23)
+#define TSCADC_STEPCONFIG_RFM_Y		(1 << 24)
+#define TSCADC_STEPCONFIG_OPENDLY	(0xf << 0)
+#define TSCADC_STEPCONFIG_SAMPLEDLY	BIT(25)
+#define TSCADC_STEPCHARGE_INM		BIT(18)
+#define TSCADC_STEPCHARGE_RFM		(3 << 23)
+#define TSCADC_CNTRLREG_TSCSSENB	BIT(0)
+#define TSCADC_CNTRLREG_STEPID		BIT(1)
+#define TSCADC_CNTRLREG_STEPCONFIGWRT	BIT(2)
+#define TSCADC_CNTRLREG_TSCENB		BIT(7)
+#define TSCADC_CNTRLREG_4WIRE		(0x1 << 5)
+#define TSCADC_CNTRLREG_5WIRE		(0x1 << 6)
+#define TSCADC_CNTRLREG_8WIRE		(0x3 << 5)
+#define TSCADC_ADCFSM_STEPID		0x10
+#define TSCADC_ADCFSM_FSM		BIT(5)
+
+#define ADC_CLK				3000000
+
+#define MAX_12BIT                       ((1 << 12) - 1)
+
+struct tscadc {
+	struct input_dev *input;
+	struct clk *clk;
+	void __iomem *tsc_base;
+	unsigned int wires;
+	int irq;
+};
+
+static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg)
+{
+	return readl(ts->tsc_base + reg);
+}
+
+static void tscadc_writel(struct tscadc *tsc,
+			  unsigned int reg, unsigned int val)
+{
+	writel(val, tsc->tsc_base + reg);
+}
+
+static void tsc_step_config(struct tscadc *ts_dev)
+{
+	int stepconfig1, stepconfig2, delay;
+
+	/* Configure the Step registers */
+	stepconfig1 = TSCADC_STEPCONFIG_MODE_HWSYNC |
+			TSCADC_STEPCONFIG_2SAMPLES_AVG | TSCADC_STEPCONFIG_XPP |
+			TSCADC_STEPCONFIG_XNN | TSCADC_STEPCONFIG_INM |
+			TSCADC_STEPCONFIG_RFP_X;
+
+	stepconfig2 = TSCADC_STEPCONFIG_MODE_HWSYNC |
+			TSCADC_STEPCONFIG_2SAMPLES_AVG | TSCADC_STEPCONFIG_YNN |
+			TSCADC_STEPCONFIG_INM | TSCADC_STEPCONFIG_RFM_Y;
+
+	switch (ts_dev->wires) {
+	case 4:
+		stepconfig1 |= TSCADC_STEPCONFIG_INP_4 |
+				TSCADC_STEPCONFIG_RFM_4_X;
+
+		stepconfig2 |= TSCADC_STEPCONFIG_YPP |
+				TSCADC_STEPCONFIG_RFP_4_Y;
+		break;
+
+	case 5:
+		stepconfig1 |= TSCADC_STEPCONFIG_YPP |
+				TSCADC_STEPCONFIG_YNN |
+				TSCADC_STEPCONFIG_INP_5 |
+				TSCADC_STEPCONFIG_RFM_5_X;
+
+		stepconfig2 |= TSCADC_STEPCONFIG_XPP |
+				TSCADC_STEPCONFIG_XNP |
+				TSCADC_STEPCONFIG_YPN |
+				TSCADC_STEPCONFIG_RFP_5_Y |
+				TSCADC_STEPCONFIG_INP_5;
+		break;
+
+	case 8:
+		stepconfig1 |= TSCADC_STEPCONFIG_INP_8_X |
+				TSCADC_STEPCONFIG_RFM_8_X;
+
+		stepconfig2 |= TSCADC_STEPCONFIG_YPP |
+				TSCADC_STEPCONFIG_RFP_8_Y |
+				TSCADC_STEPCONFIG_INP_8_Y;
+		break;
+	}
+
+	delay = TSCADC_STEPCONFIG_OPENDLY | TSCADC_STEPCONFIG_SAMPLEDLY;
+
+	tscadc_writel(ts_dev, TSCADC_REG_STEPCONFIG1, stepconfig1);
+	tscadc_writel(ts_dev, TSCADC_REG_STEPDELAY1, delay);
+	tscadc_writel(ts_dev, TSCADC_REG_STEPCONFIG2, stepconfig2);
+	tscadc_writel(ts_dev, TSCADC_REG_STEPDELAY2, delay);
+
+	tscadc_writel(ts_dev, TSCADC_REG_SE, TSCADC_STPENB_STEPENB);
+}
+
+static void tsc_idle_config(struct tscadc *ts_config)
+{
+	/* Idle mode touch screen config */
+	unsigned int idleconfig;
+
+	idleconfig = TSCADC_STEPCONFIG_YNN |
+				TSCADC_STEPCONFIG_RFP_X |
+				TSCADC_STEPCONFIG_INM ;
+
+	switch (ts_config->wires) {
+	case 4:
+		idleconfig |= TSCADC_STEPCONFIG_INP_4 |
+				TSCADC_STEPCONFIG_RFM_4_X;
+		break;
+
+	case 5:
+		idleconfig |= TSCADC_STEPCONFIG_INP_5 |
+				TSCADC_STEPCONFIG_RFM_5_X;
+		break;
+
+	case 8:
+		idleconfig |= TSCADC_STEPCONFIG_INP_4 |
+				TSCADC_STEPCONFIG_RFM_8_X;
+		break;
+	}
+
+	tscadc_writel(ts_config, TSCADC_REG_IDLECONFIG, idleconfig);
+}
+
+static irqreturn_t tscadc_interrupt(int irq, void *dev)
+{
+	struct tscadc *ts_dev = dev;
+	struct input_dev *input_dev = ts_dev->input;
+	unsigned int irqclr = 0;
+	unsigned int status, store, cntrlreg;
+	int absx, absy;
+	int charge, fsm;
+
+	status = tscadc_readl(ts_dev, TSCADC_REG_IRQSTATUS);
+	tscadc_writel(ts_dev, TSCADC_REG_SE, 0x0);
+
+	/* Pen touch event */
+	if (status & TSCADC_IRQENB_IRQHWPEN)
+		irqclr = TSCADC_IRQENB_IRQHWPEN;
+
+	if (status & TSCADC_IRQENB_PENUP) {
+		/* Pen up event */
+		charge = TSCADC_STEPCHARGE_INM | TSCADC_STEPCHARGE_RFM;
+		tscadc_writel(ts_dev, TSCADC_REG_CHARGECONFIG, charge);
+
+		input_report_key(input_dev, BTN_TOUCH, 0);
+		input_sync(input_dev);
+
+		tsc_idle_config(ts_dev);
+		irqclr |= TSCADC_IRQENB_PENUP;
+	}
+
+	if (status & TSCADC_IRQENB_IRQEOS) {
+		/* ADC is done with sampling, ready to read the data */
+		absx = tscadc_readl(ts_dev, TSCADC_REG_FIFO0);
+		absy = tscadc_readl(ts_dev, TSCADC_REG_FIFO0);
+
+		input_report_abs(input_dev, ABS_X, absx);
+		input_report_abs(input_dev, ABS_Y, absy);
+		input_report_key(input_dev, BTN_TOUCH, 1);
+		input_sync(input_dev);
+
+		irqclr |= TSCADC_IRQENB_IRQEOS;
+	}
+
+	if (status & TSCADC_IRQENB_FIFO_OVERFLOW) {
+		/* FIFO overflow condition */
+		cntrlreg = tscadc_readl(ts_dev, TSCADC_REG_CTRL);
+		cntrlreg &= ~TSCADC_CNTRLREG_TSCSSENB;
+		tscadc_writel(ts_dev, TSCADC_REG_CTRL, cntrlreg);
+
+		irqclr |= TSCADC_IRQENB_FIFO_OVERFLOW;
+	}
+
+	tscadc_writel(ts_dev, TSCADC_REG_IRQSTATUS, irqclr);
+	fsm = tscadc_readl(ts_dev, TSCADC_REG_ADCFSM);
+	if ((fsm & TSCADC_ADCFSM_FSM) &&
+	    (fsm & TSCADC_ADCFSM_STEPID)) {
+		store = tscadc_readl(ts_dev, TSCADC_REG_CTRL);
+		store |= TSCADC_CNTRLREG_TSCSSENB;
+		tscadc_writel(ts_dev, TSCADC_REG_CTRL, store);
+	}
+
+	 /* check pending interrupts */
+	tscadc_writel(ts_dev, TSCADC_REG_IRQEOI, 0x0);
+
+	tscadc_writel(ts_dev, TSCADC_REG_SE, TSCADC_STPENB_STEPENB);
+	return IRQ_HANDLED;
+}
+
+/*
+* The functions for inserting/removing driver as a module.
+*/
+
+static int __devinit tscadc_probe(struct platform_device *pdev)
+{
+	const struct tsc_data *pdata = pdev->dev.platform_data;
+	struct resource *res;
+	struct tscadc *ts_dev;
+	struct input_dev *input_dev;
+	int err;
+	int irq;
+	int clk_value;
+	int clock_rate, irqenable, ctrl;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "missing platform data.\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined.\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no IRQ is specified.\n");
+		return -EINVAL;
+	}
+
+	ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ts_dev || !input_dev) {
+		dev_err(&pdev->dev, "failed to allocate memory.\n");
+		err = -ENOMEM;
+		goto err_free_mem;
+	}
+
+	ts_dev->input = input_dev;
+	ts_dev->irq = irq;
+	ts_dev->wires = pdata->wires;
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to reserve registers.\n");
+		err = -EBUSY;
+		goto err_free_mem;
+	}
+
+	ts_dev->tsc_base = ioremap(res->start, resource_size(res));
+	if (!ts_dev->tsc_base) {
+		dev_err(&pdev->dev, "failed to map registers.\n");
+		err = -ENOMEM;
+		goto err_release_mem_region;
+	}
+
+	err = request_irq(ts_dev->irq, tscadc_interrupt, IRQF_DISABLED,
+			  pdev->dev.driver->name, ts_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to allocate irq.\n");
+		goto err_unmap_regs;
+	}
+
+	ts_dev->clk = clk_get(&pdev->dev, "tsc_clk");
+	if (IS_ERR(ts_dev->clk)) {
+		dev_err(&pdev->dev, "failed to get ts_clk\n");
+		err = PTR_ERR(ts_dev->clk);
+		goto err_free_irq;
+	}
+	clk_enable(ts_dev->clk);
+
+	clock_rate = clk_get_rate(ts_dev->clk);
+	clk_value = clock_rate / ADC_CLK;
+	if (clk_value < 7) {
+		dev_err(&pdev->dev, "clock input less than min clock requirement\n");
+		goto err_disable_clk;
+	}
+
+	/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
+	clk_value = clk_value - 1;
+	tscadc_writel(ts_dev, TSCADC_REG_CLKDIV, clk_value);
+
+	 /* Enable wake-up of the SoC using touchscreen */
+	tscadc_writel(ts_dev, TSCADC_REG_IRQWAKEUP, TSCADC_IRQWKUP_ENB);
+
+	/* Set the control register bits */
+	ctrl = TSCADC_CNTRLREG_STEPCONFIGWRT |
+			TSCADC_CNTRLREG_TSCENB |
+			TSCADC_CNTRLREG_STEPID;
+
+	switch (ts_dev->wires) {
+	case 4:
+		ctrl |= TSCADC_CNTRLREG_4WIRE;
+		break;
+
+	case 5:
+		ctrl |= TSCADC_CNTRLREG_5WIRE;
+		break;
+
+	case 8:
+		ctrl |= TSCADC_CNTRLREG_8WIRE;
+		break;
+	}
+
+	tscadc_writel(ts_dev, TSCADC_REG_CTRL, ctrl);
+
+	/* Set register bits for Idel Config Mode */
+	tsc_idle_config(ts_dev);
+
+	/* IRQ Enable */
+	irqenable = TSCADC_IRQENB_IRQHWPEN |
+			TSCADC_IRQENB_IRQEOS |
+			TSCADC_IRQENB_PENUP | TSCADC_IRQENB_FIFO_OVERFLOW;
+	tscadc_writel(ts_dev, TSCADC_REG_IRQENABLE, irqenable);
+
+	tsc_step_config(ts_dev);
+
+	ctrl |= TSCADC_CNTRLREG_TSCSSENB;
+	tscadc_writel(ts_dev, TSCADC_REG_CTRL, ctrl);
+
+	input_dev->name = "ti-tsc-adcc";
+	input_dev->dev.parent = &pdev->dev;
+
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+	/* register to the input system */
+	err = input_register_device(input_dev);
+	if (err)
+		goto err_disable_clk;
+
+	platform_set_drvdata(pdev, ts_dev);
+	return 0;
+
+err_disable_clk:
+	clk_disable(ts_dev->clk);
+	clk_put(ts_dev->clk);
+err_free_irq:
+	free_irq(ts_dev->irq, ts_dev);
+err_unmap_regs:
+	iounmap(ts_dev->tsc_base);
+err_release_mem_region:
+	release_mem_region(res->start, resource_size(res));
+err_free_mem:
+	input_free_device(input_dev);
+	kfree(ts_dev);
+	return err;
+}
+
+static int __devexit tscadc_remove(struct platform_device *pdev)
+{
+	struct tscadc *ts_dev = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	free_irq(ts_dev->irq, ts_dev);
+
+	input_unregister_device(ts_dev->input);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	iounmap(ts_dev->tsc_base);
+	release_mem_region(res->start, resource_size(res));
+
+	clk_disable(ts_dev->clk);
+	clk_put(ts_dev->clk);
+
+	kfree(ts_dev);
+
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver ti_tsc_driver = {
+	.probe	= tscadc_probe,
+	.remove	= __devexit_p(tscadc_remove),
+	.driver	= {
+		.name	= "tsc",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ti_tsc_init(void)
+{
+	return platform_driver_register(&ti_tsc_driver);
+}
+module_init(ti_tsc_init);
+
+static void __exit ti_tsc_exit(void)
+{
+	platform_driver_unregister(&ti_tsc_driver);
+}
+module_exit(ti_tsc_exit);
+
+MODULE_DESCRIPTION("TI touchscreen controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tsc");
diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h
new file mode 100644
index 0000000..f3ae05c
--- /dev/null
+++ b/include/linux/input/ti_tscadc.h
@@ -0,0 +1,15 @@
+#ifndef __LINUX_TI_TSCADC_H
+#define __LINUX_TI_TSCADC_H
+
+/**
+ * struct tsc_data	Touchscreen wire configuration
+ * @wires:		Wires refer to application modes
+ *			i.e. 4/5/8 wire touchscreen support
+ *			on the platform
+ */
+
+struct tsc_data {
+	unsigned int wires;
+};
+
+#endif

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

* RE: [PATCH v3] input: add support for TI Touchscreen controller.
       [not found]   ` <CAM=Q2csKJBaOrTG2-6im+b-UpexMs33wqK6DK6gpmRhScxidww@mail.gmail.com>
@ 2011-08-02 13:46     ` Patil, Rachna
  0 siblings, 0 replies; 4+ messages in thread
From: Patil, Rachna @ 2011-08-02 13:46 UTC (permalink / raw)
  To: Shubhrajyoti Datta, linux-input@vger.kernel.org

Hi Shubhrajyoti,


On Mon, Aug 01, 2011 at 17:33:26, Shubhrajyoti Datta wrote:
> 
> Hello,
> 
> 
> On Fri, Jul 22, 2011 at 2:43 PM, Patil, Rachna <rachna@ti.com> wrote:
> 
> 
> 	This patch adds support for TI's touchscreen
> 	controller for a 4/5/8 wire resistive panel
> 	that is directly fed to the ADC.
> 	
> 	This touchscreen controller will be part of
> 	an upcoming TI SoC and has been tested on
> 	an emulation platform.
> 	
> 
> Is it possible to move to runtime pm instead of the clock calls. 

As discussed earlier, at present we are not handling PM related
issues. This will be added in patches submitted in days to come.

> 
>  
> 
> 
> 	ChangeLog:
> 	
> 	v2: fixed comments from Dimitry
> 	v3: made the driver depend on HAVE_CLK for build
> 	
> 
> 	Signed-off-by: Patil, Rachna <rachna@ti.com>
> 	---
> 	
> 	 drivers/input/touchscreen/Kconfig     |   12 +
> 	
> 	 drivers/input/touchscreen/Makefile    |    1 +
> 	 drivers/input/touchscreen/ti_tscadc.c |  431
> +++++++++++++++++++++++++++++++++
> 	 include/linux/input/ti_tscadc.h       |   10 +
> 	
> 	 4 files changed, 454 insertions(+), 0 deletions(-)
> 	
> 	 create mode 100644 drivers/input/touchscreen/ti_tscadc.c
> 	 create mode 100644 include/linux/input/ti_tscadc.h
> 	
> 	diff --git a/drivers/input/touchscreen/Kconfig
> b/drivers/input/touchscreen/Kconfig
> 	
> 	index 44589f5..69d367a 100644
> 	
> 	--- a/drivers/input/touchscreen/Kconfig
> 	+++ b/drivers/input/touchscreen/Kconfig
> 	
> 	@@ -395,6 +395,18 @@ config TOUCHSCREEN_TOUCHWIN
> 	
> 	         To compile this driver as a module, choose M here: the
> 	         module will be called touchwin.
> 	
> 	+config TOUCHSCREEN_TI_TSCADC
> 	+       tristate "TI Touchscreen Interface"
> 	
> 	+       depends on HAVE_CLK
> 	+       help
> 	
> 	+         Say Y here if you have 4/5/8 wire touchscreen
> controller
> 	+         to be connected to the ADC controller on your TI SoC.
> 	+
> 	+         If unsure, say N.
> 	+
> 	+         To compile this driver as a module, choose M here: the
> 	+         module will be called ti_tscadc.
> 	+
> 	 config TOUCHSCREEN_ATMEL_TSADCC
> 	       tristate "Atmel Touchscreen Interface"
> 	       depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
> 	diff --git a/drivers/input/touchscreen/Makefile
> b/drivers/input/touchscreen/Makefile
> 	index 51b08b0..86b7ab6 100644
> 	--- a/drivers/input/touchscreen/Makefile
> 	+++ b/drivers/input/touchscreen/Makefile
> 	@@ -41,6 +41,7 @@ obj-$(CONFIG_TOUCHSCREEN_QT602240)    +=
> qt602240_ts.o
> 	 obj-$(CONFIG_TOUCHSCREEN_S3C2410)      += s3c2410_ts.o
> 	 obj-$(CONFIG_TOUCHSCREEN_ST1232)       += st1232.o
> 	 obj-$(CONFIG_TOUCHSCREEN_STMPE)                += stmpe-ts.o
> 	+obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC)     += ti_tscadc.o
> 	 obj-$(CONFIG_TOUCHSCREEN_TNETV107X)    += tnetv107x-ts.o
> 	 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
> 	 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
> 	
> 	diff --git a/drivers/input/touchscreen/ti_tscadc.c
> b/drivers/input/touchscreen/ti_tscadc.c
> 	
> 	new file mode 100644
> 	index 0000000..86264fc
> 	--- /dev/null
> 	+++ b/drivers/input/touchscreen/ti_tscadc.c
> 	@@ -0,0 +1,431 @@
> 	+/*
> 	+ * TI Touch Screen driver
> 	+ *
> 	+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> 	+ *
> 	+ * This program is free software; you can redistribute it and/or
> 	+ * modify it under the terms of the GNU General Public License as
> 	+ * published by the Free Software Foundation 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/init.h>
> 	+#include <linux/kernel.h>
> 	+#include <linux/err.h>
> 	+#include <linux/module.h>
> 	+#include <linux/input.h>
> 	+#include <linux/slab.h>
> 	+#include <linux/interrupt.h>
> 	+#include <linux/clk.h>
> 	+#include <linux/platform_device.h>
> 	+#include <linux/io.h>
> 	+#include <linux/input/ti_tscadc.h>
> 	
> 	
> 
> <snip> 
> 
> 
> 	+static int __devinit tscadc_probe(struct platform_device *pdev)
> 	+{
> 	+       struct tscadc                   *ts_dev;
> 	+       struct input_dev                *input_dev;
> 	+       int                             err;
> 	+       int                             clk_value;
> 	+       int                             clock_rate, irqenable,
> ctrl;
> 	+       struct  tsc_data                *pdata =
> pdev->dev.platform_data;
> 	+       struct resource                 *res;
> 	+
> 	+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> 	+       if (!res) {
> 	+               dev_err(&pdev->dev, "no memory resource
> defined.\n");
> 	+               return -EINVAL;
> 	+       }
> 	+
> 	+       /* Allocate memory for device */
> 	+       ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
> 	+       if (!ts_dev) {
> 	+               dev_err(&pdev->dev, "failed to allocate
> memory.\n");
> 	+               return -ENOMEM;
> 	+       }
> 	+
> 	+       ts_dev->irq = platform_get_irq(pdev, 0);
> 	+       if (ts_dev->irq < 0) {
> 	+               dev_err(&pdev->dev, "no irq ID is
> specified.\n");
> 	
> 
> 
> why not free the earlier allocated memory?

Ok. I will add this.

> 
>  
> 
> 	+               return -ENODEV;
> 
> 	+       }
> 	+
> 	+       input_dev = input_allocate_device();
> 	+       if (!input_dev) {
> 	+               dev_err(&pdev->dev, "failed to allocate input
> device.\n");
> 	+               err = -ENOMEM;
> 	+               goto err_free_mem;
> 	+       }
> 	+       ts_dev->input = input_dev;
> 	+
> 	+       ts_dev->tsc_base = ioremap(res->start,
> resource_size(res));
> 	+       if (!ts_dev->tsc_base) {
> 	+               dev_err(&pdev->dev, "failed to map
> registers.\n");
> 	+               err = -ENOMEM;
> 	+               goto err_release_mem;
> 	+       }
> 	+
> 	+       err = request_irq(ts_dev->irq, tscadc_interrupt,
> IRQF_DISABLED,
> 	+                               pdev->dev.driver->name, ts_dev);
> 	+       if (err) {
> 	+               dev_err(&pdev->dev, "failed to allocate
> irq.\n");
> 	+               goto err_unmap_regs;
> 	+       }
> 	+
> 	+       ts_dev->clk = clk_get(&pdev->dev, "tsc_clk");
> 	+       if (IS_ERR(ts_dev->clk)) {
> 	+               dev_err(&pdev->dev, "failed to get ts_clk\n");
> 	+               err = PTR_ERR(ts_dev->clk);
> 	+               goto err_free_irq;
> 	+       }
> 	+       clk_enable(ts_dev->clk);
> 	+
> 	+       clock_rate = clk_get_rate(ts_dev->clk);
> 	+       clk_value = clock_rate / ADC_CLK;
> 	+       if (clk_value < 7) {
> 	+               dev_err(&pdev->dev, "clock input less than min
> clock requirement\n");
> 	+               goto err_fail;
> 	+       }
> 	+       /* TSCADC_CLKDIV needs to be configured to the value
> minus 1 */
> 	+       clk_value = clk_value - 1;
> 	+       tscadc_writel(ts_dev, TSCADC_REG_CLKDIV, clk_value);
> 	+
> 	+        /* Enable wake-up of the SoC using touchscreen */
> 	+       tscadc_writel(ts_dev, TSCADC_REG_IRQWAKEUP,
> TSCADC_IRQWKUP_ENB);
> 	+
> 	+       ts_dev->wires = pdata->wires;
> 	+
> 	+       /* Set the control register bits */
> 	+       ctrl = TSCADC_CNTRLREG_STEPCONFIGWRT |
> 	+                       TSCADC_CNTRLREG_TSCENB |
> 	+                       TSCADC_CNTRLREG_STEPID;
> 	+       switch (ts_dev->wires) {
> 	+       case 4:
> 	+               ctrl |= TSCADC_CNTRLREG_4WIRE;
> 	+               break;
> 	+       case 5:
> 	+               ctrl |= TSCADC_CNTRLREG_5WIRE;
> 	+               break;
> 	+       case 8:
> 	+               ctrl |= TSCADC_CNTRLREG_8WIRE;
> 	+               break;
> 	+       }
> 	+       tscadc_writel(ts_dev, TSCADC_REG_CTRL, ctrl);
> 	+
> 	+       /* Set register bits for Idel Config Mode */
> 	+       tsc_idle_config(ts_dev);
> 	+
> 	+       /* IRQ Enable */
> 	+       irqenable = TSCADC_IRQENB_IRQHWPEN |
> 	+                       TSCADC_IRQENB_IRQEOS |
> 	+                       TSCADC_IRQENB_PENUP |
> TSCADC_IRQENB_FIFO_OVERFLOW;
> 	+       tscadc_writel(ts_dev, TSCADC_REG_IRQENABLE, irqenable);
> 	+
> 	+       tsc_step_config(ts_dev);
> 	+
> 	+       ctrl |= TSCADC_CNTRLREG_TSCSSENB;
> 	+       tscadc_writel(ts_dev, TSCADC_REG_CTRL, ctrl);
> 	+
> 	+       input_dev->name = "ti-tsc-adcc";
> 	+       input_dev->dev.parent = &pdev->dev;
> 	+
> 	+       input_dev->evbit[0] = BIT_MASK(EV_KEY) |
> BIT_MASK(EV_ABS);
> 	+       input_dev->keybit[BIT_WORD(BTN_TOUCH)] =
> BIT_MASK(BTN_TOUCH);
> 	+
> 	+       input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0,
> 0);
> 	+       input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0,
> 0);
> 	+
> 	+       /* register to the input system */
> 	+       err = input_register_device(input_dev);
> 	+       if (err)
> 	+               goto err_fail;
> 	+
> 	+       return 0;
> 	+
> 	+err_fail:
> 	+       clk_disable(ts_dev->clk);
> 	+       clk_put(ts_dev->clk);
> 	+err_free_irq:
> 	+       free_irq(ts_dev->irq, ts_dev);
> 	+err_unmap_regs:
> 	+       iounmap(ts_dev->tsc_base);
> 	+err_release_mem:
> 	+       release_mem_region(res->start, resource_size(res));
> 	+       input_free_device(ts_dev->input);
> 	+err_free_mem:
> 	+       kfree(ts_dev);
> 	+       return err;
> 	+}
> 	+
> 	+static int __devexit tscadc_remove(struct platform_device
> *pdev)
> 	+{
> 	+       struct tscadc           *ts_dev =
> dev_get_drvdata(&pdev->dev);
> 	+       struct resource         *res;
> 	+
> 	+       free_irq(ts_dev->irq, ts_dev);
> 	+
> 	+       input_unregister_device(ts_dev->input);
> 	+
> 	+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> 	+       iounmap(ts_dev->tsc_base);
> 	+       release_mem_region(res->start, resource_size(res));
> 	+
> 	+       clk_disable(ts_dev->clk);
> 	+       clk_put(ts_dev->clk);
> 	+
> 	+       kfree(ts_dev);
> 	+
> 	+       return 0;
> 	+}
> 	+
> 	+static struct platform_driver ti_tsc_driver = {
> 	+       .probe    = tscadc_probe,
> 	+       .remove  = __devexit_p(tscadc_remove),
> 	+       .driver  = {
> 	+               .name   = "tsc",
> 	+       },
> 	+};
> 	+
> 	+static int __init ti_tsc_init(void)
> 	+{
> 	+       return platform_driver_register(&ti_tsc_driver);
> 	+}
> 	+
> 	+static void __exit ti_tsc_exit(void)
> 	+{
> 	+       platform_driver_unregister(&ti_tsc_driver);
> 	+}
> 	+
> 	+module_init(ti_tsc_init);
> 	+module_exit(ti_tsc_exit);
> 	diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h
> 	new file mode 100644
> 	index 0000000..29f87db
> 	--- /dev/null
> 	+++ b/include/linux/input/ti_tscadc.h
> 	@@ -0,0 +1,10 @@
> 	+/**
> 	+ * struct tsc_data     Touchscreen wire configuration
> 	+ * @wires:             Wires refer to application modes
> 	+ *                     i.e. 4/5/8 wire touchscreen support
> 	+ *                     on the platform
> 	+ */
> 	+
> 	+struct tsc_data {
> 	+       int wires;
> 	+};
> 	--
> 	1.6.2.4
> 	
> 	--
> 	
> 	To unsubscribe from this list: send the line "unsubscribe linux-input" in
> 	the body of a message to majordomo@vger.kernel.org
> 	More majordomo info at
> http://vger.kernel.org/majordomo-info.html
> 	
> 
> 

Regards,
Rachna.
 


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

* RE: [PATCH v3] input: add support for TI Touchscreen controller
       [not found] <1327052812-14895-1-git-send-email-rachna@ti.com>
@ 2012-02-17  9:47 ` Patil, Rachna
  0 siblings, 0 replies; 4+ messages in thread
From: Patil, Rachna @ 2012-02-17  9:47 UTC (permalink / raw)
  To: Patil, Rachna, linux-input@vger.kernel.org
  Cc: Dmitry Torokhov, Dmitry Torokhov

Dmitry,

A gentle reminder for the pull request.


Regards,
Rachna.

On Fri, Jan 20, 2012 at 15:16:52, Patil, Rachna wrote:
> From: Rachna Patil <rachna@ti.com>
> 
> This patch adds support for TI's touchscreen controller for a 4/5/8 wire resistive panel that is directly fed to the ADC.
> 
> This touchscreen controller will be part of AM335x TI SoC. The TRM can be found at:
> http://www.ti.com/lit/ug/spruh73a/spruh73a.pdf
> 
> Signed-off-by: Patil, Rachna <rachna@ti.com>
> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
> ---
> Changes for v3:
> Addressed review comments by Dmitry
> 
>  drivers/input/touchscreen/Kconfig     |   12 +
>  drivers/input/touchscreen/Makefile    |    1 +
>  drivers/input/touchscreen/ti_tscadc.c |  503 +++++++++++++++++++++++++++++++++
>  include/linux/input/ti_tscadc.h       |   17 ++
>  4 files changed, 533 insertions(+), 0 deletions(-)  create mode 100644 drivers/input/touchscreen/ti_tscadc.c
>  create mode 100644 include/linux/input/ti_tscadc.h
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index cabd9e5..de4ee06 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -407,6 +407,18 @@ config TOUCHSCREEN_TOUCHWIN
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called touchwin.
>  
> +config TOUCHSCREEN_TI_TSCADC
> +	tristate "TI Touchscreen Interface"
> +	depends on ARCH_OMAP2PLUS
> +	help
> +	  Say Y here if you have 4/5/8 wire touchscreen controller
> +	  to be connected to the ADC controller on your TI AM335x SoC.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ti_tscadc.
> +
>  config TOUCHSCREEN_ATMEL_TSADCC
>  	tristate "Atmel Touchscreen Interface"
>  	depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 282d6f7..e353bcb 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -42,6 +42,7 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
>  obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
>  obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
> +obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC)	+= ti_tscadc.o
>  obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
>  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
> diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c
> new file mode 100644
> index 0000000..c2035e6
> --- /dev/null
> +++ b/drivers/input/touchscreen/ti_tscadc.c
> @@ -0,0 +1,503 @@
> +/*
> + * TI Touch Screen driver
> + *
> + * Copyright (C) 2011 Texas Instruments Incorporated - 
> +http://www.ti.com/
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation 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/init.h>
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/input/ti_tscadc.h>
> +#include <linux/delay.h>
> +
> +#define REG_IRQEOI		0x020
> +#define REG_RAWIRQSTATUS	0x024
> +#define REG_IRQSTATUS		0x028
> +#define REG_IRQENABLE		0x02C
> +#define REG_IRQWAKEUP		0x034
> +#define REG_CTRL		0x040
> +#define REG_ADCFSM		0x044
> +#define REG_CLKDIV		0x04C
> +#define REG_SE			0x054
> +#define REG_IDLECONFIG		0x058
> +#define REG_CHARGECONFIG	0x05C
> +#define REG_CHARGEDELAY		0x060
> +#define REG_STEPCONFIG(n)	(0x64 + ((n - 1) * 8))
> +#define REG_STEPDELAY(n)	(0x68 + ((n - 1) * 8))
> +#define REG_STEPCONFIG13	0x0C4
> +#define REG_STEPDELAY13		0x0C8
> +#define REG_STEPCONFIG14	0x0CC
> +#define REG_STEPDELAY14		0x0D0
> +#define REG_FIFO0CNT		0xE4
> +#define REG_FIFO1THR		0xF4
> +#define REG_FIFO0		0x100
> +#define REG_FIFO1		0x200
> +
> +/*	Register Bitfields	*/
> +#define IRQWKUP_ENB		BIT(0)
> +#define STPENB_STEPENB		0x7FFF
> +#define IRQENB_FIFO1THRES	BIT(5)
> +#define IRQENB_PENUP		BIT(9)
> +#define STEPCONFIG_MODE_HWSYNC	0x2
> +#define STEPCONFIG_SAMPLES_AVG	(1 << 4)
> +#define STEPCONFIG_XPP		(1 << 5)
> +#define STEPCONFIG_XNN		(1 << 6)
> +#define STEPCONFIG_YPP		(1 << 7)
> +#define STEPCONFIG_YNN		(1 << 8)
> +#define STEPCONFIG_XNP		(1 << 9)
> +#define STEPCONFIG_YPN		(1 << 10)
> +#define STEPCONFIG_INM		(1 << 18)
> +#define STEPCONFIG_INP		(1 << 20)
> +#define STEPCONFIG_INP_5	(1 << 21)
> +#define STEPCONFIG_FIFO1	(1 << 26)
> +#define STEPCONFIG_OPENDLY	0xff
> +#define STEPCONFIG_Z1		(3 << 19)
> +#define STEPIDLE_INP		(1 << 22)
> +#define STEPCHARGE_RFP		(1 << 12)
> +#define STEPCHARGE_INM		(1 << 15)
> +#define STEPCHARGE_INP		(1 << 19)
> +#define STEPCHARGE_RFM		(1 << 23)
> +#define STEPCHARGE_DELAY	0x1
> +#define CNTRLREG_TSCSSENB	(1 << 0)
> +#define CNTRLREG_STEPID		(1 << 1)
> +#define CNTRLREG_STEPCONFIGWRT	(1 << 2)
> +#define CNTRLREG_4WIRE		(1 << 5)
> +#define CNTRLREG_5WIRE		(1 << 6)
> +#define CNTRLREG_8WIRE		(3 << 5)
> +#define CNTRLREG_TSCENB		(1 << 7)
> +#define ADCFSM_STEPID		0x10
> +
> +#define SEQ_SETTLE		275
> +#define ADC_CLK			3000000
> +#define MAX_12BIT		((1 << 12) - 1)
> +#define TSCADC_DELTA_X		15
> +#define TSCADC_DELTA_Y		15
> +
> +struct tscadc {
> +	struct input_dev	*input;
> +	struct clk		*tsc_ick;
> +	void __iomem		*tsc_base;
> +	unsigned int		irq;
> +	unsigned int		wires;
> +	unsigned int		x_plate_resistance;
> +	unsigned int		bckup_x;
> +	unsigned int		bckup_y;
> +	bool			pen_down;
> +};
> +
> +static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) {
> +	return readl(ts->tsc_base + reg);
> +}
> +
> +static void tscadc_writel(struct tscadc *tsc, unsigned int reg,
> +					unsigned int val)
> +{
> +	writel(val, tsc->tsc_base + reg);
> +}
> +
> +static void tscadc_step_config(struct tscadc *ts_dev) {
> +	unsigned int	config;
> +	int i;
> +
> +	/* Configure the Step registers */
> +
> +	config = STEPCONFIG_MODE_HWSYNC |
> +			STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP;
> +	switch (ts_dev->wires) {
> +	case 4:
> +		config |= STEPCONFIG_INP | STEPCONFIG_XNN;
> +		break;
> +	case 5:
> +		config |= STEPCONFIG_YNN |
> +				STEPCONFIG_INP_5 | STEPCONFIG_XNN |
> +				STEPCONFIG_YPP;
> +		break;
> +	case 8:
> +		config |= STEPCONFIG_INP | STEPCONFIG_XNN;
> +		break;
> +	}
> +
> +	for (i = 1; i < 7; i++) {
> +		tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
> +		tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
> +	}
> +
> +	config = 0;
> +	config = STEPCONFIG_MODE_HWSYNC |
> +			STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN |
> +			STEPCONFIG_INM | STEPCONFIG_FIFO1;
> +	switch (ts_dev->wires) {
> +	case 4:
> +		config |= STEPCONFIG_YPP;
> +		break;
> +	case 5:
> +		config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 |
> +				STEPCONFIG_XNP | STEPCONFIG_YPN;
> +		break;
> +	case 8:
> +		config |= STEPCONFIG_YPP;
> +		break;
> +	}
> +
> +	for (i = 7; i < 13; i++) {
> +		tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
> +		tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
> +	}
> +
> +	config = 0;
> +	/* Charge step configuration */
> +	config = STEPCONFIG_XPP | STEPCONFIG_YNN |
> +			STEPCHARGE_RFP | STEPCHARGE_RFM |
> +			STEPCHARGE_INM | STEPCHARGE_INP;
> +
> +	tscadc_writel(ts_dev, REG_CHARGECONFIG, config);
> +	tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY);
> +
> +	config = 0;
> +	/* Configure to calculate pressure */
> +	config = STEPCONFIG_MODE_HWSYNC |
> +			STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP |
> +			STEPCONFIG_XNN | STEPCONFIG_INM;
> +	tscadc_writel(ts_dev, REG_STEPCONFIG13, config);
> +	tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY);
> +
> +	config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1;
> +	tscadc_writel(ts_dev, REG_STEPCONFIG14, config);
> +	tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY);
> +
> +	tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); }
> +
> +static void tscadc_idle_config(struct tscadc *ts_config) {
> +	unsigned int idleconfig;
> +
> +	idleconfig = STEPCONFIG_YNN |
> +			STEPCONFIG_INM |
> +			STEPCONFIG_YPN | STEPIDLE_INP;
> +	tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); }
> +
> +static void tscadc_read_coordinates(struct tscadc *ts_dev,
> +				    unsigned int *x, unsigned int *y) {
> +	unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT);
> +	unsigned int prev_val_x = ~0, prev_val_y = ~0;
> +	unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
> +	unsigned int read, diff;
> +	unsigned int i;
> +
> +	/*
> +	 * Delta filter is used to remove large variations
> +	 * in sampled values from ADC. The filter tries to
> +	 * predict where the next coordinate could be.
> +	 * This is done by taking a previous coordinate and
> +	 * subtracting it form current one. Further the
> +	 * algorithm compares the difference with that of
> +	 * a present value, if true the value is reported
> +	 * to the sub system.
> +	 */
> +	for (i = 0; i < fifocount - 1; i++) {
> +		read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
> +		diff = abs(read - prev_val_x);
> +		if (diff < prev_diff_x) {
> +			prev_diff_x = diff;
> +			*x = read;
> +		}
> +		prev_val_x = read;
> +
> +		read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
> +		diff = abs(read - prev_val_y);
> +		if (diff < prev_diff_y) {
> +			prev_diff_y = diff;
> +			*y = read;
> +		}
> +		prev_val_y = read;
> +	}
> +}
> +
> +static irqreturn_t tscadc_irq(int irq, void *dev) {
> +	struct tscadc *ts_dev = dev;
> +	struct input_dev *input_dev = ts_dev->input;
> +	unsigned int status, irqclr = 0;
> +	unsigned int x = 0, y = 0;
> +	unsigned int diffx, diffy;
> +	unsigned int z1, z2, z;
> +	unsigned int fsm;
> +
> +	status = tscadc_readl(ts_dev, REG_IRQSTATUS);
> +	if (status & IRQENB_FIFO1THRES) {
> +		tscadc_read_coordinates(ts_dev, &x, &y);
> +
> +		diffx = abs(x - (ts_dev->bckup_x));
> +		diffy = abs(y - (ts_dev->bckup_y));
> +		ts_dev->bckup_x = x;
> +		ts_dev->bckup_y = y;
> +
> +		z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
> +		z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
> +
> +		if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
> +			/*
> +			 * Calculate pressure using formula
> +			 * Resistance(touch) = x plate resistance *
> +			 * x postion/4096 * ((z2 / z1) - 1)
> +			 */
> +			z = z2 - z1;
> +			z *= x;
> +			z *= ts_dev->x_plate_resistance;
> +			z /= z1;
> +			z = (z + 2047) >> 12;
> +
> +			if ((z <= MAX_12BIT) && (diffx < TSCADC_DELTA_X) &&
> +					(diffy < TSCADC_DELTA_Y)) {
> +				input_report_abs(input_dev, ABS_X, x);
> +				input_report_abs(input_dev, ABS_Y, y);
> +				input_report_abs(input_dev, ABS_PRESSURE, z);
> +				input_report_key(input_dev, BTN_TOUCH, 1);
> +				input_sync(input_dev);
> +			}
> +		}
> +		irqclr |= IRQENB_FIFO1THRES;
> +	}
> +
> +	/*
> +	 * Time for sequencer to settle, to read
> +	 * correct state of the sequencer.
> +	 */
> +	udelay(SEQ_SETTLE);
> +
> +	status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS);
> +	if (status & IRQENB_PENUP) {
> +		/* Pen up event */
> +		fsm = tscadc_readl(ts_dev, REG_ADCFSM);
> +		if (fsm == ADCFSM_STEPID) {
> +			ts_dev->pen_down = false;
> +			input_report_key(input_dev, BTN_TOUCH, 0);
> +			input_report_abs(input_dev, ABS_PRESSURE, 0);
> +			input_sync(input_dev);
> +		} else {
> +			ts_dev->pen_down = true;
> +		}
> +		irqclr |= IRQENB_PENUP;
> +	}
> +
> +	tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr);
> +	/* check pending interrupts */
> +	tscadc_writel(ts_dev, REG_IRQEOI, 0x0);
> +
> +	tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * The functions for inserting/removing driver as a module.
> + */
> +
> +static int __devinit tscadc_probe(struct platform_device *pdev) {
> +	const struct tsc_data *pdata = pdev->dev.platform_data;
> +	struct resource *res;
> +	struct tscadc *ts_dev;
> +	struct input_dev *input_dev;
> +	struct clk *clk;
> +	int err;
> +	int clk_value, ctrl, irq;
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "missing platform data.\n");
> +		return -EINVAL;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "no memory resource defined.\n");
> +		return -EINVAL;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq ID is specified.\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Allocate memory for device */
> +	ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
> +	input_dev = input_allocate_device();
> +	if (!ts_dev || !input_dev) {
> +		dev_err(&pdev->dev, "failed to allocate memory.\n");
> +		err = -ENOMEM;
> +		goto err_free_mem;
> +	}
> +
> +	if (ts_dev->irq < 0) {
> +		dev_err(&pdev->dev, "no irq ID is specified.\n");
> +		err = -ENODEV;
> +		goto err_free_mem;
> +	}
> +
> +	ts_dev->input = input_dev;
> +	ts_dev->irq = irq;
> +	ts_dev->wires = pdata->wires;
> +	ts_dev->x_plate_resistance = pdata->x_plate_resistance;
> +
> +	res = request_mem_region(res->start, resource_size(res), pdev->name);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to reserve registers.\n");
> +		err = -EBUSY;
> +		goto err_free_mem;
> +	}
> +
> +	ts_dev->tsc_base = ioremap(res->start, resource_size(res));
> +	if (!ts_dev->tsc_base) {
> +		dev_err(&pdev->dev, "failed to map registers.\n");
> +		err = -ENOMEM;
> +		goto err_release_mem_region;
> +	}
> +
> +	err = request_irq(ts_dev->irq, tscadc_irq,
> +			  0, pdev->dev.driver->name, ts_dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to allocate irq.\n");
> +		goto err_unmap_regs;
> +	}
> +
> +	ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick");
> +	if (IS_ERR(ts_dev->tsc_ick)) {
> +		dev_err(&pdev->dev, "failed to get TSC ick\n");
> +		goto err_free_irq;
> +	}
> +	clk_enable(ts_dev->tsc_ick);
> +
> +	clk = clk_get(&pdev->dev, "adc_tsc_fck");
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "failed to get TSC fck\n");
> +		err = PTR_ERR(clk);
> +		goto err_disable_clk;
> +	}
> +
> +	clk_value = clk_get_rate(clk) / ADC_CLK;
> +	clk_put(clk);
> +
> +	if (clk_value < 7) {
> +		dev_err(&pdev->dev, "clock input less than min clock requirement\n");
> +		goto err_disable_clk;
> +	}
> +	/* CLKDIV needs to be configured to the value minus 1 */
> +	tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1);
> +
> +	 /* Enable wake-up of the SoC using touchscreen */
> +	tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
> +
> +	ctrl = CNTRLREG_STEPCONFIGWRT |
> +			CNTRLREG_TSCENB |
> +			CNTRLREG_STEPID;
> +	switch (ts_dev->wires) {
> +	case 4:
> +		ctrl |= CNTRLREG_4WIRE;
> +		break;
> +	case 5:
> +		ctrl |= CNTRLREG_5WIRE;
> +		break;
> +	case 8:
> +		ctrl |= CNTRLREG_8WIRE;
> +		break;
> +	}
> +	tscadc_writel(ts_dev, REG_CTRL, ctrl);
> +
> +	tscadc_idle_config(ts_dev);
> +	tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
> +	tscadc_step_config(ts_dev);
> +	tscadc_writel(ts_dev, REG_FIFO1THR, 6);
> +
> +	ctrl |= CNTRLREG_TSCSSENB;
> +	tscadc_writel(ts_dev, REG_CTRL, ctrl);
> +
> +	input_dev->name = "ti-tsc-adc";
> +	input_dev->dev.parent = &pdev->dev;
> +
> +	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> +
> +	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
> +	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
> +	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
> +
> +	/* register to the input system */
> +	err = input_register_device(input_dev);
> +	if (err)
> +		goto err_disable_clk;
> +
> +	platform_set_drvdata(pdev, ts_dev);
> +	return 0;
> +
> +err_disable_clk:
> +	clk_disable(ts_dev->tsc_ick);
> +	clk_put(ts_dev->tsc_ick);
> +err_free_irq:
> +	free_irq(ts_dev->irq, ts_dev);
> +err_unmap_regs:
> +	iounmap(ts_dev->tsc_base);
> +err_release_mem_region:
> +	release_mem_region(res->start, resource_size(res));
> +err_free_mem:
> +	input_free_device(input_dev);
> +	kfree(ts_dev);
> +	return err;
> +}
> +
> +static int __devexit tscadc_remove(struct platform_device *pdev) {
> +	struct tscadc *ts_dev = platform_get_drvdata(pdev);
> +	struct resource *res;
> +
> +	free_irq(ts_dev->irq, ts_dev);
> +
> +	input_unregister_device(ts_dev->input);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	iounmap(ts_dev->tsc_base);
> +	release_mem_region(res->start, resource_size(res));
> +
> +	clk_disable(ts_dev->tsc_ick);
> +	clk_put(ts_dev->tsc_ick);
> +
> +	kfree(ts_dev);
> +
> +	platform_set_drvdata(pdev, NULL);
> +	return 0;
> +}
> +
> +static struct platform_driver ti_tsc_driver = {
> +	.probe	= tscadc_probe,
> +	.remove	= __devexit_p(tscadc_remove),
> +	.driver	= {
> +		.name   = "tsc",
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +module_platform_driver(ti_tsc_driver);
> +
> +MODULE_DESCRIPTION("TI touchscreen controller driver"); 
> +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); MODULE_LICENSE("GPL");
> diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h new file mode 100644 index 0000000..b10a527
> --- /dev/null
> +++ b/include/linux/input/ti_tscadc.h
> @@ -0,0 +1,17 @@
> +#ifndef __LINUX_TI_TSCADC_H
> +#define __LINUX_TI_TSCADC_H
> +
> +/**
> + * struct tsc_data	Touchscreen wire configuration
> + * @wires:		Wires refer to application modes
> + *			i.e. 4/5/8 wire touchscreen support
> + *			on the platform.
> + * @x_plate_resistance:	X plate resistance.
> + */
> +
> +struct tsc_data {
> +	int wires;
> +	int x_plate_resistance;
> +};
> +
> +#endif
> --
> 1.7.1
> 
> 


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

end of thread, other threads:[~2012-02-17  9:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-22  9:13 [PATCH v3] input: add support for TI Touchscreen controller Patil, Rachna
2011-08-01  6:21 ` Dmitry Torokhov
     [not found] ` <CAM=Q2cunVNO92ngc0_YaiH8Eikk66xqSUrRU4eLm8xB2n4Y8og@mail.gmail.com>
     [not found]   ` <CAM=Q2csKJBaOrTG2-6im+b-UpexMs33wqK6DK6gpmRhScxidww@mail.gmail.com>
2011-08-02 13:46     ` Patil, Rachna
     [not found] <1327052812-14895-1-git-send-email-rachna@ti.com>
2012-02-17  9:47 ` Patil, Rachna

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).