summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/focaltech/focaltech.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/focaltech/focaltech.c')
-rw-r--r--drivers/input/touchscreen/focaltech/focaltech.c1053
1 files changed, 1053 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/focaltech/focaltech.c b/drivers/input/touchscreen/focaltech/focaltech.c
new file mode 100644
index 00000000..c7fe683d
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech/focaltech.c
@@ -0,0 +1,1053 @@
+/*
+ * drivers/input/touchscreen/ft5x0x_ts.c
+ *
+ * FocalTech ft5x0x TouchScreen driver.
+ *
+ * Copyright (c) 2010 Focal tech Ltd.
+ *
+ * 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.
+ *
+ * VERSION DATE AUTHOR
+ * 1.0 2010-01-05 WenFS
+ *
+ * note: only support mulititouch Wenfs 2010-10-01
+ */
+
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <asm/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+//#include <linux/i2c/ft53x6_ts.h>
+#include <soc/sprd/regulator.h>
+#include <linux/input/mt.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+
+#if(defined(CONFIG_I2C_SPRD) ||defined(CONFIG_I2C_SPRD_V1))
+#include <soc/sprd/i2c-sprd.h>
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+//#define FT53X6_DBG
+#ifdef FT53X6_DBG
+#define ENTER printk(KERN_INFO "[FT53X6_DBG] func: %s line: %04d\n", __func__, __LINE__);
+#define PRINT_DBG(x...) printk(KERN_INFO "[FT53X6_DBG] " x)
+#define PRINT_INFO(x...) printk(KERN_INFO "[FT53X6_INFO] " x)
+#define PRINT_WARN(x...) printk(KERN_INFO "[FT53X6_WARN] " x)
+#define PRINT_ERR(format,x...) printk(KERN_ERR "[FT53X6_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x)
+#else
+#define ENTER
+#define PRINT_DBG(x...)
+#define PRINT_INFO(x...) printk(KERN_INFO "[FT53X6_INFO] " x)
+#define PRINT_WARN(x...) printk(KERN_INFO "[FT53X6_WARN] " x)
+#define PRINT_ERR(format,x...) printk(KERN_ERR "[FT53X6_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x)
+#endif
+
+#define APK_DEBUG
+#define SPRD_AUTO_UPGRADE
+#define SYSFS_DEBUG
+#define FTS_CTL_IIC
+#include <linux/i2c/focaltech.h>
+#include <linux/i2c/focaltech_ex_fun.h>
+#include <linux/i2c/focaltech_ctl.h>
+
+#define USE_WAIT_QUEUE 1
+#define USE_THREADED_IRQ 0
+#define USE_WORK_QUEUE 0
+
+#define TOUCH_VIRTUAL_KEYS
+#define MULTI_PROTOCOL_TYPE_B 1
+#ifdef CONFIG_ARCH_SCX35LT8
+#define TS_MAX_FINGER 2
+#else
+#define TS_MAX_FINGER 5
+#endif
+#define FTS_PACKET_LENGTH 128
+
+#if USE_WAIT_QUEUE
+static struct task_struct *thread = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(waiter);
+static int tpd_flag = 0;
+#endif
+
+#if 0
+static unsigned char FT5316_FW[]=
+{
+#include "ft5316_720p.i"
+};
+
+static unsigned char FT5306_FW[]=
+{
+#include "ft5306_qHD.i"
+};
+
+static unsigned char *CTPM_FW = FT5306_FW;
+#endif
+//static int fw_size;
+
+static struct ft5x0x_ts_data *g_ft5x0x_ts;
+static struct i2c_client *this_client;
+
+//static unsigned char suspend_flag = 0;
+
+
+ struct Upgrade_Info fts_updateinfo[] =
+{
+ {0x55,"FT5x06",TPD_MAX_POINTS_5,AUTO_CLB_NEED,50, 30, 0x79, 0x03, 1, 2000},
+ {0x08,"FT5606",TPD_MAX_POINTS_5,AUTO_CLB_NEED,50, 30, 0x79, 0x06, 100, 2000},
+ {0x0a,"FT5x16",TPD_MAX_POINTS_5,AUTO_CLB_NEED,50, 30, 0x79, 0x07, 1, 1500},
+ {0x05,"FT6208",TPD_MAX_POINTS_2,AUTO_CLB_NONEED,60, 30, 0x79, 0x05, 10, 2000},
+ {0x06,"FT6x06",TPD_MAX_POINTS_2,AUTO_CLB_NONEED,100, 30, 0x79, 0x08, 10, 2000},
+ {0x55,"FT5x06i",TPD_MAX_POINTS_5,AUTO_CLB_NEED,50, 30, 0x79, 0x03, 1, 2000},
+ {0x14,"FT5336",TPD_MAX_POINTS_5,AUTO_CLB_NEED,30, 30, 0x79, 0x11, 10, 2000},
+ {0x13,"FT3316",TPD_MAX_POINTS_5,AUTO_CLB_NEED,30, 30, 0x79, 0x11, 10, 2000},
+ {0x12,"FT5436i",TPD_MAX_POINTS_5,AUTO_CLB_NEED,30, 30, 0x79, 0x11, 10, 2000},
+ {0x11,"FT5336i",TPD_MAX_POINTS_5,AUTO_CLB_NEED,30, 30, 0x79, 0x11, 10, 2000},
+};
+
+struct Upgrade_Info fts_updateinfo_curr;
+#if 0//dennis
+/* Attribute */
+static ssize_t ft5x0x_show_suspend(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t ft5x0x_store_suspend(struct device* cd, struct device_attribute *attr,const char* buf, size_t len);
+static ssize_t ft5x0x_show_version(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t ft5x0x_update(struct device* cd, struct device_attribute *attr, const char* buf, size_t len);
+#endif
+//static unsigned char ft5x0x_read_fw_ver(void);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x0x_ts_suspend(struct early_suspend *handler);
+static void ft5x0x_ts_resume(struct early_suspend *handler);
+#endif
+//static int fts_ctpm_fw_update(void);
+//static int fts_ctpm_fw_upgrade_with_i_file(void);
+
+struct ts_event {
+ u16 x1;
+ u16 y1;
+ u16 x2;
+ u16 y2;
+ u16 x3;
+ u16 y3;
+ u16 x4;
+ u16 y4;
+ u16 x5;
+ u16 y5;
+ u16 pressure;
+ u8 touch_point;
+};
+
+struct ft5x0x_ts_data {
+ struct input_dev *input_dev;
+ struct i2c_client *client;
+ struct ts_event event;
+#if USE_WORK_QUEUE
+ struct work_struct pen_event_work;
+ struct workqueue_struct *ts_workqueue;
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct work_struct resume_work;
+ struct workqueue_struct *ts_resume_workqueue;
+ struct early_suspend early_suspend;
+#endif
+ struct ft5x0x_ts_platform_data *platform_data;
+};
+
+#if 0//dennis
+static DEVICE_ATTR(suspend, S_IRUGO | S_IWUSR, ft5x0x_show_suspend, ft5x0x_store_suspend);
+static DEVICE_ATTR(update, S_IRUGO | S_IWUSR, ft5x0x_show_version, ft5x0x_update);
+
+static ssize_t ft5x0x_show_suspend(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+
+ if(suspend_flag==1)
+ sprintf(buf, "FT5x0x Suspend\n");
+ else
+ sprintf(buf, "FT5x0x Resume\n");
+
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t ft5x0x_store_suspend(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ unsigned long on_off = simple_strtoul(buf, NULL, 10);
+ suspend_flag = on_off;
+
+ if(on_off==1)
+ {
+ pr_info("FT5x0x Entry Suspend\n");
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ ft5x0x_ts_suspend(NULL);
+ #endif
+ }
+ else
+ {
+ pr_info("FT5x0x Entry Resume\n");
+ #ifdef CONFIG_HAS_EARLYSUSPEND
+ ft5x0x_ts_resume(NULL);
+ #endif
+ }
+
+ return len;
+}
+
+static ssize_t ft5x0x_show_version(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+ unsigned char uc_reg_value;
+
+ uc_reg_value = ft5x0x_read_fw_ver();
+
+ sprintf(buf, "ft5x0x firmware id is V%x\n", uc_reg_value);
+
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t ft5x0x_update(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ unsigned long on_off = simple_strtoul(buf, NULL, 10);
+ unsigned char uc_reg_value;
+
+ uc_reg_value = ft5x0x_read_fw_ver();
+
+ if(on_off==1)
+ {
+ pr_info("ft5x0x update, current firmware id is V%x\n", uc_reg_value);
+ //fts_ctpm_fw_update();
+ fts_ctpm_fw_upgrade_with_i_file();
+ }
+
+ return len;
+}
+
+static int ft5x0x_create_sysfs(struct i2c_client *client)
+{
+ int err;
+ struct device *dev = &(client->dev);
+
+ err = device_create_file(dev, &dev_attr_suspend);
+ err = device_create_file(dev, &dev_attr_update);
+
+ return err;
+}
+#endif
+
+static int ft5x0x_i2c_rxdata(char *rxdata, int length)
+{
+ int ret = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxdata,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxdata,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msgs, 2) != 2) {
+ ret = -EIO;
+ pr_err("msg %s i2c read error: %d\n", __func__, ret);
+ }
+
+ return ret;
+}
+
+static int ft5x0x_i2c_txdata(char *txdata, int length)
+{
+ int ret = 0;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msg, 1) != 1) {
+ ret = -EIO;
+ pr_err("%s i2c write error: %d\n", __func__, ret);
+ }
+
+ return ret;
+}
+
+/***********************************************************************************************
+Name : ft5x0x_write_reg
+
+Input : addr -- address
+ para -- parameter
+
+Output :
+
+function : write register of ft5x0x
+
+***********************************************************************************************/
+static int ft5x0x_write_reg(u8 addr, u8 para)
+{
+ u8 buf[3];
+ int ret = -1;
+
+ buf[0] = addr;
+ buf[1] = para;
+ ret = ft5x0x_i2c_txdata(buf, 2);
+ if (ret < 0) {
+ pr_err("write reg failed! %#x ret: %d", buf[0], ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+/***********************************************************************************************
+Name : ft5x0x_read_reg
+
+Input : addr
+ pdata
+
+Output :
+
+function : read register of ft5x0x
+
+***********************************************************************************************/
+static int ft5x0x_read_reg(u8 addr, u8 *pdata)
+{
+ int ret = 0;
+ u8 buf[2] = {addr, 0};
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf+1,
+ },
+ };
+
+ if (i2c_transfer(this_client->adapter, msgs, 2) != 2) {
+ ret = -EIO;
+ pr_err("msg %s i2c read error: %d\n", __func__, ret);
+ }
+
+ *pdata = buf[1];
+ return ret;
+}
+
+
+#ifdef TOUCH_VIRTUAL_KEYS
+
+static ssize_t virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
+ struct ft5x0x_ts_platform_data *pdata = data->platform_data;
+ return sprintf(buf,"%s:%s:%d:%d:%d:%d:%s:%s:%d:%d:%d:%d:%s:%s:%d:%d:%d:%d\n"
+ ,__stringify(EV_KEY), __stringify(KEY_APPSELECT),pdata ->virtualkeys[0],pdata ->virtualkeys[1],pdata ->virtualkeys[2],pdata ->virtualkeys[3]
+ ,__stringify(EV_KEY), __stringify(KEY_HOMEPAGE),pdata ->virtualkeys[4],pdata ->virtualkeys[5],pdata ->virtualkeys[6],pdata ->virtualkeys[7]
+ ,__stringify(EV_KEY), __stringify(KEY_BACK),pdata ->virtualkeys[8],pdata ->virtualkeys[9],pdata ->virtualkeys[10],pdata ->virtualkeys[11]);
+}
+
+static struct kobj_attribute virtual_keys_attr = {
+ .attr = {
+ .name = "virtualkeys.focaltech_ts",
+ .mode = S_IRUGO,
+ },
+ .show = &virtual_keys_show,
+};
+
+static struct attribute *properties_attrs[] = {
+ &virtual_keys_attr.attr,
+ NULL
+};
+
+static struct attribute_group properties_attr_group = {
+ .attrs = properties_attrs,
+};
+
+static void ft5x0x_ts_virtual_keys_init(void)
+{
+ int ret = 0;
+ struct kobject *properties_kobj;
+
+ pr_info("[FST] %s\n",__func__);
+
+ properties_kobj = kobject_create_and_add("board_properties", NULL);
+ if (properties_kobj)
+ ret = sysfs_create_group(properties_kobj,
+ &properties_attr_group);
+ if (!properties_kobj || ret)
+ pr_err("failed to create board_properties\n");
+}
+
+#endif
+
+/***********************************************************************************************
+Name : ft5x0x_read_fw_ver
+
+Input : void
+
+Output : firmware version
+
+function : read TP firmware version
+
+***********************************************************************************************/
+/*static unsigned char ft5x0x_read_fw_ver(void)
+{
+ unsigned char ver;
+ ft5x0x_read_reg(FT5X0X_REG_FIRMID, &ver);
+ return(ver);
+}
+*/
+
+
+static void ft5x0x_clear_report_data(struct ft5x0x_ts_data *ft5x0x_ts)
+{
+ int i;
+
+ for(i = 0; i < TS_MAX_FINGER; i++) {
+ #if MULTI_PROTOCOL_TYPE_B
+ input_mt_slot(ft5x0x_ts->input_dev, i);
+ input_mt_report_slot_state(ft5x0x_ts->input_dev, MT_TOOL_FINGER, false);
+ #endif
+ }
+ input_report_key(ft5x0x_ts->input_dev, BTN_TOUCH, 0);
+ #if !MULTI_PROTOCOL_TYPE_B
+ input_mt_sync(ft5x0x_ts->input_dev);
+ #endif
+ input_sync(ft5x0x_ts->input_dev);
+}
+
+static int ft5x0x_update_data(void)
+{
+ struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
+ struct ts_event *event = &data->event;
+ u8 buf[33] = {0};
+ int ret = -1;
+ int i;
+ u16 x , y;
+ u8 ft_pressure , ft_size;
+
+ ret = ft5x0x_i2c_rxdata(buf, 33);
+
+ if (ret < 0) {
+ pr_err("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ memset(event, 0, sizeof(struct ts_event));
+ event->touch_point = buf[2] & 0x07;
+
+ for(i = 0; i < TS_MAX_FINGER; i++) {
+ if((buf[6*i+3] & 0xc0) == 0xc0)
+ continue;
+ x = (s16)(buf[6*i+3] & 0x0F)<<8 | (s16)buf[6*i+4];
+ y = (s16)(buf[6*i+5] & 0x0F)<<8 | (s16)buf[6*i+6];
+ ft_pressure = buf[6*i+7];
+ if(ft_pressure > 127)
+ ft_pressure = 127;
+ ft_size = (buf[6*i+8]>>4) & 0x0F;
+ if((buf[6*i+3] & 0x40) == 0x0) {
+ #if MULTI_PROTOCOL_TYPE_B
+ input_mt_slot(data->input_dev, buf[6*i+5]>>4);
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
+ #endif
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y, y);
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+ #if !MULTI_PROTOCOL_TYPE_B
+ input_mt_sync(data->input_dev);
+ #endif
+ pr_debug("===x%d = %d,y%d = %d ====",i, x, i, y);
+ }
+ else {
+ #if MULTI_PROTOCOL_TYPE_B
+ input_mt_slot(data->input_dev, buf[6*i+5]>>4);
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
+ #endif
+ }
+ }
+ if(0 == event->touch_point) {
+ for(i = 0; i < TS_MAX_FINGER; i ++) {
+ #if MULTI_PROTOCOL_TYPE_B
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
+ #endif
+ }
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ #if !MULTI_PROTOCOL_TYPE_B
+ input_mt_sync(data->input_dev);
+ #endif
+
+ }
+ input_sync(data->input_dev);
+
+ return 0;
+}
+
+#if USE_WAIT_QUEUE
+static int touch_event_handler(void *unused)
+{
+ struct sched_param param = { .sched_priority = 5 };
+ sched_setscheduler(current, SCHED_RR, &param);
+
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+ wait_event_interruptible(waiter, (0 != tpd_flag));
+ tpd_flag = 0;
+ set_current_state(TASK_RUNNING);
+ ft5x0x_update_data();
+
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+#endif
+
+#if USE_WORK_QUEUE
+static void ft5x0x_ts_pen_irq_work(struct work_struct *work)
+{
+ ft5x0x_update_data();
+ enable_irq(this_client->irq);
+}
+#endif
+
+static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)
+{
+#if USE_WAIT_QUEUE
+ tpd_flag = 1;
+ wake_up_interruptible(&waiter);
+ return IRQ_HANDLED;
+#endif
+
+#if USE_WORK_QUEUE
+ struct ft5x0x_ts_data *ft5x0x_ts = (struct ft5x0x_ts_data *)dev_id;
+
+ if (!work_pending(&ft5x0x_ts->pen_event_work)) {
+ queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work);
+ }
+ return IRQ_HANDLED;
+#endif
+
+#if USE_THREADED_IRQ
+ ft5x0x_update_data();
+ return IRQ_HANDLED;
+#endif
+
+}
+
+static void ft5x0x_ts_reset(void)
+{
+ struct ft5x0x_ts_platform_data *pdata = g_ft5x0x_ts->platform_data;
+
+ gpio_direction_output(pdata->reset_gpio_number, 1);
+ msleep(1);
+ gpio_set_value(pdata->reset_gpio_number, 0);
+ msleep(10);
+ gpio_set_value(pdata->reset_gpio_number, 1);
+ msleep(200);
+}
+
+#if 0
+//for future use
+
+struct regulator *vdd28 = NULL;
+
+static void ft53x6_power_off(void)
+{
+ if(vdd28 != NULL)
+ regulator_force_disable(vdd28);
+ PRINT_INFO("power off\n");
+}
+
+static void ft53x6_power_on(void)
+{
+ int err = 0;
+
+ if(vdd28 == NULL) {
+ vdd28 = regulator_get(NULL, "vdd28");
+ if (IS_ERR(vdd28)) {
+ PRINT_ERR("regulator_get failed\n");
+ return;
+ }
+ err = regulator_set_voltage(vdd28,2800000,2800000);
+ if (err)
+ PRINT_ERR("regulator_set_voltage failed\n");
+ }
+ regulator_enable(vdd28);
+
+ PRINT_INFO("power on\n");
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ft5x0x_ts_suspend(struct early_suspend *handler)
+{
+ int ret = -1;
+ pr_info("==%s==\n", __FUNCTION__);
+ ret = ft5x0x_write_reg(FT5X0X_REG_PMODE, PMODE_HIBERNATE);
+ if(ret){
+ PRINT_ERR("==ft5x0x_ts_suspend== ft5x0x_write_reg fail\n");
+ }
+ disable_irq(this_client->irq);
+ ft5x0x_clear_report_data(g_ft5x0x_ts);
+ //gpio_set_value(g_ft5x0x_ts->platform_data->reset_gpio_number, 0);//for future use
+}
+
+static void ft5x0x_ts_resume(struct early_suspend *handler)
+{
+ struct ft5x0x_ts_data *ft5x0x_ts = (struct ft5x0x_ts_data *)i2c_get_clientdata(this_client);
+ queue_work(ft5x0x_ts->ts_resume_workqueue, &ft5x0x_ts->resume_work);
+}
+
+static void ft5x0x_ts_resume_work(struct work_struct *work)
+{
+ pr_info("==%s==\n", __FUNCTION__);
+ ft5x0x_ts_reset();
+ //ft5x0x_write_reg(FT5X0X_REG_PERIODACTIVE, 7);
+ enable_irq(this_client->irq);
+ msleep(2);
+ ft5x0x_clear_report_data(g_ft5x0x_ts);
+}
+#endif
+
+static void ft5x0x_ts_hw_init(struct ft5x0x_ts_data *ft5x0x_ts)
+{
+ struct regulator *reg_vdd;
+ struct i2c_client *client = ft5x0x_ts->client;
+ struct ft5x0x_ts_platform_data *pdata = ft5x0x_ts->platform_data;
+
+ pr_info("[FST] %s [irq=%d];[rst=%d]\n",__func__,
+ pdata->irq_gpio_number,pdata->reset_gpio_number);
+ gpio_request(pdata->irq_gpio_number, "ts_irq_pin");
+ gpio_request(pdata->reset_gpio_number, "ts_rst_pin");
+ gpio_direction_output(pdata->reset_gpio_number, 1);
+ gpio_direction_input(pdata->irq_gpio_number);
+
+ reg_vdd = regulator_get(&client->dev, pdata->vdd_name);
+ if (!WARN(IS_ERR(reg_vdd), "[FST] ft5x0x_ts_hw_init regulator: failed to get %s.\n", pdata->vdd_name)) {
+ if(!strcmp(pdata->vdd_name,"vdd18"))
+ regulator_set_voltage(reg_vdd,1800000,1800000);
+ if(!strcmp(pdata->vdd_name,"vdd28"))
+ regulator_set_voltage(reg_vdd, 2800000, 2800000);
+ regulator_enable(reg_vdd);
+ }
+ msleep(100);
+ ft5x0x_ts_reset();
+}
+
+void focaltech_get_upgrade_array(struct i2c_client *client)
+{
+
+ u8 chip_id;
+ u32 i;
+
+ i2c_smbus_read_i2c_block_data(client,FT_REG_CHIP_ID,1,&chip_id);
+
+ printk("%s chip_id = %x\n", __func__, chip_id);
+
+ for(i=0;i<sizeof(fts_updateinfo)/sizeof(struct Upgrade_Info);i++)
+ {
+ if(chip_id==fts_updateinfo[i].CHIP_ID)
+ {
+ memcpy(&fts_updateinfo_curr, &fts_updateinfo[i], sizeof(struct Upgrade_Info));
+ break;
+ }
+ }
+
+ if(i >= sizeof(fts_updateinfo)/sizeof(struct Upgrade_Info))
+ {
+ memcpy(&fts_updateinfo_curr, &fts_updateinfo[0], sizeof(struct Upgrade_Info));
+ }
+}
+
+#ifdef CONFIG_OF
+static struct ft5x0x_ts_platform_data *ft5x0x_ts_parse_dt(struct device *dev)
+{
+ struct ft5x0x_ts_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret,i = 0;
+ u32 data[12] = {0};
+ u32 tmp_val_u32;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "Could not allocate struct ft5x0x_ts_platform_data");
+ return NULL;
+ }
+ pdata->reset_gpio_number = of_get_gpio(np, 0);
+ if(pdata->reset_gpio_number < 0){
+ dev_err(dev, "fail to get reset_gpio_number\n");
+ goto fail;
+ }
+ pdata->irq_gpio_number = of_get_gpio(np, 1);
+ if(pdata->irq_gpio_number < 0){
+ dev_err(dev, "fail to get irq_gpio_number\n");
+ goto fail;
+ }
+ ret = of_property_read_string(np, "vdd_name", &pdata->vdd_name);
+ if(ret){
+ dev_err(dev, "fail to get vdd_name\n");
+ goto fail;
+ }
+ ret = of_property_read_u32_array(np, "virtualkeys", data,12);
+ if(ret){
+ dev_err(dev, "fail to get virtualkeys\n");
+ goto fail;
+ }
+ for(i = 0; i < 12; i ++){
+ pdata->virtualkeys[i] = data[i];
+ }
+ ret = of_property_read_u32(np, "TP_MAX_X", &tmp_val_u32);
+ if(ret){
+ dev_err(dev, "fail to get TP_MAX_X\n");
+ goto fail;
+ }
+ pdata->TP_MAX_X = tmp_val_u32;
+ ret = of_property_read_u32(np, "TP_MAX_Y", &tmp_val_u32);
+ if(ret){
+ dev_err(dev, "fail to get TP_MAX_Y\n");
+ goto fail;
+ }
+ pdata->TP_MAX_Y = tmp_val_u32;
+ return pdata;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#endif
+
+
+static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct ft5x0x_ts_data *ft5x0x_ts;
+ struct input_dev *input_dev;
+ struct ft5x0x_ts_platform_data *pdata = client->dev.platform_data;
+ int err = 0;
+ unsigned char uc_reg_value;
+ //u8 chip_id,i;
+
+ pr_info("[FST] %s: probe\n",__func__);
+#ifdef CONFIG_OF
+ if (client->dev.of_node && !pdata){
+ pdata = ft5x0x_ts_parse_dt(&client->dev);
+ if(pdata){
+ client->dev.platform_data = pdata;
+ }
+ else{
+ err = -ENOMEM;
+ goto exit_alloc_platform_data_failed;
+ }
+ }
+#endif
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);
+ if (!ft5x0x_ts) {
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ g_ft5x0x_ts = ft5x0x_ts;
+ ft5x0x_ts->platform_data = pdata;
+ this_client = client;
+ ft5x0x_ts->client = client;
+ ft5x0x_ts_hw_init(ft5x0x_ts);
+ i2c_set_clientdata(client, ft5x0x_ts);
+ client->irq = gpio_to_irq(pdata->irq_gpio_number);
+
+ #if(defined(CONFIG_I2C_SPRD) || defined(CONFIG_I2C_SPRD_V1))
+ sprd_i2c_ctl_chg_clk(client->adapter->nr, 400000);
+ #endif
+
+ err = ft5x0x_read_reg(FT5X0X_REG_CIPHER, &uc_reg_value);
+ if (err < 0)
+ {
+ pr_err("[FST] read chip id error %x\n", uc_reg_value);
+ err = -ENODEV;
+ goto exit_chip_check_failed;
+ }
+
+ /* set report rate, about 70HZ */
+ //ft5x0x_write_reg(FT5X0X_REG_PERIODACTIVE, 7);
+#if USE_WORK_QUEUE
+ INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);
+
+ ft5x0x_ts->ts_workqueue = create_singlethread_workqueue("focal-work-queue");
+ if (!ft5x0x_ts->ts_workqueue) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ INIT_WORK(&ft5x0x_ts->resume_work, ft5x0x_ts_resume_work);
+ ft5x0x_ts->ts_resume_workqueue = create_singlethread_workqueue("ft5x0x_ts_resume_work");
+ if (!ft5x0x_ts->ts_resume_workqueue) {
+ err = -ESRCH;
+ goto exit_create_singlethread;
+ }
+#endif
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "[FST] failed to allocate input device\n");
+ goto exit_input_dev_alloc_failed;
+ }
+#ifdef TOUCH_VIRTUAL_KEYS
+ ft5x0x_ts_virtual_keys_init();
+#endif
+ ft5x0x_ts->input_dev = input_dev;
+
+ __set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
+ __set_bit(ABS_MT_POSITION_X, input_dev->absbit);
+ __set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
+ __set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
+ __set_bit(KEY_MENU, input_dev->keybit);
+ __set_bit(KEY_BACK, input_dev->keybit);
+ __set_bit(KEY_HOMEPAGE, input_dev->keybit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+#if MULTI_PROTOCOL_TYPE_B
+ input_mt_init_slots(input_dev, TS_MAX_FINGER,0);
+#endif
+ input_set_abs_params(input_dev,ABS_MT_POSITION_X, 0, pdata->TP_MAX_X, 0, 0);
+ input_set_abs_params(input_dev,ABS_MT_POSITION_Y, 0, pdata->TP_MAX_Y, 0, 0);
+ input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(input_dev,ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ #if 0
+ /*ft5306's firmware is qhd, ft5316's firmware is 720p*/
+ if (uc_reg_value == 0x0a || uc_reg_value == 0x0) {
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_X, 0, 720, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_Y, 0, 1280, 0, 0);
+ } else {
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_X, 0, 540, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_POSITION_Y, 0, 960, 0, 0);
+ }
+ input_set_abs_params(input_dev,
+ ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+
+ #endif
+
+ set_bit(EV_ABS, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+
+ input_dev->name = FOCALTECH_TS_NAME;
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&client->dev,
+ "[FST] ft5x0x_ts_probe: failed to register input device: %s\n",
+ dev_name(&client->dev));
+ goto exit_input_register_device_failed;
+ }
+
+#if USE_THREADED_IRQ
+ err = request_threaded_irq(client->irq, NULL, ft5x0x_ts_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ft5x0x_ts);
+#else
+ err = request_irq(client->irq, ft5x0x_ts_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ft5x0x_ts);
+#endif
+ if (err < 0) {
+ dev_err(&client->dev, "[FST] ft5x0x_probe: request irq failed %d\n",err);
+ goto exit_irq_request_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;
+ ft5x0x_ts->early_suspend.resume = ft5x0x_ts_resume;
+ register_early_suspend(&ft5x0x_ts->early_suspend);
+#endif
+
+focaltech_get_upgrade_array(client);
+
+#ifdef SYSFS_DEBUG
+fts_create_sysfs(client);
+#endif
+
+#ifdef FTS_CTL_IIC
+if (ft_rw_iic_drv_init(client) < 0)
+{
+ dev_err(&client->dev, "%s:[FTS] create fts control iic driver failed\n", __func__);
+}
+#endif
+
+#ifdef SPRD_AUTO_UPGRADE
+ printk("********************Enter CTP Auto Upgrade********************\n");
+ fts_ctpm_auto_upgrade(client);
+#endif
+
+#ifdef APK_DEBUG
+ ft5x0x_create_apk_debug_channel(client);
+#endif
+
+#if USE_WAIT_QUEUE
+ thread = kthread_run(touch_event_handler, 0, "focal-wait-queue");
+ if (IS_ERR(thread))
+ {
+ err = PTR_ERR(thread);
+ PRINT_ERR("failed to create kernel thread: %d\n", err);
+ }
+#endif
+
+ return 0;
+
+exit_irq_request_failed:
+ input_unregister_device(input_dev);
+exit_input_register_device_failed:
+ input_free_device(input_dev);
+exit_input_dev_alloc_failed:
+exit_create_singlethread:
+exit_chip_check_failed:
+ gpio_free(pdata->irq_gpio_number);
+ gpio_free(pdata->reset_gpio_number);
+ kfree(ft5x0x_ts);
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ ft5x0x_ts = NULL;
+ i2c_set_clientdata(client, ft5x0x_ts);
+exit_alloc_platform_data_failed:
+ return err;
+}
+
+static int ft5x0x_ts_remove(struct i2c_client *client)
+{
+ struct ft5x0x_ts_data *ft5x0x_ts = i2c_get_clientdata(client);
+
+ pr_info("==ft5x0x_ts_remove=\n");
+
+ #ifdef SYSFS_DEBUG
+ fts_release_sysfs(client);
+ #endif
+ #ifdef FTS_CTL_IIC
+ ft_rw_iic_drv_exit();
+ #endif
+ #ifdef APK_DEBUG
+ ft5x0x_release_apk_debug_channel();
+ #endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ft5x0x_ts->early_suspend);
+#endif
+ free_irq(client->irq, ft5x0x_ts);
+ input_unregister_device(ft5x0x_ts->input_dev);
+ input_free_device(ft5x0x_ts->input_dev);
+#if USE_WORK_QUEUE
+ cancel_work_sync(&ft5x0x_ts->pen_event_work);
+ destroy_workqueue(ft5x0x_ts->ts_workqueue);
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ cancel_work_sync(&ft5x0x_ts->resume_work);
+ destroy_workqueue(ft5x0x_ts->ts_resume_workqueue);
+#endif
+ kfree(ft5x0x_ts);
+ ft5x0x_ts = NULL;
+ i2c_set_clientdata(client, ft5x0x_ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id ft5x0x_ts_id[] = {
+ { FOCALTECH_TS_NAME, 0 },{ }
+};
+
+static int ft5x0x_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ PRINT_INFO("ft5x0x_suspend\n");
+ return 0;
+}
+static int ft5x0x_resume(struct i2c_client *client)
+{
+ PRINT_INFO("ft5x0x_resume\n");
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);
+
+static const struct of_device_id focaltech_of_match[] = {
+ { .compatible = "focaltech,focaltech_ts", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, focaltech_of_match);
+static struct i2c_driver ft5x0x_ts_driver = {
+ .probe = ft5x0x_ts_probe,
+ .remove = ft5x0x_ts_remove,
+ .id_table = ft5x0x_ts_id,
+ .driver = {
+ .name = FOCALTECH_TS_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = focaltech_of_match,
+ },
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init ft5x0x_ts_init(void)
+{
+ return i2c_add_driver(&ft5x0x_ts_driver);
+}
+
+static void __exit ft5x0x_ts_exit(void)
+{
+ i2c_del_driver(&ft5x0x_ts_driver);
+}
+
+module_init(ft5x0x_ts_init);
+module_exit(ft5x0x_ts_exit);
+
+MODULE_AUTHOR("<wenfs@Focaltech-systems.com>");
+MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");
+MODULE_LICENSE("GPL");