diff options
author | Srikant Patnaik | 2015-01-13 15:08:24 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-13 15:08:24 +0530 |
commit | 97327692361306d1e6259021bc425e32832fdb50 (patch) | |
tree | fe9088f3248ec61e24f404f21b9793cb644b7f01 /drivers/input/touchscreen/gt9xx_ts/gt9xx.c | |
parent | 2d05a8f663478a44e088d122e0d62109bbc801d0 (diff) | |
parent | a3a8b90b61e21be3dde9101c4e86c881e0f06210 (diff) | |
download | FOSSEE-netbook-kernel-source-97327692361306d1e6259021bc425e32832fdb50.tar.gz FOSSEE-netbook-kernel-source-97327692361306d1e6259021bc425e32832fdb50.tar.bz2 FOSSEE-netbook-kernel-source-97327692361306d1e6259021bc425e32832fdb50.zip |
dirty fix to merging
Diffstat (limited to 'drivers/input/touchscreen/gt9xx_ts/gt9xx.c')
-rwxr-xr-x | drivers/input/touchscreen/gt9xx_ts/gt9xx.c | 2163 |
1 files changed, 2163 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/gt9xx_ts/gt9xx.c b/drivers/input/touchscreen/gt9xx_ts/gt9xx.c new file mode 100755 index 00000000..cc02513e --- /dev/null +++ b/drivers/input/touchscreen/gt9xx_ts/gt9xx.c @@ -0,0 +1,2163 @@ +/* drivers/input/touchscreen/gt9xx.c + * + * 2010 - 2013 Goodix Technology. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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: 1.8 + * Authors: andrew@goodix.com, meta@goodix.com + * Release Date: 2013/04/25 + * Revision record: + * V1.0: + * first Release. By Andrew, 2012/08/31 + * V1.2: + * modify gtp_reset_guitar,slot report,tracking_id & 0x0F. By Andrew, 2012/10/15 + * V1.4: + * modify gt9xx_update.c. By Andrew, 2012/12/12 + * V1.6: + * 1. new heartbeat/esd_protect mechanism(add external watchdog) + * 2. doze mode, sliding wakeup + * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5) + * 3. config length verification + * 4. names & comments + * By Meta, 2013/03/11 + * V1.8: + * 1. pen/stylus identification + * 2. read double check & fixed config support + * 2. new esd & slide wakeup optimization + * By Meta, 2013/06/08 + */ + +#include <linux/irq.h> +#include <linux/firmware.h> +#include "gt9xx.h" + +#if GTP_ICS_SLOT_REPORT + #include <linux/input/mt.h> +#endif + +static const char *goodix_ts_name = "Goodix Capacitive TouchScreen"; +static struct workqueue_struct *goodix_wq; +struct goodix_ts_data *l_ts; +int l_suspend = 0; +struct i2c_client * i2c_connect_client = NULL; +u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] + = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff}; + +#if GTP_HAVE_TOUCH_KEY + static const u16 touch_key_array[] = GTP_KEY_TAB; + #define GTP_MAX_KEY_NUM (sizeof(touch_key_array)/sizeof(touch_key_array[0])) + +#if GTP_DEBUG_ON + static const int key_codes[] = {KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH}; + static const char *key_names[] = {"Key_Home", "Key_Back", "Key_Menu", "Key_Search"}; +#endif + +#endif + +static s8 gtp_i2c_test(struct i2c_client *client); +void gtp_reset_guitar(struct i2c_client *client, s32 ms); +void gtp_int_sync(s32 ms); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void goodix_ts_early_suspend(struct early_suspend *h); +static void goodix_ts_late_resume(struct early_suspend *h); +#endif + +#if GTP_CREATE_WR_NODE +extern s32 init_wr_node(struct i2c_client*); +extern void uninit_wr_node(void); +#endif + +#if GTP_AUTO_UPDATE +extern u8 gup_init_update_proc(struct goodix_ts_data *); +#endif + +#if GTP_ESD_PROTECT +static struct delayed_work gtp_esd_check_work; +static struct workqueue_struct * gtp_esd_check_workqueue = NULL; +static void gtp_esd_check_func(struct work_struct *); +static s32 gtp_init_ext_watchdog(struct i2c_client *client); +void gtp_esd_switch(struct goodix_ts_data *, s32); +#endif + + +#if GTP_SLIDE_WAKEUP +typedef enum +{ + DOZE_DISABLED = 0, + DOZE_ENABLED = 1, + DOZE_WAKEUP = 2, +}DOZE_T; +static DOZE_T doze_status = DOZE_DISABLED; +static s8 gtp_enter_doze(struct goodix_ts_data *ts); +#endif + +static u8 chip_gt9xxs = 0; // true if ic is gt9xxs, like gt915s +u8 grp_cfg_version = 0; + +/******************************************************* +Function: + Read data from the i2c slave device. +Input: + client: i2c device. + buf[0~1]: read start address. + buf[2~len-1]: read data buffer. + len: GTP_ADDR_LENGTH + read bytes count +Output: + numbers of i2c_msgs to transfer: + 2: succeed, otherwise: failed +*********************************************************/ +s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len) +{ + struct i2c_msg msgs[2]; + s32 ret=-1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + //msgs[0].flags = !I2C_M_RD; + msgs[0].flags = 0 | I2C_M_NOSTART; + msgs[0].addr = client->addr; + msgs[0].len = GTP_ADDR_LENGTH; + msgs[0].buf = &buf[0]; + //msgs[0].scl_rate = 300 * 1000; // for Rockchip + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = client->addr; + msgs[1].len = len - GTP_ADDR_LENGTH; + msgs[1].buf = &buf[GTP_ADDR_LENGTH]; + //msgs[1].scl_rate = 300 * 1000; + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, msgs, 2); + if(ret == 2)break; + retries++; + } + if((retries >= 5)) + { + #if GTP_SLIDE_WAKEUP + // reset chip would quit doze mode + if (DOZE_ENABLED == doze_status) + { + return ret; + } + #endif + GTP_DEBUG("I2C communication timeout, resetting chip..."); + gtp_reset_guitar(client, 10); + } + return ret; +} + +/******************************************************* +Function: + Write data to the i2c slave device. +Input: + client: i2c device. + buf[0~1]: write start address. + buf[2~len-1]: data buffer + len: GTP_ADDR_LENGTH + write bytes count +Output: + numbers of i2c_msgs to transfer: + 1: succeed, otherwise: failed +*********************************************************/ +s32 gtp_i2c_write(struct i2c_client *client,u8 *buf,s32 len) +{ + struct i2c_msg msg; + s32 ret = -1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = len; + msg.buf = buf; + //msg.scl_rate = 300 * 1000; // for Rockchip + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1)break; + retries++; + } + if((retries >= 5)) + { + #if GTP_SLIDE_WAKEUP + if (DOZE_ENABLED == doze_status) + { + return ret; + } + #endif + GTP_DEBUG("I2C communication timeout, resetting chip..."); + gtp_reset_guitar(client, 10); + } + return ret; +} +/******************************************************* +Function: + i2c read twice, compare the results +Input: + client: i2c device + addr: operate address + rxbuf: read data to store, if compare successful + len: bytes to read +Output: + FAIL: read failed + SUCCESS: read successful +*********************************************************/ +s32 gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, u8 *rxbuf, int len) +{ + u8 buf[16] = {0}; + u8 confirm_buf[16] = {0}; + u8 retry = 0; + + while (retry++ < 3) + { + memset(buf, 0xAA, 16); + buf[0] = (u8)(addr >> 8); + buf[1] = (u8)(addr & 0xFF); + gtp_i2c_read(client, buf, len + 2); + + memset(confirm_buf, 0xAB, 16); + confirm_buf[0] = (u8)(addr >> 8); + confirm_buf[1] = (u8)(addr & 0xFF); + gtp_i2c_read(client, confirm_buf, len + 2); + + if (!memcmp(buf, confirm_buf, len+2)) + { + break; + } + } + if (retry < 3) + { + memcpy(rxbuf, confirm_buf+2, len); + return SUCCESS; + } + else + { + GTP_ERROR("i2c read 0x%04X, %d bytes, double check failed!", addr, len); + return FAIL; + } +} + +/******************************************************* +Function: + Send config. +Input: + client: i2c device. +Output: + result of i2c write operation. + 1: succeed, otherwise: failed +*********************************************************/ +s32 gtp_send_cfg(struct goodix_ts_data * ts) +{ + s32 ret = 2; + +#if GTP_DRIVER_SEND_CFG + s32 retry = 0; + + if (ts->fixed_cfg) + { + GTP_INFO("Ic fixed config, no config sent!"); + return 2; + } + GTP_INFO("driver send config"); + for (retry = 0; retry < 5; retry++) + { + ret = gtp_i2c_write(ts->client, config , GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); + if (ret > 0) + { + break; + } + } +#endif + + return ret; +} + +/******************************************************* +Function: + Disable irq function +Input: + ts: goodix i2c_client private data +Output: + None. +*********************************************************/ +void gtp_irq_disable(struct goodix_ts_data *ts) +{ + //unsigned long irqflags; + + GTP_DEBUG_FUNC(); + + //spin_lock_irqsave(&ts->irq_lock, irqflags); + if (!ts->irq_is_disable) + { + ts->irq_is_disable = 1; + //disable_irq_nosync(ts->client->irq); + wmt_gpio_mask_irq(ts->irq_gpio); + } + //spin_unlock_irqrestore(&ts->irq_lock, irqflags); +} + +/******************************************************* +Function: + Enable irq function +Input: + ts: goodix i2c_client private data +Output: + None. +*********************************************************/ +void gtp_irq_enable(struct goodix_ts_data *ts) +{ + //unsigned long irqflags = 0; + + GTP_DEBUG_FUNC(); + + //spin_lock_irqsave(&ts->irq_lock, irqflags); + if (ts->irq_is_disable) + { + //enable_irq(ts->client->irq); + wmt_gpio_unmask_irq(ts->irq_gpio); + ts->irq_is_disable = 0; + } + //spin_unlock_irqrestore(&ts->irq_lock, irqflags); +} + + +/******************************************************* +Function: + Report touch point event +Input: + ts: goodix i2c_client private data + id: trackId + x: input x coordinate + y: input y coordinate + w: input pressure +Output: + None. +*********************************************************/ +static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w) +{ + s32 px = 0, py = 0; +#if GTP_CHANGE_X2Y + GTP_SWAP(x, y); +#endif + + if (ts->swap) { + px = y; + py = x; + } else { + px = x; + py = y; + } + if (ts->xdir == -1) + px = ts->abs_x_max - px; + if (ts->ydir == -1) + py = ts->abs_y_max - py; + + if (ts->lcd_exchg) { + int tmp; + tmp = px; + px = py; + py = ts->abs_x_max - tmp; + } + +#if GTP_ICS_SLOT_REPORT + input_mt_slot(ts->input_dev, id); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); + 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_TOUCH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); +#else + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, px); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, py); + //input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); + //input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); + input_mt_sync(ts->input_dev); +#endif + + GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, px, py, w); +} + +/******************************************************* +Function: + Report touch release event +Input: + ts: goodix i2c_client private data +Output: + None. +*********************************************************/ +static void gtp_touch_up(struct goodix_ts_data* ts, s32 id) +{ +#if GTP_ICS_SLOT_REPORT + input_mt_slot(ts->input_dev, id); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); + GTP_DEBUG("Touch id[%2d] release!", id); +#else + //input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + //input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_mt_sync(ts->input_dev); +#endif +} + + +/******************************************************* +Function: + Goodix touchscreen work function +Input: + work: work struct of goodix_workqueue +Output: + None. +*********************************************************/ +static void goodix_ts_work_func(struct work_struct *work) +{ + u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0}; + u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF}; + u8 touch_num = 0; + u8 finger = 0; + static u16 pre_touch = 0; + static u8 pre_key = 0; +#if GTP_WITH_PEN + static u8 pre_pen = 0; +#endif + u8 key_value = 0; + u8* coor_data = NULL; + s32 input_x = 0; + s32 input_y = 0; + s32 input_w = 0; + s32 id = 0; + s32 i = 0; + s32 ret = -1; + struct goodix_ts_data *ts = NULL; + +#if GTP_SLIDE_WAKEUP + u8 doze_buf[3] = {0x81, 0x4B}; +#endif + + GTP_DEBUG_FUNC(); + ts = container_of(work, struct goodix_ts_data, work); + if (ts->enter_update) + { + return; + } +#if GTP_SLIDE_WAKEUP + if (DOZE_ENABLED == doze_status) + { + ret = gtp_i2c_read(i2c_connect_client, doze_buf, 3); + GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]); + if (ret > 0) + { + if (doze_buf[2] == 0xAA) + { + GTP_INFO("Slide(0xAA) To Light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + // clear 0x814B + doze_buf[2] = 0x00; + gtp_i2c_write(i2c_connect_client, doze_buf, 3); + } + else if (doze_buf[2] == 0xBB) + { + GTP_INFO("Slide(0xBB) To Light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + // clear 0x814B + doze_buf[2] = 0x00; + gtp_i2c_write(i2c_connect_client, doze_buf, 3); + } + else if (0xC0 == (doze_buf[2] & 0xC0)) + { + GTP_INFO("double click to light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + // clear 0x814B + doze_buf[2] = 0x00; + gtp_i2c_write(i2c_connect_client, doze_buf, 3); + } + else + { + gtp_enter_doze(ts); + } + } + if (ts->use_irq) + { + gtp_irq_enable(ts); + } + return; + } +#endif + + ret = gtp_i2c_read(ts->client, point_data, 12); + if (ret < 0) + { + GTP_ERROR("I2C transfer error. errno:%d\n ", ret); + goto exit_work_func; + } + + finger = point_data[GTP_ADDR_LENGTH]; + if((finger & 0x80) == 0) + { + goto exit_work_func; + } + + touch_num = finger & 0x0f; + if (touch_num > GTP_MAX_TOUCH) + { + goto exit_work_func; + } + + if (touch_num > 1) + { + u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff}; + + ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1)); + memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); + } + +#if GTP_HAVE_TOUCH_KEY + key_value = point_data[3 + 8 * touch_num]; + + if(key_value || pre_key) + { + for (i = 0; i < GTP_MAX_KEY_NUM; i++) + { + #if GTP_DEBUG_ON + for (ret = 0; ret < 4; ++ret) + { + if (key_codes[ret] == touch_key_array[i]) + { + GTP_DEBUG("Key: %s %s", key_names[ret], (key_value & (0x01 << i)) ? "Down" : "Up"); + break; + } + } + #endif + input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01<<i)); + } + touch_num = 0; + pre_touch = 0; + } +#endif + pre_key = key_value; + + GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger); + +#if GTP_ICS_SLOT_REPORT + +#if GTP_WITH_PEN + if (pre_pen && (touch_num == 0)) + { + GTP_DEBUG("Pen touch UP(Slot)!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); + input_mt_slot(ts->input_dev, 5); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); + pre_pen = 0; + } +#endif + if (pre_touch || touch_num) + { + s32 pos = 0; + u16 touch_index = 0; + + coor_data = &point_data[3]; + + if(touch_num) + { + id = coor_data[pos] & 0x0F; + + #if GTP_WITH_PEN + id = coor_data[pos]; + if ((id == 128)) + { + GTP_DEBUG("Pen touch DOWN(Slot)!"); + input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8); + input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8); + input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8); + + input_report_key(ts->input_dev, BTN_TOOL_PEN, 1); + input_mt_slot(ts->input_dev, 5); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 5); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w); + pre_pen = 1; + pre_touch = 0; + } + #endif + + touch_index |= (0x01<<id); + } + + GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",id, touch_index,pre_touch); + for (i = 0; i < GTP_MAX_TOUCH; i++) + { + #if GTP_WITH_PEN + if (pre_pen == 1) + { + break; + } + #endif + + if (touch_index & (0x01<<i)) + { + input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8); + input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8); + input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8); + + gtp_touch_down(ts, id, input_x, input_y, input_w); + pre_touch |= 0x01 << i; + + pos += 8; + id = coor_data[pos] & 0x0F; + touch_index |= (0x01<<id); + } + else + { + gtp_touch_up(ts, i); + pre_touch &= ~(0x01 << i); + } + } + } +#else + //input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value)); + if (touch_num) + { + for (i = 0; i < touch_num; i++) + { + coor_data = &point_data[i * 8 + 3]; + + id = coor_data[0]; // & 0x0F; + input_x = coor_data[1] | (coor_data[2] << 8); + input_y = coor_data[3] | (coor_data[4] << 8); + input_w = coor_data[5] | (coor_data[6] << 8); + + #if GTP_WITH_PEN + if (id == 128) + { + GTP_DEBUG("Pen touch DOWN!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 1); + pre_pen = 1; + id = 0; + } + #endif + + gtp_touch_down(ts, id, input_x, input_y, input_w); + } + } + else if (pre_touch) + { + #if GTP_WITH_PEN + if (pre_pen == 1) + { + GTP_DEBUG("Pen touch UP!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); + pre_pen = 0; + } + #endif + + GTP_DEBUG("Touch Release!"); + gtp_touch_up(ts, 0); + } + + pre_touch = touch_num; +#endif + + input_sync(ts->input_dev); + +exit_work_func: + if(!ts->gtp_rawdiff_mode) + { + ret = gtp_i2c_write(ts->client, end_cmd, 3); + if (ret < 0) + { + GTP_INFO("I2C write end_cmd error!"); + } + } + if (ts->use_irq) + { + gtp_irq_enable(ts); + } +} + +/******************************************************* +Function: + Timer interrupt service routine for polling mode. +Input: + timer: timer struct pointer +Output: + Timer work mode. + HRTIMER_NORESTART: no restart mode +*********************************************************/ +static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer) +{ + struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer); + + GTP_DEBUG_FUNC(); + + queue_work(goodix_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +/******************************************************* +Function: + External interrupt service routine for interrupt mode. +Input: + irq: interrupt number. + dev_id: private data pointer +Output: + Handle Result. + IRQ_HANDLED: interrupt handled successfully +*********************************************************/ +static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) +{ + struct goodix_ts_data *ts = dev_id; + + GTP_DEBUG_FUNC(); + + if (gpio_irqstatus(ts->irq_gpio)) + { + wmt_gpio_ack_irq(ts->irq_gpio); + if (is_gpio_irqenable(ts->irq_gpio) && l_suspend == 0) + { + gtp_irq_disable(ts); + queue_work(goodix_wq, &ts->work); + } + return IRQ_HANDLED; + } + + return IRQ_NONE; +} +/******************************************************* +Function: + Synchronization. +Input: + ms: synchronization time in millisecond. +Output: + None. +*******************************************************/ +void gtp_int_sync(s32 ms) +{ + GTP_GPIO_OUTPUT(l_ts->irq_gpio, 0); + msleep(ms); + GTP_GPIO_AS_INPUT(l_ts->irq_gpio); + //GTP_GPIO_AS_INT(ts->irq_gpio); +} + +/******************************************************* +Function: + Reset chip. +Input: + ms: reset time in millisecond +Output: + None. +*******************************************************/ +void gtp_reset_guitar(struct i2c_client *client, s32 ms) +{ + GTP_DEBUG_FUNC(); + + GTP_GPIO_OUTPUT(l_ts->rst_gpio, 0); // begin select I2C slave addr + msleep(ms); // T2: > 10ms + // HIGH: 0x28/0x29, LOW: 0xBA/0xBB + GTP_GPIO_OUTPUT(l_ts->irq_gpio, client->addr == 0x14); + + msleep(2); // T3: > 100us + GTP_GPIO_OUTPUT(l_ts->rst_gpio, 1); + + msleep(6); // T4: > 5ms + + GTP_GPIO_AS_INPUT(l_ts->rst_gpio); // end select I2C slave addr + + gtp_int_sync(50); + +#if GTP_ESD_PROTECT + gtp_init_ext_watchdog(client); +#endif +} + +#if GTP_SLIDE_WAKEUP +/******************************************************* +Function: + Enter doze mode for sliding wakeup. +Input: + ts: goodix tp private data +Output: + 1: succeed, otherwise failed +*******************************************************/ +static s8 gtp_enter_doze(struct goodix_ts_data *ts) +{ + s8 ret = -1; + s8 retry = 0; + u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8}; + + GTP_DEBUG_FUNC(); + +#if GTP_DBL_CLK_WAKEUP + i2c_control_buf[2] = 0x09; +#endif + + gtp_irq_disable(ts); + + GTP_DEBUG("entering doze mode..."); + while(retry++ < 5) + { + i2c_control_buf[0] = 0x80; + i2c_control_buf[1] = 0x46; + ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); + if (ret < 0) + { + GTP_DEBUG("failed to set doze flag into 0x8046, %d", retry); + continue; + } + i2c_control_buf[0] = 0x80; + i2c_control_buf[1] = 0x40; + ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); + if (ret > 0) + { + doze_status = DOZE_ENABLED; + GTP_INFO("GTP has been working in doze mode!"); + gtp_irq_enable(ts); + return ret; + } + msleep(10); + } + GTP_ERROR("GTP send doze cmd failed."); + gtp_irq_enable(ts); + return ret; +} +#else +/******************************************************* +Function: + Enter sleep mode. +Input: + ts: private data. +Output: + Executive outcomes. + 1: succeed, otherwise failed. +*******************************************************/ +#if 0 +static s8 gtp_enter_sleep(struct goodix_ts_data * ts) +{ + s8 ret = -1; + s8 retry = 0; + u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5}; + + GTP_DEBUG_FUNC(); + + GTP_GPIO_OUTPUT(ts->irq_gpio, 0); + msleep(5); + + while(retry++ < 5) + { + ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); + if (ret > 0) + { + GTP_INFO("GTP enter sleep!"); + + return ret; + } + msleep(10); + } + GTP_ERROR("GTP send sleep cmd failed."); + return ret; +} +#endif +#endif +/******************************************************* +Function: + Wakeup from sleep. +Input: + ts: private data. +Output: + Executive outcomes. + >0: succeed, otherwise: failed. +*******************************************************/ +#if 0 +static s8 gtp_wakeup_sleep(struct goodix_ts_data * ts) +{ + u8 retry = 0; + s8 ret = -1; + + GTP_DEBUG_FUNC(); + +#if GTP_POWER_CTRL_SLEEP + while(retry++ < 5) + { + gtp_reset_guitar(ts->client, 20); + + ret = gtp_send_cfg(ts); + if (ret < 0) + { + GTP_INFO("Wakeup sleep send config failed!"); + continue; + } + GTP_INFO("GTP wakeup sleep"); + return 1; + } +#else + while(retry++ < 10) + { + #if GTP_SLIDE_WAKEUP + if (DOZE_WAKEUP != doze_status) // wakeup not by slide + { + gtp_reset_guitar(ts->client, 10); + } + else // wakeup by slide + { + doze_status = DOZE_DISABLED; + } + #else + if (chip_gt9xxs == 1) + { + gtp_reset_guitar(ts->client, 10); + } + else + { + GTP_GPIO_OUTPUT(ts->irq_gpio, 1); + msleep(5); + } + #endif + ret = gtp_i2c_test(ts->client); + if (ret > 0) + { + GTP_INFO("GTP wakeup sleep."); + + #if (!GTP_SLIDE_WAKEUP) + if (chip_gt9xxs == 0) + { + gtp_int_sync(25); + msleep(20); + #if GTP_ESD_PROTECT + gtp_init_ext_watchdog(ts->client); + #endif + } + #endif + return ret; + } + gtp_reset_guitar(ts->client, 20); + } +#endif + + GTP_ERROR("GTP wakeup sleep failed."); + return ret; +} +#endif + +static int wmt_ts_load_firmware(char* firmwarename, unsigned char* firmdata) +{ + struct file *fp; + mm_segment_t fs; + loff_t pos; + long fsize; + int alloclen; + char filepath[64]; + + sprintf(filepath, "/system/etc/firmware/%s", firmwarename); + printk("ts firmware file:%s\n",filepath); + + fp = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(fp)) { + printk("create file error\n"); + return -1; + } + fs = get_fs(); + set_fs(KERNEL_DS); + alloclen = fp->f_op->llseek(fp, 0, SEEK_END); + printk("firmware file lengh:0x%x,\n", alloclen); + alloclen += alloclen%4; + + fp->f_op->llseek(fp,0,0); + pos = 0; + fsize = vfs_read(fp, firmdata, alloclen, &pos); + printk("filesize:0x%ld,alloclen:0x%d\n",fsize,alloclen); + if (fsize <= 0) + { + printk("alloc size is too small.\n"); + goto error_vfs_read; + } + filp_close(fp, NULL); + set_fs(fs); + printk("success to read firmware file!\n");; + + return 0; +error_vfs_read: + filp_close(fp, NULL); + set_fs(fs); + return -1; +} + +static int read_cfg(char* cfgname, u8* cfg, int len_max) +{ + char endflag[]="/* End flag */"; + unsigned char* p; + int val; + int i = 0; + unsigned char *rawdata; + + rawdata = kzalloc(1024, GFP_KERNEL); + if (rawdata == NULL) + { + printk("Error when alloc memory for firmware file!\n"); + return -ENOMEM; + } + + if (wmt_ts_load_firmware(cfgname, rawdata)) + return -1; + + p = rawdata; + while (*p!='{') p++; + p++; + + while (strncmp(p,endflag,strlen(endflag))) + { + if (!strncmp(p,"0x",strlen("0x"))) + { + sscanf(p,"%x,",&val); + *(cfg++) = val&0x00FF; + i++; + } + if (i == len_max) + break; + p++; + + }; + + kfree(rawdata); + + return i; +} + +/******************************************************* +Function: + Initialize gtp. +Input: + ts: goodix private data +Output: + Executive outcomes. + 0: succeed, otherwise: failed +*******************************************************/ +static s32 gtp_init_panel(struct goodix_ts_data *ts) +{ + s32 ret = -1; + +#if GTP_DRIVER_SEND_CFG + s32 i; + u8 check_sum = 0; + u8 opr_buf[16]; + u8 sensor_id = 0; + + u8 send_cfg_buf[256] = {0}; + char cfgname[32] = {0}; + + ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); + if (SUCCESS == ret) + { + if (opr_buf[0] != 0xBE) + { + ts->fw_error = 1; + GTP_ERROR("Firmware error, no config sent!"); + return -1; + } + } + + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1); + if (SUCCESS == ret) + { + if (sensor_id >= 0x06) + { + //GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id); + //return -1; + GTP_ERROR("Invalid sensor_id(0x%02X), Force set id to 0!", sensor_id); + sensor_id = 0; + } + } + else + { + GTP_ERROR("Failed to get sensor_id, No config sent!"); + return -1; + } + GTP_DEBUG("Sensor_ID: %d", sensor_id); + + sprintf(cfgname, "%s_id%d.cfg", ts->fw_name, sensor_id); + GTP_INFO("config file name: %s.", cfgname); + ret = read_cfg(cfgname, send_cfg_buf, 256); + if (ret < 0) + return -1; + ts->gtp_cfg_len = ret; + + if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) + { + GTP_ERROR("INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!"); + return -1; + } + + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1); + + if (ret == SUCCESS) + { + GTP_DEBUG("Config Version: %d, 0x%02X; IC Config Version: %d, 0x%02X", + send_cfg_buf[0], send_cfg_buf[0], opr_buf[0], opr_buf[0]); + + if (opr_buf[0] < 90) + { + grp_cfg_version = send_cfg_buf[0]; // backup group config version + send_cfg_buf[0] = 0x00; + ts->fixed_cfg = 0; + } + else // treated as fixed config, not send config + { + GTP_INFO("Ic fixed config with config version(%d, 0x%02X)", opr_buf[0], opr_buf[0]); + ts->fixed_cfg = 1; + } + } + else + { + GTP_ERROR("Failed to get ic config version!No config sent!"); + return -1; + } + + memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf, ts->gtp_cfg_len); + +#if GTP_CUSTOM_CFG + config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; + config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); + config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; + config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); + + if (GTP_INT_TRIGGER == 0) //RISING + { + config[TRIGGER_LOC] &= 0xfe; + } + else if (GTP_INT_TRIGGER == 1) //FALLING + { + config[TRIGGER_LOC] |= 0x01; + } +#endif // GTP_CUSTOM_CFG + + check_sum = 0; + for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) + { + check_sum += config[i]; + } + config[ts->gtp_cfg_len] = (~check_sum) + 1; + +#else // DRIVER NOT SEND CONFIG + ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH; + ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH); + if (ret < 0) + { + GTP_ERROR("Read Config Failed, Using Default Resolution & INT Trigger!"); + ts->abs_x_max = GTP_MAX_WIDTH; + ts->abs_y_max = GTP_MAX_HEIGHT; + ts->int_trigger_type = GTP_INT_TRIGGER; + } +#endif // GTP_DRIVER_SEND_CFG + + GTP_DEBUG_FUNC(); + if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) + { + ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC]; + ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2]; + ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; + } + + ret = gtp_send_cfg(ts); + if (ret < 0) + { + GTP_ERROR("Send config error."); + } + //GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", + //ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type); + GTP_INFO("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x(%s).", + ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type, ts->int_trigger_type?"Falling":"Rising"); + + msleep(10); + return 0; +} + +/******************************************************* +Function: + Read chip version. +Input: + client: i2c device + version: buffer to keep ic firmware version +Output: + read operation return. + 2: succeed, otherwise: failed +*******************************************************/ +s32 gtp_read_version(struct i2c_client *client, u16* version) +{ + s32 ret = -1; + u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff}; + + GTP_DEBUG_FUNC(); + + ret = gtp_i2c_read(client, buf, sizeof(buf)); + if (ret < 0) + { + GTP_ERROR("GTP read version failed"); + return ret; + } + + if (version) + { + *version = (buf[7] << 8) | buf[6]; + } + + if (buf[5] == 0x00) + { + GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]); + } + else + { + if (buf[5] == 'S' || buf[5] == 's') + { + chip_gt9xxs = 1; + } + GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]); + } + return ret; +} + +/******************************************************* +Function: + I2c test Function. +Input: + client:i2c client. +Output: + Executive outcomes. + 2: succeed, otherwise failed. +*******************************************************/ +static s8 gtp_i2c_test(struct i2c_client *client) +{ + u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff}; + u8 retry = 0; + s8 ret = -1; + + GTP_DEBUG_FUNC(); + + while(retry++ < 5) + { + ret = gtp_i2c_read(client, test, 3); + if (ret > 0) + { + return ret; + } + GTP_ERROR("GTP i2c test failed time %d.",retry); + msleep(10); + } + return ret; +} + +/******************************************************* +Function: + Request gpio(INT & RST) ports. +Input: + ts: private data. +Output: + Executive outcomes. + >= 0: succeed, < 0: failed +*******************************************************/ +static s8 gtp_request_io_port(struct goodix_ts_data *ts) +{ + s32 ret = 0; + + ret = GTP_GPIO_REQUEST(ts->irq_gpio, "GTP_INT_IRQ"); + if (ret < 0) + { + GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32)ts->irq_gpio, ret); + ret = -ENODEV; + } + else + { + GTP_GPIO_AS_INPUT(ts->irq_gpio); + //GTP_GPIO_AS_INT(ts->irq_gpio); + //ts->client->irq = IRQ_GPIO; + } + + ret = GTP_GPIO_REQUEST(ts->rst_gpio, "GTP_RST_PORT"); + if (ret < 0) + { + GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d",(s32)ts->rst_gpio,ret); + ret = -ENODEV; + } + + GTP_GPIO_AS_INPUT(ts->rst_gpio); + gtp_reset_guitar(ts->client, 20); + + + if(ret < 0) + { + GTP_GPIO_FREE(ts->rst_gpio); + GTP_GPIO_FREE(ts->irq_gpio); + } + + return ret; +} + +/******************************************************* +Function: + Request interrupt. +Input: + ts: private data. +Output: + Executive outcomes. + 0: succeed, -1: failed. +*******************************************************/ +static s8 gtp_request_irq(struct goodix_ts_data *ts) +{ + s32 ret = -1; + //const u8 irq_table[] = GTP_IRQ_TAB; + + GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type); + + ret = request_irq(ts->client->irq, + goodix_ts_irq_handler, + IRQF_SHARED, + ts->client->name, + ts); + if (ret) + { + GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret); + GTP_GPIO_AS_INPUT(ts->irq_gpio); + GTP_GPIO_FREE(ts->irq_gpio); + + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = goodix_ts_timer_handler; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + return -1; + } + else + { + gtp_irq_disable(ts); + ts->use_irq = 1; + return 0; + } +} + +/******************************************************* +Function: + Request input device Function. +Input: + ts:private data. +Output: + Executive outcomes. + 0: succeed, otherwise: failed. +*******************************************************/ +static s8 gtp_request_input_dev(struct goodix_ts_data *ts) +{ + s8 ret = -1; + s8 phys[32]; +#if GTP_HAVE_TOUCH_KEY + u8 index = 0; +#endif + + GTP_DEBUG_FUNC(); + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) + { + GTP_ERROR("Failed to allocate input device."); + return -ENOMEM; + } + + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); +#if GTP_ICS_SLOT_REPORT + __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + input_mt_init_slots(ts->input_dev, 10); // in case of "out of memory" +#else + //ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); +#endif + +#if GTP_HAVE_TOUCH_KEY + for (index = 0; index < GTP_MAX_KEY_NUM; index++) + { + input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]); + } +#endif + +#if GTP_SLIDE_WAKEUP + input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); +#endif + +#if GTP_WITH_PEN + // pen support + __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit); +#endif + +#if GTP_CHANGE_X2Y + GTP_SWAP(ts->abs_x_max, ts->abs_y_max); +#endif + + if (ts->swap) { + s32 temp; + temp = ts->abs_x_max; + ts->abs_x_max = ts->abs_y_max; + ts->abs_y_max = temp; + } + + if (ts->lcd_exchg) { + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_x_max, 0, 0); + } else { + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); + } + //input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + //input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0); + + sprintf(phys, "input/ts"); + ts->input_dev->name = goodix_ts_name; + ts->input_dev->phys = phys; + ts->input_dev->id.bustype = BUS_I2C; + ts->input_dev->id.vendor = 0xDEAD; + ts->input_dev->id.product = 0xBEEF; + ts->input_dev->id.version = 10427; + + ret = input_register_device(ts->input_dev); + if (ret) + { + GTP_ERROR("Register %s input device failed", ts->input_dev->name); + return -ENODEV; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = goodix_ts_early_suspend; + ts->early_suspend.resume = goodix_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + return 0; +} + + +static int wmt_check_touch_env(struct goodix_ts_data *ts) +{ + int ret = 0; + int len = 127; + char retval[128] = {0},*p=NULL,*s=NULL; + int Enable=0; + int x,y; + + // Get u-boot parameter + ret = wmt_getsyspara("wmt.io.touch", retval, &len); + if(ret){ + GTP_ERROR("Read wmt.io.touch Failed.\n"); + return -EIO; + } + + //check touch enable + p = retval; + sscanf(p,"%d:", &Enable); + if(Enable == 0){ + GTP_ERROR("Touch Screen Is Disabled.\n"); + return -ENODEV; + } + + //check touch IC name + p = strchr(p,':');p++; + if (strncmp(p, "gt9xx", strlen("gt9xx"))) { + GTP_ERROR("Can't find gt9xx!\n"); + return -ENODEV; + } + + //get firmware file name + s = strchr(p,':'); + //p = p + strlen(fw_name) + 1; + if (s > (p + strlen("gt9xx") + 1)) { + memset(ts->fw_name,0x00,sizeof(ts->fw_name)); + strncpy(ts->fw_name, p, (s-p)); + GTP_DEBUG("ts_fwname=%s\n", ts->fw_name); + } else + GTP_DEBUG("needn't firmware\n"); + + //get other needed args + p = s + 1; + ret = sscanf(p,"%d:%d:%d:%d:%d:%d:%d:%d:%x", + &ts->irq_gpio,&x,&y,&ts->rst_gpio, + &ts->swap,&ts->xdir,&ts->ydir, + &ts->max_touch_num, + &ts->i2c_addr); + if (ret != 9) + { + GTP_ERROR("Wrong format ts u-boot param(%d)!\n",ret); + return -ENODEV; + } + + ret = wmt_getsyspara("wmt.display.fb0", retval, &len); + if (!ret) { + int tmp[6]; + p = retval; + sscanf(p, "%d:[%d:%d:%d:%d:%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]); + if (tmp[4] > tmp[5]) + ts->lcd_exchg = 1; + } + + return 0; +} + +/******************************************************* +Function: + I2c probe. +Input: + client: i2c device struct. + id: device id. +Output: + Executive outcomes. + 0: succeed. +*******************************************************/ +static int goodix_ts_probe(struct platform_device *pdev) +{ + s32 ret = -1; + struct goodix_ts_data *ts; + u16 version_info; + + GTP_DEBUG_FUNC(); + + //do NOT remove these logs + GTP_INFO("GTP Driver Version: %s", GTP_DRIVER_VERSION); + GTP_INFO("GTP Driver Built@%s, %s", __TIME__, __DATE__); + GTP_INFO("GTP I2C Address: 0x%02x", i2c_connect_client->addr); + + //i2c_connect_client = client; + + if (!i2c_check_functionality(i2c_connect_client->adapter, I2C_FUNC_I2C)) + { + GTP_ERROR("I2C check functionality failed."); + return -ENODEV; + } + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) + { + GTP_ERROR("Alloc GFP_KERNEL memory failed."); + return -ENOMEM; + } + memset(ts, 0, sizeof(*ts)); + l_ts = ts; + + ret = wmt_check_touch_env(ts); + if (ret < 0) + { + GTP_ERROR("GTP get touch env failed."); + kfree(ts); + return ret; + } + + i2c_connect_client->addr = ts->i2c_addr; + INIT_WORK(&ts->work, goodix_ts_work_func); + ts->client = i2c_connect_client; + //spin_lock_init(&ts->irq_lock); // 2.6.39 later + // ts->irq_lock = SPIN_LOCK_UNLOCKED; // 2.6.39 & before + platform_set_drvdata(pdev, ts); + + ts->gtp_rawdiff_mode = 0; + + ret = gtp_request_io_port(ts); + if (ret < 0) + { + GTP_ERROR("GTP request IO port failed."); + kfree(ts); + return ret; + } + + ret = gtp_i2c_test(ts->client); + if (ret < 0) + { + GTP_ERROR("I2C communication ERROR!"); + } + +#if GTP_AUTO_UPDATE + ret = gup_init_update_proc(ts); + if (ret < 0) + { + GTP_ERROR("Create update thread error."); + } +#endif + + ret = gtp_init_panel(ts); + if (ret < 0) + { + GTP_ERROR("GTP init panel failed."); + ts->abs_x_max = GTP_MAX_WIDTH; + ts->abs_y_max = GTP_MAX_HEIGHT; + ts->int_trigger_type = GTP_INT_TRIGGER; + } + + ret = gtp_request_input_dev(ts); + if (ret < 0) + { + GTP_ERROR("GTP request input dev failed"); + } + GTP_GPIO_AS_INT(ts->irq_gpio,IRQ_TYPE_EDGE_FALLING); + ts->client->irq = IRQ_GPIO; + ret = gtp_request_irq(ts); + if (ret < 0) + { + GTP_INFO("GTP works in polling mode."); + } + else + { + GTP_INFO("GTP works in interrupt mode."); + } + + ret = gtp_read_version(ts->client, &version_info); + if (ret < 0) + { + GTP_ERROR("Read version failed."); + } + if (ts->use_irq) + { + gtp_irq_enable(ts); + } + +#if GTP_CREATE_WR_NODE + init_wr_node(ts->client); +#endif + +#if GTP_ESD_PROTECT + gtp_esd_switch(ts, SWITCH_ON); +#endif + return 0; +} + + +/******************************************************* +Function: + Goodix touchscreen driver release function. +Input: + client: i2c device struct. +Output: + Executive outcomes. 0---succeed. +*******************************************************/ +static int goodix_ts_remove(struct platform_device *pdev) +{ + struct goodix_ts_data *ts = platform_get_drvdata(pdev); + + GTP_DEBUG_FUNC(); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + +#if GTP_CREATE_WR_NODE + uninit_wr_node(); +#endif + +#if GTP_ESD_PROTECT + destroy_workqueue(gtp_esd_check_workqueue); +#endif + + if (ts) + { + if (ts->use_irq) + { + GTP_GPIO_AS_INPUT(ts->irq_gpio); + GTP_GPIO_FREE(ts->irq_gpio); + free_irq(ts->client->irq, ts); + } + else + { + hrtimer_cancel(&ts->timer); + } + } + + GTP_GPIO_FREE(ts->rst_gpio); + GTP_INFO("GTP driver removing..."); + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +/******************************************************* +Function: + Early suspend function. +Input: + h: early_suspend struct. +Output: + None. +*******************************************************/ +static void goodix_ts_early_suspend(struct early_suspend *h) +{ + struct goodix_ts_data *ts; + s8 ret = -1; + ts = container_of(h, struct goodix_ts_data, early_suspend); + + GTP_DEBUG_FUNC(); + +#if GTP_ESD_PROTECT + ts->gtp_is_suspend = 1; + gtp_esd_switch(ts, SWITCH_OFF); +#endif + +#if GTP_SLIDE_WAKEUP + ret = gtp_enter_doze(ts); +#else + if (ts->use_irq) + { + gtp_irq_disable(ts); + } + else + { + hrtimer_cancel(&ts->timer); + } + ret = gtp_enter_sleep(ts); +#endif + if (ret < 0) + { + GTP_ERROR("GTP early suspend failed."); + } + // to avoid waking up while not sleeping + // delay 48 + 10ms to ensure reliability + msleep(58); +} + +/******************************************************* +Function: + Late resume function. +Input: + h: early_suspend struct. +Output: + None. +*******************************************************/ +static void goodix_ts_late_resume(struct early_suspend *h) +{ + struct goodix_ts_data *ts; + s8 ret = -1; + ts = container_of(h, struct goodix_ts_data, early_suspend); + + GTP_DEBUG_FUNC(); + + ret = gtp_wakeup_sleep(ts); + +#if GTP_SLIDE_WAKEUP + doze_status = DOZE_DISABLED; +#endif + + if (ret < 0) + { + GTP_ERROR("GTP later resume failed."); + } + + if (ts->use_irq) + { + gtp_irq_enable(ts); + } + else + { + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } + +#if GTP_ESD_PROTECT + ts->gtp_is_suspend = 0; + gtp_esd_switch(ts, SWITCH_ON); +#endif +} +#endif + +#if 1 +/******************************************************* +Function: + Suspend function. +Input: + client: i2c_client struct. + mesg: pm_message_t struct. +Output: + None. +*******************************************************/ +static int goodix_ts_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct goodix_ts_data *ts; + ts = dev_get_drvdata(&pdev->dev); + + GTP_DEBUG_FUNC(); + +#if GTP_ESD_PROTECT + ts->gtp_is_suspend = 1; + gtp_esd_switch(ts, SWITCH_OFF); +#endif + +#if GTP_SLIDE_WAKEUP + ret = gtp_enter_doze(ts); +#else + if (ts->use_irq) + { + gtp_irq_disable(ts); + } + else + { + hrtimer_cancel(&ts->timer); + } +#endif + // to avoid waking up while not sleeping + // delay 48 + 10ms to ensure reliability + l_suspend = 1; + return 0; +} + + +/******************************************************* +Function: + Late resume function. +Input: + client: i2c_client struct. +Output: + None. +*******************************************************/ +static int goodix_ts_resume(struct platform_device *pdev) +{ + struct goodix_ts_data *ts; + s8 ret = -1; + ts = dev_get_drvdata(&pdev->dev); + + GTP_DEBUG_FUNC(); + + GTP_GPIO_AS_INPUT(ts->irq_gpio); + GTP_GPIO_AS_INPUT(ts->rst_gpio); + gtp_reset_guitar(ts->client, 20); + + ret = gtp_i2c_test(ts->client); + if (ret < 0) + { + GTP_ERROR("I2C communication ERROR!"); + } + +#if GTP_SLIDE_WAKEUP + doze_status = DOZE_DISABLED; +#endif + + if (ts->use_irq) + { + GTP_GPIO_AS_INT(ts->irq_gpio,IRQ_TYPE_EDGE_FALLING); + gtp_irq_enable(ts); + } + else + { + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } + +#if GTP_ESD_PROTECT + ts->gtp_is_suspend = 0; + gtp_esd_switch(ts, SWITCH_ON); +#endif + l_suspend = 0; + return 0; +} +#endif + +#if GTP_ESD_PROTECT +/******************************************************* +Function: + switch on & off esd delayed work +Input: + client: i2c device + on: SWITCH_ON / SWITCH_OFF +Output: + void +*********************************************************/ +void gtp_esd_switch(struct goodix_ts_data * ts, s32 on) +{ + + if (SWITCH_ON == on) // switch on esd + { + if (!ts->esd_running) + { + ts->esd_running = 1; + GTP_INFO("Esd started"); + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); + } + } + else // switch off esd + { + if (ts->esd_running) + { + ts->esd_running = 0; + GTP_INFO("Esd cancelled"); + cancel_delayed_work_sync(>p_esd_check_work); + } + } +} + +/******************************************************* +Function: + Initialize external watchdog for esd protect +Input: + client: i2c device. +Output: + result of i2c write operation. + 1: succeed, otherwise: failed +*********************************************************/ +static s32 gtp_init_ext_watchdog(struct i2c_client *client) +{ + u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA}; + + struct i2c_msg msg; // in case of recursively reset by calling gtp_i2c_write + s32 ret = -1; + s32 retries = 0; + + GTP_DEBUG("Init external watchdog..."); + GTP_DEBUG_FUNC(); + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = 4; + msg.buf = opr_buffer; + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) + { + return 1; + } + retries++; + } + if (retries >= 5) + { + GTP_ERROR("init external watchdog failed!"); + } + return 0; +} + +/******************************************************* +Function: + Esd protect function. + Added external watchdog by meta, 2013/03/07 +Input: + work: delayed work +Output: + None. +*******************************************************/ +static void gtp_esd_check_func(struct work_struct *work) +{ + s32 i; + s32 ret = -1; + struct goodix_ts_data *ts = NULL; + u8 test[4] = {0x80, 0x40}; + + GTP_DEBUG_FUNC(); + + ts = container_of(work, struct goodix_ts_data, work); + + if (ts->gtp_is_suspend) + { + ts->esd_running = 0; + GTP_INFO("Esd terminated!"); + return; + } + + for (i = 0; i < 3; i++) + { + ret = gtp_i2c_read(ts->client, test, 4); + + GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]); + if ((ret < 0)) + { + // IIC communication problem + continue; + } + else + { + if ((test[2] == 0xAA) || (test[3] != 0xAA)) + { + // IC works abnormally.. + i = 3; + break; + } + else + { + // IC works normally, Write 0x8040 0xAA, feed the dog + test[2] = 0xAA; + gtp_i2c_write(ts->client, test, 3); + break; + } + } + } + if (i >= 3) + { + GTP_ERROR("IC Working ABNORMALLY, Resetting Guitar..."); + gtp_reset_guitar(ts->client, 50); + } + + if(!ts->gtp_is_suspend) + { + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); + } + else + { + GTP_INFO("Esd terminated!"); + ts->esd_running = 0; + } + return; +} +#endif + +static void gt9xx_release(struct device *device) +{ + return; +} + + +static struct platform_device gt9xx_device = { + .name = GTP_I2C_NAME, + .id = 0, + .dev = {.release = gt9xx_release}, +}; + +static struct platform_driver gt9xx_driver = { + .driver = { + .name = GTP_I2C_NAME, + .owner = THIS_MODULE, + }, + .probe = goodix_ts_probe, + .remove = goodix_ts_remove, + .suspend = goodix_ts_suspend, + .resume = goodix_ts_resume, +}; + + +struct i2c_board_info ts_i2c_board_info = { + .type = GTP_I2C_NAME, + .flags = 0x00, + .addr = GTP_I2C_ADDR, + .platform_data = NULL, + .archdata = NULL, + .irq = -1, +}; + +static int ts_i2c_register_device (void) +{ + struct i2c_board_info *ts_i2c_bi; + struct i2c_adapter *adapter = NULL; + + ts_i2c_bi = &ts_i2c_board_info; + adapter = i2c_get_adapter(0x01);/*in bus 1*/ + if (NULL == adapter) { + GTP_ERROR("can not get i2c adapter, client address error."); + return -1; + } + i2c_connect_client = i2c_new_device(adapter, ts_i2c_bi); + if (i2c_connect_client == NULL) { + GTP_ERROR("allocate i2c client failed."); + return -1; + } + i2c_put_adapter(adapter); + return 0; +} + +static void ts_i2c_unregister_device(void) +{ + if (i2c_connect_client != NULL) + { + i2c_unregister_device(i2c_connect_client); + i2c_connect_client = NULL; + } +} + +/******************************************************* +Function: + Driver Install function. +Input: + None. +Output: + Executive Outcomes. 0---succeed. +********************************************************/ +static int __devinit goodix_ts_init(void) +{ + s32 ret; + + GTP_DEBUG_FUNC(); + GTP_INFO("GTP driver installing..."); + goodix_wq = create_singlethread_workqueue("goodix_wq"); + if (!goodix_wq) + { + GTP_ERROR("Creat workqueue failed."); + return -ENOMEM; + } +#if GTP_ESD_PROTECT + INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func); + gtp_esd_check_workqueue = create_workqueue("gtp_esd_check"); +#endif + if (ts_i2c_register_device()<0) + { + destroy_workqueue(goodix_wq); + GTP_ERROR("Error to run ts_i2c_register_device()!\n"); + return -1; + } + + ret = platform_device_register(>9xx_device); + if(ret){ + GTP_ERROR("register platform drivver failed!\n"); + goto err_register_platdev; + } + + ret = platform_driver_register(>9xx_driver); + if(ret){ + GTP_ERROR("register platform device failed!\n"); + goto err_register_platdriver; + } + return 0; +err_register_platdriver: + platform_device_unregister(>9xx_device); +err_register_platdev: + destroy_workqueue(goodix_wq); + ts_i2c_unregister_device(); + + return ret; +} + +/******************************************************* +Function: + Driver uninstall function. +Input: + None. +Output: + Executive Outcomes. 0---succeed. +********************************************************/ +static void __exit goodix_ts_exit(void) +{ + GTP_DEBUG_FUNC(); + GTP_INFO("GTP driver exited."); + ts_i2c_unregister_device(); + platform_driver_unregister(>9xx_driver); + platform_device_unregister(>9xx_device); + if (goodix_wq) + { + destroy_workqueue(goodix_wq); + } +} + +late_initcall(goodix_ts_init); +module_exit(goodix_ts_exit); + +MODULE_DESCRIPTION("GTP Series Driver"); +MODULE_LICENSE("GPL"); |