/* * 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 #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #if(defined(CONFIG_I2C_SPRD) ||defined(CONFIG_I2C_SPRD_V1)) #include #endif #ifdef CONFIG_HAS_EARLYSUSPEND #include #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 #include #include #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, ¶m); 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)) { 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(""); MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver"); MODULE_LICENSE("GPL");