diff mbox

[V2] input: add support for TI Touchscreen controller.

Message ID 1310619775-29886-1-git-send-email-rachna@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Patil, Rachna July 14, 2011, 5:02 a.m. UTC
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 Dmitry

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

Comments

Patil, Rachna July 20, 2011, 1:12 p.m. UTC | #1
Hi Dmitry,

This is just a gentle reminder for the patch I had submitted viz. "[PATCH] input: add support for TI Touchscreen controller."
Can this patch be included in the 3.1 version?
I know this is a bit late but I presume that this patch just adds a new kind of driver and does not break anything existing.

Thanks & regards,
Rachna.

> -----Original Message-----
> From: Patil, Rachna
> Sent: Thursday, July 14, 2011 10:33 AM
> To: linux-input@vger.kernel.org
> Cc: Dmitry Torokhov; Dmitry Torokhov; Patil, Rachna
> Subject: [PATCH V2] input: add support for TI Touchscreen controller.
>
> 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 Dmitry
>
> Signed-off-by: Patil, Rachna <rachna@ti.com>
> ---
>  drivers/input/touchscreen/Kconfig     |   11 +
>  drivers/input/touchscreen/Makefile    |    1 +
>  drivers/input/touchscreen/ti_tscadc.c |  431
> +++++++++++++++++++++++++++++++++
>  include/linux/input/ti_tscadc.h       |   10 +
>  4 files changed, 453 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..7d976d7 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -395,6 +395,17 @@ 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"
> +        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

--
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
Dmitry Torokhov July 22, 2011, 6:35 a.m. UTC | #2
Hi Rachna,

On Wed, Jul 20, 2011 at 06:42:20PM +0530, Patil, Rachna wrote:
> Hi Dmitry,
> 
> This is just a gentle reminder for the patch I had submitted viz. "[PATCH] input: add support for TI Touchscreen controller."
> Can this patch be included in the 3.1 version?
> I know this is a bit late but I presume that this patch just adds a new kind of driver and does not break anything existing.
> 

I am getting the following while building the module:

ERROR: "clk_get_rate" [drivers/input/touchscreen/ti_tscadc.ko]
undefined!
ERROR: "clk_enable" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
ERROR: "clk_get" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
ERROR: "clk_put" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
ERROR: "clk_disable" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
make[1]: *** [__modpost] Error 1

Apparently we need to add some arch or platform dependency... Hmm, maybe
the driver should depend on CONFIG_HAVE_CLK?

Thanks.
Patil, Rachna July 22, 2011, 9:12 a.m. UTC | #3
Hi Dmitry,

> -----Original Message-----
> From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com]
> Sent: Friday, July 22, 2011 12:06 PM
> To: Patil, Rachna
> Cc: linux-input@vger.kernel.org
> Subject: Re: [PATCH V2] input: add support for TI Touchscreen controller.
> 
> Hi Rachna,
> 
> On Wed, Jul 20, 2011 at 06:42:20PM +0530, Patil, Rachna wrote:
> > Hi Dmitry,
> >
> > This is just a gentle reminder for the patch I had submitted viz.
> "[PATCH] input: add support for TI Touchscreen controller."
> > Can this patch be included in the 3.1 version?
> > I know this is a bit late but I presume that this patch just adds a new
> kind of driver and does not break anything existing.
> >
> 
> I am getting the following while building the module:
> 
> ERROR: "clk_get_rate" [drivers/input/touchscreen/ti_tscadc.ko]
> undefined!
> ERROR: "clk_enable" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
> ERROR: "clk_get" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
> ERROR: "clk_put" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
> ERROR: "clk_disable" [drivers/input/touchscreen/ti_tscadc.ko] undefined!
> make[1]: *** [__modpost] Error 1
> 
> Apparently we need to add some arch or platform dependency... Hmm, maybe
> the driver should depend on CONFIG_HAVE_CLK?

Yes, this is platform dependent. I will add CONFIG_HAVE_CLK against
the driver.

> 
> Thanks.
> 
> --
> Dmitry

Thanks & Regards,
Rachna.
--
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
diff mbox

Patch

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 44589f5..7d976d7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -395,6 +395,17 @@  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"
+        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;
+};