From: hhb <hhb@rock-chips.com>
Date: Wed, 23 Mar 2011 08:12:49 +0000 (+0800)
Subject: add gt801 touch screen driver
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=9013c7ddde2dd09707d5dc90c6b731ee411c1973;p=firefly-linux-kernel-4.4.55.git

add gt801 touch screen driver
---

diff --git a/arch/arm/mach-rk29/board-rk29-phonesdk.c b/arch/arm/mach-rk29/board-rk29-phonesdk.c
index 3cb3453a6ea1..3e4a2b858546 100755
--- a/arch/arm/mach-rk29/board-rk29-phonesdk.c
+++ b/arch/arm/mach-rk29/board-rk29-phonesdk.c
@@ -357,6 +357,28 @@ struct p1003_platform_data p1003_info = {
 
 };
 #endif
+
+#if defined(CONFIG_TOUCHSCREEN_GT801_IIC) 
+#include "../../../drivers/input/touchscreen/gt801_ts.h"
+#define GT801_GPIO_INT      RK29_PIN4_PD5
+#define GT801_GPIO_RESET    RK29_PIN6_PC3
+static struct gt801_platform_data gt801_info = {
+	.model			= 801,
+	.swap_xy		= 0,
+	.x_min			= 0,
+	.x_max			= 480,
+	.y_min			= 0,
+	.y_max			= 800,
+	.gpio_reset     = GT801_GPIO_RESET,
+	.gpio_reset_active_low = 1,
+	.gpio_pendown		= GT801_GPIO_INT,
+    .pendown_iomux_name = GPIO4D5_CPUTRACECTL_NAME,
+    .resetpin_iomux_name = NULL,
+    .pendown_iomux_mode = GPIO4H_GPIO4D5,
+    .resetpin_iomux_mode = 0,
+};
+#endif
+
 #if defined (CONFIG_EETI_EGALAX)
 #define TOUCH_RESET_PIN RK29_PIN6_PC3
 #define TOUCH_INT_PIN   RK29_PIN4_PD5
@@ -1403,6 +1425,15 @@ static struct i2c_board_info __initdata board_i2c1_devices[] = {
 
 #ifdef CONFIG_I2C2_RK29
 static struct i2c_board_info __initdata board_i2c2_devices[] = {
+#if defined (CONFIG_TOUCHSCREEN_GT801_IIC)
+{
+	.type           = "gt801_ts",
+	.addr           = 0x55,
+	.flags          = 0,
+	.irq            = RK29_PIN4_PD5,
+	.platform_data = &gt801_info,
+},	
+#endif
 #if defined (CONFIG_MFD_WM831X_I2C)
 {
 	.type           = "wm8310",
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 0e9e0bbdfee2..425f28ba19dc 100755
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -742,4 +742,7 @@ config TOUCHSCREEN_IT7260
 
 	  If unsure, say N (but it's safe to say "Y").
 
+config TOUCHSCREEN_GT801_IIC
+	tristate "GT801_IIC based touchscreens"
+	depends on I2C2_RK29
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index e2288952af1d..71076518e8e9 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_TOUCHSCREEN_IT7260)		+= it7260_ts.o
 obj-$(CONFIG_SINTEK_3FA16)	+=  sintek_3FA16.o
 obj-$(CONFIG_EETI_EGALAX)		+= eeti_egalax_i2c.o
 obj-$(CONFIG_ATMEL_MXT224)               += atmel_mxt224.o
+obj-$(CONFIG_TOUCHSCREEN_GT801_IIC)      += gt801_ts.o
diff --git a/drivers/input/touchscreen/gt801_ts.c b/drivers/input/touchscreen/gt801_ts.c
new file mode 100755
index 000000000000..d7a46b25fc71
--- /dev/null
+++ b/drivers/input/touchscreen/gt801_ts.c
@@ -0,0 +1,644 @@
+/*
+ * drivers/input/touchscreen/gt801_ts.c
+ *
+ * Copyright (C) 2010 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <mach/iomux.h>
+#include <linux/platform_device.h>
+
+#include "gt801_ts.h"
+
+#define GT801_DEBUG			0
+#if GT801_DEBUG
+	#define gt801printk(msg...)	printk(msg);
+#else
+	#define gt801printk(msg...)
+#endif
+
+#define SINGLTOUCH_MODE 0 
+#define GT801_REGS_NUM 53
+
+#if SINGLTOUCH_MODE
+	#define TOUCH_NUMBER 1
+#else
+    #define TOUCH_NUMBER 2
+#endif
+
+#define TOUCH_REG_NUM 5 //ÿ×é×ø±êÐèÒªµÄ¼Ä´æÆ÷ÊýÄ¿
+
+const unsigned char GT801_RegData[GT801_REGS_NUM]={	
+	0x19,0x05,0x06,0x28,0x02,0x14,0x14,0x10,0x40,0xB0,0x01,0xE0,0x03,0x4C,0x78,0x9A,0xBC,0xDE,0x65,0x43,0x20,0x11,0x00,0x00,0x00,0x00,0x05,0xCF,0x20,0x0B,0x0D,0x8D,0x32,0x3C,0x1E,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
+};
+
+struct gt801_ts_data {
+	u16		model;			/* 801. */	
+	bool	swap_xy;		/* swap x and y axes */	
+	u16		x_min, x_max;	
+	u16		y_min, y_max;
+    uint16_t addr;
+    int 	use_irq;
+	int 	gpio_pendown;
+	int 	gpio_reset;
+	int 	gpio_reset_active_low;
+	int		pendown_iomux_mode;	
+	int		resetpin_iomux_mode;
+	char	pendown_iomux_name[IOMUX_NAME_SIZE];	
+	char	resetpin_iomux_name[IOMUX_NAME_SIZE];	
+	char	phys[32];
+	char	name[32];
+	struct 	i2c_client *client;
+    struct 	input_dev *input_dev;
+    struct 	hrtimer timer;
+    struct 	work_struct  work;
+    struct 	early_suspend early_suspend;
+};
+/*tochscreen private data*/
+static int touch_state[TOUCH_NUMBER] = {TOUCH_UP,TOUCH_UP};
+static struct workqueue_struct *gt801_wq;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void gt801_ts_early_suspend(struct early_suspend *h);
+static void gt801_ts_late_resume(struct early_suspend *h);
+#endif
+
+static int verify_coord(struct gt801_ts_data *ts,unsigned short *x,unsigned short *y)
+{
+
+	gt801printk("%s:(%d/%d)\n",__FUNCTION__,*x, *y);
+	if((*x< ts->x_min) || (*x > ts->x_max))
+		return -1;
+
+	if((*y< ts->y_min) || (*y > ts->y_max))
+		return -1;
+//	*x = ts->x_max - *x;
+	//if(*y <780)
+	*y = ts->y_max -*y;
+
+	return 0;
+}
+
+/*read the gt801 register ,used i2c bus*/
+static int gt801_read_regs(struct i2c_client *client, u8 reg, u8 buf[], unsigned len)
+{
+	int ret;
+	ret =i2c_master_reg8_recv(client, reg, buf, len, 200*1000);
+	if(ret < 0)
+		printk("gt801_ts_work_func:i2c_transfer fail =%d\n",ret);
+	return ret;
+}
+/* set the gt801 registe,used i2c bus*/
+static int gt801_write_regs(struct i2c_client *client, u8 reg, u8 const buf[], unsigned short len)
+{
+	int ret;
+	ret = i2c_master_reg8_send(client,reg, buf, len, 200*1000);
+ 	if (ret < 0) {
+	  printk("gt801_ts_work_func:i2c_transfer fail =%d\n",ret);
+    }
+	return ret;
+}
+static int gt801_init_panel(struct gt801_ts_data *ts)
+{
+    return 0;
+}
+
+static void gt801_ts_work_func(struct work_struct *work)
+{
+#if  SINGLTOUCH_MODE
+  
+#else
+	int  touch_state_index = 0;
+#endif
+
+	unsigned char start_reg = 0x02;
+    unsigned char buf[TOUCH_NUMBER*TOUCH_REG_NUM];
+	unsigned short x;
+	unsigned short y;
+    int i,ret;
+	int syn_flag = 0;
+	int bufLen = TOUCH_NUMBER*TOUCH_REG_NUM;
+
+    struct gt801_ts_data *ts = container_of(work, struct gt801_ts_data, work);
+	
+	gt801printk("%s\n",__FUNCTION__);
+    
+	ret=gt801_read_regs(ts->client, start_reg, buf,bufLen);
+	if (ret < 0) {
+	  	printk("%s:i2c_transfer fail =%d\n",__FUNCTION__,ret);
+		if (ts->use_irq) 
+   	  		enable_irq(ts->client->irq);
+		
+		return;
+    }
+	       
+#if  SINGLTOUCH_MODE 
+	i = 0;
+	if(buf[i+ptpressure] == 0)
+	{
+		gt801printk(" realse ts_dev->point.x=%d ,ts_dev->point.y=%d \n",ts->point.x,ts->point.y);
+
+		if (touch_state[i] == TOUCH_DOWN)
+		{
+			input_report_key(ts->input_dev,BTN_TOUCH,0);
+			syn_flag = 1;
+			touch_state[i] = TOUCH_UP;
+			gt801printk("SINGLTOUCH_MODE up\n");
+		}
+	}
+	else
+	{
+		x = ((( ((unsigned short)buf[i+ptxh] )<< 8) ) | buf[i+ptxl]);
+		y= (((((unsigned short)buf[i+ptyh] )<< 8) )| buf[i+ptyl]);	
+		
+		if (ts->swap_xy)
+			swap(x, y);
+		
+		if (verify_coord(ts,&x,&y))
+			goto out;
+		
+		if (touch_state[i] == TOUCH_UP)
+		{
+			gt801printk("SINGLTOUCH_MODE down\n");
+			input_report_key(ts->input_dev,BTN_TOUCH,1);
+			touch_state[i] = TOUCH_DOWN;
+		}
+		
+		gt801printk("input_report_abs(%d/%d)\n",x,y);
+		input_report_abs(ts->input_dev,ABS_X,x );
+		input_report_abs(ts->input_dev,ABS_Y,y );
+		syn_flag = 1;
+	}
+
+#else   
+
+    for(i=0; i<bufLen; i+=TOUCH_REG_NUM)
+	{
+		if(buf[i+ptpressure] == 0){
+			gt801printk("%s:buf=%d touch up\n",__FUNCTION__,buf[i+ptpressure]);
+			if (touch_state[touch_state_index] == TOUCH_DOWN)
+			{
+				gt801printk("%s:%d touch up\n",__FUNCTION__,i);
+				input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); //Finger Size
+				input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); //Touch Size
+				input_mt_sync(ts->input_dev);
+				syn_flag =1;
+				touch_state[touch_state_index] = TOUCH_UP;
+			}	  
+		}
+		else{
+			x = ((( ((unsigned short)buf[i+ptxh] )<< 8) ) | buf[i+ptxl]);
+			y= (((((unsigned short)buf[i+ptyh] )<< 8) )| buf[i+ptyl]);
+			
+			if (ts->swap_xy)
+				swap(x, y);
+			
+			if (verify_coord(ts,&x,&y))
+				;//goto out;	
+			
+			gt801printk("input_report_abs--%d-%d-(%d/%d)\n", i,touch_state_index, x, y);	
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 1); //Finger Size
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
+			input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 5); //Touch Size
+			input_mt_sync(ts->input_dev);
+			syn_flag = 1;
+			touch_state[touch_state_index] = TOUCH_DOWN;
+		}
+		
+		touch_state_index++;
+    }
+	
+#endif
+
+	if(syn_flag)
+		input_sync(ts->input_dev);
+out:
+   	if (ts->use_irq) 
+		enable_irq(ts->client->irq);
+   
+	return;
+}
+static enum hrtimer_restart gt801_ts_timer_func(struct hrtimer *timer)
+{
+    struct gt801_ts_data *ts = container_of(timer, struct gt801_ts_data, timer);
+    gt801printk("%s\n",__FUNCTION__); 
+
+    queue_work(gt801_wq, &ts->work);
+
+    hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
+    return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gt801_ts_irq_handler(int irq, void *dev_id)
+{
+    struct gt801_ts_data *ts = dev_id;
+	gt801printk("%s=%d,%d\n",__FUNCTION__,ts->client->irq,ts->use_irq);
+	
+	//if (ts->use_irq)
+    	disable_irq_nosync(ts->client->irq);
+    queue_work(gt801_wq, &ts->work);
+    return IRQ_HANDLED;
+}
+static int __devinit setup_resetPin(struct i2c_client *client, struct gt801_ts_data *ts)
+{
+	struct gt801_platform_data	*pdata = client->dev.platform_data;
+	int err;
+	
+	ts->gpio_reset = pdata->gpio_reset;
+    ts->gpio_reset_active_low = pdata->gpio_reset_active_low;
+    ts->resetpin_iomux_mode = pdata->resetpin_iomux_mode;
+
+    if(pdata->resetpin_iomux_name != NULL)
+	    strcpy(ts->resetpin_iomux_name,pdata->resetpin_iomux_name);
+		 
+	gt801printk("%s=%d,%s,%d,%d\n",__FUNCTION__,ts->gpio_reset,ts->resetpin_iomux_name,ts->resetpin_iomux_mode,ts->gpio_reset_active_low);
+	if (!gpio_is_valid(ts->gpio_reset)) {
+		dev_err(&client->dev, "no gpio_reset?\n");
+		return -EINVAL;
+	}
+
+    rk29_mux_api_set(ts->resetpin_iomux_name,ts->resetpin_iomux_mode); 
+
+	err = gpio_request(ts->gpio_reset, "gt801_resetPin");
+	if (err) {
+		dev_err(&client->dev, "failed to request resetPin GPIO%d\n",
+				ts->gpio_reset);
+		return err;
+	}
+	
+	gpio_set_value(ts->gpio_reset, ts->gpio_reset_active_low? GPIO_LOW:GPIO_HIGH);
+
+	err = gpio_direction_output(ts->gpio_reset, ts->gpio_reset_active_low? GPIO_LOW:GPIO_HIGH);
+	if (err) {
+		dev_err(&client->dev, "failed to pulldown resetPin GPIO%d,err%d\n",
+				ts->gpio_reset,err);
+		gpio_free(ts->gpio_reset);
+		return err;
+	}
+	
+    mdelay(100);
+
+	gpio_set_value(ts->gpio_reset, ts->gpio_reset_active_low? GPIO_HIGH:GPIO_LOW);
+
+	mdelay(100);
+	 
+	return 0;
+}
+static int __devinit setup_pendown(struct i2c_client *client, struct gt801_ts_data *ts)
+{
+	int err;
+	struct gt801_platform_data	*pdata = client->dev.platform_data;
+	
+	if (!client->irq) {
+		dev_dbg(&client->dev, "no IRQ?\n");
+		return -ENODEV;
+	}
+	
+	if (!gpio_is_valid(pdata->gpio_pendown)) {
+		dev_err(&client->dev, "no gpio_pendown?\n");
+		return -EINVAL;
+	}
+	
+	ts->gpio_pendown = pdata->gpio_pendown;
+	strcpy(ts->pendown_iomux_name,pdata->pendown_iomux_name);
+	ts->pendown_iomux_mode = pdata->pendown_iomux_mode;
+	
+	gt801printk("%s=%d,%s,%d\n",__FUNCTION__,ts->gpio_pendown,ts->pendown_iomux_name,ts->pendown_iomux_mode);
+	
+	if (!gpio_is_valid(ts->gpio_pendown)) {
+		dev_err(&client->dev, "no gpio_pendown?\n");
+		return -EINVAL;
+	}
+	
+    rk29_mux_api_set(ts->pendown_iomux_name,ts->pendown_iomux_mode); 
+	err = gpio_request(ts->gpio_pendown, "gt801_pendown");
+	if (err) {
+		dev_err(&client->dev, "failed to request pendown GPIO%d\n",
+				ts->gpio_pendown);
+		return err;
+	}
+	
+	err = gpio_pull_updown(ts->gpio_pendown, GPIOPullUp);
+	if (err) {
+		dev_err(&client->dev, "failed to pullup pendown GPIO%d\n",
+				ts->gpio_pendown);
+		gpio_free(ts->gpio_pendown);
+		return err;
+	}
+	return 0;
+}
+static int gt801_chip_Init(struct i2c_client *client)
+{
+	u8 i,j;
+	int ret=0;
+	u8 start_reg=0x30;
+	u8 buf[GT801_REGS_NUM];
+	
+	gt801printk("enter gt801_chip_Init!!!!\n");
+
+	for(j=0;j<2;j++)
+	{
+		ret=gt801_write_regs(client,start_reg, GT801_RegData,GT801_REGS_NUM);	
+		if(ret<0)
+		{
+			printk("\n--%s--Set Register values error !!!\n",__FUNCTION__);
+		}
+		
+		ret=gt801_read_regs(client, start_reg, buf,GT801_REGS_NUM);
+		if(ret<0)
+		{
+			printk("\n--%s--Read Register values error !!!\n",__FUNCTION__);
+		}
+			
+	 	for(i=0;i<GT801_REGS_NUM-1;i++)
+		{
+			if(buf[i]!=GT801_RegData[i])
+			{
+				printk("!!!!!!!!gt801_chip_Init err may be i2c errorat adress=%x var=%x i=%x\n",0x30+i, buf[i],i);
+				break;
+			}
+		}
+		if(i==GT801_REGS_NUM-1)
+			break;
+		else if(j==1)
+			return -1;
+		
+		mdelay(500);
+	}
+	mdelay(100);
+	
+	return ret;
+}
+static int gt801_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+    struct gt801_ts_data *ts;
+	struct gt801_platform_data	*pdata = client->dev.platform_data;
+    int ret = 0;
+
+    gt801printk("%s \n",__FUNCTION__);
+	
+    if (!pdata) {
+		dev_err(&client->dev, "empty platform_data\n");
+		goto err_check_functionality_failed;
+    }
+    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+        printk(KERN_ERR "gt801_ts_probe: need I2C_FUNC_I2C\n");
+        ret = -ENODEV;
+        goto err_check_functionality_failed;
+    }
+	
+    ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+    if (ts == NULL) {
+        ret = -ENOMEM;
+        goto err_alloc_data_failed;
+    }
+    INIT_WORK(&ts->work, gt801_ts_work_func);
+    ts->client = client;
+    i2c_set_clientdata(client, ts);
+
+	ret = setup_resetPin(client,ts);
+	if(ret)
+	{
+		 printk("%s:setup_resetPin fail\n",__FUNCTION__);
+		 goto err_input_dev_alloc_failed;
+	}
+	
+	ret=gt801_chip_Init(ts->client);
+	if(ret<0)
+	{
+		printk("%s:chips init failed\n",__FUNCTION__);
+		goto err_resetpin_failed;
+	}
+	
+    /* allocate input device */
+    ts->input_dev = input_allocate_device();
+    if (ts->input_dev == NULL) {
+        ret = -ENOMEM;
+        printk(KERN_ERR "%s: Failed to allocate input device\n",__FUNCTION__);
+        goto err_input_dev_alloc_failed;
+    }
+	
+	ts->model = pdata->model ? : 801;
+	ts->swap_xy = pdata->swap_xy;
+	ts->x_min = pdata->x_min;
+	ts->x_max = pdata->x_max;
+	ts->y_min = pdata->y_min;
+	ts->y_max = pdata->y_max;
+	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev));
+	snprintf(ts->name, sizeof(ts->name), "gt%d-touchscreen", ts->model);
+	ts->input_dev->phys = ts->phys;
+	ts->input_dev->name = ts->name;
+	ts->input_dev->dev.parent = &client->dev;
+
+#if SINGLTOUCH_MODE
+	ts->input_dev->evbit[0] = BIT_MASK(EV_ABS)|BIT_MASK(EV_KEY);
+	ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(ts->input_dev,ABS_X,
+		    ts->x_min ? : 0,
+			ts->x_max ? : 480,
+			0, 0);
+	input_set_abs_params(ts->input_dev,ABS_Y,
+			ts->y_min ? : 0,
+			ts->y_max ? : 800,
+			0, 0);
+
+#else
+    ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_ABS);
+  //  ts->input_dev->absbit[0] = 
+	//	BIT(ABS_MT_POSITION_X) | BIT(ABS_MT_POSITION_Y) | 
+	//	BIT(ABS_MT_TOUCH_MAJOR) | BIT(ABS_MT_WIDTH_MAJOR);  // for android
+    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 
+		    ts->x_min ? : 0,
+			ts->x_max ? : 480,
+			0, 0);
+    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+			ts->y_min ? : 0,
+			ts->y_max ? : 800,
+			0, 0);
+    input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0); //Finger Size
+    input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 10, 0, 0); //Touch Size
+#endif
+    ret = input_register_device(ts->input_dev);
+    if (ret) {
+        printk(KERN_ERR "%s: Unable to register %s input device\n", __FUNCTION__,ts->input_dev->name);
+        goto err_input_register_device_failed;
+    }
+	
+	client->irq = gpio_to_irq(client->irq);
+    if (client->irq) {
+		ret = setup_pendown(client,ts);
+		if(ret)
+		{
+			 printk("%s:setup_pendown fail\n",__FUNCTION__);
+			 goto err_input_register_device_failed;
+		}
+		
+        ret = request_irq(client->irq, gt801_ts_irq_handler, IRQF_DISABLED | IRQF_TRIGGER_LOW, client->name, ts);
+        if (ret == 0) {
+            gt801printk("%s:register ISR (irq=%d)\n", __FUNCTION__,client->irq);
+            ts->use_irq = 1;
+        }
+        else 
+			dev_err(&client->dev, "request_irq failed\n");
+    }
+
+    if (!ts->use_irq) {
+        hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+        ts->timer.function = gt801_ts_timer_func;
+        hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+    }
+#ifdef CONFIG_HAS_EARLYSUSPEND
+    ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+    ts->early_suspend.suspend = gt801_ts_early_suspend;
+    ts->early_suspend.resume = gt801_ts_late_resume;
+    register_early_suspend(&ts->early_suspend);
+#endif
+
+    printk(KERN_INFO "%s: Start touchscreen %s in %s mode\n", __FUNCTION__,ts->input_dev->name, ts->use_irq ? "interrupt" : "polling");
+
+    return 0;
+
+err_input_register_device_failed:
+    input_free_device(ts->input_dev);
+err_resetpin_failed:
+	gpio_free(ts->gpio_reset);
+err_input_dev_alloc_failed:
+	kfree(ts);
+err_alloc_data_failed:
+err_check_functionality_failed:
+	
+    return ret;
+}
+
+static int gt801_ts_remove(struct i2c_client *client)
+{
+    struct gt801_ts_data *ts = i2c_get_clientdata(client);
+    unregister_early_suspend(&ts->early_suspend);
+    if (ts->use_irq)
+        free_irq(client->irq, ts);
+    else
+        hrtimer_cancel(&ts->timer);
+    input_unregister_device(ts->input_dev);
+	gpio_free(ts->gpio_pendown);
+	gpio_free(ts->gpio_reset);
+    kfree(ts);
+    return 0;
+}
+
+static int gt801_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+    int ret;
+    struct gt801_ts_data *ts = i2c_get_clientdata(client);
+
+    printk("gt801 TS Suspend\n");
+    
+    if (ts->use_irq)
+        disable_irq(client->irq);
+    else
+        hrtimer_cancel(&ts->timer);
+
+    ret = cancel_work_sync(&ts->work);
+    if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */
+        enable_irq(client->irq);
+
+	gpio_set_value(ts->gpio_reset, ts->gpio_reset_active_low? GPIO_LOW:GPIO_HIGH);
+	
+    return 0;
+}
+
+static int gt801_ts_resume(struct i2c_client *client)
+{
+    struct gt801_ts_data *ts = i2c_get_clientdata(client);
+
+    gt801_init_panel(ts);
+    
+    printk("gt801 TS Resume\n");
+	
+    gpio_set_value(ts->gpio_reset, ts->gpio_reset_active_low? GPIO_HIGH:GPIO_LOW);
+	
+    if (ts->use_irq) {
+        printk("enabling IRQ %d\n", client->irq);
+        enable_irq(client->irq);
+    }
+    else
+        hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+
+    return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void gt801_ts_early_suspend(struct early_suspend *h)
+{
+    struct gt801_ts_data *ts;
+    ts = container_of(h, struct gt801_ts_data, early_suspend);
+    gt801_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void gt801_ts_late_resume(struct early_suspend *h)
+{
+    struct gt801_ts_data *ts;
+    ts = container_of(h, struct gt801_ts_data, early_suspend);
+    gt801_ts_resume(ts->client);
+}
+#endif
+
+#define gt801_TS_NAME "gt801_ts"
+
+static const struct i2c_device_id gt801_ts_id[] = {
+    { gt801_TS_NAME, 0 },
+    { }
+};
+
+static struct i2c_driver gt801_ts_driver = {
+    .probe      = gt801_ts_probe,
+    .remove     = gt801_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+    .suspend    = gt801_ts_suspend,
+    .resume     = gt801_ts_resume,
+#endif
+    .id_table   = gt801_ts_id,
+    .driver = {
+        .name   = gt801_TS_NAME,
+    },
+};
+
+static int __devinit gt801_ts_init(void)
+{
+    printk("%s\n",__FUNCTION__);
+    gt801_wq = create_singlethread_workqueue("gt801_wq");
+    if (!gt801_wq)
+        return -ENOMEM;
+    return i2c_add_driver(&gt801_ts_driver);
+}
+
+static void __exit gt801_ts_exit(void)
+{
+    printk("%s\n",__FUNCTION__);
+    i2c_del_driver(&gt801_ts_driver);
+    if (gt801_wq)
+        destroy_workqueue(gt801_wq);
+}
+
+module_init(gt801_ts_init);
+module_exit(gt801_ts_exit);
+
+MODULE_DESCRIPTION("gt801 Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/gt801_ts.h b/drivers/input/touchscreen/gt801_ts.h
new file mode 100755
index 000000000000..e41f26ddd32c
--- /dev/null
+++ b/drivers/input/touchscreen/gt801_ts.h
@@ -0,0 +1,45 @@
+/*
+ * drivers/input/touchscreen/gt801_ts.h
+ *
+ * Copyright (C) 2010 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DRIVERS_TOUCHSCREEN_GT801_TS_H
+#define __DRIVERS_TOUCHSCREEN_GT801_TS_H
+
+#define IOMUX_NAME_SIZE 48
+
+enum regadd {
+	ptxh = 0, ptxl = 1, ptyh = 2, ptyl = 3, ptpressure = 4,
+};
+enum touchstate {
+	TOUCH_UP = 0, TOUCH_DOWN = 1,
+};	
+
+struct gt801_platform_data {
+
+	u16		model;			/* 801. */
+	bool	swap_xy;		/* swap x and y axes */
+	u16		x_min, x_max;
+	u16		y_min, y_max;
+    int 	gpio_reset;
+    int     gpio_reset_active_low;
+	int		gpio_pendown;		/* the GPIO used to decide the pendown */
+
+	char	pendown_iomux_name[IOMUX_NAME_SIZE];
+	char	resetpin_iomux_name[IOMUX_NAME_SIZE];
+	int		pendown_iomux_mode;
+	int		resetpin_iomux_mode;
+	
+	int	    (*get_pendown_state)(void);
+};
+#endif