blob: c9f5a75cd9979902224252e162499c4ad97f7d07 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include "goodix_ts_core.h"
#include "goodix_cfg_bin.h"
#define TS_DRIVER_NAME "gt738x"
#define I2C_MAX_TRANSFER_SIZE 256
#define TS_ADDR_LENGTH 2
#define TS_WAIT_CFG_READY_RETRY_TIMES 20
#define TS_WAIT_CMD_FREE_RETRY_TIMES 20
#define TS_REG_COORDS_BASE 0x824E
#define TS_REG_CMD 0x8040
#define TS_REG_VERSION 0x8240
#define TS_REG_CFG_BASE 0x8050
#define TS_REG_ESD_TICK_R 0x8040
#define TS_REG_STATIC_ESD 0x8043
#define REQUEST_HANDLED 0x00
#define REQUEST_CONFIG 0x01
#define COMMAND_SLEEP 0x05
#define COMMAND_START_SEND_CFG 0x80
#define COMMAND_END_SEND_CFG 0x83
#define COMMAND_SEND_CFG_PREPARE_OK 0x82
#define COMMAND_START_READ_CFG 0x86
#define COMMAND_READ_CFG_PREPARE_OK 0x85
#define TS_CMD_REG_READY 0xFF
#define BYTES_PER_COORD 8
#define BYTES_PEN_COORD 12
#define IRQ_HEAD_LEN 2
#define PEN_FLAG 0x20
#define PEN_HIGHER_PRIORITY
/* WARNING: externs should be avoided in .c files move below to .h*/
/* void goodix_ts_core_init(struct work_struct *work); */
#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 0x8240
#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.
*/
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(10000);
mdelay(10);
gpio_direction_output(ts_dev->board_data.reset_gpio, 1);
msleep(120);
if (!goodix_i2c_test(ts_dev)) {
msleep(320);
return 0;
}
}
return -EINVAL;
}
/**
* 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)
{
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(len > I2C_MAX_TRANSFER_SIZE
? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
if (!msgs[1].buf)
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 - 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)
{
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(len + TS_ADDR_LENGTH > I2C_MAX_TRANSFER_SIZE
? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH,
GFP_KERNEL);
if (!msg.buf)
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_debug("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_i2c_read_nodelay - read device register through i2c bus, no delay
* @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_nodelay(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(len > I2C_MAX_TRANSFER_SIZE
? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
if (!msgs[1].buf)
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);
}
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_nodelay - write device register through i2c bus, no delay
* @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_nodelay(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(len + TS_ADDR_LENGTH > I2C_MAX_TRANSFER_SIZE
? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH,
GFP_KERNEL);
if (!msg.buf)
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);
}
if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
ts_debug("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_i2c_write_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_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(len + TS_ADDR_LENGTH > I2C_MAX_TRANSFER_SIZE
? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH,
GFP_KERNEL);
if (!msg.buf) {
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_cmds_init(struct goodix_ts_cmd *ts_cmd,
u8 cmds, u16 cmd_data, u32 reg_addr)
{
ts_cmd->initialized = false;
if (!reg_addr || !cmds)
return;
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;
}
/**
* 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];
u8 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;
}
/*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);
goto exit;
}
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:
return r;
}
static int _do_goodix_send_config(struct goodix_ts_device *dev,
struct goodix_ts_config *config)
{
int ret = 0;
int try_times = 0;
u8 buf = 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 < GOODIX_RETRY_NUM_20; try_times++) {
if (!goodix_i2c_read(dev, command_reg, &buf, 1) && buf == TS_CMD_REG_READY)
break;
usleep_range(10000, 11000);
}
if (try_times >= GOODIX_RETRY_NUM_20) {
ts_err("Send cfg FAILED, before send, reg:0x%04x is not 0xff\n", command_reg);
ret = -EINVAL;
goto exit;
}
/*2. send "start write cfg" command*/
goodix_cmds_init(&ts_cmd, COMMAND_START_SEND_CFG, 0, dev->reg.command);
if (goodix_send_command(dev, &ts_cmd)) {
ts_err("Send cfg FAILED, send COMMAND_START_SEND_CFG ERROR\n");
ret = -EINVAL;
goto exit;
}
usleep_range(3000, 3100);
/*3. wait ic set command_reg to 0x82*/
for (try_times = 0; try_times < GOODIX_RETRY_NUM_20; try_times++) {
if (!goodix_i2c_read(dev, command_reg, &buf, 1) &&
buf == COMMAND_SEND_CFG_PREPARE_OK)
break;
usleep_range(3000, 3100);
}
if (try_times >= GOODIX_RETRY_NUM_20) {
ts_err("Send cfg FAILED, reg:0x%04x is not 0x82\n", command_reg);
ret = -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\n");
ret = -EINVAL;
goto exit;
}
/*5. send "end send cfg" command*/
goodix_cmds_init(&ts_cmd, COMMAND_END_SEND_CFG, 0, dev->reg.command);
if (goodix_send_command(dev, &ts_cmd)) {
ts_err("Send cfg FAILED, send COMMAND_END_SEND_CFG ERROR\n");
ret = -EINVAL;
goto exit;
}
/*6. wait ic set command_reg to 0xff*/
for (try_times = 0; try_times < GOODIX_RETRY_NUM_10; try_times++) {
if (!goodix_i2c_read(dev, command_reg, &buf, 1) && buf == TS_CMD_REG_READY)
break;
usleep_range(10000, 11000);
}
if (try_times >= GOODIX_RETRY_NUM_10) {
ts_err("Send cfg FAILED, after send, reg:0x%04x is not 0xff\n", command_reg);
ret = -EINVAL;
goto exit;
}
ts_info("Send cfg SUCCESS\n");
ret = 0;
exit:
return ret;
}
/**
* goodix_check_cfg_valid - check config valid.
* @cfg : config data pointer.
* @length : config length.
* Returns 0 - succeed, <0 - failed
*/
static int goodix_check_cfg_valid(struct goodix_ts_device *dev, u8 *cfg, u32 length)
{
int ret = 0;
u8 data_sum = 0;
ts_info("%s run\n", __func__);
if (!cfg || length == 0) {
ts_err("%s: cfg is invalid , length:%d\n", __func__, length);
ret = -EINVAL;
goto exit;
}
data_sum = checksum_u8(cfg, length);
if (data_sum == 0) {
ret = 0;
} else {
ts_err("cfg sum:0x%x\n", data_sum);
ret = -EINVAL;
}
exit:
ts_info("%s exit, ret:%d\n", __func__, ret);
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*/
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);
r = _do_goodix_send_config(dev, config);
if (r != 0)
ts_err("send_cfg FAILED, ic_type:%d, cfg_len:%d",
dev->ic_type, config->length);
mutex_unlock(&config->lock);
return r;
}
/* 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;
u16 cfg_reg = dev->reg.cfg_addr;
u16 config_len = GOODIX_CFG_LEN;
int r = 0;
int i;
if (!config_data) {
ts_err("Illegal params");
return -EINVAL;
}
if (!cmd_reg || !cfg_reg) {
ts_err("cmd or cfg register ERROR:0x%04x, 0x%04x", cmd_reg, cfg_reg);
return -EINVAL;
}
/* 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(5000, 5200);
}
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_cmds_init(&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;
}
usleep_range(3000, 3100);
/* wait for config data ready */
for (i = 0; i < GOODIX_RETRY_NUM_20; i++) {
cmd_flag = 0;
r = goodix_i2c_read(dev, cmd_reg, &cmd_flag, 1);
if (r < 0 || cmd_flag == COMMAND_READ_CFG_PREPARE_OK)
break;
usleep_range(3000, 3100);
}
if (cmd_flag != COMMAND_READ_CFG_PREPARE_OK) {
ts_err("Wait for config data ready timeout\n");
r = -EAGAIN;
goto exit;
}
r = goodix_i2c_read(dev, cfg_reg, config_data, config_len);
if (r < 0) {
ts_err("Failed read config data\n");
r = -EAGAIN;
goto exit;
}
r = config_len;
ts_info("success read config, len:%d\n", config_len);
/* clear command */
goodix_cmds_init(&ts_cmd, TS_CMD_REG_READY, 0, cmd_reg);
goodix_send_command(dev, &ts_cmd);
exit:
return r;
}
/**
* goodix_init_esd
*
* @dev: pointer to touch device
* Returns 0 - succeed, <0 - failed
*/
int goodix_init_esd(struct goodix_ts_device *dev)
{
u8 data[2] = {0x00};
int r = 0;
// init static esd
data[0] = GOODIX_ESD_TICK_WRITE_DATA;
r = goodix_i2c_write(dev, TS_REG_STATIC_ESD, data, 1);
if (r < 0)
ts_err("static ESD init failed\n");
/*init dynamic esd*/
if (dev->reg.esd) {
r = goodix_i2c_write(dev, dev->reg.esd, data, 1);
if (r < 0)
ts_err("IC reset, init dynamic esd FAILED");
} else {
ts_debug("reg.esd is NULL, skip dynamic esd init");
}
return 0;
}
/**
* goodix_hw_reset - reset device
*
* @dev: pointer to touch device
* Returns 0 - succeed, <0 - failed
*/
int goodix_hw_reset(struct goodix_ts_device *dev)
{
ts_info("HW reset");
gpio_direction_output(dev->board_data.reset_gpio, 0);
//udelay(10000);
mdelay(10);
gpio_direction_output(dev->board_data.reset_gpio, 1);
msleep(440);
return goodix_init_esd(dev);
}
/**
* 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(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 failed");
break;
default:
ts_debug("Unknown hw request:%d", buffer[0]);
break;
}
buffer[0] = 0x00;
r = goodix_i2c_write(dev, dev->reg.fw_request, buffer, 1);
return r;
}
static int goodix_remap_trace_id(u8 *coor_buf, u32 coor_buf_len, int touch_num)
{
static u8 remap_array[20] = {0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff};
int i, j;
int offset = 0;
bool need_leave = false;
bool need_add = false;
u8 temp_buf[BYTES_PER_COORD] = {0x00};
u8 *small;
bool need_swap = false;
int max_touch_num = GOODIX_MAX_TOUCH;
if (touch_num > GOODIX_MAX_TOUCH) {
ts_info("touch num error, trace id no remap:%d", touch_num);
return 0;
}
if (!coor_buf || coor_buf_len > (3 + BYTES_PER_COORD *
max_touch_num + 4)) {
ts_info("touch data buff error, !coor_buf:%d, len:%d",
!coor_buf, coor_buf_len);
return 0;
}
/*clear and reset remap_array*/
if (touch_num == 0) {
for (i = 0; i < sizeof(remap_array); i++)
remap_array[i] = 0xff;
return 0;
}
/*scan remap_array, find and remove leave point*/
for (i = 0; i < sizeof(remap_array); i++) {
if (remap_array[i] == 0xff) {
continue;
} else {
need_leave = true;
offset = 0;
for (j = 0; j < touch_num; j++) {
if (remap_array[i] == coor_buf[offset]) {
need_leave = false;
break;
}
offset += BYTES_PER_COORD;
}
/*if (need_leave == true) {CHECK: Using comparison to true is error prone*/
if (need_leave) {
/*ts_info("---leave, trace id:%d:%d", remap_array[i], i);*/
remap_array[i] = 0xff;
}
}
}
/*find and add new point*/
offset = 0;
for (i = 0; i < touch_num; i++) {
need_add = true;
for (j = 0; j < sizeof(remap_array); j++) {
if (coor_buf[offset] == remap_array[j]) {
need_add = false;
break;
}
}
/*if (need_add == true) { CHECK: Using comparison to true is error prone */
if (need_add) {
for (j = 0; j < sizeof(remap_array); j++) {
if (remap_array[j] == 0xff) {
remap_array[j] = coor_buf[offset];
break;
}
}
}
offset += BYTES_PER_COORD;
}
/*remap trace id*/
offset = 0;
for (i = 0; i < touch_num; i++) {
/*do not remap pen's trace ID*/
if (coor_buf[offset] >= 0x80) {
offset += BYTES_PER_COORD;
continue;
} else {
for (j = 0; j < sizeof(remap_array); j++) {
if (remap_array[j] == coor_buf[offset]) {
/*ts_info("***remap, %d--->%d", coor_buf[offset], j);*/
coor_buf[offset] = j;
break;
}
}
if (j >= sizeof(remap_array)) {
ts_info("remap ERROR!!trace id:%d", coor_buf[offset]);
ts_info("remap_array:%*ph",
(int)sizeof(remap_array), remap_array);
}
offset += BYTES_PER_COORD;
}
}
/*realign coor data by new trace ID*/
for (i = 0; i < touch_num - 1; i++) {
small = &coor_buf[BYTES_PER_COORD * i];
need_swap = false;
for (j = i + 1; j < touch_num; j++) {
if (coor_buf[BYTES_PER_COORD * j] < *small) {
need_swap = true;
small = &coor_buf[BYTES_PER_COORD * j];
}
}
/*swap*/
if (need_swap) {
memcpy(temp_buf, small, BYTES_PER_COORD);
memcpy(small,
&coor_buf[BYTES_PER_COORD * i],
BYTES_PER_COORD);
memcpy(&coor_buf[BYTES_PER_COORD * i],
temp_buf,
BYTES_PER_COORD);
}
}
return 0;
}
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_key(struct goodix_ts_device *dev,
struct goodix_ts_event *ts_event,
struct goodix_touch_data *touch_data, unsigned char *buf)
{
static u8 pre_key_map;
u8 cur_key_map = 0;
int i;
int touch_num = 0;
touch_num = buf[1] & 0x0F;
if (buf[1] & GOODIX_KEY_STATE) {
/* have key */
cur_key_map = buf[touch_num * BYTES_PER_COORD + 2] & 0x0F;
/* pen flag*/
if ((buf[1] & PEN_FLAG) == PEN_FLAG)
cur_key_map = buf[touch_num * BYTES_PER_COORD + 2 + 4] & 0x0F;
ts_debug("key touch, cur key map:0x%02x!\n", cur_key_map);
for (i = 0; i < GOODIX_MAX_TP_KEY; i++) {
if (cur_key_map & (1 << i)) {
ts_event->event_type |= EVENT_TOUCH;
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;
ts_event->event_type |= EVENT_TOUCH;
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(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;
int touch_num_valid = 0;
u8 *coor_data;
int i;
touch_num_valid = touch_num;
coor_data = &buf[IRQ_HEAD_LEN];
for (i = 0; i < touch_num; i++) {
id = coor_data[0];
if (id >= GOODIX_MAX_TOUCH) {
ts_info("invalid finger id =%d", id);
break;
}
x = le16_to_cpup((__be16 *)(coor_data + 1));
y = le16_to_cpup((__be16 *)(coor_data + 3));
w = le16_to_cpup((__be16 *)(coor_data + 5));
goodix_swap_coords(dev, &x, &y);
if (w != 0) {
touch_data->coords[id].status = TS_TOUCH;
cur_finger_map |= (1 << id);
} else {
touch_data->coords[id].status = TS_RELEASE;
touch_num_valid--;
}
touch_data->coords[id].x = x;
touch_data->coords[id].y = y;
touch_data->coords[id].w = w;
coor_data += BYTES_PER_COORD;
}
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_valid;
}
static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2};
static void goodix_parse_pen(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;
u8 *coor_data;
s16 x_angle = 0, y_angle = 0;
int i;
pen_data->coords.tool_type = BTN_TOOL_PEN;
if (touch_num) {
pen_data->coords.status = TS_TOUCH;
} else {
pen_data->coords.status = TS_RELEASE;
cur_key_map = 0;
goto PEN_KEY_PROCESS;
}
/*pen data */
coor_data = &buf[IRQ_HEAD_LEN + BYTES_PER_COORD * (touch_num - 1)];
id = coor_data[0] & 0x80;
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));
x_angle = le16_to_cpup((__le16 *)&coor_data[7]);
y_angle = le16_to_cpup((__le16 *)&coor_data[9]);
pen_data->coords.tilt_x = x_angle / 100;
pen_data->coords.tilt_y = y_angle / 100;
/* currently only support one stylus */
/* process pen button */
cur_key_map = (coor_data[0] >> 4) & 0x03;
PEN_KEY_PROCESS:
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 int goodix_touch_handler(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 + BYTES_PER_COORD *
(GOODIX_MAX_TOUCH - 1) + BYTES_PEN_COORD + 2];
int touch_num = 0, r = -EINVAL;
unsigned char chksum = 0;
static u8 pre_finger_num;
static u8 pre_pen_num;
bool have_pen = false;
int chksum_len = pre_buf_len;
/* 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;
/* pen flag*/
if ((buffer[1] & PEN_FLAG) == PEN_FLAG)
have_pen = true;
else
chksum_len = pre_buf_len - BYTES_PEN_COORD + BYTES_PER_COORD;
if (unlikely(touch_num > GOODIX_MAX_TOUCH)) {
touch_num = -EINVAL;
goto exit_clean_sta;
}
if (unlikely(touch_num > 1)) {
chksum_len += BYTES_PER_COORD * (touch_num - 1);
r = goodix_i2c_read_nodelay(dev,
dev->reg.coor + pre_buf_len,
&buffer[pre_buf_len],
chksum_len - pre_buf_len);
if (unlikely(r < 0))
goto exit_clean_sta;
}
if (touch_num != 0) {
chksum = checksum_u8(&buffer[1], chksum_len - 1);
if (unlikely(chksum != 0)) {
ts_info("checksum error:%X, ic_type:%d", chksum, dev->ic_type);
r = -EINVAL;
goto exit_clean_sta;
}
}
if (have_pen)
goodix_remap_trace_id(&buffer[IRQ_HEAD_LEN],
BYTES_PER_COORD * GOODIX_MAX_TOUCH,
touch_num - 1);
else
goodix_remap_trace_id(&buffer[IRQ_HEAD_LEN],
BYTES_PER_COORD * GOODIX_MAX_TOUCH, touch_num);
if (have_pen && touch_num == 1) {
#ifdef PEN_HIGHER_PRIORITY
if (pre_finger_num) {
ts_event->event_type |= EVENT_TOUCH;
goodix_parse_finger(dev, touch_data,
buffer, 0);
pre_finger_num = 0;
} else {
ts_event->event_type |= EVENT_PEN;
goodix_parse_pen(dev, pen_data, buffer, touch_num);
pre_pen_num = touch_num;
}
} else if (have_pen && (touch_num > 1)) {
if (pre_finger_num) {
ts_event->event_type |= EVENT_TOUCH;
goodix_parse_finger(dev, touch_data,
buffer, 0);
pre_finger_num = 0;
} else {
ts_event->event_type |= EVENT_PEN;
goodix_parse_pen(dev, pen_data, buffer, touch_num);
pre_pen_num = 1;
}
#else
ts_event->event_type |= EVENT_PEN;
goodix_parse_pen(dev, pen_data, buffer, touch_num);
pre_pen_num = touch_num;
} else if (have_pen && (touch_num > 1)) {
ts_event->event_type |= EVENT_TOUCH;
goodix_parse_finger(dev, touch_data,
buffer, touch_num - 1);
pre_finger_num = touch_num - 1;
ts_event->event_type |= EVENT_PEN;
goodix_parse_pen(dev, pen_data, buffer, touch_num);
pre_pen_num = 1;
#endif
} else if ((!have_pen) && touch_num > 0) {
ts_event->event_type |= EVENT_TOUCH;
goodix_parse_finger(dev, touch_data, buffer, touch_num);
pre_finger_num = touch_num;
if (pre_pen_num != 0) {
ts_event->event_type |= EVENT_PEN;
goodix_parse_pen(dev, pen_data, buffer, 0);
}
pre_pen_num = 0;
} else {
if (pre_pen_num != 0) {
ts_event->event_type |= EVENT_PEN;
goodix_parse_pen(dev, pen_data, buffer, 0);
}
if (pre_finger_num != 0) {
ts_event->event_type |= EVENT_TOUCH;
goodix_parse_finger(dev, touch_data,
buffer, 0);
}
pre_pen_num = 0;
pre_finger_num = 0;
}
goodix_parse_key(dev, ts_event, touch_data, buffer);
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;
pre_read_len = 2 + BYTES_PEN_COORD + 2;
r = goodix_i2c_read_nodelay(dev, dev->reg.coor,
pre_buf, pre_read_len);
if (unlikely(r < 0))
return r;
/* buffer[0]: event state */
event_sta = pre_buf[0];
if (likely((event_sta & GOODIX_TOUCH_EVENT) == GOODIX_TOUCH_EVENT)) {
/* handle touch event */
goodix_touch_handler(dev, ts_event, pre_buf,
pre_read_len);
} else if ((event_sta & GOODIX_GESTURE_EVENT) ==
GOODIX_GESTURE_EVENT) {
/* handle gesture event */
ts_debug("Gesture event");
} else if (!pre_buf[1]) {
/* maybe request event */
r = goodix_request_handler(dev);
} else {
ts_debug("unknown event type:0x%x", event_sta);
r = -EINVAL;
}
return r;
}
/**
* goodix_hw_suspend - Let touch device 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_cmds_init(&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 device 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;
//static esd
r = dev->hw_ops->read(dev, TS_REG_STATIC_ESD, &data, 1);
if (r < 0 || data != GOODIX_ESD_TICK_WRITE_DATA) {
ts_err("hw status:Error, static{0x8043}:%d\n", data);
return -EIO;
}
/*check dynamic esd*/
if (dev->reg.esd == 0) {
ts_debug("dynamic esd reg is NULL(skip)");
return 0;
}
r = dev->hw_ops->read(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_nodelay = goodix_i2c_read_nodelay,
.write_nodelay = goodix_i2c_write_nodelay,
.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,
.init_esd = goodix_init_esd,
.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 void goodix_core_init_work(void)
{
static struct delayed_work core_init_wk;
INIT_DELAYED_WORK(&core_init_wk, goodix_ts_core_init);
schedule_delayed_work(&core_init_wk, 2 * HZ);
ts_info("schedule delayed core init work");
}
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("%s IN", __func__);
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(*goodix_pdev), 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/platform/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 device.
*/
r = platform_device_register(goodix_pdev);
if (r) {
ts_err("failed register goodix platform device, %d", r);
goto err_pdev;
}
goodix_core_init_work();
ts_info("i2c probe out");
return r;
err_pdev:
kfree(goodix_pdev);
goodix_pdev = NULL;
ts_info("i2c probe out, %d", r);
return r;
}
static int goodix_i2c_remove(struct i2c_client *client)
{
if (goodix_pdev) {
platform_device_unregister(goodix_pdev);
kfree(goodix_pdev);
goodix_pdev = NULL;
}
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id i2c_matches[] = {
{.compatible = "goodix, gt738x", },
{},
};
MODULE_DEVICE_TABLE(of, i2c_matches);
#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_matches),
},
.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");