/* drivers/input/touchscreen/gt9xx.c * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * Linux Foundation chooses to take subject only to the GPLv2 license * terms, and distributes only under these terms. * * 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 #include "gt9xx.h" #include #include #include #include #include #define GOODIX_DEV_NAME "Goodix-CTP" #define CFG_MAX_TOUCH_POINTS 5 #define GOODIX_COORDS_ARR_SIZE 4 #define MAX_BUTTONS 4 #define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0])) #define GOODIX_VTG_MIN_UV 2600000 #define GOODIX_VTG_MAX_UV 3300000 #define GOODIX_I2C_VTG_MIN_UV 1800000 #define GOODIX_I2C_VTG_MAX_UV 1800000 #define GOODIX_VDD_LOAD_MIN_UA 0 #define GOODIX_VDD_LOAD_MAX_UA 10000 #define GOODIX_VIO_LOAD_MIN_UA 0 #define GOODIX_VIO_LOAD_MAX_UA 10000 #define RESET_DELAY_T3_US 200 /* T3: > 100us */ #define RESET_DELAY_T4 20 /* T4: > 5ms */ #define PHY_BUF_SIZE 32 #define PROP_NAME_SIZE 24 #define GTP_MAX_TOUCH 5 #define GTP_ESD_CHECK_CIRCLE_MS 2000 static void gtp_int_sync(struct goodix_ts_data *ts, int ms); static int gtp_i2c_test(struct i2c_client *client); static int goodix_power_off(struct goodix_ts_data *ts); static int goodix_power_on(struct goodix_ts_data *ts); #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); static int goodix_ts_suspend(struct device *dev); static int goodix_ts_resume(struct device *dev); #elif defined(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_ESD_PROTECT static struct delayed_work gtp_esd_check_work; static struct workqueue_struct *gtp_esd_check_workqueue; static void gtp_esd_check_func(struct work_struct *work); static int gtp_init_ext_watchdog(struct i2c_client *client); #endif enum doze { DOZE_DISABLED = 0, DOZE_ENABLED = 1, DOZE_WAKEUP = 2, }; static enum doze doze_status = DOZE_DISABLED; static s8 gtp_enter_doze(struct goodix_ts_data *ts); bool init_done; static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */ u8 grp_cfg_version; struct i2c_client *i2c_connect_client; #define GTP_DEBUGFS_DIR "ts_debug" #define GTP_DEBUGFS_FILE_SUSPEND "suspend" #define GTP_DEBUGFS_FILE_DATA "data" #define GTP_DEBUGFS_FILE_ADDR "addr" /******************************************************* 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 *********************************************************/ int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len) { struct goodix_ts_data *ts = i2c_get_clientdata(client); int ret = -EIO; u8 retries; struct i2c_msg msgs[2] = { { .flags = !I2C_M_RD, .addr = client->addr, .len = GTP_ADDR_LENGTH, .buf = &buf[0], }, { .flags = I2C_M_RD, .addr = client->addr, .len = len - GTP_ADDR_LENGTH, .buf = &buf[GTP_ADDR_LENGTH], }, }; for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) { ret = i2c_transfer(client->adapter, msgs, 2); if (ret == 2) break; dev_err(&client->dev, "I2C retry: %d\n", retries + 1); } if (retries == GTP_I2C_RETRY_5) { if (ts->pdata->slide_wakeup) /* reset chip would quit doze mode */ if (DOZE_ENABLED == doze_status) return ret; if (init_done) gtp_reset_guitar(ts, 10); else dev_warn(&client->dev, "gtp_reset_guitar exit init_done=%d:\n", init_done); } 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 *********************************************************/ int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len) { struct goodix_ts_data *ts = i2c_get_clientdata(client); int ret = -EIO; u8 retries; struct i2c_msg msg = { .flags = !I2C_M_RD, .addr = client->addr, .len = len, .buf = buf, }; for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) { ret = i2c_transfer(client->adapter, &msg, 1); if (ret == 1) break; dev_err(&client->dev, "I2C retry: %d\n", retries + 1); } if (retries == GTP_I2C_RETRY_5) { if (ts->pdata->slide_wakeup) if (DOZE_ENABLED == doze_status) return ret; if (init_done) gtp_reset_guitar(ts, 10); else dev_warn(&client->dev, "gtp_reset_guitar exit init_done=%d:\n", init_done); } 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 *********************************************************/ int 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++ < GTP_I2C_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 < GTP_I2C_RETRY_3) { memcpy(rxbuf, confirm_buf + 2, len); return SUCCESS; } dev_err(&client->dev, "i2c read 0x%04X, %d bytes, double check failed!", addr, len); return FAIL; } /******************************************************* Function: Send config data. Input: client: i2c device. Output: result of i2c write operation. > 0: succeed, otherwise: failed *********************************************************/ int gtp_send_cfg(struct goodix_ts_data *ts) { int ret = 0; int retry; if (ts->pdata->driver_send_cfg) { if (ts->fixed_cfg) { dev_dbg(&ts->client->dev, "Ic fixed config, no config sent!"); ret = 2; } else { for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) { ret = gtp_i2c_write(ts->client, ts->config_data, GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); if (ret > 0) break; } } } 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; spin_lock_irqsave(&ts->irq_lock, irqflags); if (!ts->irq_is_disabled) { ts->irq_is_disabled = true; disable_irq_nosync(ts->client->irq); } 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; spin_lock_irqsave(&ts->irq_lock, irqflags); if (ts->irq_is_disabled) { enable_irq(ts->client->irq); ts->irq_is_disabled = false; } 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, int id, int x, int y, int w) { if (ts->pdata->change_x2y) swap(x, y); input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); 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); } /******************************************************* Function: Report touch release event Input: ts: goodix i2c_client private data Output: None. *********************************************************/ static void gtp_touch_up(struct goodix_ts_data *ts, int id) { input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); } /******************************************************* 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; static u8 pre_key; static u8 pre_pen; 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; int ret = -1; struct goodix_ts_data *ts = NULL; u8 doze_buf[3] = {0x81, 0x4B}; ts = container_of(work, struct goodix_ts_data, work); #ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE if (ts->enter_update) return; #endif if (ts->pdata->slide_wakeup) { if (DOZE_ENABLED == doze_status) { ret = gtp_i2c_read(ts->client, doze_buf, 3); if (ret > 0) { if (doze_buf[2] == 0xAA) { dev_dbg(&ts->client->dev, "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(ts->client, doze_buf, 3); } else if (doze_buf[2] == 0xBB) { dev_dbg(&ts->client->dev, "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(ts->client, doze_buf, 3); } else if (0xC0 == (doze_buf[2] & 0xC0)) { dev_dbg(&ts->client->dev, "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(ts->client, doze_buf, 3); } else { gtp_enter_doze(ts); } } if (ts->use_irq) gtp_irq_enable(ts); return; } } ret = gtp_i2c_read(ts->client, point_data, 12); if (ret < 0) { dev_err(&ts->client->dev, "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)); } key_value = point_data[3 + 8 * touch_num]; if (key_value || pre_key) { for (i = 0; i < ts->pdata->num_button; i++) { input_report_key(ts->input_dev, ts->pdata->button_map[i], key_value & (0x01<pdata->with_pen) { if (pre_pen && (touch_num == 0)) { dev_dbg(&ts->client->dev, "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; } } 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 (ts->pdata->with_pen) { id = coor_data[pos]; if (id == 128) { dev_dbg(&ts->client->dev, "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); dev_dbg(&ts->client->dev, "Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w); pre_pen = 1; pre_touch = 0; } } touch_index |= (0x01<pdata->with_pen) if (pre_pen == 1) break; if (touch_index & (0x01<input_dev); exit_work_func: if (!ts->gtp_rawdiff_mode) { ret = gtp_i2c_write(ts->client, end_cmd, 3); if (ret < 0) dev_warn(&ts->client->dev, "I2C write end_cmd error!\n"); } if (ts->use_irq) gtp_irq_enable(ts); return; } /******************************************************* 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_irq_disable(ts); queue_work(ts->goodix_wq, &ts->work); return IRQ_HANDLED; } /******************************************************* Function: Synchronization. Input: ms: synchronization time in millisecond. Output: None. *******************************************************/ void gtp_int_sync(struct goodix_ts_data *ts, int ms) { gpio_direction_output(ts->pdata->irq_gpio, 0); msleep(ms); gpio_direction_input(ts->pdata->irq_gpio); } /******************************************************* Function: Reset chip. Input: ms: reset time in millisecond, must >10ms Output: None. *******************************************************/ void gtp_reset_guitar(struct goodix_ts_data *ts, int ms) { /* This reset sequence will selcet I2C slave address */ gpio_direction_output(ts->pdata->reset_gpio, 0); msleep(ms); if (ts->client->addr == GTP_I2C_ADDRESS_HIGH) gpio_direction_output(ts->pdata->irq_gpio, 1); else gpio_direction_output(ts->pdata->irq_gpio, 0); usleep(RESET_DELAY_T3_US); gpio_direction_output(ts->pdata->reset_gpio, 1); msleep(RESET_DELAY_T4); gpio_direction_input(ts->pdata->reset_gpio); gtp_int_sync(ts, 50); #if GTP_ESD_PROTECT gtp_init_ext_watchdog(ts->client); #endif } #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB) /******************************************************* 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) { int ret = -1; s8 retry = 0; u8 i2c_control_buf[3] = { (u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8}; if (ts->pdata->dbl_clk_wakeup) i2c_control_buf[2] = 0x09; gtp_irq_disable(ts); while (retry++ < GTP_I2C_RETRY_3) { i2c_control_buf[0] = 0x80; i2c_control_buf[1] = 0x46; ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); if (ret < 0) { dev_err(&ts->client->dev, "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; dev_dbg(&ts->client->dev, "GTP has been working in doze mode!"); gtp_irq_enable(ts); return ret; } msleep(20); } dev_err(&ts->client->dev, "GTP send doze cmd failed.\n"); gtp_irq_enable(ts); return ret; } /** * gtp_enter_sleep - Enter sleep mode * @ts: driver private data * * Returns zero on success, else an error. */ static u8 gtp_enter_sleep(struct goodix_ts_data *ts) { int ret = -1; s8 retry = 0; u8 i2c_control_buf[3] = { (u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5}; ret = gpio_direction_output(ts->pdata->irq_gpio, 0); if (ret) dev_err(&ts->client->dev, "GTP sleep: Cannot reconfig gpio %d.\n", ts->pdata->irq_gpio); if (ts->pdata->enable_power_off) { ret = gpio_direction_output(ts->pdata->reset_gpio, 0); if (ret) dev_err(&ts->client->dev, "GTP sleep: Cannot reconfig gpio %d.\n", ts->pdata->reset_gpio); ret = goodix_power_off(ts); if (ret) { dev_err(&ts->client->dev, "GTP power off failed.\n"); return ret; } return 0; } usleep(5000); while (retry++ < GTP_I2C_RETRY_5) { ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); if (ret == 1) { dev_dbg(&ts->client->dev, "GTP enter sleep!"); return 0; } msleep(20); } dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n"); return ret; } /******************************************************* Function: Wakeup from sleep. Input: ts: private data. Output: Executive outcomes. >0: succeed, otherwise: failed. *******************************************************/ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts) { u8 retry = 0; s8 ret = -1; if (ts->pdata->enable_power_off) { ret = gpio_direction_output(ts->pdata->irq_gpio, 0); if (ret) dev_err(&ts->client->dev, "GTP wakeup: Cannot reconfig gpio %d.\n", ts->pdata->irq_gpio); ret = gpio_direction_output(ts->pdata->reset_gpio, 0); if (ret) dev_err(&ts->client->dev, "GTP wakeup: Cannot reconfig gpio %d.\n", ts->pdata->reset_gpio); ret = goodix_power_on(ts); if (ret) { dev_err(&ts->client->dev, "GTP power on failed.\n"); return 0; } gtp_reset_guitar(ts, 20); ret = gtp_send_cfg(ts); if (ret <= 0) { dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n"); return ret; } dev_dbg(&ts->client->dev, "Wakeup sleep send config success."); } else { err_retry: if (ts->pdata->slide_wakeup) { /* wakeup not by slide */ if (DOZE_WAKEUP != doze_status) gtp_reset_guitar(ts, 10); else /* wakeup by slide */ doze_status = DOZE_DISABLED; } else { if (chip_gt9xxs == 1) { gtp_reset_guitar(ts, 10); } else { ret = gpio_direction_output( ts->pdata->irq_gpio, 1); usleep(5000); } } ret = gtp_i2c_test(ts->client); if (ret == 2) { dev_dbg(&ts->client->dev, "GTP wakeup sleep."); if (!ts->pdata->slide_wakeup) { if (chip_gt9xxs == 0) { gtp_int_sync(ts, 25); msleep(20); #if GTP_ESD_PROTECT gtp_init_ext_watchdog(ts->client); #endif } } return ret; } gtp_reset_guitar(ts, 20); if (retry++ < GTP_I2C_RETRY_10) goto err_retry; dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n"); } return ret; } #endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/ /******************************************************* Function: Initialize gtp. Input: ts: goodix private data Output: Executive outcomes. > =0: succeed, otherwise: failed *******************************************************/ static int gtp_init_panel(struct goodix_ts_data *ts) { struct i2c_client *client = ts->client; unsigned char *config_data = NULL; int ret = -EIO; int i; u8 check_sum = 0; u8 opr_buf[16]; u8 sensor_id = 0; if (ts->pdata->driver_send_cfg) { for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) dev_dbg(&client->dev, "Config Groups(%d) Lengths: %d", i, ts->pdata->config_data_len[i]); ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); if (SUCCESS == ret) { if (opr_buf[0] != 0xBE) { ts->fw_error = 1; dev_err(&client->dev, "Firmware error, no config sent!"); return -EINVAL; } } for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) { if (ts->pdata->config_data_len[i]) break; } if (i == GOODIX_MAX_CFG_GROUP) { sensor_id = 0; } else { ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1); if (SUCCESS == ret) { if (sensor_id >= GOODIX_MAX_CFG_GROUP) { dev_err(&client->dev, "Invalid sensor_id(0x%02X), No Config Sent!", sensor_id); return -EINVAL; } } else { dev_err(&client->dev, "Failed to get sensor_id, No config sent!"); return -EINVAL; } } dev_info(&client->dev, "Sensor ID selected: %d", sensor_id); if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH || !ts->pdata->config_data[sensor_id]) { dev_err(&client->dev, "Sensor_ID(%d) matches with NULL or invalid config group!\n", sensor_id); return -EINVAL; } ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1); if (ret == SUCCESS) { if (opr_buf[0] < 90) { /* backup group config version */ grp_cfg_version = ts->pdata-> config_data[sensor_id][GTP_ADDR_LENGTH]; ts->pdata-> config_data[sensor_id][GTP_ADDR_LENGTH] = 0x00; ts->fixed_cfg = 0; } else { /* treated as fixed config, not send config */ dev_warn(&client->dev, "Ic fixed config with config version(%d, 0x%02X)", opr_buf[0], opr_buf[0]); ts->fixed_cfg = 1; } } else { dev_err(&client->dev, "Failed to get ic config version!No config sent!"); return -EINVAL; } config_data = ts->pdata->config_data[sensor_id]; ts->config_data = ts->pdata->config_data[sensor_id]; ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id]; #if GTP_CUSTOM_CFG config_data[RESOLUTION_LOC] = (unsigned char)(GTP_MAX_WIDTH && 0xFF); config_data[RESOLUTION_LOC + 1] = (unsigned char)(GTP_MAX_WIDTH >> 8); config_data[RESOLUTION_LOC + 2] = (unsigned char)(GTP_MAX_HEIGHT && 0xFF); config_data[RESOLUTION_LOC + 3] = (unsigned char)(GTP_MAX_HEIGHT >> 8); if (GTP_INT_TRIGGER == 0) config_data[TRIGGER_LOC] &= 0xfe; else if (GTP_INT_TRIGGER == 1) config_data[TRIGGER_LOC] |= 0x01; #endif /* !GTP_CUSTOM_CFG */ check_sum = 0; for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) check_sum += config_data[i]; config_data[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_data, ts->gtp_cfg_len + GTP_ADDR_LENGTH); if (ret < 0) { dev_err(&client->dev, "Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n"); ts->abs_x_max = GTP_MAX_WIDTH; ts->abs_y_max = GTP_MAX_HEIGHT; ts->int_trigger_type = GTP_INT_TRIGGER; } } /* !DRIVER NOT SEND CONFIG */ if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) { ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8) + config_data[RESOLUTION_LOC]; ts->abs_y_max = (config_data[RESOLUTION_LOC + 3] << 8) + config_data[RESOLUTION_LOC + 2]; ts->int_trigger_type = (config_data[TRIGGER_LOC]) & 0x03; } ret = gtp_send_cfg(ts); if (ret < 0) dev_err(&client->dev, "%s: Send config error.\n", __func__); msleep(20); return ret; } /******************************************************* Function: Read firmware version Input: client: i2c device version: buffer to keep ic firmware version Output: read operation return. 0: succeed, otherwise: failed *******************************************************/ static int gtp_read_fw_version(struct i2c_client *client, u16 *version) { int ret = 0; u8 buf[GTP_FW_VERSION_BUFFER_MAXSIZE] = { GTP_REG_FW_VERSION >> 8, GTP_REG_FW_VERSION & 0xff }; ret = gtp_i2c_read(client, buf, sizeof(buf)); if (ret < 0) { dev_err(&client->dev, "GTP read version failed.\n"); return -EIO; } if (version) *version = (buf[3] << 8) | buf[2]; return ret; } /******************************************************* Function: Read and check chip id. Input: client: i2c device Output: read operation return. 0: succeed, otherwise: failed *******************************************************/ static int gtp_check_product_id(struct i2c_client *client) { int ret = 0; char product_id[GTP_PRODUCT_ID_MAXSIZE]; struct goodix_ts_data *ts = i2c_get_clientdata(client); /* 04 bytes are used for the Product-id in the register space.*/ u8 buf[GTP_PRODUCT_ID_BUFFER_MAXSIZE] = { GTP_REG_PRODUCT_ID >> 8, GTP_REG_PRODUCT_ID & 0xff }; ret = gtp_i2c_read(client, buf, sizeof(buf)); if (ret < 0) { dev_err(&client->dev, "GTP read product_id failed.\n"); return -EIO; } if (buf[5] == 0x00) { /* copy (GTP_PRODUCT_ID_MAXSIZE - 1) from buffer. Ex: 915 */ strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE - 1); } else { if (buf[5] == 'S' || buf[5] == 's') chip_gt9xxs = 1; /* copy GTP_PRODUCT_ID_MAXSIZE from buffer. Ex: 915s */ strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE); } dev_info(&client->dev, "Goodix Product ID = %s\n", product_id); ret = strcmp(product_id, ts->pdata->product_id); if (ret != 0) return -EINVAL; return ret; } /******************************************************* Function: I2c test Function. Input: client:i2c client. Output: Executive outcomes. 2: succeed, otherwise failed. *******************************************************/ static int gtp_i2c_test(struct i2c_client *client) { u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; int retry = GTP_I2C_RETRY_5; int ret = -EIO; while (retry--) { ret = gtp_i2c_read(client, buf, 3); if (ret > 0) return ret; dev_err(&client->dev, "GTP i2c test failed time %d.\n", retry); msleep(20); } return ret; } /******************************************************* Function: Request gpio(INT & RST) ports. Input: ts: private data. Output: Executive outcomes. = 0: succeed, != 0: failed *******************************************************/ static int gtp_request_io_port(struct goodix_ts_data *ts) { struct i2c_client *client = ts->client; struct goodix_ts_platform_data *pdata = ts->pdata; int ret; if (gpio_is_valid(pdata->irq_gpio)) { ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio"); if (ret) { dev_err(&client->dev, "Unable to request irq gpio [%d]\n", pdata->irq_gpio); goto err_pwr_off; } ret = gpio_direction_input(pdata->irq_gpio); if (ret) { dev_err(&client->dev, "Unable to set direction for irq gpio [%d]\n", pdata->irq_gpio); goto err_free_irq_gpio; } } else { dev_err(&client->dev, "Invalid irq gpio [%d]!\n", pdata->irq_gpio); ret = -EINVAL; goto err_pwr_off; } if (gpio_is_valid(pdata->reset_gpio)) { ret = gpio_request(pdata->reset_gpio, "goodix_ts_reset_gpio"); if (ret) { dev_err(&client->dev, "Unable to request reset gpio [%d]\n", pdata->reset_gpio); goto err_free_irq_gpio; } ret = gpio_direction_output(pdata->reset_gpio, 0); if (ret) { dev_err(&client->dev, "Unable to set direction for reset gpio [%d]\n", pdata->reset_gpio); goto err_free_reset_gpio; } } else { dev_err(&client->dev, "Invalid irq gpio [%d]!\n", pdata->reset_gpio); ret = -EINVAL; goto err_free_irq_gpio; } /* IRQ GPIO is an input signal, but we are setting it to output * direction and pulling it down, to comply with power up timing * requirements, mentioned in power up timing section of device * datasheet. */ ret = gpio_direction_output(pdata->irq_gpio, 0); if (ret) dev_warn(&client->dev, "pull down interrupt gpio failed\n"); ret = gpio_direction_output(pdata->reset_gpio, 0); if (ret) dev_warn(&client->dev, "pull down reset gpio failed\n"); return ret; err_free_reset_gpio: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); err_free_irq_gpio: if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); err_pwr_off: return ret; } /******************************************************* Function: Request interrupt. Input: ts: private data. Output: Executive outcomes. 0: succeed, -1: failed. *******************************************************/ static int gtp_request_irq(struct goodix_ts_data *ts) { int ret = 0; const u8 irq_table[] = GTP_IRQ_TAB; ret = request_threaded_irq(ts->client->irq, NULL, goodix_ts_irq_handler, irq_table[ts->int_trigger_type], ts->client->name, ts); if (ret) { ts->use_irq = false; return ret; } gtp_irq_disable(ts); ts->use_irq = true; return ret; } /******************************************************* Function: Request input device Function. Input: ts:private data. Output: Executive outcomes. 0: succeed, otherwise: failed. *******************************************************/ static int gtp_request_input_dev(struct goodix_ts_data *ts) { int ret; char phys[PHY_BUF_SIZE]; int index = 0; ts->input_dev = input_allocate_device(); if (ts->input_dev == NULL) { dev_err(&ts->client->dev, "Failed to allocate input device.\n"); return -ENOMEM; } ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); /* in case of "out of memory" */ input_mt_init_slots(ts->input_dev, 10, 0); if (ts->pdata->have_touch_key) { for (index = 0; index < ts->pdata->num_button; index++) { input_set_capability(ts->input_dev, EV_KEY, ts->pdata->button_map[index]); } } if (ts->pdata->slide_wakeup) input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); if (ts->pdata->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); } if (ts->pdata->change_x2y) swap(ts->abs_x_max, ts->abs_y_max); 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); snprintf(phys, PHY_BUF_SIZE, "input/ts"); ts->input_dev->name = GOODIX_DEV_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) { dev_err(&ts->client->dev, "Register %s input device failed.\n", ts->input_dev->name); goto exit_free_inputdev; } return 0; exit_free_inputdev: input_free_device(ts->input_dev); ts->input_dev = NULL; return ret; } static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) { return (regulator_count_voltages(reg) > 0) ? regulator_set_optimum_mode(reg, load_uA) : 0; } /** * goodix_power_on - Turn device power ON * @ts: driver private data * * Returns zero on success, else an error. */ static int goodix_power_on(struct goodix_ts_data *ts) { int ret; if (ts->power_on) { dev_info(&ts->client->dev, "Device already power on\n"); return 0; } if (!IS_ERR(ts->avdd)) { ret = reg_set_optimum_mode_check(ts->avdd, GOODIX_VDD_LOAD_MAX_UA); if (ret < 0) { dev_err(&ts->client->dev, "Regulator avdd set_opt failed rc=%d\n", ret); goto err_set_opt_avdd; } ret = regulator_enable(ts->avdd); if (ret) { dev_err(&ts->client->dev, "Regulator avdd enable failed ret=%d\n", ret); goto err_enable_avdd; } } if (!IS_ERR(ts->vdd)) { ret = regulator_set_voltage(ts->vdd, GOODIX_VTG_MIN_UV, GOODIX_VTG_MAX_UV); if (ret) { dev_err(&ts->client->dev, "Regulator set_vtg failed vdd ret=%d\n", ret); goto err_set_vtg_vdd; } ret = reg_set_optimum_mode_check(ts->vdd, GOODIX_VDD_LOAD_MAX_UA); if (ret < 0) { dev_err(&ts->client->dev, "Regulator vdd set_opt failed rc=%d\n", ret); goto err_set_opt_vdd; } ret = regulator_enable(ts->vdd); if (ret) { dev_err(&ts->client->dev, "Regulator vdd enable failed ret=%d\n", ret); goto err_enable_vdd; } } if (!IS_ERR(ts->vcc_i2c)) { ret = regulator_set_voltage(ts->vcc_i2c, GOODIX_I2C_VTG_MIN_UV, GOODIX_I2C_VTG_MAX_UV); if (ret) { dev_err(&ts->client->dev, "Regulator set_vtg failed vcc_i2c ret=%d\n", ret); goto err_set_vtg_vcc_i2c; } ret = reg_set_optimum_mode_check(ts->vcc_i2c, GOODIX_VIO_LOAD_MAX_UA); if (ret < 0) { dev_err(&ts->client->dev, "Regulator vcc_i2c set_opt failed rc=%d\n", ret); goto err_set_opt_vcc_i2c; } ret = regulator_enable(ts->vcc_i2c); if (ret) { dev_err(&ts->client->dev, "Regulator vcc_i2c enable failed ret=%d\n", ret); regulator_disable(ts->vdd); goto err_enable_vcc_i2c; } } ts->power_on = true; return 0; err_enable_vcc_i2c: err_set_opt_vcc_i2c: if (!IS_ERR(ts->vcc_i2c)) regulator_set_voltage(ts->vcc_i2c, 0, GOODIX_I2C_VTG_MAX_UV); err_set_vtg_vcc_i2c: if (!IS_ERR(ts->vdd)) regulator_disable(ts->vdd); err_enable_vdd: err_set_opt_vdd: if (!IS_ERR(ts->vdd)) regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV); err_set_vtg_vdd: if (!IS_ERR(ts->avdd)) regulator_disable(ts->avdd); err_enable_avdd: err_set_opt_avdd: ts->power_on = false; return ret; } /** * goodix_power_off - Turn device power OFF * @ts: driver private data * * Returns zero on success, else an error. */ static int goodix_power_off(struct goodix_ts_data *ts) { int ret; if (!ts->power_on) { dev_info(&ts->client->dev, "Device already power off\n"); return 0; } if (!IS_ERR(ts->vcc_i2c)) { ret = regulator_set_voltage(ts->vcc_i2c, 0, GOODIX_I2C_VTG_MAX_UV); if (ret < 0) dev_err(&ts->client->dev, "Regulator vcc_i2c set_vtg failed ret=%d\n", ret); ret = regulator_disable(ts->vcc_i2c); if (ret) dev_err(&ts->client->dev, "Regulator vcc_i2c disable failed ret=%d\n", ret); } if (!IS_ERR(ts->vdd)) { ret = regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV); if (ret < 0) dev_err(&ts->client->dev, "Regulator vdd set_vtg failed ret=%d\n", ret); ret = regulator_disable(ts->vdd); if (ret) dev_err(&ts->client->dev, "Regulator vdd disable failed ret=%d\n", ret); } if (!IS_ERR(ts->avdd)) { ret = regulator_disable(ts->avdd); if (ret) dev_err(&ts->client->dev, "Regulator avdd disable failed ret=%d\n", ret); } ts->power_on = false; return 0; } /** * goodix_power_init - Initialize device power * @ts: driver private data * * Returns zero on success, else an error. */ static int goodix_power_init(struct goodix_ts_data *ts) { int ret; ts->avdd = regulator_get(&ts->client->dev, "avdd"); if (IS_ERR(ts->avdd)) { ret = PTR_ERR(ts->avdd); dev_info(&ts->client->dev, "Regulator get failed avdd ret=%d\n", ret); } ts->vdd = regulator_get(&ts->client->dev, "vdd"); if (IS_ERR(ts->vdd)) { ret = PTR_ERR(ts->vdd); dev_info(&ts->client->dev, "Regulator get failed vdd ret=%d\n", ret); } ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc-i2c"); if (IS_ERR(ts->vcc_i2c)) { ret = PTR_ERR(ts->vcc_i2c); dev_info(&ts->client->dev, "Regulator get failed vcc_i2c ret=%d\n", ret); } return 0; } /** * goodix_power_deinit - Deinitialize device power * @ts: driver private data * * Returns zero on success, else an error. */ static int goodix_power_deinit(struct goodix_ts_data *ts) { regulator_put(ts->vdd); regulator_put(ts->vcc_i2c); regulator_put(ts->avdd); return 0; } static ssize_t gtp_fw_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct goodix_ts_data *ts = dev_get_drvdata(dev); if (!strlen(ts->fw_name)) return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1, "No fw name has been given."); else return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1, "%s\n", ts->fw_name); } static ssize_t gtp_fw_name_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct goodix_ts_data *ts = dev_get_drvdata(dev); if (size > GTP_FW_NAME_MAXSIZE - 1) { dev_err(dev, "FW name size exceeds the limit."); return -EINVAL; } strlcpy(ts->fw_name, buf, size); if (ts->fw_name[size-1] == '\n') ts->fw_name[size-1] = '\0'; return size; } static ssize_t gtp_fw_upgrade_show(struct device *dev, struct device_attribute *attr, char *buf) { struct goodix_ts_data *ts = dev_get_drvdata(dev); return snprintf(buf, 2, "%d\n", ts->fw_loading); } static ssize_t gtp_fw_upgrade_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct goodix_ts_data *ts = dev_get_drvdata(dev); unsigned int val; int ret; if (size > 2) return -EINVAL; ret = kstrtouint(buf, 10, &val); if (ret) return ret; if (ts->gtp_is_suspend) { dev_err(&ts->client->dev, "Can't start fw upgrade. Device is in suspend state."); return -EBUSY; } mutex_lock(&ts->input_dev->mutex); if (!ts->fw_loading && val) { disable_irq(ts->client->irq); ts->fw_loading = true; if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) { ret = gup_update_proc(NULL); if (ret == FAIL) dev_err(&ts->client->dev, "Fail to update GTP firmware.\n"); } ts->fw_loading = false; enable_irq(ts->client->irq); } mutex_unlock(&ts->input_dev->mutex); return size; } static ssize_t gtp_force_fw_upgrade_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct goodix_ts_data *ts = dev_get_drvdata(dev); unsigned int val; int ret; if (size > 2) return -EINVAL; ret = kstrtouint(buf, 10, &val); if (ret) return ret; if (ts->gtp_is_suspend) { dev_err(&ts->client->dev, "Can't start fw upgrade. Device is in suspend state."); return -EBUSY; } mutex_lock(&ts->input_dev->mutex); if (!ts->fw_loading && val) { disable_irq(ts->client->irq); ts->fw_loading = true; ts->force_update = true; if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) { ret = gup_update_proc(NULL); if (ret == FAIL) dev_err(&ts->client->dev, "Fail to force update GTP firmware.\n"); } ts->force_update = false; ts->fw_loading = false; enable_irq(ts->client->irq); } mutex_unlock(&ts->input_dev->mutex); return size; } static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP), gtp_fw_name_show, gtp_fw_name_store); static DEVICE_ATTR(fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP), gtp_fw_upgrade_show, gtp_fw_upgrade_store); static DEVICE_ATTR(force_fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP), gtp_fw_upgrade_show, gtp_force_fw_upgrade_store); static struct attribute *gtp_attrs[] = { &dev_attr_fw_name.attr, &dev_attr_fw_upgrade.attr, &dev_attr_force_fw_upgrade.attr, NULL }; static const struct attribute_group gtp_attr_grp = { .attrs = gtp_attrs, }; static int gtp_debug_addr_is_valid(u16 addr) { if (addr < GTP_VALID_ADDR_START || addr > GTP_VALID_ADDR_END) { pr_err("GTP reg address is invalid: 0x%x\n", addr); return false; } return true; } static int gtp_debug_data_set(void *_data, u64 val) { struct goodix_ts_data *ts = _data; mutex_lock(&ts->input_dev->mutex); if (gtp_debug_addr_is_valid(ts->addr)) dev_err(&ts->client->dev, "Writing to GTP registers not supported.\n"); mutex_unlock(&ts->input_dev->mutex); return 0; } static int gtp_debug_data_get(void *_data, u64 *val) { struct goodix_ts_data *ts = _data; int ret; u8 buf[3] = {0}; mutex_lock(&ts->input_dev->mutex); buf[0] = ts->addr >> 8; buf[1] = ts->addr & 0x00ff; if (gtp_debug_addr_is_valid(ts->addr)) { ret = gtp_i2c_read(ts->client, buf, 3); if (ret < 0) dev_err(&ts->client->dev, "GTP read register 0x%x failed (%d)\n", ts->addr, ret); else *val = buf[2]; } mutex_unlock(&ts->input_dev->mutex); return 0; } DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, gtp_debug_data_get, gtp_debug_data_set, "%llx\n"); static int gtp_debug_addr_set(void *_data, u64 val) { struct goodix_ts_data *ts = _data; if (gtp_debug_addr_is_valid(val)) { mutex_lock(&ts->input_dev->mutex); ts->addr = val; mutex_unlock(&ts->input_dev->mutex); } return 0; } static int gtp_debug_addr_get(void *_data, u64 *val) { struct goodix_ts_data *ts = _data; mutex_lock(&ts->input_dev->mutex); if (gtp_debug_addr_is_valid(ts->addr)) *val = ts->addr; mutex_unlock(&ts->input_dev->mutex); return 0; } DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, gtp_debug_addr_get, gtp_debug_addr_set, "%llx\n"); static int gtp_debug_suspend_set(void *_data, u64 val) { struct goodix_ts_data *ts = _data; mutex_lock(&ts->input_dev->mutex); if (val) goodix_ts_suspend(&ts->client->dev); else goodix_ts_resume(&ts->client->dev); mutex_unlock(&ts->input_dev->mutex); return 0; } static int gtp_debug_suspend_get(void *_data, u64 *val) { struct goodix_ts_data *ts = _data; mutex_lock(&ts->input_dev->mutex); *val = ts->gtp_is_suspend; mutex_unlock(&ts->input_dev->mutex); return 0; } DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, gtp_debug_suspend_get, gtp_debug_suspend_set, "%lld\n"); static int gtp_debugfs_init(struct goodix_ts_data *data) { data->debug_base = debugfs_create_dir(GTP_DEBUGFS_DIR, NULL); if (IS_ERR_OR_NULL(data->debug_base)) { dev_err(&data->client->dev, "Failed to create debugfs dir.\n"); return -EINVAL; } if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_SUSPEND, S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, data->debug_base, data, &debug_suspend_fops)))) { dev_err(&data->client->dev, "Failed to create suspend file.\n"); debugfs_remove_recursive(data->debug_base); return -EINVAL; } if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_DATA, S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, data->debug_base, data, &debug_data_fops)))) { dev_err(&data->client->dev, "Failed to create data file.\n"); debugfs_remove_recursive(data->debug_base); return -EINVAL; } if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_ADDR, S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, data->debug_base, data, &debug_addr_fops)))) { dev_err(&data->client->dev, "Failed to create addr file.\n"); debugfs_remove_recursive(data->debug_base); return -EINVAL; } return 0; } static int goodix_ts_get_dt_coords(struct device *dev, char *name, struct goodix_ts_platform_data *pdata) { struct property *prop; struct device_node *np = dev->of_node; int rc; u32 coords[GOODIX_COORDS_ARR_SIZE]; prop = of_find_property(np, name, NULL); if (!prop) return -EINVAL; if (!prop->value) return -ENODATA; rc = of_property_read_u32_array(np, name, coords, GOODIX_COORDS_ARR_SIZE); if (rc && (rc != -EINVAL)) { dev_err(dev, "Unable to read %s\n", name); return rc; } if (!strcmp(name, "goodix,panel-coords")) { pdata->panel_minx = coords[0]; pdata->panel_miny = coords[1]; pdata->panel_maxx = coords[2]; pdata->panel_maxy = coords[3]; } else if (!strcmp(name, "goodix,display-coords")) { pdata->x_min = coords[0]; pdata->y_min = coords[1]; pdata->x_max = coords[2]; pdata->y_max = coords[3]; } else { dev_err(dev, "unsupported property %s\n", name); return -EINVAL; } return 0; } static int goodix_parse_dt(struct device *dev, struct goodix_ts_platform_data *pdata) { int rc; struct device_node *np = dev->of_node; struct property *prop; u32 temp_val, num_buttons; u32 button_map[MAX_BUTTONS]; char prop_name[PROP_NAME_SIZE]; int i, read_cfg_num; rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata); if (rc && (rc != -EINVAL)) return rc; rc = goodix_ts_get_dt_coords(dev, "goodix,display-coords", pdata); if (rc) return rc; pdata->i2c_pull_up = of_property_read_bool(np, "goodix,i2c-pull-up"); pdata->force_update = of_property_read_bool(np, "goodix,force-update"); pdata->enable_power_off = of_property_read_bool(np, "goodix,enable-power-off"); pdata->have_touch_key = of_property_read_bool(np, "goodix,have-touch-key"); pdata->driver_send_cfg = of_property_read_bool(np, "goodix,driver-send-cfg"); pdata->change_x2y = of_property_read_bool(np, "goodix,change-x2y"); pdata->with_pen = of_property_read_bool(np, "goodix,with-pen"); pdata->slide_wakeup = of_property_read_bool(np, "goodix,slide-wakeup"); pdata->dbl_clk_wakeup = of_property_read_bool(np, "goodix,dbl_clk_wakeup"); /* reset, irq gpio info */ pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &pdata->reset_gpio_flags); if (pdata->reset_gpio < 0) return pdata->reset_gpio; pdata->irq_gpio = of_get_named_gpio_flags(np, "interrupt-gpios", 0, &pdata->irq_gpio_flags); if (pdata->irq_gpio < 0) return pdata->irq_gpio; rc = of_property_read_string(np, "goodix,product-id", &pdata->product_id); if (rc && (rc != -EINVAL)) { dev_err(dev, "Failed to parse product_id."); return -EINVAL; } rc = of_property_read_string(np, "goodix,fw_name", &pdata->fw_name); if (rc && (rc != -EINVAL)) { dev_err(dev, "Failed to parse firmware name.\n"); return -EINVAL; } prop = of_find_property(np, "goodix,button-map", NULL); if (prop) { num_buttons = prop->length / sizeof(temp_val); if (num_buttons > MAX_BUTTONS) return -EINVAL; rc = of_property_read_u32_array(np, "goodix,button-map", button_map, num_buttons); if (rc) { dev_err(dev, "Unable to read key codes\n"); return rc; } pdata->num_button = num_buttons; memcpy(pdata->button_map, button_map, pdata->num_button * sizeof(u32)); } read_cfg_num = 0; for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) { snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i); prop = of_find_property(np, prop_name, &pdata->config_data_len[i]); if (!prop || !prop->value) { pdata->config_data_len[i] = 0; pdata->config_data[i] = NULL; continue; } pdata->config_data[i] = devm_kzalloc(dev, GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH, GFP_KERNEL); if (!pdata->config_data[i]) { dev_err(dev, "Not enough memory for panel config data %d\n", i); return -ENOMEM; } pdata->config_data[i][0] = GTP_REG_CONFIG_DATA >> 8; pdata->config_data[i][1] = GTP_REG_CONFIG_DATA & 0xff; memcpy(&pdata->config_data[i][GTP_ADDR_LENGTH], prop->value, pdata->config_data_len[i]); read_cfg_num++; } dev_dbg(dev, "%d config data read from device tree.\n", read_cfg_num); return 0; } /******************************************************* Function: I2c probe. Input: client: i2c device struct. id: device id. Output: Executive outcomes. 0: succeed. *******************************************************/ static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct goodix_ts_platform_data *pdata; struct goodix_ts_data *ts; u16 version_info; int ret; dev_dbg(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr); if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, sizeof(struct goodix_ts_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; ret = goodix_parse_dt(&client->dev, pdata); if (ret) return ret; } else { pdata = client->dev.platform_data; } if (!pdata) { dev_err(&client->dev, "GTP invalid pdata\n"); return -EINVAL; } i2c_connect_client = client; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "GTP I2C not supported\n"); return -ENODEV; } ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; memset(ts, 0, sizeof(*ts)); ts->client = client; ts->pdata = pdata; /* For 2.6.39 & later use spin_lock_init(&ts->irq_lock) * For 2.6.39 & before, use ts->irq_lock = SPIN_LOCK_UNLOCKED */ spin_lock_init(&ts->irq_lock); i2c_set_clientdata(client, ts); ts->gtp_rawdiff_mode = 0; ts->power_on = false; ret = gtp_request_io_port(ts); if (ret) { dev_err(&client->dev, "GTP request IO port failed.\n"); goto exit_free_client_data; } ret = goodix_power_init(ts); if (ret) { dev_err(&client->dev, "GTP power init failed\n"); goto exit_free_io_port; } ret = goodix_power_on(ts); if (ret) { dev_err(&client->dev, "GTP power on failed\n"); goto exit_deinit_power; } gtp_reset_guitar(ts, 20); ret = gtp_i2c_test(client); if (ret != 2) { dev_err(&client->dev, "I2C communication ERROR!\n"); goto exit_power_off; } if (pdata->force_update) ts->force_update = true; if (pdata->fw_name) strlcpy(ts->fw_name, pdata->fw_name, strlen(pdata->fw_name) + 1); if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) { ret = gup_init_update_proc(ts); if (ret < 0) { dev_err(&client->dev, "GTP Create firmware update thread error.\n"); goto exit_power_off; } } ret = gtp_init_panel(ts); if (ret < 0) { dev_err(&client->dev, "GTP init panel failed.\n"); 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) { dev_err(&client->dev, "GTP request input dev failed.\n"); goto exit_free_inputdev; } input_set_drvdata(ts->input_dev, ts); mutex_init(&ts->lock); #if defined(CONFIG_FB) ts->fb_notif.notifier_call = fb_notifier_callback; ret = fb_register_client(&ts->fb_notif); if (ret) dev_err(&ts->client->dev, "Unable to register fb_notifier: %d\n", ret); #elif defined(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 ts->goodix_wq = create_singlethread_workqueue("goodix_wq"); INIT_WORK(&ts->work, goodix_ts_work_func); ret = gtp_request_irq(ts); if (ret) dev_info(&client->dev, "GTP request irq failed %d.\n", ret); else dev_info(&client->dev, "GTP works in interrupt mode.\n"); ret = gtp_read_fw_version(client, &version_info); if (ret != 2) dev_err(&client->dev, "GTP firmware version read failed.\n"); ret = gtp_check_product_id(client); if (ret != 0) { dev_err(&client->dev, "GTP Product id doesn't match.\n"); goto exit_free_irq; } if (ts->use_irq) gtp_irq_enable(ts); #ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG init_wr_node(client); #endif #if GTP_ESD_PROTECT gtp_esd_switch(client, SWITCH_ON); #endif ret = sysfs_create_group(&client->dev.kobj, >p_attr_grp); if (ret < 0) { dev_err(&client->dev, "sys file creation failed.\n"); goto exit_free_irq; } ret = gtp_debugfs_init(ts); if (ret != 0) { dev_err(&client->dev, "Failed to create debugfs entries, %d\n", ret); goto exit_remove_sysfs; } init_done = true; return 0; exit_free_irq: mutex_destroy(&ts->lock); #if defined(CONFIG_FB) if (fb_unregister_client(&ts->fb_notif)) dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&ts->early_suspend); #endif if (ts->use_irq) free_irq(client->irq, ts); cancel_work_sync(&ts->work); flush_workqueue(ts->goodix_wq); destroy_workqueue(ts->goodix_wq); input_unregister_device(ts->input_dev); if (ts->input_dev) { input_free_device(ts->input_dev); ts->input_dev = NULL; } exit_remove_sysfs: sysfs_remove_group(&ts->input_dev->dev.kobj, >p_attr_grp); exit_free_inputdev: kfree(ts->config_data); exit_power_off: goodix_power_off(ts); exit_deinit_power: goodix_power_deinit(ts); exit_free_io_port: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); exit_free_client_data: i2c_set_clientdata(client, NULL); return ret; } /******************************************************* Function: Goodix touchscreen driver release function. Input: client: i2c device struct. Output: Executive outcomes. 0---succeed. *******************************************************/ static int goodix_ts_remove(struct i2c_client *client) { struct goodix_ts_data *ts = i2c_get_clientdata(client); sysfs_remove_group(&ts->input_dev->dev.kobj, >p_attr_grp); #if defined(CONFIG_FB) if (fb_unregister_client(&ts->fb_notif)) dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&ts->early_suspend); #endif mutex_destroy(&ts->lock); #ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG uninit_wr_node(); #endif #if GTP_ESD_PROTECT cancel_work_sync(gtp_esd_check_workqueue); flush_workqueue(gtp_esd_check_workqueue); destroy_workqueue(gtp_esd_check_workqueue); #endif if (ts) { if (ts->use_irq) free_irq(client->irq, ts); cancel_work_sync(&ts->work); flush_workqueue(ts->goodix_wq); destroy_workqueue(ts->goodix_wq); input_unregister_device(ts->input_dev); if (ts->input_dev) { input_free_device(ts->input_dev); ts->input_dev = NULL; } if (gpio_is_valid(ts->pdata->reset_gpio)) gpio_free(ts->pdata->reset_gpio); if (gpio_is_valid(ts->pdata->irq_gpio)) gpio_free(ts->pdata->irq_gpio); goodix_power_off(ts); goodix_power_deinit(ts); i2c_set_clientdata(client, NULL); } debugfs_remove_recursive(ts->debug_base); return 0; } #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB) /******************************************************* Function: Early suspend function. Input: h: early_suspend struct. Output: None. *******************************************************/ static int goodix_ts_suspend(struct device *dev) { struct goodix_ts_data *ts = dev_get_drvdata(dev); int ret = 0, i; if (ts->gtp_is_suspend) { dev_dbg(&ts->client->dev, "Already in suspend state.\n"); return 0; } mutex_lock(&ts->lock); if (ts->fw_loading) { dev_info(&ts->client->dev, "Fw upgrade in progress, can't go to suspend."); mutex_unlock(&ts->lock); return 0; } #if GTP_ESD_PROTECT gtp_esd_switch(ts->client, SWITCH_OFF); #endif if (ts->pdata->slide_wakeup) { ret = gtp_enter_doze(ts); } else { if (ts->use_irq) gtp_irq_disable(ts); for (i = 0; i < GTP_MAX_TOUCH; i++) gtp_touch_up(ts, i); input_sync(ts->input_dev); ret = gtp_enter_sleep(ts); if (ret < 0) dev_err(&ts->client->dev, "GTP early suspend failed.\n"); } /* to avoid waking up while not sleeping, * delay 48 + 10ms to ensure reliability */ msleep(58); mutex_unlock(&ts->lock); ts->gtp_is_suspend = 1; return ret; } /******************************************************* Function: Late resume function. Input: h: early_suspend struct. Output: None. *******************************************************/ static int goodix_ts_resume(struct device *dev) { struct goodix_ts_data *ts = dev_get_drvdata(dev); int ret = 0; if (!ts->gtp_is_suspend) { dev_dbg(&ts->client->dev, "Already in awake state.\n"); return 0; } mutex_lock(&ts->lock); ret = gtp_wakeup_sleep(ts); if (ts->pdata->slide_wakeup) doze_status = DOZE_DISABLED; if (ret <= 0) dev_err(&ts->client->dev, "GTP resume failed.\n"); if (ts->use_irq) gtp_irq_enable(ts); #if GTP_ESD_PROTECT gtp_esd_switch(ts->client, SWITCH_ON); #endif mutex_unlock(&ts->lock); ts->gtp_is_suspend = 0; return ret; } #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct fb_event *evdata = data; int *blank; struct goodix_ts_data *ts = container_of(self, struct goodix_ts_data, fb_notif); if (evdata && evdata->data && event == FB_EVENT_BLANK && ts && ts->client) { blank = evdata->data; if (*blank == FB_BLANK_UNBLANK) goodix_ts_resume(&ts->client->dev); else if (*blank == FB_BLANK_POWERDOWN) goodix_ts_suspend(&ts->client->dev); } return 0; } #elif defined(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; ts = container_of(h, struct goodix_ts_data, early_suspend); goodix_ts_suspend(&ts->client->dev); return; } /******************************************************* 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; ts = container_of(h, struct goodix_ts_data, early_suspend); goodix_ts_late_resume(ts); } #endif #endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/ #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 i2c_client *client, int on) { struct goodix_ts_data *ts; ts = i2c_get_clientdata(client); if (SWITCH_ON == on) { /* switch on esd */ if (!ts->esd_running) { ts->esd_running = 1; dev_dbg(&client->dev, "Esd started\n"); 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; dev_dbg(&client->dev, "Esd cancelled\n"); 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 int gtp_init_ext_watchdog(struct i2c_client *client) { /* in case of recursively reset by calling gtp_i2c_write*/ struct i2c_msg msg; u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA}; int ret; int retries = 0; msg.flags = !I2C_M_RD; msg.addr = client->addr; msg.len = 4; msg.buf = opr_buffer; while (retries < GTP_I2C_RETRY_5) { ret = i2c_transfer(client->adapter, &msg, 1); if (ret == 1) return 1; retries++; } if (retries == GTP_I2C_RETRY_5) dev_err(&client->dev, "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 retry; s32 ret = -1; struct goodix_ts_data *ts = NULL; u8 test[4] = {0x80, 0x40}; ts = i2c_get_clientdata(i2c_connect_client); if (ts->gtp_is_suspend) { dev_dbg(&ts->client->dev, "Esd terminated!\n"); ts->esd_running = 0; return; } #ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE if (ts->enter_update) return; #endif for (retry = 0; retry < GTP_I2C_RETRY_3; retry++) { ret = gtp_i2c_read(ts->client, test, 4); if ((ret < 0)) { /* IC works abnormally..*/ continue; } else { if ((test[2] == 0xAA) || (test[3] != 0xAA)) { /* IC works abnormally..*/ retry = GTP_I2C_RETRY_3; break; } /* IC works normally, Write 0x8040 0xAA*/ test[2] = 0xAA; gtp_i2c_write(ts->client, test, 3); break; } } if (retry == GTP_I2C_RETRY_3) { dev_err(&ts->client->dev, "IC Working ABNORMALLY, Resetting Guitar...\n"); gtp_reset_guitar(ts, 50); } if (!ts->gtp_is_suspend) queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); else { dev_dbg(&ts->client->dev, "Esd terminated!\n"); ts->esd_running = 0; } return; } #endif #if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) static const struct dev_pm_ops goodix_ts_dev_pm_ops = { .suspend = goodix_ts_suspend, .resume = goodix_ts_resume, }; #else static const struct dev_pm_ops goodix_ts_dev_pm_ops = { }; #endif static const struct i2c_device_id goodix_ts_id[] = { { GTP_I2C_NAME, 0 }, { } }; static struct of_device_id goodix_match_table[] = { { .compatible = "goodix,gt9xx", }, { }, }; static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, .remove = goodix_ts_remove, #ifdef CONFIG_HAS_EARLYSUSPEND .suspend = goodix_ts_early_suspend, .resume = goodix_ts_late_resume, #endif .id_table = goodix_ts_id, .driver = { .name = GTP_I2C_NAME, .owner = THIS_MODULE, .of_match_table = goodix_match_table, #if CONFIG_PM .pm = &goodix_ts_dev_pm_ops, #endif }, }; /******************************************************* Function: Driver Install function. Input: None. Output: Executive Outcomes. 0---succeed. ********************************************************/ static int __init goodix_ts_init(void) { int ret; #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 ret = i2c_add_driver(&goodix_ts_driver); return ret; } /******************************************************* Function: Driver uninstall function. Input: None. Output: Executive Outcomes. 0---succeed. ********************************************************/ static void __exit goodix_ts_exit(void) { i2c_del_driver(&goodix_ts_driver); } module_init(goodix_ts_init); module_exit(goodix_ts_exit); MODULE_DESCRIPTION("GTP Series Driver"); MODULE_LICENSE("GPL");