From 1c29b633d3b5de590a2b6138d15aa94f40281c71 Mon Sep 17 00:00:00 2001 From: wuhao Date: Mon, 5 Nov 2012 19:30:03 +0800 Subject: [PATCH] add ft5x0x for v86 --- arch/arm/mach-rk2928/board-rk2926-sdk.c | 58 ++ arch/arm/mach-rk2928/include/mach/board.h | 6 + drivers/input/touchscreen/Kconfig | 4 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ft5x0x.c | 721 ++++++++++++++++++++++ 5 files changed, 790 insertions(+) create mode 100644 drivers/input/touchscreen/ft5x0x.c diff --git a/arch/arm/mach-rk2928/board-rk2926-sdk.c b/arch/arm/mach-rk2928/board-rk2926-sdk.c index 2a200dda3342..cc5dd46724a2 100755 --- a/arch/arm/mach-rk2928/board-rk2926-sdk.c +++ b/arch/arm/mach-rk2928/board-rk2926-sdk.c @@ -388,6 +388,54 @@ static struct platform_device device_ion = { }; #endif +/*ft5x0x touchpad*/ +#if defined (CONFIG_TOUCHSCREEN_FT5X0X) + +#define TOUCH_RESET_PIN RK2928_PIN0_PD3 +#define TOUCH_EN_PIN NULL +#define TOUCH_INT_PIN RK2928_PIN1_PB0 + +static struct ts_hw_data ts_hw_info = { + .reset_gpio = TOUCH_RESET_PIN, +}; +#endif + +#if defined(CONFIG_TOUCHSCREEN_GT811_IIC) +#define TOUCH_RESET_PIN INVALID_GPIO//RK2928_PIN0_PD3//RK2928_PIN1_PA3 +#define TOUCH_INT_PIN RK2928_PIN1_PB0 +int goodix_init_platform_hw(void) +{ + + //printk("ft5306_init_platform_hw\n"); + if(gpio_request(TOUCH_RESET_PIN,NULL) != 0){ + gpio_free(TOUCH_RESET_PIN); + printk("ft5306_init_platform_hw gpio_request error\n"); + return -EIO; + } + + if(gpio_request(TOUCH_INT_PIN,NULL) != 0){ + gpio_free(TOUCH_INT_PIN); + printk("ift5306_init_platform_hw gpio_request error\n"); + return -EIO; + } + gpio_direction_output(TOUCH_RESET_PIN, GPIO_HIGH); + mdelay(10); + gpio_set_value(TOUCH_RESET_PIN,GPIO_LOW); + mdelay(10); + gpio_set_value(TOUCH_RESET_PIN,GPIO_HIGH); + msleep(300); + return 0; + +} + +struct goodix_platform_data goodix_info = { + .model = 8105, + //.irq_pin = RK2928_PIN1_PB0, + .rest_pin = TOUCH_RESET_PIN, + .init_platform_hw = goodix_init_platform_hw, +}; +#endif + #if defined(CONFIG_TOUCHSCREEN_SITRONIX_A720) @@ -1046,6 +1094,16 @@ static struct i2c_board_info __initdata i2c2_info[] = { .platform_data = &sitronix_info, }, #endif +#if defined (CONFIG_TOUCHSCREEN_FT5X0X) + { + .type = "ft5x0x_ts", + .addr = 0x38, + .flags = 0, + .irq = TOUCH_INT_PIN, + //.platform_data = &ft5x0x_info, + .platform_data = &ts_hw_info, + }, +#endif }; #endif #ifdef CONFIG_I2C3_RK30 diff --git a/arch/arm/mach-rk2928/include/mach/board.h b/arch/arm/mach-rk2928/include/mach/board.h index b6a2b0b557ee..34a8035f78ba 100755 --- a/arch/arm/mach-rk2928/include/mach/board.h +++ b/arch/arm/mach-rk2928/include/mach/board.h @@ -35,6 +35,12 @@ void __sramfunc board_pmu_resume(void); extern struct sys_timer rk2928_timer; +struct ts_hw_data { + int reset_gpio; + int touch_en_gpio; +}; + + //#if defined CONFIG_BATTERY_RK30_ADC_FAC /* adc battery */ struct rk30_adc_battery_platform_data { diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 5e02303f19e1..6f7669cdb9c8 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1046,6 +1046,10 @@ config TOUCHSCREEN_FT5406 If unsure, say N(but it's safe to say "Y"). +config TOUCHSCREEN_FT5X0X + tristate "ft5x0x touchscreen panel support " + depends on I2C2_RK29 || I2C2_RK30 + config ATMEL_MXT1386 tristate "ATMEL_MXT1386 touchscreen panel support" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index b624bdc544a1..aecaf8766e59 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -95,4 +95,5 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_RMI4_I2C_RK) += rmi4/ obj-$(CONFIG_TOUCHSCREEN_I30) += i30_ts.o obj-$(CONFIG_TOUCHSCREEN_BYD693X) += byd693x_ts.o +obj-$(CONFIG_TOUCHSCREEN_FT5X0X) += ft5x0x.o diff --git a/drivers/input/touchscreen/ft5x0x.c b/drivers/input/touchscreen/ft5x0x.c new file mode 100644 index 000000000000..8e6665f096d7 --- /dev/null +++ b/drivers/input/touchscreen/ft5x0x.c @@ -0,0 +1,721 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define FT5X0X_DEBUG +#ifdef FT5X0X_DEBUG +#define DBG(fmt, args...) printk("*** " fmt, ## args) +#else +#define DBG(fmt, args...) do{}while(0) +#endif + +#define EV_MENU KEY_MENU + +#define I2C_SPEED 200*1000 +#define MAX_POINT 5 + +#if defined (CONFIG_TOUCHSCREEN_1024X768) +#define SCREEN_MAX_X 1024 +#define SCREEN_MAX_Y 768 +#elif defined (CONFIG_TOUCHSCREEN_1024X600) +#define SCREEN_MAX_X 1024 +#define SCREEN_MAX_Y 600 +#elif defined (CONFIG_TOUCHSCREEN_800X600) +#define SCREEN_MAX_X 800 +#define SCREEN_MAX_Y 600 +#elif defined (CONFIG_TOUCHSCREEN_800X480) +#define SCREEN_MAX_X 800 +#define SCREEN_MAX_Y 480 +#else +#define SCREEN_MAX_X 800 +#define SCREEN_MAX_Y 480 +#endif + +#define PRESS_MAX 200 + +#define VID_OF 0x51 //OuFei +#define VID_MD 0x53 //MuDong +#define VID_BYD 0x59 +#define VID_BM 0x5D //BaoMing +#define VID_YJ 0x80 +#define VID_DSW 0x8C //DingShengWei +#define VID_YM 0x94 //0xC0 +static unsigned char g_vid; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static struct early_suspend ft5x0x_early_suspend; +#endif + +#ifndef TOUCH_EN_LEVEL +#define TOUCH_EN_LEVEL GPIO_HIGH +#endif + +static int ft5x0x_probe(struct i2c_client *client, const struct i2c_device_id *id); + +struct ts_event { + u16 flag; + u16 x; + u16 y; + u16 pressure; + u16 w; +}; +static struct ts_event ts_point[MAX_POINT]; + +struct ft5x0x_data +{ + struct i2c_client *client; + struct input_dev *input_dev; + int reset_gpio; + int touch_en_gpio; + int last_point_num; + struct work_struct pen_event_work; + struct workqueue_struct *ts_workqueue; +}; + +struct i2c_client *g_client; + +/*********************************************************************************************** +Name : ft5x0x_i2c_rxdata + +Input : *rxdata + *length + +Output : ret + +function : + +***********************************************************************************************/ +int ft5x0x_i2c_Read(char * writebuf, int writelen, char *readbuf, int readlen) +{ + int ret; + + if(writelen > 0) + { + struct i2c_msg msgs[] = { + { + .addr = g_client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + .scl_rate = I2C_SPEED, + }, + { + .addr = g_client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + .scl_rate = I2C_SPEED, + }, + }; + ret = i2c_transfer(g_client->adapter, msgs, 2); + if (ret < 0) + DBG("msg %s i2c read error: %d\n", __func__, ret); + } + else + { + struct i2c_msg msgs[] = { + { + .addr = g_client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + .scl_rate = I2C_SPEED, + }, + }; + ret = i2c_transfer(g_client->adapter, msgs, 1); + if (ret < 0) + DBG("msg %s i2c read error: %d\n", __func__, ret); + } + return ret; +}EXPORT_SYMBOL(ft5x0x_i2c_Read); +/*********************************************************************************************** +Name : ft5x0x_i2c_Write + +Input : + + +Output :0-write success + other-error code +function : write data by i2c + +***********************************************************************************************/ +int ft5x0x_i2c_Write(char *writebuf, int writelen) +{ + int ret; + + struct i2c_msg msg[] = { + { + .addr = g_client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + .scl_rate = I2C_SPEED, + }, + }; + + ret = i2c_transfer(g_client->adapter, msg, 1); + if (ret < 0) + DBG("%s i2c write error: %d\n", __func__, ret); + + return ret; +}EXPORT_SYMBOL(ft5x0x_i2c_Write); + +int ft5x0x_rx_data(struct i2c_client *client, char *rxData, int length) +{ + int ret = 0; + char reg = rxData[0]; + ret = i2c_master_reg8_recv(client, reg, rxData, length, I2C_SPEED); + return (ret > 0)? 0 : ret; +} + +static int ft5x0x_tx_data(struct i2c_client *client, char *txData, int length) +{ + int ret = 0; + char reg = txData[0]; + ret = i2c_master_reg8_send(client, reg, &txData[1], length-1, I2C_SPEED); + return (ret > 0)? 0 : ret; +} + +char ft5x0x_read_reg(struct i2c_client *client, int addr) +{ + char tmp; + int ret = 0; + + tmp = addr; + ret = ft5x0x_rx_data(client, &tmp, 1); + if (ret < 0) { + return ret; + } + return tmp; +} + +int ft5x0x_write_reg(struct i2c_client *client,int addr,int value) +{ + char buffer[3]; + int ret = 0; + + buffer[0] = addr; + buffer[1] = value; + ret = ft5x0x_tx_data(client, &buffer[0], 2); + return ret; +} + +static void ft5x0x_power_en(struct ft5x0x_data *tsdata, int on) +{ +#if defined (TOUCH_POWER_PIN) + if (on) { + gpio_direction_output(tsdata->touch_en_gpio, TOUCH_EN_LEVEL); + gpio_set_value(tsdata->touch_en_gpio, TOUCH_EN_LEVEL); + mdelay(10); + } else { + gpio_direction_output(tsdata->touch_en_gpio, !TOUCH_EN_LEVEL); + gpio_set_value(tsdata->touch_en_gpio, !TOUCH_EN_LEVEL); + mdelay(10); + } +#endif +} + +static void ft5x0x_chip_reset(struct ft5x0x_data *tsdata) +{ + gpio_direction_output(tsdata->reset_gpio, 0); + gpio_set_value(tsdata->reset_gpio, 1); + mdelay(20); + gpio_set_value(tsdata->reset_gpio, 0); + mdelay(20); + gpio_set_value(tsdata->reset_gpio, 1); +} + +static int i2c_write_interface(unsigned char* pbt_buf, int dw_lenth) +{ + int ret; + ret = i2c_master_send(g_client, pbt_buf, dw_lenth); + if (ret <= 0) { + printk("i2c_write_interface error\n"); + return -1; + } + + return 0; +} + +static int ft_cmd_write(unsigned char btcmd, unsigned char btPara1, unsigned char btPara2, + unsigned char btPara3, int num) +{ + unsigned char write_cmd[4] = {0}; + + write_cmd[0] = btcmd; + write_cmd[1] = btPara1; + write_cmd[2] = btPara2; + write_cmd[3] = btPara3; + return i2c_write_interface(&write_cmd, num); +} + + +static int ft5x0x_chip_init(struct i2c_client * client) +{ + int ret = 0; + int w_value; + char r_value; + int err = -1; + int reg; + int i = 0, flag = 1; + struct ft5x0x_data *tsdata = i2c_get_clientdata(client); + + gpio_free(tsdata->reset_gpio); + err = gpio_request(tsdata->reset_gpio, "ft5x0x rst"); + if (err) { + DBG( "failed to request ft5x0x reset GPIO%d\n", tsdata->reset_gpio); + goto exit_alloc_gpio_rst_failed; + } + +#if defined (TOUCH_POWER_PIN) +#if defined (TOUCH_POWER_MUX_NAME) + rk29_mux_api_set(TOUCH_POWER_MUX_NAME, TOUCH_POWER_MUX_MODE_GPIO); +#endif + gpio_free(tsdata->touch_en_gpio); + err = gpio_request(tsdata->touch_en_gpio, "ft5x0x power enable"); + if (err) { + DBG( "failed to request ft5x0x power enable GPIO%d\n", tsdata->touch_en_gpio); + goto exit_alloc_gpio_power_failed; + } +#endif + + ft5x0x_power_en(tsdata, 0); + mdelay(100); + ft5x0x_chip_reset(tsdata); + ft5x0x_power_en(tsdata, 1); + mdelay(500); + ft_cmd_write(0x07,0x00,0x00,0x00,1); + mdelay(10); + +#if 1 + while (1) { + reg = 0x88; + w_value = 7; + ret = ft5x0x_write_reg(client, reg, w_value); /* adjust frequency 70Hz */ + if (ret < 0) { + printk(KERN_ERR "ft5x0x i2c txdata failed\n"); + //goto out; + } + + r_value = ft5x0x_read_reg(client, reg); + if (ret < 0) { + printk(KERN_ERR "ft5x0x i2c rxdata failed\n"); + //goto out; + } + printk("r_value = %d\n, i = %d, flag = %d", r_value, i, flag); + i++; + + if (w_value != r_value) { + ret = -1; + flag = 0; + if (i > 10) { /* test 5 count */ + break; + } + } else { + ret = 0; + break; + } + } + + if( ret == -1) + return ret; + +#endif + ret = ft5x0x_read_reg(client, 0xA8);//read touchpad ID for adjust touchkey place + if (ret < 0) { + printk(KERN_ERR "ft5x0x i2c rxdata failed\n"); + //goto out; + } + printk("ft5406 g_vid = 0x%X\n", ret); + g_vid = ret; + + return ret; + +exit_alloc_gpio_power_failed: +#if defined (TOUCH_POWER_PIN) + gpio_free(tsdata->touch_en_gpio); +#endif +exit_alloc_gpio_rst_failed: + gpio_free(tsdata->reset_gpio); + printk("%s error\n",__FUNCTION__); + return err; +} + +static void key_led_ctrl(int on) +{ +#ifdef TOUCH_KEY_LED + gpio_set_value(TOUCH_KEY_LED, on); +#endif +} + +static int g_screen_key=0; + +static int ft5x0x_process_points(struct ft5x0x_data *data) +{ + struct i2c_client *client = data->client; + u8 start_reg = 0x0; + u8 buf[32] = {0}; + int ret = -1; + int status = 0, id, x, y, p, w, touch_num; + int offset, i; + int back_press = 0, search_press=0, menu_press=0, home_press=0; + int points; + + start_reg = 0; + buf[0] = start_reg; + + //printk("ft5406 g_vid = 0x%X\n", g_vid); + if (MAX_POINT == 5) { + ret = ft5x0x_rx_data(client, buf, 31); + } else { + ret = ft5x0x_rx_data(client, buf, 13); + } + + if (ret < 0) { + printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret); + return ret; + } + + + if (MAX_POINT == 5) { + touch_num = buf[2] & 0x07; + } else { + touch_num = buf[2] & 0x03; + } + + if (touch_num == 0) { + for (i = 0; i < MAX_POINT; i++) { + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + } + + input_sync(data->input_dev); + DBG("release all points!!!!!!\n"); + return 0; + } + + points = touch_num; + if (data->last_point_num > touch_num) { + touch_num = data->last_point_num; + } + data->last_point_num = points; + + offset = 0; + for (i = 0; i < touch_num; i++) { + id = buf[5 + offset] >> 4; + status = buf[3 + offset] >> 6; + x = (s16)(buf[3 + offset] & 0x0F) << 8 | (s16)buf[4 + offset]; + y = (s16)(buf[5 + offset] & 0x0F) << 8 | (s16)buf[6 + offset]; + + //p = buf[7 + offset]; + //w = buf[8 + offset]; + + offset += 6; + + //printk("%d-%d(%d,%d)%d-%d\n", id, status, x, y, p, w); + DBG("TOUCH_NO=%d: ID=%d,(X=%d,Y=%d), status=%d, pressure=%d, w=%d\n", i, id, x, y, status, 0, 0); + + if (x < (SCREEN_MAX_X + 10)) { + if (status == 1) { + input_mt_slot(data->input_dev, id); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + } else { + input_mt_slot(data->input_dev, id); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 200); + input_report_abs(data->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, y); + } + } else { + } + } + + input_sync(data->input_dev); + + return 0; +} + +static void ft5x0x_delaywork_func(struct work_struct *work) +{ + struct ft5x0x_data *ft5x0x = container_of(work, struct ft5x0x_data, pen_event_work); + struct i2c_client *client = ft5x0x->client; + + ft5x0x_process_points(ft5x0x); + enable_irq(client->irq); +} + +static irqreturn_t ft5x0x_interrupt(int irq, void *handle) +{ + struct ft5x0x_data *ft5x0x_ts = handle; + + //printk("Enter:%s %d\n",__FUNCTION__,__LINE__); + disable_irq_nosync(irq); + //if (!work_pending(&ft5x0x_ts->pen_event_work)) { + queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work); + //} + return IRQ_HANDLED; +} + + +static int ft5x0x_remove(struct i2c_client *client) +{ + struct ft5x0x_data *ft5x0x = i2c_get_clientdata(client); + + input_unregister_device(ft5x0x->input_dev); + input_free_device(ft5x0x->input_dev); + free_irq(client->irq, ft5x0x); + kfree(ft5x0x); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ft5x0x_early_suspend); +#endif + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ft5x0x_suspend(struct early_suspend *h) +{ + int err; + int w_value; + int reg; + struct ft5x0x_data *ft5x0x = i2c_get_clientdata(g_client); + + printk("==ft5x0x_ts_suspend=\n"); + key_led_ctrl(0); + +#if 1 + w_value = 3; + reg = 0xa5; + err = ft5x0x_write_reg(g_client, reg, w_value); /* enter sleep mode */ + if (err < 0) { + printk("ft5x0x enter sleep mode failed\n"); + } +#endif + disable_irq(g_client->irq); + //ft5x0x_power_en(ft5x0x, 0); +} + +static void ft5x0x_resume(struct early_suspend *h) +{ + struct ft5x0x_data *ft5x0x = i2c_get_clientdata(g_client); + + key_led_ctrl(0); + + printk("==ft5x0x_ts_resume=\n"); + //ft5x0x_power_en(ft5x0x, 1); + ft5x0x_chip_reset(ft5x0x); + + mdelay(100); + + enable_irq(g_client->irq); +} +#else +static int ft5x0x_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} +static int ft5x0x_resume(struct i2c_client *client) +{ + return 0; +} +#endif + +static const struct i2c_device_id ft5x0x_id[] = { + {"ft5x0x_ts", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ft5x0x_id); + +static struct i2c_driver ft5x0x_driver = { + .driver = { + .name = "ft5x0x_ts", + }, + .id_table = ft5x0x_id, + .probe = ft5x0x_probe, + .remove = __devexit_p(ft5x0x_remove), +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = &ft5x0x_suspend, + .resume = &ft5x0x_resume, +#endif +}; + +static int ft5x0x_client_init(struct i2c_client *client) +{ + struct ft5x0x_data *tsdata = i2c_get_clientdata(client); + int ret = 0; + + DBG("gpio_to_irq(%d) is %d\n", client->irq, gpio_to_irq(client->irq)); + if ( !gpio_is_valid(client->irq)) { + DBG("+++++++++++gpio_is_invalid\n"); + return -EINVAL; + } + + gpio_free(client->irq); + ret = gpio_request(client->irq, "ft5x0x_int"); + if (ret) { + DBG( "failed to request ft5x0x GPIO%d\n", gpio_to_irq(client->irq)); + return ret; + } + + ret = gpio_direction_input(client->irq); + if (ret) { + DBG("failed to set ft5x0x gpio input\n"); + return ret; + } + + gpio_pull_updown(client->irq, GPIOPullUp); + client->irq = gpio_to_irq(client->irq); + //ft5x0x->irq = client->irq; + ret = request_irq(client->irq, ft5x0x_interrupt, IRQF_TRIGGER_FALLING, client->dev.driver->name, tsdata); + DBG("request irq is %d,ret is 0x%x\n", client->irq, ret); + if (ret ) { + DBG(KERN_ERR "ft5x0x_client_init: request irq failed,ret is %d\n", ret); + return ret; + } + //disable_irq(client->irq); + + return 0; +} + +static int ft5x0x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct ft5x0x_data *ft5x0x_ts; + struct ts_hw_data *pdata = client->dev.platform_data; + int err = 0; + int i; + + printk("%s enter\n",__FUNCTION__); + ft5x0x_ts = kzalloc(sizeof(struct ft5x0x_data), GFP_KERNEL); + if (!ft5x0x_ts) { + DBG("[ft5x0x]:alloc data failed.\n"); + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + memset(ts_point, 0x0, sizeof(struct ts_event) * MAX_POINT); + + g_client = client; + ft5x0x_ts->client = client; + ft5x0x_ts->last_point_num = 0; + ft5x0x_ts->reset_gpio = pdata->reset_gpio; + ft5x0x_ts->touch_en_gpio = pdata->touch_en_gpio; + i2c_set_clientdata(client, ft5x0x_ts); + + err = ft5x0x_chip_init(client); + if (err < 0) { + printk(KERN_ERR + "ft5x0x_probe: ft5x0x chip init failed\n"); + goto exit_request_gpio_irq_failed; + } + + err = ft5x0x_client_init(client); + if (err < 0) { + printk(KERN_ERR + "ft5x0x_probe: ft5x0x_client_init failed\n"); + goto exit_request_gpio_irq_failed; + } + + ft5x0x_ts->input_dev = input_allocate_device(); + if (!ft5x0x_ts->input_dev) { + err = -ENOMEM; + printk(KERN_ERR + "ft5x0x_probe: Failed to allocate input device\n"); + goto exit_input_allocate_device_failed; + } + + ft5x0x_ts->input_dev->name = "ft5x0x-ts"; + ft5x0x_ts->input_dev->dev.parent = &client->dev; + + err = input_register_device(ft5x0x_ts->input_dev); + if (err < 0) { + printk(KERN_ERR + "ft5x0x_probe: Unable to register input device: %s\n", + ft5x0x_ts->input_dev->name); + goto exit_input_register_device_failed; + } + + INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_delaywork_func); + ft5x0x_ts->ts_workqueue = create_singlethread_workqueue("ft5x0x_ts"); + if (!ft5x0x_ts->ts_workqueue) { + err = -ESRCH; + goto exit_request_gpio_irq_failed; + } + + + __set_bit(EV_SYN, ft5x0x_ts->input_dev->evbit); + __set_bit(EV_KEY, ft5x0x_ts->input_dev->evbit); + __set_bit(EV_ABS, ft5x0x_ts->input_dev->evbit); + __set_bit(INPUT_PROP_DIRECT, ft5x0x_ts->input_dev->propbit); + + input_mt_init_slots(ft5x0x_ts->input_dev, MAX_POINT); + input_set_abs_params(ft5x0x_ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0); + input_set_abs_params(ft5x0x_ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0); + input_set_abs_params(ft5x0x_ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0); + +#ifdef CONFIG_HAS_EARLYSUSPEND + ft5x0x_early_suspend.suspend = ft5x0x_suspend; + ft5x0x_early_suspend.resume = ft5x0x_resume; + ft5x0x_early_suspend.level = 0x2; + register_early_suspend(&ft5x0x_early_suspend); +#endif + + return 0; + +exit_input_register_device_failed: + input_free_device(ft5x0x_ts->input_dev); +exit_input_allocate_device_failed: + free_irq(client->irq, ft5x0x_ts); +exit_request_gpio_irq_failed: + kfree(ft5x0x_ts); +exit_alloc_gpio_power_failed: +#if defined (TOUCH_POWER_PIN) + gpio_free(ft5x0x_ts->touch_en_gpio); +#endif +exit_alloc_gpio_rst_failed: + gpio_free(ft5x0x_ts->reset_gpio); +exit_alloc_data_failed: + printk("%s error\n",__FUNCTION__); + return err; +} + +static void __init ft5x0x_init_async(void *unused, async_cookie_t cookie) +{ + i2c_add_driver(&ft5x0x_driver); +} + +static int __init ft5x0x_mod_init(void) +{ + printk("ft5x0x module init\n"); + async_schedule(ft5x0x_init_async, NULL); + return 0; +} + +static void __exit ft5x0x_mod_exit(void) +{ + i2c_del_driver(&ft5x0x_driver); +} + +module_init(ft5x0x_mod_init); +module_exit(ft5x0x_mod_exit); + +MODULE_DESCRIPTION("ft5406 touch driver"); +MODULE_AUTHOR("zqqu"); +MODULE_LICENSE("GPL"); + -- 2.34.1