This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TSC2007 probe failed with AM3352 (SDK 07.00)

Other Parts Discussed in Thread: TSC2007, AM3352, TPS65217, DA8XX

Hello Everyone,

I have a custom design with AM3352 and I'm running SDK 07.00 based kernel. I have added the device tree entry for TSC2007 based on the following link. (The link indicates 0x49 but since A0 and A1 are ground in my design, I'm using 0x48)

http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/input/touchscreen/tsc2007.txt

/*
	TSC2007 Touch controller on I2C0 bus shared with TPS65217
*/
tsc2007: tsc2007@48 {
		compatible = "ti,tsc2007";
		reg = <0x48>;
		interrupt-parent = <&gpio3>;			
		interrupts = <16>;
		gpios = <&gpio3 8 GPIO_ACTIVE_LOW>;
		ti,x-plate-ohms = <200>;
	};

I have also made the change to the omap-i2c0 pin mux entry and added the PENIRQ shown below:

i2c0_pins: pinmux_i2c0_pins {
            pinctrl-single,pins = <
                0x188 (PIN_INPUT_PULLUP | MUX_MODE0)    /* i2c0_sda.i2c0_sda */
                0x18C (PIN_INPUT_PULLUP | MUX_MODE0)    /* i2c0_scl.i2c0_scl */
                0x1E8 (PIN_INPUT_PULLUP | MUX_MODE7)    /* TSC2007 PENIRQ - jtag_emu1.gpio3_8 */
            >;
        };


Additionally, I have also made sure the Kernel config is setup properly to include the TSC2007 driver (not as a module) but part of the kernel.

CONFIG_TOUCHSCREEN_TSC2007=y


However, during boot up, I get the following message:

[    2.301333] tps65217 0-0024: TPS65217 ID 0x7 version 1.1
[    2.308113] tsc2007 0-0048: platform data is required!
[    2.313784] tsc2007: probe of 0-0048 failed with error -22

Additionally, when I use i2cdetect -r 0 it does recognize the chip at the correct location. 0x24 is the PMIC and 0x48 is the TSC2007.

root@am335x:~# i2cdetect -r 0
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-0 using read byte commands.
I will probe address range 0x03-0x77.
Continue? [Y/n] Y
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- UU -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Please note that on the same I2C bus, TPS65217 is detected properly, however, TSC2007 probe function fails. The probe functions for TPS65217 and TSC2007 are slightly different. The TSC2007 probe function returns an error at this point:

static int tsc2007_probe(struct i2c_client *client,
                   const struct i2c_device_id *id)
{
    struct tsc2007 *ts;
    struct tsc2007_platform_data *pdata = client->dev.platform_data;
    struct input_dev *input_dev;
    int err;

    if (!pdata) {
        dev_err(&client->dev, "platform data is required!\n");
        return -EINVAL;
    }

    if (!i2c_check_functionality(client->adapter,
                     I2C_FUNC_SMBUS_READ_WORD_DATA))
        return -EIO;

Any idea as to how this issue can be fixed? Thanks in advance for your help.

Regards

Santhosh

  • Guys,


    I do have couple of updates, I hooked up an oscilloscope to the I2C bus and it seems that there is no calls to the chip during boot up - the probe fails right at the beginning.

    If I perform an i2cdetect, I do get the correct information that a device is available at address 0x48. An i2cdump gives me the following data:

    root@am335x:~# i2cdump 0 0x48
    No size specified (using byte-data access)
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will probe file /dev/i2c-0, address 0x48, mode byte
    Continue? [Y/n] Y
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
    00: 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d    ----------------
    10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    20: 02 02 02 02 03 03 03 03 03 03 03 03 04 04 04 04    ????????????????
    30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    40: 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36    6666666666666666
    50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    80: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
    f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX

    But if I perform an i2cdetect immediately after this, it locks up and I can see the data line stuck low.

    root@am335x:~# i2cdetect -r 0
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will probe file /dev/i2c-0 using read byte commands.
    I will probe address range 0x03-0x77.
    Continue? [Y/n] Y
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: -- -- -- -- -- -- -- -- [  135.738441] omap_i2c 44e0b000.i2c: timeout waiting for bus ready
    -- [  136.758441] omap_i2c 44e0b000.i2c: timeout waiting for bus ready
    -- [  137.778440] omap_i2c 44e0b000.i2c: timeout waiting for bus ready
    -- [  138.798439] omap_i2c 44e0b000.i2c: timeout waiting for bus ready
    -- [  139.818439] omap_i2c 44e0b000.i2c: timeout waiting for bus ready


    Any inputs are welcome. Thanks for the help guys.

    Regards

    Santhosh

  • Hi Santosh,

    Santhosh Ramani said:
        TSC2007 Touch controller on I2C0 bus shared with TPS65217
    */
    tsc2007: tsc2007@48 {
            compatible = "ti,tsc2007";
            reg = <0x48>;
            interrupt-parent = <&gpio3>;         
            interrupts = <16>;
            gpios = <&gpio3 8 GPIO_ACTIVE_LOW>;
            ti,x-plate-ohms = <200>;

        };

    Is this description placed under the correct i2c peripheral description?

    Best regards,
    Miroslav

  • Hello Miroslav,

    Yes, it is placed under the correct i2c peripheral description - I have other i2c devices working fine. I found that the driver for the tsc2007 in the SDK was an old one - it didn't include parsing of the device tree entries. I was able to get the latest driver online - at this link https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/input/touchscreen/tsc2007.c

    I've also attached the old and the updates driver file for reference. Now the touch device is probed successfully by the kernel. Please see the log output below:

    [    2.262952] input: TSC2007 Touchscreen as /devices/ocp.2/44e0b000.i2c/i2c-0/0-0048/input/input0

    However, the kernel still doesn't interact with the touch device - I can see the GPIO line go low whenever there is a touch on the LCD, but there are no I2C communications to the device. i2cdetect clearly shows presence of the device and a driver.

    root@am335x:~# i2cdetect -r 0
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will probe file /dev/i2c-0 using read byte commands.
    I will probe address range 0x03-0x77.
    Continue? [Y/n] Y
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- --


    Any thoughts on how I can set up the TSC2007 as my input device?

    Thank you once again for all your help and patience.

    Regards

    Santhosh

  • Hello Miroslav,

    I have additional inputs, I added couple of print statements to the hard_irq which should be executed when the PENIRQ signal goes low - I found that this IRQ doesn't run at all.

    static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
    {
        struct tsc2007 *ts = handle;
        
        dev_err(&ts->client->dev, "Entering %s\n", __func__);
        
        if (tsc2007_is_pen_down(ts))
        {
            dev_err(&ts->client->dev, "Pen down returns true - %s\n", __func__);
            return IRQ_WAKE_THREAD;
        }

        if (ts->clear_penirq)
        {
            dev_err(&ts->client->dev, "Clear pending interrupt - %s\n", __func__);
            ts->clear_penirq();
        }

        return IRQ_HANDLED;
    }


    I can also confirm that the interrupt is registered, this is output of the /proc/interrupt

    root@aa-hcc:~# cat /proc/interrupts
               CPU0
     20:          0      INTC   4  48080000.elm
     28:         34      INTC  12  edma
     30:          0      INTC  14  edma_error
     46:       7005      INTC  30  4819c000.i2c
     52:      78107      INTC  36  da8xx_lcdc
     56:          0      INTC  40  eth0
     57:      28281      INTC  41  eth0
     58:      11887      INTC  42  eth-tx
     59:          0      INTC  43  eth0
     61:       4600      INTC  45
     80:        117      INTC  64  mmc0
     84:      13106      INTC  68  gp_timer
     86:          3      INTC  70  44e0b000.i2c
     88:        149      INTC  72  OMAP UART0
     94:          0      INTC  78  wkup_m3_txev
    116:          0      INTC 100  gpmc
    125:          0      INTC 109  53100000.sham
    127:          0      INTC 111  48310000.rng
    173:          0      GPIO  29  mmc0
    255:          0      GPIO  16  tsc2007
    Err:          0

    Any thoughts on what needs to happen to setup GPIO interrupt? Also is it possible for GPIO bank3 EMU1 pin to provide a change notification interrupt?

    Thank you

    Regards

    Santhosh

  • Hello Miroslav,


    Well I was able to partially solve my problem - I now have the TSC2007 providing interrupts and touch is also clearly detected. I had to make modifications to allow for gpio interrupt to be registered by the device. I'm attaching the modified file to this post.

    The problem now, is that there are several instances where the I2C bus gives me a "controller timed out" error. This happens randomly and only once in a while.

    I have 2 I2C buses, I2C-0 and I2C-2 - I'm using I2C-0 for the TSC2007 (no other devices on this bus) and I2C-2 has SHT21 sensor that is read every 10 sec. Is it possible that these sensor reads are causing this issue where a touch event happens exactly the same time as the sensor is being read? Even though the buses are different, I guess the underlying I2C driver is common.

    New TSC2007 File:

    /*
     * drivers/input/touchscreen/tsc2007.c
     *
     * Copyright (c) 2008 MtekVision Co., Ltd.
     *	Kwangwoo Lee <kwlee@mtekvision.com>
     *
     * Using code from:
     *  - ads7846.c
     *	Copyright (c) 2005 David Brownell
     *	Copyright (c) 2006 Nokia Corporation
     *  - corgi_ts.c
     *	Copyright (C) 2004-2005 Richard Purdie
     *  - omap_ts.[hc], ads7846.h, ts_osk.c
     *	Copyright (C) 2002 MontaVista Software
     *	Copyright (C) 2004 Texas Instruments
     *	Copyright (C) 2005 Dirk Behme
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License version 2 as
     *  published by the Free Software Foundation.
     */
    
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/input.h>
    #include <linux/interrupt.h>
    #include <linux/i2c.h>
    #include <linux/i2c/tsc2007.h>
    #include <linux/of_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    
    #define TSC2007_MEASURE_TEMP0		(0x0 << 4)
    #define TSC2007_MEASURE_AUX		(0x2 << 4)
    #define TSC2007_MEASURE_TEMP1		(0x4 << 4)
    #define TSC2007_ACTIVATE_XN		(0x8 << 4)
    #define TSC2007_ACTIVATE_YN		(0x9 << 4)
    #define TSC2007_ACTIVATE_YP_XN		(0xa << 4)
    #define TSC2007_SETUP			(0xb << 4)
    #define TSC2007_MEASURE_X		(0xc << 4)
    #define TSC2007_MEASURE_Y		(0xd << 4)
    #define TSC2007_MEASURE_Z1		(0xe << 4)
    #define TSC2007_MEASURE_Z2		(0xf << 4)
    
    #define TSC2007_POWER_OFF_IRQ_EN	(0x0 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS0		(0x1 << 2)
    #define TSC2007_ADC_OFF_IRQ_EN		(0x2 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS1		(0x3 << 2)
    
    #define TSC2007_12BIT			(0x0 << 1)
    #define TSC2007_8BIT			(0x1 << 1)
    
    #define	MAX_12BIT			((1 << 12) - 1)
    
    #define ADC_ON_12BIT	(TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
    
    #define READ_Y		(ADC_ON_12BIT | TSC2007_MEASURE_Y)
    #define READ_Z1		(ADC_ON_12BIT | TSC2007_MEASURE_Z1)
    #define READ_Z2		(ADC_ON_12BIT | TSC2007_MEASURE_Z2)
    #define READ_X		(ADC_ON_12BIT | TSC2007_MEASURE_X)
    #define PWRDOWN		(TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
    
    struct ts_event {
    	u16	x;
    	u16	y;
    	u16	z1, z2;
    };
    
    struct tsc2007 {
    	struct input_dev	*input;
    	char			phys[32];
    
    	struct i2c_client	*client;
    
    	u16			model;
    	u16			x_plate_ohms;
    	u16			max_rt;
    	unsigned long		poll_period;
    	int			fuzzx;
    	int			fuzzy;
    	int			fuzzz;
    
    	unsigned		gpio;
    	int			irq;
    
    	wait_queue_head_t	wait;
    	bool			stopped;
    
    	int			(*get_pendown_state)(struct device *);
    	void			(*clear_penirq)(void);
    };
    
    static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
    {
    	s32 data;
    	u16 val;
    
    	data = i2c_smbus_read_word_data(tsc->client, cmd);
    	if (data < 0) {
    		dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
    		return data;
    	}
    
    	/* The protocol and raw data format from i2c interface:
    	 * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
    	 * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
    	 */
    	val = swab16(data) >> 4;
    
    	dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
    
    	return val;
    }
    
    static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	/* y- still on; turn on only y+ (and ADC) */
    	tc->y = tsc2007_xfer(tsc, READ_Y);
    
    	/* turn y- off, x+ on, then leave in lowpower */
    	tc->x = tsc2007_xfer(tsc, READ_X);
    
    	/* turn y+ off, x- on; we'll use formula #1 */
    	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
    	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
    
    	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
    	tsc2007_xfer(tsc, PWRDOWN);
    }
    
    static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	u32 rt = 0;
    
    	/* range filtering */
    	if (tc->x == MAX_12BIT)
    		tc->x = 0;
    
    	if (likely(tc->x && tc->z1)) {
    		/* compute touch pressure resistance using equation #1 */
    		rt = tc->z2 - tc->z1;
    		rt *= tc->x;
    		rt *= tsc->x_plate_ohms;
    		rt /= tc->z1;
    		rt = (rt + 2047) >> 12;
    	}
    
    	return rt;
    }
    
    static bool tsc2007_is_pen_down(struct tsc2007 *ts)
    {
    	/*
    	 * NOTE: We can't rely on the pressure to determine the pen down
    	 * state, even though this controller has a pressure sensor.
    	 * The pressure value can fluctuate for quite a while after
    	 * lifting the pen and in some cases may not even settle at the
    	 * expected value.
    	 *
    	 * The only safe way to check for the pen up condition is in the
    	 * work function by reading the pen signal state (it's a GPIO
    	 * and IRQ). Unfortunately such callback is not always available,
    	 * in that case we assume that the pen is down and expect caller
    	 * to fall back on the pressure reading.
    	 */
    
    	if (!ts->get_pendown_state)
    		return true;
    
    	return ts->get_pendown_state(&ts->client->dev);
    }
    
    static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	struct input_dev *input = ts->input;
    	struct ts_event tc;
    	u32 rt;
    
    	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
    
    		/* pen is down, continue with the measurement */
    		tsc2007_read_values(ts, &tc);
    
    		rt = tsc2007_calculate_pressure(ts, &tc);
    
    		if (!rt && !ts->get_pendown_state) {
    			/*
    			 * If pressure reported is 0 and we don't have
    			 * callback to check pendown state, we have to
    			 * assume that pen was lifted up.
    			 */
    			break;
    		}
    
    		if (rt <= ts->max_rt) {
    			dev_dbg(&ts->client->dev,
    				"DOWN point(%4d,%4d), pressure (%4u)\n",
    				tc.x, tc.y, rt);
    
    			input_report_key(input, BTN_TOUCH, 1);
    			input_report_abs(input, ABS_X, tc.x);
    			input_report_abs(input, ABS_Y, tc.y);
    			input_report_abs(input, ABS_PRESSURE, rt);
    
    			input_sync(input);
    
    		} else {
    			/*
    			 * Sample found inconsistent by debouncing or pressure is
    			 * beyond the maximum. Don't report it to user space,
    			 * repeat at least once more the measurement.
    			 */
    			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
    		}
    
    		wait_event_timeout(ts->wait, ts->stopped,
    				   msecs_to_jiffies(ts->poll_period));
    	}
    
    	dev_dbg(&ts->client->dev, "UP\n");
    
    	input_report_key(input, BTN_TOUCH, 0);
    	input_report_abs(input, ABS_PRESSURE, 0);
    	input_sync(input);
    
    	if (ts->clear_penirq)
    		ts->clear_penirq();
    
    	return IRQ_HANDLED;
    }
    
    static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    
    	if (tsc2007_is_pen_down(ts))
    		return IRQ_WAKE_THREAD;
    
    	if (ts->clear_penirq)
    		ts->clear_penirq();
    
    	return IRQ_HANDLED;
    }
    
    static void tsc2007_stop(struct tsc2007 *ts)
    {
    	ts->stopped = true;
    	mb();
    	wake_up(&ts->wait);
    
    	disable_irq(ts->irq);
    }
    
    static int tsc2007_open(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    	int err;
    
    	ts->stopped = false;
    	mb();
    
    	enable_irq(ts->irq);
    
    	/* Prepare for touch readings - power down ADC and enable PENIRQ */
    	err = tsc2007_xfer(ts, PWRDOWN);
    	if (err < 0) {
    		tsc2007_stop(ts);
    		return err;
    	}
    
    	return 0;
    }
    
    static void tsc2007_close(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    
    	tsc2007_stop(ts);
    }
    
    #ifdef CONFIG_OF
    static int tsc2007_get_pendown_state_gpio(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct tsc2007 *ts = i2c_get_clientdata(client);
    
    	return !gpio_get_value(ts->gpio);
    }
    
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	struct device_node *np = client->dev.of_node;
    	u32 val32;
    	u64 val64;
    
    	if (!np) {
    		dev_err(&client->dev, "missing device tree data\n");
    		return -EINVAL;
    	}
    
    	if (!of_property_read_u32(np, "ti,max-rt", &val32))
    		ts->max_rt = val32;
    	else
    		ts->max_rt = MAX_12BIT;
    
    	if (!of_property_read_u32(np, "ti,fuzzx", &val32))
    		ts->fuzzx = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzy", &val32))
    		ts->fuzzy = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzz", &val32))
    		ts->fuzzz = val32;
    
    	if (!of_property_read_u64(np, "ti,poll-period", &val64))
    		ts->poll_period = val64;
    	else
    		ts->poll_period = 1;
    
    	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
    		ts->x_plate_ohms = val32;
    	} else {
    		dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
    		return -EINVAL;
    	}
    
    	ts->gpio = of_get_gpio(np, 0);
    	if (gpio_is_valid(ts->gpio))
    		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
    	else
    		dev_warn(&client->dev,
    			 "GPIO not specified in DT (of_get_gpio returned %d)\n",
    			 ts->gpio);
    
    	return 0;
    }
    #else
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	dev_err(&client->dev, "platform data is required!\n");
    	return -EINVAL;
    }
    #endif
    
    static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
    			      const struct tsc2007_platform_data *pdata,
    			      const struct i2c_device_id *id)
    {
    	ts->model             = pdata->model;
    	ts->x_plate_ohms      = pdata->x_plate_ohms;
    	ts->max_rt            = pdata->max_rt ? : MAX_12BIT;
    	ts->poll_period       = pdata->poll_period ? : 1;
    	ts->get_pendown_state = pdata->get_pendown_state;
    	ts->clear_penirq      = pdata->clear_penirq;
    	ts->fuzzx             = pdata->fuzzx;
    	ts->fuzzy             = pdata->fuzzy;
    	ts->fuzzz             = pdata->fuzzz;
    
    	if (pdata->x_plate_ohms == 0) {
    		dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static void tsc2007_call_exit_platform_hw(void *data)
    {
    	struct device *dev = data;
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
    
    	pdata->exit_platform_hw();
    }
    
    static int tsc2007_probe(struct i2c_client *client,
    			 const struct i2c_device_id *id)
    {
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
    	struct tsc2007 *ts;
    	struct input_dev *input_dev;
    	int err;
    
    	if (!i2c_check_functionality(client->adapter,
    				     I2C_FUNC_SMBUS_READ_WORD_DATA))
    		return -EIO;
    
    	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
    	if (!ts)
    		return -ENOMEM;
    
    	if (pdata)
    		err = tsc2007_probe_pdev(client, ts, pdata, id);
    	else
    		err = tsc2007_probe_dt(client, ts);
    	if (err)
    		return err;
    
    	input_dev = devm_input_allocate_device(&client->dev);
    	if (!input_dev)
    		return -ENOMEM;
    
    	i2c_set_clientdata(client, ts);
    
    	ts->client = client;
    	ts->irq = client->irq;
    	ts->input = input_dev;
    	init_waitqueue_head(&ts->wait);
    
    	snprintf(ts->phys, sizeof(ts->phys),
    		 "%s/input0", dev_name(&client->dev));
    
    	input_dev->name = "TSC2007 Touchscreen";
    	input_dev->phys = ts->phys;
    	input_dev->id.bustype = BUS_I2C;
    
    	input_dev->open = tsc2007_open;
    	input_dev->close = tsc2007_close;
    
    	input_set_drvdata(input_dev, ts);
    
    	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, ts->fuzzx, 0);
    	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
    	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
    			     ts->fuzzz, 0);
    
    	if (pdata) {
    		if (pdata->exit_platform_hw) {
    			err = devm_add_action(&client->dev,
    					      tsc2007_call_exit_platform_hw,
    					      &client->dev);
    			if (err) {
    				dev_err(&client->dev,
    					"Failed to register exit_platform_hw action, %d\n",
    					err);
    				return err;
    			}
    		}
    
    		if (pdata->init_platform_hw)
    			pdata->init_platform_hw();
    	}
    
    	err = devm_request_threaded_irq(&client->dev, ts->irq,
    					tsc2007_hard_irq, tsc2007_soft_irq,
    					IRQF_ONESHOT,
    					client->dev.driver->name, ts);
    	if (err) {
    		dev_err(&client->dev, "Failed to request irq %d: %d\n",
    			ts->irq, err);
    		return err;
    	}
    
    	tsc2007_stop(ts);
    
    	err = input_register_device(input_dev);
    	if (err) {
    		dev_err(&client->dev,
    			"Failed to register input device: %d\n", err);
    		return err;
    	}
    
    	return 0;
    }
    
    static const struct i2c_device_id tsc2007_idtable[] = {
    	{ "tsc2007", 0 },
    	{ }
    };
    
    MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
    
    #ifdef CONFIG_OF
    static const struct of_device_id tsc2007_of_match[] = {
    	{ .compatible = "ti,tsc2007" },
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, tsc2007_of_match);
    #endif
    
    static struct i2c_driver tsc2007_driver = {
    	.driver = {
    		.owner	= THIS_MODULE,
    		.name	= "tsc2007",
    		.of_match_table = of_match_ptr(tsc2007_of_match),
    	},
    	.id_table	= tsc2007_idtable,
    	.probe		= tsc2007_probe,
    };
    
    module_i2c_driver(tsc2007_driver);
    
    MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
    MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
    MODULE_LICENSE("GPL");
    

    New TSC2007 modified by me: (partially working)

    /*
     * drivers/input/touchscreen/tsc2007.c
     *
     * Copyright (c) 2008 MtekVision Co., Ltd.
     *	Kwangwoo Lee <kwlee@mtekvision.com>
     *
     * Using code from:
     *  - ads7846.c
     *	Copyright (c) 2005 David Brownell
     *	Copyright (c) 2006 Nokia Corporation
     *  - corgi_ts.c
     *	Copyright (C) 2004-2005 Richard Purdie
     *  - omap_ts.[hc], ads7846.h, ts_osk.c
     *	Copyright (C) 2002 MontaVista Software
     *	Copyright (C) 2004 Texas Instruments
     *	Copyright (C) 2005 Dirk Behme
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License version 2 as
     *  published by the Free Software Foundation.
     */
    
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/input.h>
    #include <linux/interrupt.h>
    #include <linux/i2c.h>
    #include <linux/i2c/tsc2007.h>
    #include <linux/of_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    
    #define TSC2007_MEASURE_TEMP0		(0x0 << 4)
    #define TSC2007_MEASURE_AUX		(0x2 << 4)
    #define TSC2007_MEASURE_TEMP1		(0x4 << 4)
    #define TSC2007_ACTIVATE_XN		(0x8 << 4)
    #define TSC2007_ACTIVATE_YN		(0x9 << 4)
    #define TSC2007_ACTIVATE_YP_XN		(0xa << 4)
    #define TSC2007_SETUP			(0xb << 4)
    #define TSC2007_MEASURE_X		(0xc << 4)
    #define TSC2007_MEASURE_Y		(0xd << 4)
    #define TSC2007_MEASURE_Z1		(0xe << 4)
    #define TSC2007_MEASURE_Z2		(0xf << 4)
    
    #define TSC2007_POWER_OFF_IRQ_EN	(0x0 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS0		(0x1 << 2)
    #define TSC2007_ADC_OFF_IRQ_EN		(0x2 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS1		(0x3 << 2)
    
    #define TSC2007_12BIT			(0x0 << 1)
    #define TSC2007_8BIT			(0x1 << 1)
    
    #define	MAX_12BIT			((1 << 12) - 1)
    
    #define ADC_ON_12BIT	(TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
    
    #define READ_Y		(ADC_ON_12BIT | TSC2007_MEASURE_Y)
    #define READ_Z1		(ADC_ON_12BIT | TSC2007_MEASURE_Z1)
    #define READ_Z2		(ADC_ON_12BIT | TSC2007_MEASURE_Z2)
    #define READ_X		(ADC_ON_12BIT | TSC2007_MEASURE_X)
    #define PWRDOWN		(TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
    
    
    /*
     * This define forces debug prints during development
     */
    #define DEBUG_PRINT		(1)
    #if(DEBUG_PRINT == 1)
    	#define SR_DEBUG	dev_err
    #else
    	#define SR_DEBUG	dev_dbg
    #endif
    
    
    struct ts_event {
    	u16	x;
    	u16	y;
    	u16	z1, z2;
    };
    
    struct tsc2007 {
    	struct input_dev	*input;
    	char			phys[32];
    
    	struct i2c_client	*client;
    
    	u16			model;
    	u16			x_plate_ohms;
    	u16			max_rt;
    	unsigned long		poll_period;
    	int			fuzzx;
    	int			fuzzy;
    	int			fuzzz;
    
    	unsigned		gpio;
    	int			irq;
    
    	wait_queue_head_t	wait;
    	bool			stopped;
    
    	int			(*get_pendown_state)(struct device *);
    	void			(*clear_penirq)(void);
    };
    
    static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
    {
    	s32 data;
    	u16 val;
    
    	data = i2c_smbus_read_word_data(tsc->client, cmd);
    	if (data < 0) {
    		dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
    		return data;
    	}
    
    	/* The protocol and raw data format from i2c interface:
    	 * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
    	 * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
    	 */
    	val = swab16(data) >> 4;
    
    	dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
    
    	return val;
    }
    
    static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	/* y- still on; turn on only y+ (and ADC) */
    	tc->y = tsc2007_xfer(tsc, READ_Y);
    
    	/* turn y- off, x+ on, then leave in lowpower */
    	tc->x = tsc2007_xfer(tsc, READ_X);
    
    	/* turn y+ off, x- on; we'll use formula #1 */
    	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
    	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
    
    	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
    	tsc2007_xfer(tsc, PWRDOWN);
    }
    
    static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	u32 rt = 0;
    
    	/* range filtering */
    	if (tc->x == MAX_12BIT)
    		tc->x = 0;
    
    	if (likely(tc->x && tc->z1)) {
    		/* compute touch pressure resistance using equation #1 */
    		rt = tc->z2 - tc->z1;
    		rt *= tc->x;
    		rt *= tsc->x_plate_ohms;
    		rt /= tc->z1;
    		rt = (rt + 2047) >> 12;
    	}
    
    	return rt;
    }
    
    static bool tsc2007_is_pen_down(struct tsc2007 *ts)
    {
    	/*
    	 * NOTE: We can't rely on the pressure to determine the pen down
    	 * state, even though this controller has a pressure sensor.
    	 * The pressure value can fluctuate for quite a while after
    	 * lifting the pen and in some cases may not even settle at the
    	 * expected value.
    	 *
    	 * The only safe way to check for the pen up condition is in the
    	 * work function by reading the pen signal state (it's a GPIO
    	 * and IRQ). Unfortunately such callback is not always available,
    	 * in that case we assume that the pen is down and expect caller
    	 * to fall back on the pressure reading.
    	 */
    
    	if (!ts->get_pendown_state)
    		return true;
    
    	return ts->get_pendown_state(&ts->client->dev);
    }
    
    static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	struct input_dev *input = ts->input;
    	struct ts_event tc;
    	u32 rt;
    	
    	SR_DEBUG(&ts->client->dev, "%s: Entering\n", __func__);	
    		
    	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
    
    		/* pen is down, continue with the measurement */
    		tsc2007_read_values(ts, &tc);
    
    		rt = tsc2007_calculate_pressure(ts, &tc);
    
    		if (!rt && !ts->get_pendown_state) {
    			/*
    			 * If pressure reported is 0 and we don't have
    			 * callback to check pendown state, we have to
    			 * assume that pen was lifted up.
    			 */
    			break;
    		}
    
    		if (rt <= ts->max_rt) {
    			dev_dbg(&ts->client->dev,
    				"DOWN point(%4d,%4d), pressure (%4u)\n",
    				tc.x, tc.y, rt);
    
    			input_report_key(input, BTN_TOUCH, 1);
    			input_report_abs(input, ABS_X, tc.x);
    			input_report_abs(input, ABS_Y, tc.y);
    			input_report_abs(input, ABS_PRESSURE, rt);
    
    			input_sync(input);
    
    		} else {
    			/*
    			 * Sample found inconsistent by debouncing or pressure is
    			 * beyond the maximum. Don't report it to user space,
    			 * repeat at least once more the measurement.
    			 */
    			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
    		}
    
    		wait_event_timeout(ts->wait, ts->stopped,
    				   msecs_to_jiffies(10));			/* ts->poll_period */
    	}
    
    	//dev_dbg(&ts->client->dev, "UP\n");
    	SR_DEBUG(&ts->client->dev, "UP\n");
    	
    	input_report_key(input, BTN_TOUCH, 0);
    	input_report_abs(input, ABS_PRESSURE, 0);
    	input_sync(input);
    
    	if (ts->clear_penirq)
    		ts->clear_penirq();
    	
    	/* Now that the touch has been handled, re-enabled the PENIRQ */
    	enable_irq(ts->irq);	
    	return IRQ_HANDLED;
    }
    
    static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	
    	SR_DEBUG(&ts->client->dev, "%s: Entering\n", __func__);
    	
    	if (tsc2007_is_pen_down(ts))	
    	{
    		/* Once the IRQ handling starts, disable the interrupt */	
    		disable_irq_nosync(ts->irq);
    		SR_DEBUG(&ts->client->dev, "%s: Pen down returns true\n", __func__);				
    		return IRQ_WAKE_THREAD;
    	}
    
    	if (ts->clear_penirq)
    	{
    		SR_DEBUG(&ts->client->dev, "%s: Clear pending interrupt", __func__);
    		ts->clear_penirq();
    	}
    
    	return IRQ_HANDLED;
    }
    
    static void tsc2007_stop(struct tsc2007 *ts)
    {
    	ts->stopped = true;
    	mb();
    	wake_up(&ts->wait);
    
    	disable_irq(ts->irq);
    }
    
    static int tsc2007_open(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    	int err;
    
    	ts->stopped = false;
    	mb();
    
    	enable_irq(ts->irq);
    	SR_DEBUG(&ts->client->dev, "%s: Executed\n", __func__);
    	/* Prepare for touch readings - power down ADC and enable PENIRQ */
    	err = tsc2007_xfer(ts, PWRDOWN);
    	if (err < 0) {
    		tsc2007_stop(ts);
    		return err;
    	}
    
    	return 0;
    }
    
    static void tsc2007_close(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    
    	tsc2007_stop(ts);
    }
    
    #ifdef CONFIG_OF
    static int tsc2007_get_pendown_state_gpio(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct tsc2007 *ts = i2c_get_clientdata(client);
    
    	return !gpio_get_value(ts->gpio);
    }
    
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	struct device_node *np = client->dev.of_node;
    	u32 val32;
    	u64 val64;
    
    	if (!np) {
    		dev_err(&client->dev, "missing device tree data\n");
    		return -EINVAL;
    	}
    
    	if (!of_property_read_u32(np, "ti,max-rt", &val32))
    		ts->max_rt = val32;
    	else
    		ts->max_rt = MAX_12BIT;
    
    	if (!of_property_read_u32(np, "ti,fuzzx", &val32))
    		ts->fuzzx = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzy", &val32))
    		ts->fuzzy = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzz", &val32))
    		ts->fuzzz = val32;
    
    	if (!of_property_read_u64(np, "ti,poll-period", &val64))
    		ts->poll_period = val64;
    	else
    		ts->poll_period = 1;
    
    	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
    		ts->x_plate_ohms = val32;
    	} else {
    		dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
    		return -EINVAL;
    	}
    
    	ts->gpio = of_get_gpio(np, 0);
    	if (gpio_is_valid(ts->gpio))
    	{
    		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
    		
    		/* If GPIO is valid, then request the GPIO for use */
    		SR_DEBUG(&client->dev, "%s: Requesting GPIO", __func__);
    		
    		if (gpio_request(ts->gpio, "nPENIRQ"))
    		{
    			//SR_DEBUG(&client->dev, "%s: Requesting GPIO failed", __func__);
    			return 0;
    		}
    		
    		/* Set the direction to be input - may not be required */
    		SR_DEBUG(&client->dev, "%s: Setting direction for GPIO", __func__);
    		if (gpio_direction_input(ts->gpio))
    		{
    			gpio_free(ts->gpio);
    			return 0;
    		}
    		
    		/* Set the debounce limit/value for the GPIO 
    		//SR_DEBUG(&client->dev, "%s: Setting debounce for GPIO", __func__);
    		if (gpio_set_debounce(ts->gpio, 50000))
    		{
    			gpio_free(ts->gpio);
    			return 0;
    		}*/
    	}
    	else
    		dev_warn(&client->dev,
    			 "GPIO not specified in DT (of_get_gpio returned %d)\n",
    			 ts->gpio);
    
    	return 0;
    }
    #else
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	dev_err(&client->dev, "platform data is required!\n");
    	return -EINVAL;
    }
    #endif
    
    static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
    			      const struct tsc2007_platform_data *pdata,
    			      const struct i2c_device_id *id)
    {
    	ts->model             = pdata->model;
    	ts->x_plate_ohms      = pdata->x_plate_ohms;
    	ts->max_rt            = pdata->max_rt ? : MAX_12BIT;
    	ts->poll_period       = pdata->poll_period ? : 1;
    	ts->get_pendown_state = pdata->get_pendown_state;
    	ts->clear_penirq      = pdata->clear_penirq;
    	ts->fuzzx             = pdata->fuzzx;
    	ts->fuzzy             = pdata->fuzzy;
    	ts->fuzzz             = pdata->fuzzz;
    
    	if (pdata->x_plate_ohms == 0) {
    		dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static void tsc2007_call_exit_platform_hw(void *data)
    {
    	struct device *dev = data;
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
    
    	pdata->exit_platform_hw();
    }
    
    static int tsc2007_probe(struct i2c_client *client,
    			 const struct i2c_device_id *id)
    {
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
    	struct tsc2007 *ts;
    	struct input_dev *input_dev;
    	int err;
    
    	if (!i2c_check_functionality(client->adapter,
    				     I2C_FUNC_SMBUS_READ_WORD_DATA))
    		return -EIO;
    
    	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
    	if (!ts)
    		return -ENOMEM;
    
    	if (pdata)
    		err = tsc2007_probe_pdev(client, ts, pdata, id);
    	else
    		err = tsc2007_probe_dt(client, ts);
    	if (err)
    		return err;
    
    	input_dev = devm_input_allocate_device(&client->dev);
    	if (!input_dev)
    		return -ENOMEM;
    
    	i2c_set_clientdata(client, ts);
    
    	ts->client = client;
    	
    	/* map GPIO numbers to IRQ numbers */
    	client->irq = gpio_to_irq(ts->gpio);
    	ts->irq = client->irq;
    	SR_DEBUG(&client->dev, "%s: IRQ number is %d", __func__, ts->irq);
    		
    	ts->input = input_dev;
    	init_waitqueue_head(&ts->wait);
    
    	snprintf(ts->phys, sizeof(ts->phys),
    		 "%s/input0", dev_name(&client->dev));
    
    	input_dev->name = "TSC2007 Touchscreen";
    	input_dev->phys = ts->phys;
    	input_dev->id.bustype = BUS_I2C;
    
    	input_dev->open = tsc2007_open;
    	input_dev->close = tsc2007_close;
    
    	input_set_drvdata(input_dev, ts);
    
    	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, ts->fuzzx, 0);
    	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
    	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
    			     ts->fuzzz, 0);
    
    	if (pdata) {
    		if (pdata->exit_platform_hw) {
    			err = devm_add_action(&client->dev,
    					      tsc2007_call_exit_platform_hw,
    					      &client->dev);
    			if (err) {
    				dev_err(&client->dev,
    					"Failed to register exit_platform_hw action, %d\n",
    					err);
    				return err;
    			}
    		}
    
    		if (pdata->init_platform_hw)
    			pdata->init_platform_hw();
    	}
    
    	err = devm_request_threaded_irq(&client->dev, ts->irq,
    					tsc2007_hard_irq, tsc2007_soft_irq,
    					IRQF_TRIGGER_FALLING /*| IRQF_TRIGGER_RISING | IRQF_ONESHOT*/,
    					client->dev.driver->name, ts);
    	if (err) {
    		dev_err(&client->dev, "Failed to request irq %d: %d\n",
    			ts->irq, err);
    		return err;
    	}
    
    	tsc2007_stop(ts);
    
    	err = input_register_device(input_dev);
    	if (err) {
    		dev_err(&client->dev,
    			"Failed to register input device: %d\n", err);
    		return err;
    	}
    
    	return 0;
    }
    
    static const struct i2c_device_id tsc2007_idtable[] = {
    	{ "tsc2007", 0 },
    	{ }
    };
    
    MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
    
    #ifdef CONFIG_OF
    static const struct of_device_id tsc2007_of_match[] = {
    	{ .compatible = "ti,tsc2007" },
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, tsc2007_of_match);
    #endif
    
    static struct i2c_driver tsc2007_driver = {
    	.driver = {
    		.owner	= THIS_MODULE,
    		.name	= "tsc2007",
    		.of_match_table = of_match_ptr(tsc2007_of_match),
    	},
    	.id_table	= tsc2007_idtable,
    	.probe		= tsc2007_probe,
    };
    
    module_i2c_driver(tsc2007_driver);
    
    MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
    MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
    MODULE_LICENSE("GPL");
    

    If you have any additional question, please do let me know.

    Thank you

    Regards

    Santhosh

  • Hello Miroslav,

    I was able to finally fix the issue with the I2C bus - it had to do with noise on the level on the bus and the oscilloscope probe was causing the problem. Once I fixed the driver code and then cleaned up the hardware, the touch worked perfectly.

    I have made several updates to the touch driver, I'm attaching the new modifications to the thread. Can you please let me know if the changes make sense? Also how to incorporate this into the mainline or SDK?

    Thanks a lot for your help.

    Regards

    Santhosh

    Documentation -

    * Texas Instruments tsc2007 touchscreen controller
    
    Required properties:
    - compatible: must be "ti,tsc2007".
    - reg: I2C address of the chip.
    - ti,x-plate-ohms: X-plate resistance in ohms.
    
    Optional properties:
    - gpios: the interrupt gpio the chip is connected to (trough the penirq pin).
      The penirq pin goes to low when the panel is touched.
      (see GPIO binding[1] for more details).
    - interrupt-parent: the phandle for the gpio controller
      (see interrupt binding[0]).
    - interrupts: (gpio) interrupt to which the chip is connected
      (see interrupt binding[0]).
    - ti,max-rt: maximum pressure.
    - ti,fuzzx: specifies the absolute input fuzz x value.
      If set, it will permit noise in the data up to +- the value given to the fuzz
      parameter, that is used to filter noise from the event stream.
    - ti,fuzzy: specifies the absolute input fuzz y value.
    - ti,fuzzz: specifies the absolute input fuzz z value.
    - ti,intial-delay: how much time to wait (in milliseconds) to start sampling (first time)
    - ti,report-delay: how much time to wait (in milliseconds) before the PENIRQ signal
      is checked again before reporting the last reading
    - ti,sample-delay: how much time to wait (in milliseconds) before continuing sampling
    
      Poll period is a total of report-delay and sample-delay.
    
    [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
    [1]: Documentation/devicetree/bindings/gpio/gpio.txt
    
    Example:
    	&i2c1 {
    		/* ... */
    		tsc2007@49 {
    			compatible = "ti,tsc2007";
    			reg = <0x49>;
    			interrupt-parent = <&gpio4>;
    			interrupts = <0x0 0x8>;
    			gpios = <&gpio4 0 0>;
    			ti,x-plate-ohms = <180>;
    			ti,initial-delay = <10>;				/* Wait 10ms before the first sample */
    			ti,report-delay = <5>;					/* Wait 5ms before reporting touch */
    			ti,sample-delay = <5>;					/* Wait 5ms before sampling again */
    		};
    
    		/* ... */
    	};
    

    Header - 5153.tsc2007.h

    Source File -

    /*
     * drivers/input/touchscreen/tsc2007.c
     *
     * Copyright (c) 2014 Aprilaire (Research Products Corporation)
     *	Santhosh Ramani <sr@aprilaire.com>
     * 
     * Modified the TSC2007 driver from:
     *	Copyright (c) 2008 MtekVision Co., Ltd.
     *	Kwangwoo Lee <kwlee@mtekvision.com>
     *
     * Using code from:
     *  - ads7846.c
     *	Copyright (c) 2005 David Brownell
     *	Copyright (c) 2006 Nokia Corporation
     *  - corgi_ts.c
     *	Copyright (C) 2004-2005 Richard Purdie
     *  - omap_ts.[hc], ads7846.h, ts_osk.c
     *	Copyright (C) 2002 MontaVista Software
     *	Copyright (C) 2004 Texas Instruments
     *	Copyright (C) 2005 Dirk Behme
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License version 2 as
     *  published by the Free Software Foundation.
     */
    
    
    /*
     * 	This code has been modified to pickup falling and rising edge of 
     *	the PENIRQ. Once the falling edge is detected, 
     *		Interrupt is disabled
     *		Touch is sampled
     *		And upon PENIRQ release (high) interrupt is re-enabled		
     *
     * 	Modified poll-period: to sample delay (because is delays sampling)
     *
     *	Added report-delay: this prevents reporting incorrect touch events
     *		(picked up during the pen-up event at the time of sampling)
     *
     *	Added initial-delay: adds some delay before the first sample
     *
     * Technically, polling period is now (report_delay + sample_delay)
     */
    
    
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/input.h>
    #include <linux/interrupt.h>
    #include <linux/i2c.h>
    #include <linux/i2c/tsc2007.h>
    #include <linux/of_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/delay.h>
    
    #define TSC2007_MEASURE_TEMP0		(0x0 << 4)
    #define TSC2007_MEASURE_AUX			(0x2 << 4)
    #define TSC2007_MEASURE_TEMP1		(0x4 << 4)
    #define TSC2007_ACTIVATE_XN			(0x8 << 4)
    #define TSC2007_ACTIVATE_YN			(0x9 << 4)
    #define TSC2007_ACTIVATE_YP_XN		(0xa << 4)
    #define TSC2007_SETUP				(0xb << 4)
    #define TSC2007_MEASURE_X			(0xc << 4)
    #define TSC2007_MEASURE_Y			(0xd << 4)
    #define TSC2007_MEASURE_Z1			(0xe << 4)
    #define TSC2007_MEASURE_Z2			(0xf << 4)
    
    #define TSC2007_POWER_OFF_IRQ_EN	(0x0 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS0		(0x1 << 2)
    #define TSC2007_ADC_OFF_IRQ_EN		(0x2 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS1		(0x3 << 2)
    
    #define TSC2007_12BIT				(0x0 << 1)
    #define TSC2007_8BIT				(0x1 << 1)
    
    #define	MAX_12BIT					((1 << 12) - 1)
    
    #define ADC_ON_12BIT	(TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
    
    #define READ_Y		(ADC_ON_12BIT | TSC2007_MEASURE_Y)				/* 1101 0100 */
    #define READ_Z1		(ADC_ON_12BIT | TSC2007_MEASURE_Z1)				/* 1110 0100 */
    #define READ_Z2		(ADC_ON_12BIT | TSC2007_MEASURE_Z2)				/* 1111 0100 */
    #define READ_X		(ADC_ON_12BIT | TSC2007_MEASURE_X)				/* 1100 0100 */
    #define PWRDOWN		(TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)		/* 0000 0000 */
    
    
    struct ts_event {
    	u16	x;
    	u16	y;
    	u16	z1, z2;
    };
    
    struct tsc2007 {
    	struct input_dev	*input;
    	char phys[32];
    
    	struct i2c_client	*client;
    
    	u16			model;
    	u16			x_plate_ohms;
    	u16			max_rt;
    	
    	unsigned long	sample_delay;
    	unsigned long	report_delay;
    	unsigned long	initial_delay;
    	
    	int			fuzzx;
    	int			fuzzy;
    	int			fuzzz;
    
    	unsigned	gpio;
    	int			irq;
    
    	wait_queue_head_t	wait;
    	bool			stopped;
    
    	int			(*get_pendown_state)(struct device *);
    	void		(*clear_penirq)(void);
    };
    
    static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
    {
    	s32 data;
    	u16 val;
    
    	data = i2c_smbus_read_word_data(tsc->client, cmd);
    	if (data < 0) {
    		dev_err(&tsc->client->dev, "i2c io error: %d\n\n\n\n", data);
    		return data;
    	}
    
    	/* The protocol and raw data format from i2c interface:
    	 * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
    	 * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
    	 */
    	val = swab16(data) >> 4;
    
    	dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
    
    	return val;
    }
    
    static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	/* y- still on; turn on only y+ (and ADC) */
    	tc->y = tsc2007_xfer(tsc, READ_Y);
    
    	/* turn y- off, x+ on, then leave in lowpower */
    	tc->x = tsc2007_xfer(tsc, READ_X);
    
    	/* turn y+ off, x- on; we'll use formula #1 */
    	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
    	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
    
    	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
    	tsc2007_xfer(tsc, PWRDOWN);
    }
    
    static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	u32 rt = 0;
    
    	/* range filtering */
    	if (tc->x == MAX_12BIT)
    		tc->x = 0;
    
    	if (likely(tc->x && tc->z1)) {
    		/* compute touch pressure resistance using equation #1 */
    		rt = tc->z2 - tc->z1;
    		rt *= tc->x;
    		rt *= tsc->x_plate_ohms;
    		rt /= tc->z1;
    		rt = (rt + 2047) >> 12;
    	}
    
    	return rt;
    }
    
    static bool tsc2007_is_pen_down(struct tsc2007 *ts)
    {
    	/*
    	 * NOTE: We can't rely on the pressure to determine the pen down
    	 * state, even though this controller has a pressure sensor.
    	 * The pressure value can fluctuate for quite a while after
    	 * lifting the pen and in some cases may not even settle at the
    	 * expected value.
    	 *
    	 * The only safe way to check for the pen up condition is in the
    	 * work function by reading the pen signal state (it's a GPIO
    	 * and IRQ). Unfortunately such callback is not always available,
    	 * in that case we assume that the pen is down and expect caller
    	 * to fall back on the pressure reading.
    	 */
    
    	if (!ts->get_pendown_state)
    		return true;
    
    	return ts->get_pendown_state(&ts->client->dev);
    }
    
    static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	struct input_dev *input = ts->input;
    	struct ts_event tc;
    	u32 rt;
    	
    	/*
    	 * With some panels we need to wait a bit otherwise the first value
     	 * is often wrong. (Allows the touch screen to settle)
    	 */
    	if (ts->initial_delay > 0)
    		msleep(ts->initial_delay);
    				
    	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
    
    		/* pen is down, continue with the measurement */
    		tsc2007_read_values(ts, &tc);
    
    		rt = tsc2007_calculate_pressure(ts, &tc);
    
    		if (!rt && !ts->get_pendown_state) {
    			/*
    			 * If pressure reported is 0 and we don't have
    			 * callback to check pendown state, we have to
    			 * assume that pen was lifted up.
    			 */
    			break;
    		}
    		
    		/*
    			Before reporting the new reading, check to make sure that
    			the pen is still down. This ensures that readings taken
    			during Pen-up event are ignored.
    		*/
    		wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->report_delay));
    		
    		if (ts->stopped || (!tsc2007_is_pen_down(ts)))
    			break;	
    		
    		if (rt <= ts->max_rt) {
    			dev_dbg(&ts->client->dev,
    				"DOWN point(%4d,%4d), pressure (%4u)\n",
    				tc.x, tc.y, rt);
    
    			input_report_key(input, BTN_TOUCH, 1);
    			input_report_abs(input, ABS_X, tc.x);
    			input_report_abs(input, ABS_Y, tc.y);
    			input_report_abs(input, ABS_PRESSURE, rt);
    
    			input_sync(input);
    
    		} else {
    			/*
    			 * Sample found inconsistent by debouncing or pressure is
    			 * beyond the maximum. Don't report it to user space,
    			 * repeat at least once more the measurement.
    			 */
    			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
    		}
    
    		wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->sample_delay));
    	}
    
    	dev_dbg(&ts->client->dev, "UP\n");
    	
    	input_report_key(input, BTN_TOUCH, 0);
    	input_report_abs(input, ABS_PRESSURE, 0);
    	input_sync(input);
    
    	if (ts->clear_penirq)
    		ts->clear_penirq();
    	
    	/* Now that the touch has been handled, re-enabled the PENIRQ */
    	enable_irq(ts->irq);	
    	return IRQ_HANDLED;
    }
    
    static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	
    	if (tsc2007_is_pen_down(ts)) {
    		/* Once the IRQ handling starts, disable the interrupt */	
    		disable_irq_nosync(ts->irq);
    				
    		return IRQ_WAKE_THREAD;
    	}
    
    	if (ts->clear_penirq) {
    		ts->clear_penirq();
    	}
    	
    	return IRQ_HANDLED;
    }
    
    static void tsc2007_stop(struct tsc2007 *ts)
    {
    	ts->stopped = true;
    	mb();
    	wake_up(&ts->wait);
    
    	disable_irq(ts->irq);
    }
    
    static int tsc2007_open(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    	int err;
    	
    	ts->stopped = false;
    	mb();
    
    	enable_irq(ts->irq);
    	
    	/* Prepare for touch readings - power down ADC and enable PENIRQ */
    	err = tsc2007_xfer(ts, PWRDOWN);
    	if (err < 0) {
    		tsc2007_stop(ts);	
    		return err;
    	}
    		
    	return 0;
    }
    
    static void tsc2007_close(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    
    	tsc2007_stop(ts);
    }
    
    #ifdef CONFIG_OF
    static int tsc2007_get_pendown_state_gpio(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct tsc2007 *ts = i2c_get_clientdata(client);
    
    	return !gpio_get_value(ts->gpio);
    }
    
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	struct device_node *np = client->dev.of_node;
    	u32 val32;
    	u64 val64;
    
    	if (!np) {
    		dev_err(&client->dev, "missing device tree data\n");
    		return -EINVAL;
    	}
    
    	if (!of_property_read_u32(np, "ti,max-rt", &val32))
    		ts->max_rt = val32;
    	else
    		ts->max_rt = MAX_12BIT;
    
    	if (!of_property_read_u32(np, "ti,fuzzx", &val32))
    		ts->fuzzx = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzy", &val32))
    		ts->fuzzy = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzz", &val32))
    		ts->fuzzz = val32;
    
    	if (!of_property_read_u64(np, "ti,sample-delay", &val64))
    		ts->sample_delay = val64;
    	else
    		ts->sample_delay = 1;
    		
    	if (!of_property_read_u64(np, "ti,report-delay", &val64))
    		ts->report_delay = val64;
    	else
    		ts->report_delay = 1;
    
    	if (!of_property_read_u64(np, "ti,initial-delay", &val64))
    		ts->initial_delay = val64;
    	else
    		ts->initial_delay = 1;
    		
    	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
    		ts->x_plate_ohms = val32;
    	} else {
    		dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
    		return -EINVAL;
    	}
    
    	ts->gpio = of_get_gpio(np, 0);
    	if (gpio_is_valid(ts->gpio)) {
    		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
    		
    		/* If GPIO is valid, then request the GPIO for use */
    		if (gpio_request(ts->gpio, "nPENIRQ")) {
    			dev_err(&client->dev, "Requesting GPIO failed - %s", __func__);
    			return 0;
    		}
    		
    		/* Set the direction to be input */		
    		if (gpio_direction_input(ts->gpio)) {
    			gpio_free(ts->gpio);
    			return 0;
    		}		
    	}
    	else
    		dev_warn(&client->dev,
    			 "GPIO not specified in DT (of_get_gpio returned %d)\n",
    			 ts->gpio);
    
    	return 0;
    }
    #else
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	dev_err(&client->dev, "platform data is required!\n");
    	return -EINVAL;
    }
    #endif
    
    static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
    			      const struct tsc2007_platform_data *pdata,
    			      const struct i2c_device_id *id)
    {
    	ts->model             = pdata->model;
    	ts->x_plate_ohms      = pdata->x_plate_ohms;
    	ts->max_rt            = pdata->max_rt ? : MAX_12BIT;
    	ts->sample_delay      = pdata->sample_delay ? : 1;
    	ts->report_delay      = pdata->report_delay ? : 1;
    	ts->get_pendown_state = pdata->get_pendown_state;
    	ts->clear_penirq      = pdata->clear_penirq;
    	ts->fuzzx             = pdata->fuzzx;
    	ts->fuzzy             = pdata->fuzzy;
    	ts->fuzzz             = pdata->fuzzz;
    
    	if (pdata->x_plate_ohms == 0) {
    		dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static void tsc2007_call_exit_platform_hw(void *data)
    {
    	struct device *dev = data;
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
    
    	pdata->exit_platform_hw();
    }
    
    static int tsc2007_probe(struct i2c_client *client,
    			 const struct i2c_device_id *id)
    {
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
    	struct tsc2007 *ts;
    	struct input_dev *input_dev;
    	int err;
    
    	if (!i2c_check_functionality(client->adapter,
    				     I2C_FUNC_SMBUS_READ_WORD_DATA))
    		return -EIO;
    
    	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
    	if (!ts)
    		return -ENOMEM;
    
    	if (pdata)
    		err = tsc2007_probe_pdev(client, ts, pdata, id);
    	else
    		err = tsc2007_probe_dt(client, ts);
    	if (err)
    		return err;
    
    	input_dev = devm_input_allocate_device(&client->dev);
    	if (!input_dev)
    		return -ENOMEM;
    
    	i2c_set_clientdata(client, ts);
    
    	ts->client = client;
    	
    	/* map GPIO numbers to IRQ numbers [GPIO edge change triggers isr] */
    	client->irq = gpio_to_irq(ts->gpio);
    	ts->irq = client->irq;
    			
    	ts->input = input_dev;
    	init_waitqueue_head(&ts->wait);
    
    	snprintf(ts->phys, sizeof(ts->phys),
    		 "%s/input0", dev_name(&client->dev));
    
    	input_dev->name = "TSC2007 Touchscreen";
    	input_dev->phys = ts->phys;
    	input_dev->id.bustype = BUS_I2C;
    
    	input_dev->open = tsc2007_open;
    	input_dev->close = tsc2007_close;
    
    	input_set_drvdata(input_dev, ts);
    
    	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, ts->fuzzx, 0);
    	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
    	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
    			     ts->fuzzz, 0);
    
    	if (pdata) {
    		if (pdata->exit_platform_hw) {
    			err = devm_add_action(&client->dev,
    					      tsc2007_call_exit_platform_hw,
    					      &client->dev);
    			if (err) {
    				dev_err(&client->dev,
    					"Failed to register exit_platform_hw action, %d\n",
    					err);
    				return err;
    			}
    		}
    
    		if (pdata->init_platform_hw)
    			pdata->init_platform_hw();
    	}
    
    	err = devm_request_threaded_irq(&client->dev, ts->irq,
    					tsc2007_hard_irq, tsc2007_soft_irq,
    					IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    					client->dev.driver->name, ts);
    	if (err) {
    		dev_err(&client->dev, "Failed to request irq %d: %d\n",
    			ts->irq, err);
    		return err;
    	}
    
    	tsc2007_stop(ts);
    
    	err = input_register_device(input_dev);
    	if (err) {
    		dev_err(&client->dev,
    			"Failed to register input device: %d\n", err);
    		return err;
    	}
    
    	return 0;
    }
    
    static const struct i2c_device_id tsc2007_idtable[] = {
    	{ "tsc2007", 0 },
    	{ }
    };
    
    MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
    
    #ifdef CONFIG_OF
    static const struct of_device_id tsc2007_of_match[] = {
    	{ .compatible = "ti,tsc2007" },
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, tsc2007_of_match);
    #endif
    
    static struct i2c_driver tsc2007_driver = {
    	.driver = {
    		.owner	= THIS_MODULE,
    		.name	= "tsc2007",
    		.of_match_table = of_match_ptr(tsc2007_of_match),
    	},
    	.id_table	= tsc2007_idtable,
    	.probe		= tsc2007_probe,
    };
    
    module_i2c_driver(tsc2007_driver);
    
    MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
    MODULE_AUTHOR("Santhosh Ramani <sr@aprilaire.com>");
    MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
    MODULE_LICENSE("GPL");
    

  • Minor update to the tsc2007.c file


    New File -

    /*
     * drivers/input/touchscreen/tsc2007.c
     *
     * Copyright (c) 2014 Aprilaire (Research Products Corporation)
     *	Santhosh Ramani <sr@aprilaire.com>
     * 
     * Modified the TSC2007 driver from:
     *	Copyright (c) 2008 MtekVision Co., Ltd.
     *	Kwangwoo Lee <kwlee@mtekvision.com>
     *
     * Using code from:
     *  - ads7846.c
     *	Copyright (c) 2005 David Brownell
     *	Copyright (c) 2006 Nokia Corporation
     *  - corgi_ts.c
     *	Copyright (C) 2004-2005 Richard Purdie
     *  - omap_ts.[hc], ads7846.h, ts_osk.c
     *	Copyright (C) 2002 MontaVista Software
     *	Copyright (C) 2004 Texas Instruments
     *	Copyright (C) 2005 Dirk Behme
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License version 2 as
     *  published by the Free Software Foundation.
     */
    
    
    /*
     * 	This code has been modified to pickup falling and rising edge of 
     *	the PENIRQ. Once the falling edge is detected, 
     *		Interrupt is disabled
     *		Touch is sampled
     *		And upon PENIRQ release (high) interrupt is re-enabled		
     *
     * 	Modified poll-period: to sample delay (because is delays sampling)
     *
     *	Added report-delay: this prevents reporting incorrect touch events
     *		(picked up during the pen-up event at the time of sampling)
     *
     *	Added initial-delay: adds some delay before the first sample
     *
     * Technically, polling period is now (report_delay + sample_delay)
     */
    
    
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/input.h>
    #include <linux/interrupt.h>
    #include <linux/i2c.h>
    #include <linux/i2c/tsc2007.h>
    #include <linux/of_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/delay.h>
    
    #define TSC2007_MEASURE_TEMP0		(0x0 << 4)
    #define TSC2007_MEASURE_AUX			(0x2 << 4)
    #define TSC2007_MEASURE_TEMP1		(0x4 << 4)
    #define TSC2007_ACTIVATE_XN			(0x8 << 4)
    #define TSC2007_ACTIVATE_YN			(0x9 << 4)
    #define TSC2007_ACTIVATE_YP_XN		(0xa << 4)
    #define TSC2007_SETUP				(0xb << 4)
    #define TSC2007_MEASURE_X			(0xc << 4)
    #define TSC2007_MEASURE_Y			(0xd << 4)
    #define TSC2007_MEASURE_Z1			(0xe << 4)
    #define TSC2007_MEASURE_Z2			(0xf << 4)
    
    #define TSC2007_POWER_OFF_IRQ_EN	(0x0 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS0		(0x1 << 2)
    #define TSC2007_ADC_OFF_IRQ_EN		(0x2 << 2)
    #define TSC2007_ADC_ON_IRQ_DIS1		(0x3 << 2)
    
    #define TSC2007_12BIT				(0x0 << 1)
    #define TSC2007_8BIT				(0x1 << 1)
    
    #define	MAX_12BIT					((1 << 12) - 1)
    
    #define ADC_ON_12BIT	(TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
    
    #define READ_Y		(ADC_ON_12BIT | TSC2007_MEASURE_Y)				/* 1101 0100 */
    #define READ_Z1		(ADC_ON_12BIT | TSC2007_MEASURE_Z1)				/* 1110 0100 */
    #define READ_Z2		(ADC_ON_12BIT | TSC2007_MEASURE_Z2)				/* 1111 0100 */
    #define READ_X		(ADC_ON_12BIT | TSC2007_MEASURE_X)				/* 1100 0100 */
    #define PWRDOWN		(TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)		/* 0000 0000 */
    
    
    struct ts_event {
    	u16	x;
    	u16	y;
    	u16	z1, z2;
    };
    
    struct tsc2007 {
    	struct input_dev	*input;
    	char phys[32];
    
    	struct i2c_client	*client;
    
    	u16			model;
    	u16			x_plate_ohms;
    	u16			max_rt;
    	
    	unsigned long	sample_delay;
    	unsigned long	report_delay;
    	unsigned long	initial_delay;
    	
    	int			fuzzx;
    	int			fuzzy;
    	int			fuzzz;
    
    	unsigned	gpio;
    	int			irq;
    
    	wait_queue_head_t	wait;
    	bool			stopped;
    
    	int			(*get_pendown_state)(struct device *);
    	void		(*clear_penirq)(void);
    };
    
    static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
    {
    	s32 data;
    	u16 val;
    
    	data = i2c_smbus_read_word_data(tsc->client, cmd);
    	if (data < 0) {
    		dev_err(&tsc->client->dev, "i2c io error: %d\n\n\n\n", data);
    		return data;
    	}
    
    	/* The protocol and raw data format from i2c interface:
    	 * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
    	 * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
    	 */
    	val = swab16(data) >> 4;
    
    	dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
    
    	return val;
    }
    
    static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	/* y- still on; turn on only y+ (and ADC) */
    	tc->y = tsc2007_xfer(tsc, READ_Y);
    
    	/* turn y- off, x+ on, then leave in lowpower */
    	tc->x = tsc2007_xfer(tsc, READ_X);
    
    	/* turn y+ off, x- on; we'll use formula #1 */
    	tc->z1 = tsc2007_xfer(tsc, READ_Z1);
    	tc->z2 = tsc2007_xfer(tsc, READ_Z2);
    
    	/* Prepare for next touch reading - power down ADC, enable PENIRQ */
    	tsc2007_xfer(tsc, PWRDOWN);
    }
    
    static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
    {
    	u32 rt = 0;
    
    	/* range filtering */
    	if (tc->x == MAX_12BIT)
    		tc->x = 0;
    
    	if (likely(tc->x && tc->z1)) {
    		/* compute touch pressure resistance using equation #1 */
    		rt = tc->z2 - tc->z1;
    		rt *= tc->x;
    		rt *= tsc->x_plate_ohms;
    		rt /= tc->z1;
    		rt = (rt + 2047) >> 12;
    	}
    
    	return rt;
    }
    
    static bool tsc2007_is_pen_down(struct tsc2007 *ts)
    {
    	/*
    	 * NOTE: We can't rely on the pressure to determine the pen down
    	 * state, even though this controller has a pressure sensor.
    	 * The pressure value can fluctuate for quite a while after
    	 * lifting the pen and in some cases may not even settle at the
    	 * expected value.
    	 *
    	 * The only safe way to check for the pen up condition is in the
    	 * work function by reading the pen signal state (it's a GPIO
    	 * and IRQ). Unfortunately such callback is not always available,
    	 * in that case we assume that the pen is down and expect caller
    	 * to fall back on the pressure reading.
    	 */
    
    	if (!ts->get_pendown_state)
    		return true;
    
    	return ts->get_pendown_state(&ts->client->dev);
    }
    
    static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	struct input_dev *input = ts->input;
    	struct ts_event tc;
    	u32 rt;
    	
    	/*
    	 * With some panels we need to wait a bit otherwise the first value
     	 * is often wrong. (Allows the touch screen to settle)
    	 */
    	if (ts->initial_delay > 0)
    		msleep(ts->initial_delay);
    				
    	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
    
    		/* pen is down, continue with the measurement */
    		tsc2007_read_values(ts, &tc);
    
    		rt = tsc2007_calculate_pressure(ts, &tc);
    
    		if (!rt && !ts->get_pendown_state) {
    			/*
    			 * If pressure reported is 0 and we don't have
    			 * callback to check pendown state, we have to
    			 * assume that pen was lifted up.
    			 */
    			break;
    		}
    		
    		/*
    			Before reporting the new reading, check to make sure that
    			the pen is still down. This ensures that readings taken
    			during Pen-up event are ignored.
    		*/
    		wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->report_delay));
    		
    		if (ts->stopped || (!tsc2007_is_pen_down(ts)))
    			break;	
    		
    		if (rt <= ts->max_rt) {
    			dev_dbg(&ts->client->dev,
    				"DOWN point(%4d,%4d), pressure (%4u)\n",
    				tc.x, tc.y, rt);
    
    			input_report_key(input, BTN_TOUCH, 1);
    			input_report_abs(input, ABS_X, tc.x);
    			input_report_abs(input, ABS_Y, tc.y);
    			input_report_abs(input, ABS_PRESSURE, rt);
    
    			input_sync(input);
    
    		} else {
    			/*
    			 * Sample found inconsistent by debouncing or pressure is
    			 * beyond the maximum. Don't report it to user space,
    			 * repeat at least once more the measurement.
    			 */
    			dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
    		}
    
    		wait_event_timeout(ts->wait, ts->stopped, msecs_to_jiffies(ts->sample_delay));
    	}
    
    	dev_dbg(&ts->client->dev, "UP\n");
    	
    	input_report_key(input, BTN_TOUCH, 0);
    	input_report_abs(input, ABS_PRESSURE, 0);
    	input_sync(input);
    
    	if (ts->clear_penirq)
    		ts->clear_penirq();
    	
    	/* Now that the touch has been handled, re-enabled the PENIRQ */
    	enable_irq(ts->irq);	
    	return IRQ_HANDLED;
    }
    
    static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
    {
    	struct tsc2007 *ts = handle;
    	
    	if (tsc2007_is_pen_down(ts)) {
    		/* Once the IRQ handling starts, disable the interrupt */	
    		disable_irq_nosync(ts->irq);
    				
    		return IRQ_WAKE_THREAD;
    	}
    
    	if (ts->clear_penirq) {
    		ts->clear_penirq();
    	}
    	
    	return IRQ_HANDLED;
    }
    
    static void tsc2007_stop(struct tsc2007 *ts)
    {
    	ts->stopped = true;
    	mb();
    	wake_up(&ts->wait);
    
    	disable_irq(ts->irq);
    }
    
    static int tsc2007_open(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    	int err;
    	
    	ts->stopped = false;
    	mb();
    
    	enable_irq(ts->irq);
    	
    	/* Prepare for touch readings - power down ADC and enable PENIRQ */
    	err = tsc2007_xfer(ts, PWRDOWN);
    	if (err < 0) {
    		tsc2007_stop(ts);	
    		return err;
    	}
    		
    	return 0;
    }
    
    static void tsc2007_close(struct input_dev *input_dev)
    {
    	struct tsc2007 *ts = input_get_drvdata(input_dev);
    
    	tsc2007_stop(ts);
    }
    
    #ifdef CONFIG_OF
    static int tsc2007_get_pendown_state_gpio(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct tsc2007 *ts = i2c_get_clientdata(client);
    
    	return !gpio_get_value(ts->gpio);
    }
    
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	struct device_node *np = client->dev.of_node;
    	u32 val32;
    	u64 val64;
    
    	if (!np) {
    		dev_err(&client->dev, "missing device tree data\n");
    		return -EINVAL;
    	}
    
    	if (!of_property_read_u32(np, "ti,max-rt", &val32))
    		ts->max_rt = val32;
    	else
    		ts->max_rt = MAX_12BIT;
    
    	if (!of_property_read_u32(np, "ti,fuzzx", &val32))
    		ts->fuzzx = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzy", &val32))
    		ts->fuzzy = val32;
    
    	if (!of_property_read_u32(np, "ti,fuzzz", &val32))
    		ts->fuzzz = val32;
    
    	if (!of_property_read_u64(np, "ti,sample-delay", &val64))
    		ts->sample_delay = val64;
    	else
    		ts->sample_delay = 1;
    		
    	if (!of_property_read_u64(np, "ti,report-delay", &val64))
    		ts->report_delay = val64;
    	else
    		ts->report_delay = 1;
    
    	if (!of_property_read_u64(np, "ti,initial-delay", &val64))
    		ts->initial_delay = val64;
    	else
    		ts->initial_delay = 1;
    		
    	if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
    		ts->x_plate_ohms = val32;
    	} else {
    		dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
    		return -EINVAL;
    	}
    
    	ts->gpio = of_get_gpio(np, 0);
    	if (gpio_is_valid(ts->gpio)) {
    		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
    		
    		/* If GPIO is valid, then request the GPIO for use */
    		if (gpio_request(ts->gpio, "nPENIRQ")) {
    			dev_err(&client->dev, "Requesting GPIO failed - %s", __func__);
    			return 0;
    		}
    		
    		/* Set the direction to be input */		
    		if (gpio_direction_input(ts->gpio)) {
    			gpio_free(ts->gpio);
    			return 0;
    		}		
    	}
    	else
    		dev_warn(&client->dev,
    			 "GPIO not specified in DT (of_get_gpio returned %d)\n",
    			 ts->gpio);
    
    	return 0;
    }
    #else
    static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
    {
    	dev_err(&client->dev, "platform data is required!\n");
    	return -EINVAL;
    }
    #endif
    
    static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
    			      const struct tsc2007_platform_data *pdata,
    			      const struct i2c_device_id *id)
    {
    	ts->model             = pdata->model;
    	ts->x_plate_ohms      = pdata->x_plate_ohms;
    	ts->max_rt            = pdata->max_rt ? : MAX_12BIT;
    	ts->sample_delay      = pdata->sample_delay ? : 1;
    	ts->report_delay      = pdata->report_delay ? : 1;
    	ts->initial_delay	  = pdata->initial_delay;
    	ts->get_pendown_state = pdata->get_pendown_state;
    	ts->clear_penirq      = pdata->clear_penirq;
    	ts->fuzzx             = pdata->fuzzx;
    	ts->fuzzy             = pdata->fuzzy;
    	ts->fuzzz             = pdata->fuzzz;
    
    	if (pdata->x_plate_ohms == 0) {
    		dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static void tsc2007_call_exit_platform_hw(void *data)
    {
    	struct device *dev = data;
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(dev);
    
    	pdata->exit_platform_hw();
    }
    
    static int tsc2007_probe(struct i2c_client *client,
    			 const struct i2c_device_id *id)
    {
    	const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
    	struct tsc2007 *ts;
    	struct input_dev *input_dev;
    	int err;
    
    	if (!i2c_check_functionality(client->adapter,
    				     I2C_FUNC_SMBUS_READ_WORD_DATA))
    		return -EIO;
    
    	ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
    	if (!ts)
    		return -ENOMEM;
    
    	if (pdata)
    		err = tsc2007_probe_pdev(client, ts, pdata, id);
    	else
    		err = tsc2007_probe_dt(client, ts);
    	if (err)
    		return err;
    
    	input_dev = devm_input_allocate_device(&client->dev);
    	if (!input_dev)
    		return -ENOMEM;
    
    	i2c_set_clientdata(client, ts);
    
    	ts->client = client;
    	
    	/* map GPIO numbers to IRQ numbers [GPIO edge change triggers isr] */
    	client->irq = gpio_to_irq(ts->gpio);
    	ts->irq = client->irq;
    			
    	ts->input = input_dev;
    	init_waitqueue_head(&ts->wait);
    
    	snprintf(ts->phys, sizeof(ts->phys),
    		 "%s/input0", dev_name(&client->dev));
    
    	input_dev->name = "TSC2007 Touchscreen";
    	input_dev->phys = ts->phys;
    	input_dev->id.bustype = BUS_I2C;
    
    	input_dev->open = tsc2007_open;
    	input_dev->close = tsc2007_close;
    
    	input_set_drvdata(input_dev, ts);
    
    	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, ts->fuzzx, 0);
    	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
    	input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
    			     ts->fuzzz, 0);
    
    	if (pdata) {
    		if (pdata->exit_platform_hw) {
    			err = devm_add_action(&client->dev,
    					      tsc2007_call_exit_platform_hw,
    					      &client->dev);
    			if (err) {
    				dev_err(&client->dev,
    					"Failed to register exit_platform_hw action, %d\n",
    					err);
    				return err;
    			}
    		}
    
    		if (pdata->init_platform_hw)
    			pdata->init_platform_hw();
    	}
    
    	err = devm_request_threaded_irq(&client->dev, ts->irq,
    					tsc2007_hard_irq, tsc2007_soft_irq,
    					IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
    					client->dev.driver->name, ts);
    	if (err) {
    		dev_err(&client->dev, "Failed to request irq %d: %d\n",
    			ts->irq, err);
    		return err;
    	}
    
    	tsc2007_stop(ts);
    
    	err = input_register_device(input_dev);
    	if (err) {
    		dev_err(&client->dev,
    			"Failed to register input device: %d\n", err);
    		return err;
    	}
    
    	return 0;
    }
    
    static const struct i2c_device_id tsc2007_idtable[] = {
    	{ "tsc2007", 0 },
    	{ }
    };
    
    MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
    
    #ifdef CONFIG_OF
    static const struct of_device_id tsc2007_of_match[] = {
    	{ .compatible = "ti,tsc2007" },
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, tsc2007_of_match);
    #endif
    
    static struct i2c_driver tsc2007_driver = {
    	.driver = {
    		.owner	= THIS_MODULE,
    		.name	= "tsc2007",
    		.of_match_table = of_match_ptr(tsc2007_of_match),
    	},
    	.id_table	= tsc2007_idtable,
    	.probe		= tsc2007_probe,
    };
    
    module_i2c_driver(tsc2007_driver);
    
    MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
    MODULE_AUTHOR("Santhosh Ramani <sr@aprilaire.com>");
    MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
    MODULE_LICENSE("GPL");