/* * Goodix I2C Module * Hardware interface layer of touchdriver architecture. * * Copyright (C) 2019 - 2020 Goodix, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include "goodix_ts_core.h" #include "goodix_cfg_bin.h" #define TS_DRIVER_NAME "gtx8" #define I2C_MAX_TRANSFER_SIZE 256 #define TS_ADDR_LENGTH 2 #define TS_DOZE_ENABLE_RETRY_TIMES 3 #define TS_DOZE_DISABLE_RETRY_TIMES 9 #define TS_WAIT_CFG_READY_RETRY_TIMES 30 #define TS_WAIT_CMD_FREE_RETRY_TIMES 10 #define TS_REG_COORDS_BASE 0x824E #define TS_REG_CMD 0x8040 #define TS_REG_REQUEST 0x8044 #define TS_REG_VERSION 0x8240 #define TS_REG_CFG_BASE 0x8050 #define TS_REG_DOZE_CTRL 0x30F0 #define TS_REG_DOZE_STAT 0x3100 #define TS_REG_ESD_TICK_R 0x3103 #define CFG_XMAX_OFFSET (0x8052 - 0x8050) #define CFG_YMAX_OFFSET (0x8054 - 0x8050) #define REQUEST_HANDLED 0x00 #define REQUEST_CONFIG 0x01 #define REQUEST_BAKREF 0x02 #define REQUEST_RESET 0x03 #define REQUEST_RELOADFW 0x05 #define REQUEST_IDLE 0xff #define COMMAND_SLEEP 0x05 #define COMMAND_CLOSE_HID 0xaa #define COMMAND_START_SEND_CFG 0x80 #define COMMAND_END_SEND_CFG 0x83 #define COMMAND_SEND_SMALL_CFG 0x81 #define COMMAND_SEND_CFG_PREPARE_OK 0x82 #define COMMAND_START_READ_CFG 0x86 #define COMMAND_READ_CFG_PREPARE_OK 0x85 #define COMMAND_END_SEND_CFG_YS 0x7D #define BYTES_PER_COORD 8 #define TS_MAX_SENSORID 5 #define TS_CFG_HEAD_LEN 4 #define TS_CFG_HEAD_LEN_YS 5 #define TS_CFG_BAG_NUM_INDEX 2 #define TS_CFG_BAG_START_INDEX 4 #define TS_DOZE_DISABLE_DATA 0xAA #define TS_DOZE_CLOSE_OK_DATA 0xBB #define TS_DOZE_ENABLE_DATA 0xCC #define TS_CMD_REG_READY 0xFF #define TS_CMD_CFG_ERR 0x7E #define TS_CMD_CFG_OK 0x7F #define POINT_TYPE_STYLUS 0x01 #define POINT_TYPE_STYLUS_HOVER 0x03 enum TS_SEND_CFG_REPLY { TS_CFG_REPLY_PKGS_ERR = 0x01, TS_CFG_REPLY_CHKSUM_ERR = 0x02, TS_CFG_REPLY_DATA_ERR = 0x03, TS_CFG_REPLY_DATA_EQU = 0x07, }; #define IRQ_HEAD_LEN_YS 8 #define IRQ_HEAD_LEN_NOR 2 int goodix_ts_core_init(void); #ifdef CONFIG_OF /** * goodix_parse_dt_resolution - parse resolution from dt * @node: devicetree node * @board_data: pointer to board data structure * return: 0 - no error, <0 error */ static int goodix_parse_dt_resolution(struct device_node *node, struct goodix_ts_board_data *board_data) { int r, err; r = of_property_read_u32(node, "goodix,panel-max-x", &board_data->panel_max_x); if (r) err = -ENOENT; r = of_property_read_u32(node, "goodix,panel-max-y", &board_data->panel_max_y); if (r) err = -ENOENT; r = of_property_read_u32(node, "goodix,panel-max-w", &board_data->panel_max_w); if (r) err = -ENOENT; board_data->swap_axis = of_property_read_bool(node, "goodix,swap-axis"); board_data->x2x = of_property_read_bool(node, "goodix,x2x"); board_data->y2y = of_property_read_bool(node, "goodix,y2y"); return 0; } /** * goodix_parse_dt - parse board data from dt * @dev: pointer to device * @board_data: pointer to board data structure * return: 0 - no error, <0 error */ static int goodix_parse_dt(struct device_node *node, struct goodix_ts_board_data *board_data) { struct property *prop; const char *name_tmp; int r; if (!board_data) { ts_err("invalid board data"); return -EINVAL; } r = of_get_named_gpio(node, "goodix,reset-gpio", 0); if (r < 0) { ts_err("invalid reset-gpio in dt: %d", r); return -EINVAL; } ts_info("get reset-gpio[%d] from dt", r); board_data->reset_gpio = r; r = of_get_named_gpio(node, "goodix,irq-gpio", 0); if (r < 0) { ts_err("invalid irq-gpio in dt: %d", r); return -EINVAL; } ts_info("get irq-gpio[%d] from dt", r); board_data->irq_gpio = r; r = of_property_read_u32(node, "goodix,irq-flags", &board_data->irq_flags); if (r) { ts_err("invalid irq-flags"); return -EINVAL; } memset(board_data->avdd_name, 0, sizeof(board_data->avdd_name)); r = of_property_read_string(node, "goodix,avdd-name", &name_tmp); if (!r) { ts_info("avdd name form dt: %s", name_tmp); if (strlen(name_tmp) < sizeof(board_data->avdd_name)) strncpy(board_data->avdd_name, name_tmp, sizeof(board_data->avdd_name)); else ts_info("invalied avdd name length: %ld > %ld", strlen(name_tmp), sizeof(board_data->avdd_name)); } r = of_property_read_u32(node, "goodix,power-on-delay-us", &board_data->power_on_delay_us); if (!r) { /* 1000ms is too large, maybe you have pass a wrong value */ if (board_data->power_on_delay_us > 1000 * 1000) { ts_err("Power on delay time exceed 1s, please check"); board_data->power_on_delay_us = 0; } } r = of_property_read_u32(node, "goodix,power-off-delay-us", &board_data->power_off_delay_us); if (!r) { /* 1000ms is too large, maybe you have pass */ if (board_data->power_off_delay_us > 1000 * 1000) { ts_err("Power off delay time exceed 1s, please check"); board_data->power_off_delay_us = 0; } } /* get xyz resolutions */ r = goodix_parse_dt_resolution(node, board_data); if (r < 0) { ts_err("Failed to parse resolutions:%d", r); return r; } /* key map */ prop = of_find_property(node, "goodix,panel-key-map", NULL); if (prop && prop->length) { if (prop->length / sizeof(u32) > GOODIX_MAX_TP_KEY) { ts_err("Size of panel-key-map is invalid"); return r; } board_data->panel_max_key = prop->length / sizeof(u32); board_data->tp_key_num = prop->length / sizeof(u32); r = of_property_read_u32_array(node, "goodix,panel-key-map", &board_data->panel_key_map[0], board_data->panel_max_key); if (r) { ts_err("failed get key map, %d", r); return r; } } /*get pen-enable switch and pen keys, must after "key map"*/ board_data->pen_enable = of_property_read_bool(node, "goodix,pen-enable"); if (board_data->pen_enable) ts_info("goodix pen enabled"); ts_info("***key:%d, %d, %d, %d", board_data->panel_key_map[0], board_data->panel_key_map[1], board_data->panel_key_map[2], board_data->panel_key_map[3]); ts_debug("[DT]x:%d, y:%d, w:%d, p:%d", board_data->panel_max_x, board_data->panel_max_y, board_data->panel_max_w, board_data->panel_max_p); return 0; } #endif int goodix_i2c_test(struct goodix_ts_device *dev) { #define TEST_ADDR 0x4100 #define TEST_LEN 1 struct i2c_client *client = to_i2c_client(dev->dev); unsigned char test_buf[TEST_LEN + 1], addr_buf[2]; struct i2c_msg msgs[] = { { .addr = client->addr, .flags = !I2C_M_RD, .buf = &addr_buf[0], .len = TS_ADDR_LENGTH, }, { .addr = client->addr, .flags = I2C_M_RD, .buf = &test_buf[0], .len = TEST_LEN, } }; msgs[0].buf[0] = (TEST_ADDR >> 8) & 0xFF; msgs[0].buf[1] = TEST_ADDR & 0xFF; if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) return 0; /* test failed */ return -EINVAL; } /* confirm current device is goodix or not. * If confirmed 0 will return. */ static int goodix_ts_dev_confirm(struct goodix_ts_device *ts_dev) { #define DEV_CONFIRM_RETRY 3 int retry; for (retry = 0; retry < DEV_CONFIRM_RETRY; retry++) { gpio_direction_output(ts_dev->board_data.reset_gpio, 0); udelay(2000); gpio_direction_output(ts_dev->board_data.reset_gpio, 1); mdelay(5); if (!goodix_i2c_test(ts_dev)) { msleep(95); return 0; } } return -EINVAL; } /** * goodix_i2c_read_trans - read device register through i2c bus * @dev: pointer to device data * @addr: register address * @data: read buffer * @len: bytes to read * return: 0 - read ok, < 0 - i2c transter error */ int goodix_i2c_read_trans(struct goodix_ts_device *dev, unsigned int reg, unsigned char *data, unsigned int len) { struct i2c_client *client = to_i2c_client(dev->dev); unsigned int transfer_length = 0; unsigned int pos = 0, address = reg; unsigned char get_buf[64], addr_buf[2]; int retry, r = 0; struct i2c_msg msgs[] = { { .addr = client->addr, .flags = !I2C_M_RD, .buf = &addr_buf[0], .len = TS_ADDR_LENGTH, }, { .addr = client->addr, .flags = I2C_M_RD, } }; if (likely(len < sizeof(get_buf))) { /* code optimize, use stack memory */ msgs[1].buf = &get_buf[0]; } else { msgs[1].buf = kzalloc(I2C_MAX_TRANSFER_SIZE < len ? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL); if (msgs[1].buf == NULL) return -ENOMEM; } while (pos != len) { if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE)) transfer_length = I2C_MAX_TRANSFER_SIZE; else transfer_length = len - pos; msgs[0].buf[0] = (address >> 8) & 0xFF; msgs[0].buf[1] = address & 0xFF; msgs[1].len = transfer_length; for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) { memcpy(&data[pos], msgs[1].buf, transfer_length); pos += transfer_length; address += transfer_length; break; } ts_info("I2c read retry[%d]:0x%x", retry + 1, reg); msleep(20); } if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", client->addr, reg, len); r = -EBUS; goto read_exit; } } read_exit: if (unlikely(len >= sizeof(get_buf))) kfree(msgs[1].buf); return r; } /** * goodix_i2c_write_trans - write device register through i2c bus * @dev: pointer to device data * @addr: register address * @data: write buffer * @len: bytes to write * return: 0 - write ok; < 0 - i2c transter error. */ int goodix_i2c_write_trans(struct goodix_ts_device *dev, unsigned int reg, unsigned char *data, unsigned int len) { struct i2c_client *client = to_i2c_client(dev->dev); unsigned int pos = 0, transfer_length = 0; unsigned int address = reg; unsigned char put_buf[64]; int retry, r = 0; struct i2c_msg msg = { .addr = client->addr, .flags = !I2C_M_RD, }; if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { /* code optimize,use stack memory*/ msg.buf = &put_buf[0]; } else { msg.buf = kmalloc(I2C_MAX_TRANSFER_SIZE < len + TS_ADDR_LENGTH ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, GFP_KERNEL); if (msg.buf == NULL) return -ENOMEM; } while (pos != len) { if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) transfer_length = I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; else transfer_length = len - pos; msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); msg.buf[1] = (unsigned char)(address & 0xFF); msg.len = transfer_length + 2; memcpy(&msg.buf[2], &data[pos], transfer_length); for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { if (likely(i2c_transfer(client->adapter, &msg, 1) == 1)) { pos += transfer_length; address += transfer_length; break; } ts_debug("I2c write retry[%d]", retry + 1); msleep(20); } if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { ts_err("I2c write failed,dev:%02x,reg:%04x,size:%u", client->addr, reg, len); r = -EBUS; goto write_exit; } } write_exit: if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) kfree(msg.buf); return r; } /** * goodix_set_i2c_doze_mode - disable or enable doze mode * @dev: pointer to device data * @enable: true/flase * return: 0 - ok; < 0 - error. * This func must be used in pairs, when you disable doze * mode, then you must enable it again. * Between set_doze_false and set_doze_true, do not reset * IC! */ static int goodix_set_i2c_doze_mode(struct goodix_ts_device *dev, int enable) { static DEFINE_MUTEX(doze_mode_lock); static int doze_mode_set_count; int result = -EINVAL; int i; u8 w_data, r_data; if (dev->ic_type != IC_TYPE_NORMANDY) return 0; mutex_lock(&doze_mode_lock); if (enable) { if (doze_mode_set_count != 0) doze_mode_set_count--; /*when count equal 0, allow ic enter doze mode*/ if (doze_mode_set_count == 0) { w_data = TS_DOZE_ENABLE_DATA; for (i = 0; i < TS_DOZE_ENABLE_RETRY_TIMES; i++) { result = goodix_i2c_write_trans(dev, TS_REG_DOZE_CTRL, &w_data, 1); if (!result) { result = 0; goto exit; } usleep_range(1000, 1100); } if (i >= TS_DOZE_ENABLE_RETRY_TIMES) ts_err("i2c doze mode enable failed"); } else { /*ts_info("doze count not euqal 0, * so skip doze mode enable"); */ result = 0; goto exit; } } else { doze_mode_set_count++; if (doze_mode_set_count == 1) { w_data = TS_DOZE_DISABLE_DATA; goodix_i2c_write_trans(dev, TS_REG_DOZE_CTRL, &w_data, 1); usleep_range(1000, 1100); for (i = 0; i < TS_DOZE_DISABLE_RETRY_TIMES; i++) { goodix_i2c_read_trans(dev, TS_REG_DOZE_STAT, &r_data, 1); if (TS_DOZE_CLOSE_OK_DATA == r_data) { result = 0; goto exit; } else if (0xAA != r_data) { w_data = TS_DOZE_DISABLE_DATA; goodix_i2c_write_trans(dev, TS_REG_DOZE_CTRL, &w_data, 1); } usleep_range(10000, 10100); } ts_err("doze mode disable FAILED"); } else { result = 0; goto exit; } } exit: mutex_unlock(&doze_mode_lock); return result; } /** * goodix_i2c_write - write device register through i2c bus * @dev: pointer to device data * @addr: register address * @data: write buffer * @len: bytes to write * return: 0 - write ok; < 0 - i2c transter error. */ int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg, unsigned char *data, unsigned int len) { int r = -EINVAL; if (goodix_set_i2c_doze_mode(dev, false)) { ts_err(" faild disable doze, i2c write:0x%04x", reg); goto exit; } r = goodix_i2c_write_trans(dev, reg, data, len); exit: if (goodix_set_i2c_doze_mode(dev, true)) ts_err("failed enable doze write:0x%04x", reg); return r; } /** * goodix_i2c_read - read device register through i2c bus * @dev: pointer to device data * @addr: register address * @data: read buffer * @len: bytes to read * return: 0 - read ok, < 0 - i2c transter error */ int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg, unsigned char *data, unsigned int len) { int r = -EINVAL; if (goodix_set_i2c_doze_mode(dev, false)) { ts_err("failed disable doze:0x%04x", reg); goto exit; } r = goodix_i2c_read_trans(dev, reg, data, len); exit: if (goodix_set_i2c_doze_mode(dev, true)) ts_err("failed enable doze :0x%04x", reg); return r; } /** * goodix_i2c_write_trans_once * write device register through i2c bus, no retry * @dev: pointer to device data * @addr: register address * @data: write buffer * @len: bytes to write * return: 0 - write ok; < 0 - i2c transter error. */ int goodix_i2c_write_trans_once(struct goodix_ts_device *dev, unsigned int reg, unsigned char *data, unsigned int len) { struct i2c_client *client = to_i2c_client(dev->dev); unsigned int pos = 0, transfer_length = 0; unsigned int address = reg; unsigned char put_buf[64]; struct i2c_msg msg = { .addr = client->addr, .flags = !I2C_M_RD, }; if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { /* code optimize,use stack memory*/ msg.buf = &put_buf[0]; } else { msg.buf = kmalloc(I2C_MAX_TRANSFER_SIZE < len + TS_ADDR_LENGTH ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, GFP_KERNEL); if (msg.buf == NULL) { ts_err("Malloc failed"); return -ENOMEM; } } while (pos != len) { if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) transfer_length = I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; else transfer_length = len - pos; msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); msg.buf[1] = (unsigned char)(address & 0xFF); msg.len = transfer_length + 2; memcpy(&msg.buf[2], &data[pos], transfer_length); i2c_transfer(client->adapter, &msg, 1); pos += transfer_length; address += transfer_length; } if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) kfree(msg.buf); return 0; } static void goodix_cmd_init(struct goodix_ts_device *dev, struct goodix_ts_cmd *ts_cmd, u8 cmds, u16 cmd_data, u32 reg_addr) { u16 checksum = 0; ts_cmd->initialized = false; if (!reg_addr || !cmds) return; if (dev->ic_type == IC_TYPE_YELLOWSTONE) { ts_cmd->cmd_reg = reg_addr; ts_cmd->length = 5; ts_cmd->cmds[0] = cmds; ts_cmd->cmds[1] = (cmd_data >> 8) & 0xFF; ts_cmd->cmds[2] = cmd_data & 0xFF; checksum = ts_cmd->cmds[0] + ts_cmd->cmds[1] + ts_cmd->cmds[2]; ts_cmd->cmds[3] = (checksum >> 8) & 0xFF; ts_cmd->cmds[4] = checksum & 0xFF; ts_cmd->initialized = true; } else if (dev->ic_type == IC_TYPE_NORMANDY) { ts_cmd->cmd_reg = reg_addr; ts_cmd->length = 3; ts_cmd->cmds[0] = cmds; ts_cmd->cmds[1] = cmd_data & 0xFF; ts_cmd->cmds[2] = 0 - cmds - cmd_data; ts_cmd->initialized = true; } else { ts_err("unsupported ic type"); } } /** * goodix_send_command - seng cmd to firmware * * @dev: pointer to device * @cmd: pointer to command struct which cotain command data * Returns 0 - succeed,<0 - failed */ int goodix_send_command(struct goodix_ts_device *dev, struct goodix_ts_cmd *cmd) { int ret; if (!cmd || !cmd->initialized) return -EINVAL; ret = goodix_i2c_write(dev, cmd->cmd_reg, cmd->cmds, cmd->length); return ret; } static int goodix_read_version(struct goodix_ts_device *dev, struct goodix_ts_version *version) { u8 buffer[GOODIX_PID_MAX_LEN + 1]; u8 temp_buf[256]; u16 checksum = 0; u8 pid_read_len = dev->reg.pid_len; u8 vid_read_len = dev->reg.vid_len; u8 sensor_id_mask = dev->reg.sensor_id_mask; int r; if (!version) { ts_err("pointer of version is NULL"); return -EINVAL; } version->valid = false; /*check reg info valid*/ if (!dev->reg.pid || !dev->reg.sensor_id || !dev->reg.vid) { ts_err("reg is NULL, pid:0x%04x, vid:0x%04x, sensor_id:0x%04x", dev->reg.pid, dev->reg.vid, dev->reg.sensor_id); return -EINVAL; } if (!pid_read_len || pid_read_len > GOODIX_PID_MAX_LEN || !vid_read_len || vid_read_len > GOODIX_VID_MAX_LEN) { ts_err("invalied pid vid length, pid_len:%d, vid_len:%d", pid_read_len, vid_read_len); return -EINVAL; } /*disable doze mode, just valid for normandy * this func must be used in pairs */ if (goodix_set_i2c_doze_mode(dev, false)) { ts_err("failed disable doze"); r = -EINVAL; goto exit; } /*check checksum*/ if (dev->reg.version_base && dev->reg.version_len < sizeof(temp_buf)) { r = goodix_i2c_read(dev, dev->reg.version_base, temp_buf, dev->reg.version_len); if (r < 0) { ts_err("Read version base failed, reg:0x%02x, len:%d", dev->reg.version_base, dev->reg.version_len); if (version) version->valid = false; goto exit; } if (dev->ic_type == IC_TYPE_YELLOWSTONE) checksum = checksum_u8_ys(temp_buf, dev->reg.version_len); else checksum = checksum_u8(temp_buf, dev->reg.version_len); if (checksum) { ts_err("checksum error:0x%02x, base:0x%02x, len:%d", checksum, dev->reg.version_base, dev->reg.version_len); ts_err("%*ph", (int)(dev->reg.version_len / 2), temp_buf); ts_err("%*ph", (int)(dev->reg.version_len - dev->reg.version_len / 2), &temp_buf[dev->reg.version_len / 2]); if (version) version->valid = false; r = -EINVAL; goto exit; } } /*read pid*/ memset(buffer, 0, sizeof(buffer)); memset(version->pid, 0, sizeof(version->pid)); r = goodix_i2c_read(dev, dev->reg.pid, buffer, pid_read_len); if (r < 0) { ts_err("Read pid failed"); if (version) version->valid = false; goto exit; } /* check pid is digit or not, current we only support digital pid */ if (!isdigit(buffer[0]) || !isdigit(buffer[1])) { ts_err("pid not digit: 0x%x,0x%x", buffer[0], buffer[1]); r = -EINVAL; goto exit; } memcpy(version->pid, buffer, pid_read_len); /*read vid*/ memset(buffer, 0, sizeof(buffer)); memset(version->vid, 0, sizeof(version->vid)); r = goodix_i2c_read(dev, dev->reg.vid, buffer, vid_read_len); if (r < 0) { ts_err("Read vid failed"); if (version) version->valid = false; goto exit; } memcpy(version->vid, buffer, vid_read_len); /*read sensor_id*/ memset(buffer, 0, sizeof(buffer)); r = goodix_i2c_read(dev, dev->reg.sensor_id, buffer, 1); if (r < 0) { ts_err("Read sensor_id failed"); if (version) version->valid = false; goto exit; } if (sensor_id_mask != 0) { version->sensor_id = buffer[0] & sensor_id_mask; ts_info("sensor_id_mask:0x%02x, sensor_id:0x%02x", sensor_id_mask, version->sensor_id); } else { version->sensor_id = buffer[0]; } version->valid = true; ts_info("PID:%s,SensorID:%d, VID:%*ph", version->pid, version->sensor_id, (int)sizeof(version->vid), version->vid); exit: /*enable doze mode, just valid for normandy * this func must be used in pairs */ goodix_set_i2c_doze_mode(dev, true); return r; } static int goodix_wait_cfg_cmd_ready(struct goodix_ts_device *dev, u8 right_cmd, u8 send_cmd) { int try_times = 0; u8 cmd_flag = 0; u8 cmd_buf[3] = {0}; u16 command_reg = dev->reg.command; struct goodix_ts_cmd ts_cmd; goodix_cmd_init(dev, &ts_cmd, send_cmd, 0, command_reg); for (try_times = 0; try_times < TS_WAIT_CFG_READY_RETRY_TIMES; try_times++) { if (goodix_i2c_read(dev, command_reg, cmd_buf, 3)) { ts_err("Read cmd_reg error"); return -EINVAL; } cmd_flag = cmd_buf[0]; if (cmd_flag == right_cmd) { return 0; } else if (cmd_flag != send_cmd) { ts_err("failed cmd_reg:0x%X, 0x%X, 0x%X", cmd_buf[0], cmd_buf[1], cmd_buf[2]); if (goodix_send_command(dev, &ts_cmd)) { ts_err("Resend cmd 0x%02X FAILED", send_cmd); return -EINVAL; } } usleep_range(10000, 11000); } return -EINVAL; } static int _do_goodix_send_config(struct goodix_ts_device *dev, struct goodix_ts_config *config) { int r = 0; int try_times = 0; u8 buf[3] = {0}; u16 command_reg = dev->reg.command; u16 cfg_reg = dev->reg.cfg_addr; struct goodix_ts_cmd ts_cmd; /*1. Inquire command_reg until it's free*/ for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; try_times++) { if (!goodix_i2c_read(dev, command_reg, buf, 1) && buf[0] == TS_CMD_REG_READY) break; usleep_range(10000, 11000); } if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { ts_err("failed send cfg, reg:0x%04x is not 0xff", command_reg); r = -EINVAL; goto exit; } /*2. send "start write cfg" command*/ goodix_cmd_init(dev, &ts_cmd, COMMAND_START_SEND_CFG, 0, dev->reg.command); if (goodix_send_command(dev, &ts_cmd)) { ts_err("failed send cfg, COMMAND_START_SEND_CFG ERROR"); r = -EINVAL; goto exit; } /*3. wait ic set command_reg to 0x82*/ if (goodix_wait_cfg_cmd_ready(dev, COMMAND_SEND_CFG_PREPARE_OK, COMMAND_START_SEND_CFG)) { ts_err("failed send cfg, reg:0x%04x is not 0x82", command_reg); r = -EINVAL; goto exit; } /*4. write cfg*/ if (goodix_i2c_write(dev, cfg_reg, config->data, config->length)) { ts_err("Send cfg FAILED, write cfg to fw ERROR"); r = -EINVAL; goto exit; } /*5. send "end send cfg" command*/ goodix_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG, 0, dev->reg.command); if (goodix_send_command(dev, &ts_cmd)) { ts_err("failed send cfg, COMMAND_END_SEND_CFG ERROR"); r = -EINVAL; goto exit; } if (dev->ic_type == IC_TYPE_YELLOWSTONE) { /*6. wait 0x7f or 0x7e */ for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; try_times++) { r = goodix_i2c_read(dev, command_reg, buf, 3); if (!r && (buf[0] == TS_CMD_CFG_ERR || buf[0] == TS_CMD_CFG_OK)) break; usleep_range(10000, 11000); } ts_info("send config result: %*ph", 3, buf); /* set 0x7D to end send config process */ goodix_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG_YS, 0, dev->reg.command); if (goodix_send_command(dev, &ts_cmd)) { ts_err("failed send cfg end cmd"); r = -EINVAL; goto exit; } if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { ts_err("failed get result"); r = -EINVAL; goto exit; } if (buf[0] == TS_CMD_CFG_ERR) { if (buf[2] != TS_CFG_REPLY_DATA_EQU) ts_err("failed send cfg"); else ts_info("config data equal with flash"); r = -EINVAL; goto exit; } } else { /*6. wait ic set command_reg to 0xff*/ for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; try_times++) { if (!goodix_i2c_read(dev, command_reg, buf, 1) && buf[0] == TS_CMD_REG_READY) break; usleep_range(10000, 11000); } if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { ts_err("failed send cfg, reg:0x%04x is 0x%x not 0xff", command_reg, buf[0]); r = -EINVAL; goto exit; } } ts_info("Send cfg SUCCESS"); r = 0; exit: return r; } /*static int goodix_check_cfg_valid(struct goodix_ts_device *dev, u8 *cfg, u32 length) { int ret; u8 bag_num; u8 checksum; int i, j; int bag_start = 0; int bag_end = 0; if (!cfg || length < TS_CFG_HEAD_LEN) { ts_err("cfg is INVALID, len:%d", length); ret = -EINVAL; goto exit; } checksum = 0; for (i = 0; i < TS_CFG_HEAD_LEN; i++) checksum += cfg[i]; if (checksum != 0) { ts_err("cfg head checksum ERROR, checksum:0x%02x", checksum); ret = -EINVAL; goto exit; } bag_num = cfg[TS_CFG_BAG_NUM_INDEX]; bag_start = TS_CFG_BAG_START_INDEX; ts_info("cfg bag_num:%d, cfg length:%d", bag_num, length); for (j = 0; j < bag_num; j++) { if (bag_start >= length - 1) { ts_err("ERROR, overflow!!bag_start:%d, cfg_len:%d", bag_start, length); ret = -EINVAL; goto exit; } bag_end = bag_start + cfg[bag_start + 1] + 3; checksum = 0; if (bag_end > length) { ts_err("ERROR, overflow!!bag:%d, bag_start:%d," "bag_end:%d, cfg length:%d", j, bag_start, bag_end, length); ret = -EINVAL; goto exit; } for (i = bag_start; i < bag_end; i++) checksum += cfg[i]; if (checksum != 0) { ts_err("cfg INVALID, bag:%d checksum ERROR:0x%02x", j, checksum); ret = -EINVAL; goto exit; } bag_start = bag_end; } ret = 0; ts_info("configuration check SUCCESS"); exit: return ret; }*/ static int goodix_send_config(struct goodix_ts_device *dev, struct goodix_ts_config *config) { int r = 0; if (!config || !config->initialized) { ts_err("invalid config data"); return -EINVAL; } /*check configuration valid*/ // TODO remove this // r = goodix_check_cfg_valid(dev, config->data, config->length); // if (r != 0) { // ts_err("cfg check FAILED"); // return -EINVAL; // } ts_info("ver:%02xh,size:%d", config->data[0], config->length); mutex_lock(&config->lock); /*disable doze mode*/ if (!goodix_set_i2c_doze_mode(dev, false)) { r = _do_goodix_send_config(dev, config); } else { ts_err("failed disable doze[abort]"); r = -EINVAL; } /*enable doze mode*/ goodix_set_i2c_doze_mode(dev, true); mutex_unlock(&config->lock); return r; } /* success return config length else return -1 */ static int goodix_read_config_ys(struct goodix_ts_device *dev, u8 *buf) { u32 cfg_addr = dev->reg.cfg_addr; int sub_bags = 0; int offset = 0; int subbag_len; u16 checksum; int i; int ret; ret = goodix_i2c_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN_YS); if (ret) goto err_out; offset = TS_CFG_HEAD_LEN_YS; sub_bags = buf[TS_CFG_BAG_NUM_INDEX]; checksum = checksum_u8_ys(buf, TS_CFG_HEAD_LEN_YS); if (checksum) { ts_err("Config head checksum err:0x%x,data:%*ph", checksum, TS_CFG_HEAD_LEN_YS, buf); ret = -EINVAL; goto err_out; } ts_info("config_version:%u, vub_bags:%u", buf[0], sub_bags); for (i = 0; i < sub_bags; i++) { /* read sub head [0]: sub bag num, [1]: sub bag length */ ret = goodix_i2c_read(dev, cfg_addr + offset, buf + offset, 2); if (ret) goto err_out; /* read sub bag data */ subbag_len = buf[offset + 1]; ts_debug("sub bag num:%u,sub bag length:%u", buf[offset], subbag_len); ret = goodix_i2c_read(dev, cfg_addr + offset + 2, buf + offset + 2, subbag_len + 2); if (ret) goto err_out; checksum = checksum_u8_ys(buf + offset, subbag_len + 4); if (checksum) { ts_err("sub bag checksum err:0x%x", checksum); ret = -EINVAL; goto err_out; } offset += subbag_len + 4; ts_debug("sub bag %d, data:%*ph", buf[offset], buf[offset + 1] + 4, buf + offset); } ret = offset; err_out: return ret; } /* success return config length else return -1 */ static int goodix_read_config_nor(struct goodix_ts_device *dev, u8 *buf) { u32 cfg_addr = dev->reg.cfg_addr; int sub_bags = 0; int offset = 0; int subbag_len; u8 checksum; int i; int ret; /*disable doze mode*/ if (goodix_set_i2c_doze_mode(dev, false)) { ts_err("failed disable doze mode[abort]"); ret = -EINVAL; goto err_out; } ret = goodix_i2c_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN); if (ret) goto err_out; offset = TS_CFG_BAG_START_INDEX; sub_bags = buf[TS_CFG_BAG_NUM_INDEX]; checksum = checksum_u8(buf, TS_CFG_HEAD_LEN); if (checksum) { ts_err("Config head checksum err:0x%x,data:%*ph", checksum, TS_CFG_HEAD_LEN, buf); ret = -EINVAL; goto err_out; } ts_info("config_version:%u, vub_bags:%u", buf[0], sub_bags); for (i = 0; i < sub_bags; i++) { /* read sub head [0]: sub bag num, [1]: sub bag length */ ret = goodix_i2c_read(dev, cfg_addr + offset, buf + offset, 2); if (ret) goto err_out; /* read sub bag data */ subbag_len = buf[offset + 1]; ts_debug("sub bag num:%u,sub bag length:%u", buf[offset], subbag_len); ret = goodix_i2c_read(dev, cfg_addr + offset + 2, buf + offset + 2, subbag_len + 1); if (ret) goto err_out; checksum = checksum_u8(buf + offset, subbag_len + 3); if (checksum) { ts_err("sub bag checksum err:0x%x", checksum); ret = -EINVAL; goto err_out; } offset += subbag_len + 3; ts_debug("sub bag %d, data:%*ph", buf[offset], buf[offset + 1] + 3, buf + offset); } ret = offset; err_out: /*enable doze mode*/ goodix_set_i2c_doze_mode(dev, true); return ret; } /* success return config_len, <= 0 failed */ static int goodix_read_config(struct goodix_ts_device *dev, u8 *config_data) { struct goodix_ts_cmd ts_cmd; u8 cmd_flag; u32 cmd_reg = dev->reg.command; int r = 0; int i; if (!config_data) { ts_err("Illegal params"); return -EINVAL; } if (!dev->reg.command) { ts_err("command register ERROR:0x%04x", dev->reg.command); return -EINVAL; } /*disable doze mode*/ if (goodix_set_i2c_doze_mode(dev, false)) { ts_err("failed disabled doze[abort]"); r = -EINVAL; goto exit; } /* wait for IC in IDLE state */ for (i = 0; i < TS_WAIT_CMD_FREE_RETRY_TIMES; i++) { cmd_flag = 0; r = goodix_i2c_read(dev, cmd_reg, &cmd_flag, 1); if (r < 0 || cmd_flag == TS_CMD_REG_READY) break; usleep_range(10000, 11000); } if (cmd_flag != TS_CMD_REG_READY) { ts_err("Wait for IC ready IDEL state timeout:addr 0x%x\n", cmd_reg); r = -EAGAIN; goto exit; } /* 0x86 read config command */ goodix_cmd_init(dev, &ts_cmd, COMMAND_START_READ_CFG, 0, cmd_reg); r = goodix_send_command(dev, &ts_cmd); if (r) { ts_err("Failed send read config command"); goto exit; } /* wait for config data ready */ if (goodix_wait_cfg_cmd_ready(dev, COMMAND_READ_CFG_PREPARE_OK, COMMAND_START_READ_CFG)) { ts_err("Wait for config data ready timeout"); r = -EAGAIN; goto exit; } if (dev->ic_type == IC_TYPE_YELLOWSTONE) r = goodix_read_config_ys(dev, config_data); else r = goodix_read_config_nor(dev, config_data); if (r <= 0) ts_err("Failed read config data"); /* clear command */ goodix_cmd_init(dev, &ts_cmd, TS_CMD_REG_READY, 0, cmd_reg); goodix_send_command(dev, &ts_cmd); exit: /*enable doze mode*/ goodix_set_i2c_doze_mode(dev, true); return r; } /** * goodix_hw_reset - reset device * * @dev: pointer to touch device * Returns 0 - succeed,<0 - failed */ int goodix_hw_reset(struct goodix_ts_device *dev) { u8 data[2] = {0x00}; int r = 0; ts_info("HW reset"); gpio_direction_output(dev->board_data.reset_gpio, 0); udelay(2000); gpio_direction_output(dev->board_data.reset_gpio, 1); msleep(100); /*init dynamic esd*/ if (dev->reg.esd) { r = goodix_i2c_write_trans(dev, dev->reg.esd, data, 1); if (r < 0) ts_err("IC reset, init dynamic esd FAILED"); } else { ts_info("reg.esd is NULL, skip dynamic esd init"); } return 0; } /** * goodix_request_handler - handle firmware request * * @dev: pointer to touch device * @request_data: requset information * Returns 0 - succeed,<0 - failed */ static int goodix_request_handler(struct goodix_ts_device *dev) { unsigned char buffer[1]; int r; r = goodix_i2c_read_trans(dev, dev->reg.fw_request, buffer, 1); if (r < 0) return r; switch (buffer[0]) { case REQUEST_CONFIG: ts_info("HW request config"); r = goodix_send_config(dev, &(dev->normal_cfg)); if (r != 0) ts_info("request config, send config faild"); break; case REQUEST_BAKREF: ts_info("HW request bakref"); break; case REQUEST_RESET: ts_info("HW requset reset"); r = goodix_hw_reset(dev); if (r != 0) ts_info("request reset, reset faild"); break; case REQUEST_RELOADFW: ts_info("HW request reload fw"); goodix_do_fw_update(UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST); break; case REQUEST_IDLE: ts_info("HW request idle"); break; default: ts_info("Unknown hw request:%d", buffer[0]); break; } buffer[0] = 0x00; r = goodix_i2c_write_trans(dev, dev->reg.fw_request, buffer, 1); return r; } static void goodix_swap_coords(struct goodix_ts_device *dev, unsigned int *coor_x, unsigned int *coor_y) { unsigned int temp; struct goodix_ts_board_data *bdata = &dev->board_data; if (bdata->swap_axis) { temp = *coor_x; *coor_x = *coor_y; *coor_y = temp; } if (bdata->x2x) *coor_x = bdata->panel_max_x - *coor_x; if (bdata->y2y) *coor_y = bdata->panel_max_y - *coor_y; } #define GOODIX_KEY_STATE 0x10 static void goodix_parse_finger_nor(struct goodix_ts_device *dev, struct goodix_touch_data *touch_data, unsigned char *buf, int touch_num) { unsigned int id = 0, x = 0, y = 0, w = 0; static u8 pre_key_map; u8 cur_key_map = 0; static u32 pre_finger_map; u32 cur_finger_map = 0; u8 *coor_data; int i; coor_data = &buf[IRQ_HEAD_LEN_NOR]; for (i = 0; i < touch_num; i++) { id = coor_data[0]; if(id >= GOODIX_MAX_TOUCH){ ts_info("invaild finger id =%d", id); break; } x = le16_to_cpup((__be16 *) (coor_data + 1)); y = le16_to_cpup((__be16 *) (coor_data + 3)); w = coor_data[5]; goodix_swap_coords(dev, &x, &y); touch_data->coords[id].status = TS_TOUCH; touch_data->coords[id].x = x; touch_data->coords[id].y = y; touch_data->coords[id].w = w; cur_finger_map |= (1 << id); coor_data += BYTES_PER_COORD; } /* process finger release */ for (i = 0; i < GOODIX_MAX_TOUCH; i++) { if (cur_finger_map & (1 << i)) continue; if (pre_finger_map & (1 << i)) touch_data->coords[i].status = TS_RELEASE; } pre_finger_map = cur_finger_map; touch_data->touch_num = touch_num; if (buf[1] & GOODIX_KEY_STATE) { /* have key */ cur_key_map = buf[touch_num * BYTES_PER_COORD + 2] & 0x0F; for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { if (cur_key_map & (1 << i)) { touch_data->keys[i].status = TS_TOUCH; touch_data->keys[i].code = dev->board_data.panel_key_map[i]; } } } /* process key release */ for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { if (cur_key_map & (1 << i) || !(pre_key_map & (1 << i))) continue; touch_data->keys[i].status = TS_RELEASE; touch_data->keys[i].code = dev->board_data.panel_key_map[i]; } pre_key_map = cur_key_map; } static void goodix_parse_finger_ys(struct goodix_ts_device *dev, struct goodix_touch_data *touch_data, unsigned char *buf, int touch_num) { unsigned int id = 0, x = 0, y = 0, w = 0; static u32 pre_finger_map; u32 cur_finger_map = 0; u8 *coor_data; int i; coor_data = &buf[IRQ_HEAD_LEN_YS]; for (i = 0; i < touch_num; i++) { id = (coor_data[0] >> 4) & 0x0F; if(id >= GOODIX_MAX_TOUCH){ ts_info("invaild finger id =%d", id); break; } x = be16_to_cpup((__be16 *)(coor_data + 2)); y = be16_to_cpup((__be16 *)(coor_data + 4)); w = be16_to_cpup((__be16 *)(coor_data + 6)); goodix_swap_coords(dev, &x, &y); touch_data->coords[id].status = TS_TOUCH; touch_data->coords[id].x = x; touch_data->coords[id].y = y; touch_data->coords[id].w = w; cur_finger_map |= (1 << id); coor_data += BYTES_PER_COORD; } /* process finger release */ for (i = 0; i < GOODIX_MAX_TOUCH; i++) { if (cur_finger_map & (1 << i)) continue; if (pre_finger_map & (1 << i)) touch_data->coords[i].status = TS_RELEASE; } pre_finger_map = cur_finger_map; touch_data->touch_num = touch_num; } static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2}; static void goodix_parse_pen_nor(struct goodix_ts_device *dev, struct goodix_pen_data *pen_data, unsigned char *buf, int touch_num) { unsigned int id = 0; static u8 pre_key_map; u8 cur_key_map = 0; static u32 pre_pen_status; u32 cur_pen_status = 0; u8 *coor_data; int i; coor_data = &buf[2]; for (i = 0; i < touch_num; i++) { /* search for pen coordinate */ id = coor_data[0]; if (id < 0x80) { coor_data += BYTES_PER_COORD; continue; } pen_data->coords.x = le16_to_cpup((__be16 *)(coor_data + 1)); pen_data->coords.y = le16_to_cpup((__be16 *)(coor_data + 3)); pen_data->coords.p = le16_to_cpup((__be16 *)(coor_data + 5)); goodix_swap_coords(dev, &pen_data->coords.x, &pen_data->coords.y); pen_data->coords.status = TS_TOUCH; pen_data->coords.tool_type = BTN_TOOL_PEN; cur_pen_status = 1; /* currently only support one stylus */ break; } if (!cur_pen_status && pre_pen_status) { pen_data->coords.status = TS_RELEASE; } pre_pen_status = cur_pen_status; /* process pen button */ if (buf[1] & GOODIX_KEY_STATE) { cur_key_map = (buf[touch_num * BYTES_PER_COORD + 2] >> 4) & 0x0F; for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { if (!(cur_key_map & (1 << i))) continue; pen_data->keys[i].status = TS_TOUCH; pen_data->keys[i].code = goodix_pen_btn_code[i]; } } for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { if (cur_key_map & (1 << i) || !(pre_key_map & (1 << i))) continue; pen_data->keys[i].status = TS_RELEASE; pen_data->keys[i].code = goodix_pen_btn_code[i]; } pre_key_map = cur_key_map; } static void goodix_parse_pen_ys(struct goodix_ts_device *dev, struct goodix_pen_data *pen_data, unsigned char *buf, int touch_num) { ts_info("unsupported"); } static int goodix_touch_handler_ys(struct goodix_ts_device *dev, struct goodix_ts_event *ts_event, u8 *pre_buf, u32 pre_buf_len) { struct goodix_touch_data *touch_data = &ts_event->touch_data; struct goodix_pen_data *pen_data = &ts_event->pen_data; static u8 buffer[IRQ_HEAD_LEN_YS + BYTES_PER_COORD * GOODIX_MAX_TOUCH + 2]; int touch_num = 0, r = -EINVAL; u8 point_type = 0; u16 chksum = 0; static u8 pre_finger_num = 0; static u8 pre_pen_num = 0; /* clean event buffer */ memset(ts_event, 0, sizeof(*ts_event)); /* copy pre-data to buffer */ memcpy(buffer, pre_buf, pre_buf_len); touch_num = buffer[2] & 0x0F; if (unlikely(touch_num > GOODIX_MAX_TOUCH)) { touch_num = -EINVAL; goto exit_clean_sta; } if (unlikely(touch_num > 1)) { r = goodix_i2c_read_trans(dev, dev->reg.coor + pre_buf_len, &buffer[pre_buf_len], (touch_num - 1) * BYTES_PER_COORD); if (unlikely(r < 0)) goto exit_clean_sta; } if (touch_num > 0) { chksum = checksum_u8_ys(&buffer[IRQ_HEAD_LEN_YS], touch_num * BYTES_PER_COORD + 2); if (unlikely(chksum != 0)) { ts_debug("checksum error:%x", chksum); r = -EINVAL; goto exit_clean_sta; } } if (touch_num > 0) point_type = buffer[(touch_num - 1) * BYTES_PER_COORD + IRQ_HEAD_LEN_YS] & 0x0F; if (touch_num >= 1 && (point_type == POINT_TYPE_STYLUS || point_type == POINT_TYPE_STYLUS_HOVER)) { /* stylus info */ if (pre_finger_num) { ts_event->event_type = EVENT_TOUCH; goodix_parse_finger_ys(dev, touch_data, buffer, 0); pre_finger_num = 0; } else { pre_pen_num = 1; ts_event->event_type = EVENT_PEN; goodix_parse_pen_ys(dev, pen_data, buffer, touch_num); } } else { /* finger info */ if (pre_pen_num) { ts_event->event_type = EVENT_PEN; goodix_parse_pen_ys(dev, pen_data, buffer, 0); pre_pen_num = 0; } else { ts_event->event_type = EVENT_TOUCH; goodix_parse_finger_ys(dev, touch_data, buffer, touch_num); pre_finger_num = touch_num; } } /* process custom info */ if (buffer[3] & 0x01) { ts_debug("TODO add custom info process function"); } exit_clean_sta: return r; } static int goodix_touch_handler_nor(struct goodix_ts_device *dev, struct goodix_ts_event *ts_event, u8 *pre_buf, u32 pre_buf_len) { struct goodix_touch_data *touch_data = &ts_event->touch_data; struct goodix_pen_data *pen_data = &ts_event->pen_data; static u8 buffer[IRQ_HEAD_LEN_NOR + BYTES_PER_COORD * GOODIX_MAX_TOUCH + 2]; int touch_num = 0, r = -EINVAL; unsigned char chksum = 0; static u8 pre_finger_num = 0; static u8 pre_pen_num = 0; /* clean event buffer */ memset(ts_event, 0, sizeof(*ts_event)); /* copy pre-data to buffer */ memcpy(buffer, pre_buf, pre_buf_len); touch_num = buffer[1] & 0x0F; if (unlikely(touch_num > GOODIX_MAX_TOUCH)) { touch_num = -EINVAL; goto exit_clean_sta; } if (unlikely(touch_num > 1)) { r = goodix_i2c_read_trans(dev, dev->reg.coor + pre_buf_len, &buffer[pre_buf_len], (touch_num - 1) * BYTES_PER_COORD); if (unlikely(r < 0)) goto exit_clean_sta; } chksum = checksum_u8(&buffer[0], touch_num * BYTES_PER_COORD + 4); if (unlikely(chksum != 0)) { ts_debug("checksum error:%X, ic_type:%d", chksum, dev->ic_type); r = -EINVAL; goto exit_clean_sta; } if (touch_num >= 1 && buffer[(touch_num - 1) * BYTES_PER_COORD + 2] >= 0x80) { if (pre_finger_num) { ts_event->event_type = EVENT_TOUCH; goodix_parse_finger_nor(dev, touch_data, buffer, 0); pre_finger_num = 0; } else { pre_pen_num = 1; ts_event->event_type = EVENT_PEN; goodix_parse_pen_nor(dev, pen_data, buffer, touch_num); } } else { if (pre_pen_num) { ts_event->event_type = EVENT_PEN; goodix_parse_pen_nor(dev, pen_data, buffer, 0); pre_pen_num = 0; } else { ts_event->event_type = EVENT_TOUCH; goodix_parse_finger_nor(dev, touch_data, buffer, touch_num); pre_finger_num = touch_num; } } exit_clean_sta: return r; } static int goodix_event_handler(struct goodix_ts_device *dev, struct goodix_ts_event *ts_event) { int pre_read_len; u8 pre_buf[32]; u8 event_sta; int r; if (dev->ic_type == IC_TYPE_YELLOWSTONE) pre_read_len = IRQ_HEAD_LEN_YS + BYTES_PER_COORD + 2; else pre_read_len = IRQ_HEAD_LEN_NOR + BYTES_PER_COORD + 2; r = goodix_i2c_read_trans(dev, dev->reg.coor, pre_buf, pre_read_len); if (unlikely(r < 0)) return r; if (dev->ic_type == IC_TYPE_YELLOWSTONE && checksum_u8_ys(pre_buf, IRQ_HEAD_LEN_YS)) { ts_debug("irq head checksum error %*ph", IRQ_HEAD_LEN_YS, pre_buf); return -EINVAL; } /* buffer[0]: event state */ event_sta = pre_buf[0]; if (likely((event_sta & GOODIX_TOUCH_EVENT) == GOODIX_TOUCH_EVENT)) { /* handle touch event */ if (dev->ic_type == IC_TYPE_YELLOWSTONE) goodix_touch_handler_ys(dev, ts_event, pre_buf, pre_read_len); else goodix_touch_handler_nor(dev, ts_event, pre_buf, pre_read_len); } else if (unlikely((event_sta & GOODIX_REQUEST_EVENT) == GOODIX_REQUEST_EVENT)) { /* handle request event */ ts_event->event_type = EVENT_REQUEST; goodix_request_handler(dev); } else if ((event_sta & GOODIX_GESTURE_EVENT) == GOODIX_GESTURE_EVENT) { /* handle gesture event */ ts_debug("Gesture event"); } else if ((event_sta & GOODIX_HOTKNOT_EVENT) == GOODIX_HOTKNOT_EVENT) { /* handle hotknot event */ ts_debug("Hotknot event"); } else { ts_debug("unknow event type:0x%x", event_sta); r = -EINVAL; } return r; } /** * goodix_hw_suspend - Let touch deivce stay in lowpower mode. * @dev: pointer to goodix touch device * @return: 0 - succeed, < 0 - failed */ static int goodix_hw_suspend(struct goodix_ts_device *dev) { struct goodix_ts_cmd sleep_cmd; int r = 0; goodix_cmd_init(dev, &sleep_cmd, COMMAND_SLEEP, 0, dev->reg.command); if (sleep_cmd.initialized) { r = goodix_send_command(dev, &sleep_cmd); if (!r) ts_info("Chip in sleep mode"); } else { ts_err("Uninitialized sleep command"); } return r; } /** * goodix_hw_resume - Let touch deivce stay in active mode. * @dev: pointer to goodix touch device * @return: 0 - succeed, < 0 - failed */ static int goodix_hw_resume(struct goodix_ts_device *dev) { goodix_hw_reset(dev); return 0; } static int goodix_esd_check(struct goodix_ts_device *dev) { int r; u8 data = 0; if (dev->reg.esd == 0) { ts_err("esd reg is NULL"); return 0; } /*check dynamic esd*/ r = dev->hw_ops->read_trans(dev, TS_REG_ESD_TICK_R, &data, 1); if (r < 0 || (data == GOODIX_ESD_TICK_WRITE_DATA)) { ts_info("dynamic esd occur, r:%d, data:0x%02x", r, data); r = -EINVAL; goto exit; } exit: return r; } /* hardware opeation funstions */ static const struct goodix_ts_hw_ops hw_i2c_ops = { .dev_confirm = goodix_ts_dev_confirm, .read = goodix_i2c_read, .write = goodix_i2c_write, .read_trans = goodix_i2c_read_trans, .write_trans = goodix_i2c_write_trans, .reset = goodix_hw_reset, .event_handler = goodix_event_handler, .send_config = goodix_send_config, .read_config = goodix_read_config, .send_cmd = goodix_send_command, .read_version = goodix_read_version, .suspend = goodix_hw_suspend, .resume = goodix_hw_resume, .check_hw = goodix_esd_check, }; static struct platform_device *goodix_pdev; static void goodix_pdev_release(struct device *dev) { ts_info("goodix pdev released"); } static int goodix_i2c_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { struct goodix_ts_device *ts_device = NULL; int r = 0; ts_info("goodix_i2c_probe IN"); r = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); if (!r) return -EIO; /* ts device data */ ts_device = devm_kzalloc(&client->dev, sizeof(struct goodix_ts_device), GFP_KERNEL); if (!ts_device) return -ENOMEM; if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { /* parse devicetree property */ r = goodix_parse_dt(client->dev.of_node, &ts_device->board_data); if (r < 0) { ts_err("failed parse device info form dts, %d", r); return -EINVAL; } } else { ts_err("no valid device tree node found"); return -ENODEV; } ts_device->name = "Goodix TouchDevcie"; ts_device->dev = &client->dev; ts_device->hw_ops = &hw_i2c_ops; /* ts core device */ goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); if (!goodix_pdev) return -ENOMEM; goodix_pdev->name = GOODIX_CORE_DRIVER_NAME; goodix_pdev->id = 0; goodix_pdev->num_resources = 0; /* * you can find this platform dev in * /sys/devices/platfrom/goodix_ts.0 * goodix_pdev->dev.parent = &client->dev; */ goodix_pdev->dev.platform_data = ts_device; goodix_pdev->dev.release = goodix_pdev_release; /* register platform device, then the goodix_ts_core * module will probe the touch deivce. */ r = platform_device_register(goodix_pdev); if (r) { ts_err("failed register goodix platform device, %d", r); goto err_pdev; } r = goodix_ts_core_init(); if (r) { ts_err("failed register platform driver, %d", r); goto err_pdriver; } ts_info("i2c probe out"); return r; err_pdriver: platform_device_unregister(goodix_pdev); err_pdev: kfree(goodix_pdev); goodix_pdev = NULL; ts_info("i2c probe out, %d", r); return r; } static void goodix_i2c_remove(struct i2c_client *client) { if (goodix_pdev) { platform_device_unregister(goodix_pdev); kfree(goodix_pdev); goodix_pdev = NULL; } } #ifdef CONFIG_OF static const struct of_device_id i2c_matchs[] = { {.compatible = "goodix,gt9896",}, {.compatible = "goodix,gt9886",}, {.compatible = "goodix,gt9889",}, {.compatible = "goodix,gt5863",}, {}, }; MODULE_DEVICE_TABLE(of, i2c_matchs); #endif static const struct i2c_device_id i2c_id_table[] = { {TS_DRIVER_NAME, 0}, {}, }; MODULE_DEVICE_TABLE(i2c, i2c_id_table); static struct i2c_driver goodix_i2c_driver = { .driver = { .name = TS_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(i2c_matchs), }, .probe = goodix_i2c_probe, .remove = goodix_i2c_remove, .id_table = i2c_id_table, }; /* release manully when prob failed */ void goodix_ts_dev_release(void) { if (goodix_pdev) { platform_device_unregister(goodix_pdev); kfree(goodix_pdev); goodix_pdev = NULL; } i2c_del_driver(&goodix_i2c_driver); } static int __init goodix_i2c_init(void) { ts_info("Goodix driver init"); return i2c_add_driver(&goodix_i2c_driver); } static void __exit goodix_i2c_exit(void) { i2c_del_driver(&goodix_i2c_driver); ts_info("Goodix driver exit"); } module_init(goodix_i2c_init); module_exit(goodix_i2c_exit); MODULE_DESCRIPTION("Goodix Touchscreen Hardware Module"); MODULE_AUTHOR("Goodix, Inc."); MODULE_LICENSE("GPL v2");