
 /*
  * Goodix Touchscreen Driver
  *
  * Copyright (C) 2019 - 2020 Goodix, Inc.
  * License terms: GNU General Public License (GPL) version 2
  *
  */

#include <linux/spi/spi.h>
#include "goodix_ts_core.h"

/* name */
#define TS_DRIVER_NAME				"GT9896S"
#define TS_DT_COMPATIBLE			"goodix,gt9896s"

/* reg */
#define TS_REG_REMOVE_HOLD			0x2014
#define TS_REG_SET_SPI_ARGS			0x3082
//TODO:to modify later
#define TS_REG_ESD_TICK_R			0x3103

/* cmd */
#define COMMAND_SLEEP				0x05
#define COMMAND_END_SEND_CFG_YS		0x7D
#define COMMAND_START_SEND_CFG		0x80
#define COMMAND_SEND_SMALL_CFG		0x81
#define COMMAND_SEND_CFG_PREPARE_OK	0x82
#define COMMAND_END_SEND_CFG		0x83
#define COMMAND_READ_CFG_PREPARE_OK	0x85
#define COMMAND_START_READ_CFG		0x86

#define CMD_ACK_BUF_OVERFLOW		0x01
#define CMD_ACK_CHECKSUM_ERROR		0x02
#define CMD_ACK_BUSY				0x04
#define CMD_ACK_OK					0x80
#define CMD_ACK_IDLE				0xFF

/* flag */
#define SPI_FLAG_WR		0xF0
#define SPI_FLAG_RD		0xF1
#define MASK_8BIT 		0xFF

/* value */
#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 TS_CMD_CFG_ERR					0x7E
#define TS_CMD_CFG_OK					0x7F
#define	TS_CMD_REG_READY				0xFF

#define TS_SET_SPI_ARGS_VALUE			0x0F
#define GOODIX_ESD_TICK_WRITE_DATA_YS	0xAA

/* times*/
#define TS_WAIT_CMD_FREE_RETRY_TIMES	10
#define TS_WAIT_CFG_READY_RETRY_TIMES	30
#define TS_RESET_IC_INIT_RETRY_TIMES	10

/* length*/
#define TS_CFG_HEAD_LEN_YS		5
#define IRQ_HEAD_LEN_YS			8
#define IRQ_HEAD_LEN_NOR		2
#define GOODIX_SPI_BUFF_MAX_SIZE	(8 * 1024 + 16)

/*others*/
#define TYPE_STYLUS					1
#define TYPE_TOUCH					2
#define TYPE_STYLUS_HOVER			3
#define TS_CFG_DATA_EQUAL_FLASH		99

#define BYTES_PER_COORD				8
#define TS_CFG_BAG_NUM_INDEX		2

/*struction & enum*/
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,
};

/*for config & firmware*/
const char *gt9896s_firmware_buf;
const char *gt9896s_config_buf;
const char *gt9896s_lcm_buf;
int gt9896s_find_touch_node;
int gt9896s_cfg_flag;
char panel_firmware_buf[128];
char panel_config_buf[128];

static struct platform_device *gt9896s_pdev;
static int gt9896s_spi_probe(struct spi_device *spi);

#ifdef CONFIG_OF
static int gt9896s_parse_dt_display(struct gt9896s_ts_board_data *board_data)
{
	int r;
	struct device_node *node = NULL;

	node = of_find_compatible_node(NULL, NULL, "mediatek,touch-panel");
	if (node) {
		r = of_property_read_u32(node, "lcm-is-fake",
					 &board_data->fake_status);
		if (r) {
			ts_info("no lcm-is-fake, not find touch panel node!");
			gt9896s_find_touch_node = 0;
			return 0;
		}
		ts_info("find touch panel node!");
		gt9896s_cfg_flag = 0;
		r = of_property_read_u32(node, "lcm-width",
				 &board_data->panel_max_x);
		if (r)
			ts_info("parse lcm-width from dt fail!");

		r = of_property_read_u32(node, "lcm-height",
					 &board_data->panel_max_y);
		if (r)
			ts_info("parse lcm-height from dt fail!");

		r = of_property_read_u32(node, "lcm-fake-width",
				 &board_data->input_max_x);
		if (r)
			ts_info("parse lcm-fake-width from dt fail!");

		r = of_property_read_u32(node, "lcm-fake-height",
					 &board_data->input_max_y);
		if (r)
			ts_info("parse lcm-fake-height from dt fail!");

		r = of_property_read_string(node, "lcm-name",
				&gt9896s_lcm_buf);
		if (r < 0)
			ts_info("read lcm-name failed!");
		//check if the lcm-name is supported
		if ((strcmp("nt36672e_fhdp_dphy_vdo_jdi_120hz",
			gt9896s_lcm_buf) != 0) &&
			(strcmp("nt36672e_fhdp_cphy_vdo_jdi_120hz",
			gt9896s_lcm_buf) != 0) &&
			(strcmp("nt36672e_fhdp_dphy_vdo_jdi_144hz",
			gt9896s_lcm_buf) != 0) &&
			(strcmp("nt36672e_fhdp_dphy_vdo_jdi_60hz",
			gt9896s_lcm_buf) != 0) &&
			(strcmp("td4330_fhdp_dphy_vdo_truly",
			gt9896s_lcm_buf) != 0) &&
			(strcmp("td4330_fhdp_dphy_cmd_truly",
			gt9896s_lcm_buf) != 0) &&
			(strcmp("ft8756_fhdp_dphy_vdo_truly",
			gt9896s_lcm_buf) != 0)) {
			ts_info("lcm-name is not supported by gt9896s!");
			return -EINVAL;
		}
		gt9896s_find_touch_node = 1;
	} else {
		ts_info("not find touch panel node!");
		gt9896s_find_touch_node = 0;
	}
	return 0;
}
/**
 * gt9896s_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 gt9896s_parse_dt_resolution(struct device_node *node,
		struct gt9896s_ts_board_data *board_data)
{
	int r, err;

	if (gt9896s_find_touch_node != 1) {
		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;
		/* For unreal lcm test */
		r = of_property_read_u32(node, "goodix,input-max-x",
					 &board_data->input_max_x);
		if (r)
			err = -ENOENT;
		r = of_property_read_u32(node, "goodix,input-max-y",
					&board_data->input_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;
}

/**
 * gt9896s_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 gt9896s_parse_dt(struct device_node *node,
	struct gt9896s_ts_board_data *board_data)
{
	struct property *prop;
	const char *name_tmp;
	int r;

	if (!board_data) {
		ts_err("invalid board data");
		return -EINVAL;
	}

	/* get gpio property*/
	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;

	/* get irq trigger type property*/
	r = of_property_read_u32(node, "goodix,irq-flags",
			&board_data->irq_flags);
	if (r) {
		ts_err("invalid irq-flags");
		return -EINVAL;
	}

	if (gt9896s_find_touch_node != 1) {
		r = of_property_read_string(node, "goodix,firmware-version",
				&gt9896s_firmware_buf);
		if (r < 0)
			ts_err("Invalid firmware version in dts : %d", r);
		r = of_property_read_string(node, "goodix,config-version",
				&gt9896s_config_buf);
		if (r < 0) {
			ts_err("Invalid config version in dts : %d", r);
			return -EINVAL;
		}
	}

	/* get power property*/
	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))
			strlcpy(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 = gt9896s_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

/**
 * gt9896s_spi_read- read device register through spi bus
 * @dev: pointer to device data
 * @addr: register address
 * @data: read buffer
 * @len: bytes to read
 * return: 0 - read ok, < 0 - spi transter error
 */
int gt9896s_spi_read(struct gt9896s_ts_device *dev, unsigned int addr,
	unsigned char *data, unsigned int len)
{
	struct spi_device *spi = dev->spi_dev;
	u8 *rx_buf = dev->rx_buff;
	u8 *tx_buf = dev->tx_buff;
	struct spi_transfer xfers;
	struct spi_message spi_msg;
	int ret = 0;

	spi_message_init(&spi_msg);
	memset(&xfers, 0, sizeof(xfers));

	/*spi_read tx_buf format: 0xF1 + addr(2bytes) + data*/
	tx_buf[0] = SPI_FLAG_RD; //0xF1 start read flag
	tx_buf[1] = (addr >> 8) & MASK_8BIT;
	tx_buf[2] = addr & MASK_8BIT;
	tx_buf[3] = MASK_8BIT;
	tx_buf[4] = MASK_8BIT;

	xfers.tx_buf = tx_buf;
	xfers.rx_buf = rx_buf;
	xfers.len = len + 5;
	xfers.cs_change = 0;
	spi_message_add_tail(&xfers, &spi_msg);
	ret = spi_sync(spi, &spi_msg);
	if (ret < 0) {
		ts_err("Spi transfer error:%d\n", ret);
		return ret;
	}

	memcpy(data, &rx_buf[5], len);

	return ret;
}

/**
 * gt9896s_spi_write- write device register through spi bus
 * @dev: pointer to device data
 * @addr: register address
 * @data: write buffer
 * @len: bytes to write
 * return: 0 - write ok; < 0 - spi transter error.
 */
int gt9896s_spi_write(struct gt9896s_ts_device *dev, unsigned int addr,
		unsigned char *data, unsigned int len)
{
	struct spi_device *spi = dev->spi_dev;
	u8 *tx_buf = dev->tx_buff;
	struct spi_transfer xfers;
	struct spi_message spi_msg;
	int ret = 0;

	spi_message_init(&spi_msg);
	memset(&xfers, 0, sizeof(xfers));

	/*spi_write tx_buf format: 0xF0 + addr(2bytes) + data*/
	tx_buf[0] = SPI_FLAG_WR; //0xF0 start write flag
	tx_buf[1] = (addr >> 8) & MASK_8BIT;
	tx_buf[2] = addr & MASK_8BIT;
	memcpy(&tx_buf[3], data, len);
	xfers.tx_buf = tx_buf;
	xfers.len = len + 3;
	xfers.cs_change = 0;
	spi_message_add_tail(&xfers, &spi_msg);
	//thp_bus_lock();
	ret = spi_sync(spi, &spi_msg);
	//thp_bus_unlock();
	if (ret < 0) {
		ts_err("Spi transfer error:%d\n", ret);
	}

	return ret;
}

/**
 * gt9896s_reset_ic_init- init ic after reset
 * @ts_dev: pointer to device data
 * return: 0 - init ok, < 0 - init failed
 */
int gt9896s_reset_ic_init(struct gt9896s_ts_device *ts_dev)
{
	u8 reg_val = 0;
	u8 ack_val = 0;
	int ret = -1, retry = 0;

	/* set spi transfer args */
	reg_val = TS_SET_SPI_ARGS_VALUE;
	for (retry = 0; retry < TS_RESET_IC_INIT_RETRY_TIMES; retry++) {
		/* write spi args*/
		ret = gt9896s_spi_write(ts_dev, TS_REG_SET_SPI_ARGS, &reg_val, 1);
		if (ret) {
			ts_err("spi write spi tranfer args failed, ret %d", ret);
			goto exit;
		}

		/* read back & check spi args*/
		ret = gt9896s_spi_read(ts_dev, TS_REG_SET_SPI_ARGS, &ack_val, 1);
		if (ret) {
			ts_err("spi read spi tranfer args failed, ret %d", ret);
			goto exit;
		}
		ts_info("spi write data: %d, read data: %d!", reg_val, ack_val);
		if (ack_val == reg_val) {
			ts_info("set spi tranfer args success!");
			break;
		} else {
			ts_err("set spi tranfer args failed, retry %d!", retry);
		}
	}
	if (TS_RESET_IC_INIT_RETRY_TIMES == retry) {
		ts_err("failed to set spi tranfer args 100 times!");
		return -EINVAL;
	}

	/* remove GIO force to hold CPU, confirm fw work normally for ES_CHIP */
	reg_val = 0x0;
	for (retry = 0; retry < TS_RESET_IC_INIT_RETRY_TIMES; retry++) {
		/* spi write to remove hold*/
		ret = gt9896s_spi_write(ts_dev, TS_REG_REMOVE_HOLD, &reg_val, 1);
		if (ret) {
			ts_err("spi write to remove GIO force to hold CPU failed");
			goto exit;
		}

		/* read back & check */
		ret = gt9896s_spi_read(ts_dev, TS_REG_REMOVE_HOLD, &ack_val, 1);
		if (ret) {
			ts_err("spi read to remove GIO force to hold CPU failed");
			goto exit;
		}
		ts_info("spi write data: %d, read data: %d!", reg_val, ack_val);
		if (ack_val == reg_val) {
			ts_info("remove GIO force to hold CPU success");
			break;
		} else {
			ts_err("failed to remove GIO force to hold CPU success, retry %d",
					retry);
		}
	}
	if (TS_RESET_IC_INIT_RETRY_TIMES == retry) {
		ts_err("failed to remove GIO force to hold CPU 100 times!");
		return -EINVAL;
	}

exit:
	return ret;
}

/* confirm current device is gt9896s or not.
 * If confirmed 0 will return.
 */
static int gt9896s_ts_dev_prepare(struct gt9896s_ts_device *ts_dev)
{
	int ret = 0;

	/* reset ic */
	gpio_direction_output(ts_dev->board_data.reset_gpio, 0);
	udelay(2000);
	gpio_direction_output(ts_dev->board_data.reset_gpio, 1);
	msleep(50);

	ts_info("ts_dev->ic_type = IC_TYPE_YELLOWSTONE_SPI");
	/* set spi args & remove GIO hold for ES_CHIP*/
	ret = gt9896s_reset_ic_init(ts_dev);
	if (ret)
		ts_err("reset ic init failed, ret %d", ret);

	return ret;
}

static void gt9896s_cmd_init(struct gt9896s_ts_device *dev,
			    struct gt9896s_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_SPI) {
		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 {
		ts_err("unsupported ic type");
	}
}

/**
 * gt9896s_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 gt9896s_send_command(struct gt9896s_ts_device *dev, struct gt9896s_ts_cmd *cmd)
{
	int ret;

	if (!cmd || !cmd->initialized)
		return -EINVAL;

	ret = gt9896s_spi_write(dev, cmd->cmd_reg, cmd->cmds, cmd->length);
	if (ret < 0)
		ts_err("spi write to send command 0x%X failed,ret %d",
				cmd->cmds[0], ret);
	return ret;
}

static int gt9896s_read_version(struct gt9896s_ts_device *dev,
		struct gt9896s_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;
	}

	/*check checksum*/
	if (dev->reg.version_base && dev->reg.version_len < sizeof(temp_buf)) {
		r = gt9896s_spi_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_SPI)
			checksum = checksum_u8_ys(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 = gt9896s_spi_read(dev, dev->reg.pid, buffer, pid_read_len);
	if (r < 0) {
		ts_err("Read pid failed");
		if (version)
			version->valid = false;
		goto exit;
	}
	memcpy(version->pid, buffer, pid_read_len);

	/*read vid*/
	memset(buffer, 0, sizeof(buffer));
	memset(version->vid, 0, sizeof(version->vid));
	r = gt9896s_spi_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 = gt9896s_spi_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 gt9896s_wait_cfg_cmd_ready(struct gt9896s_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 gt9896s_ts_cmd ts_cmd;

	gt9896s_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 (gt9896s_spi_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 (gt9896s_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_gt9896s_send_config(struct gt9896s_ts_device *dev,
		struct gt9896s_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 gt9896s_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 (!gt9896s_spi_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*/
	gt9896s_cmd_init(dev, &ts_cmd, COMMAND_START_SEND_CFG,
			 0, dev->reg.command);
	if (gt9896s_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 (gt9896s_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 (gt9896s_spi_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*/
	gt9896s_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG,
			 0, dev->reg.command);
	if (gt9896s_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_SPI) {
		/*6. wait 0x7f or 0x7e */
		for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES;
		     try_times++) {
			r = gt9896s_spi_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 */
		gt9896s_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG_YS,
				 0, dev->reg.command);
		if (gt9896s_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");
				r = -EINVAL;
				goto exit;
			} else {
				ts_info("config data equal with flash");
				r = TS_CFG_DATA_EQUAL_FLASH;
				goto exit;
			}
		}
	}

	ts_info("Send cfg SUCCESS");
	r = 0;

exit:
	return r;
}

static int gt9896s_send_config(struct gt9896s_ts_device *dev,
		struct gt9896s_ts_config *config)
{
	int r = 0;

	if (!config || !config->initialized) {
		ts_err("invalid config data");
		return -EINVAL;
	}

	/*check configuration valid*/
	// TODO remove this
	// r = gt9896s_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_gt9896s_send_config(dev, config);
	if (TS_CFG_DATA_EQUAL_FLASH == r)
		ts_info("config data equal with flash, r %d!", r);
	else if (r != 0)
		ts_err("_do_gt9896s_send_config fail, r %d!", r);

	mutex_unlock(&config->lock);
	return r;
}

/* success return config length else return -1 */
static int gt9896s_read_config_ys(struct gt9896s_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;

	/*read config head*/
	ret = gt9896s_spi_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN_YS);
	if (ret) {
		ts_err("spi read failed, addr 0x%X, ret %d", cfg_addr, 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 = gt9896s_spi_read(dev, cfg_addr + offset, buf + offset, 2);
		if (ret) {
			ts_err("spi read failed, addr 0x%X, ret %d", cfg_addr + offset, ret);
			goto err_out;
		}

		/* get sub bag length */
		subbag_len = buf[offset + 1];
		ts_debug("sub bag num:%u,sub bag length:%u",
			 buf[offset], subbag_len);

		/* read sub bag data */
		ret = gt9896s_spi_read(dev, cfg_addr + offset + 2,
				      buf + offset + 2, subbag_len + 2);
		if (ret) {
			ts_err("spi read failed, addr 0x%X, ret %d",
					cfg_addr + offset + 2, ret);
			goto err_out;
		}

		/* check sub bag checksum */
		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_len, <= 0 failed */
static int gt9896s_read_config(struct gt9896s_ts_device *dev,
			      u8 *config_data)
{
	struct gt9896s_ts_cmd ts_cmd;
	u8 cmd_flag;
	u32 cmd_reg = dev->reg.command;
	int r = 0;
	int i;

	/*check config params legal*/
	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;
	}

	/* wait for IC in IDLE state */
	for (i = 0; i < TS_WAIT_CMD_FREE_RETRY_TIMES; i++) {
		cmd_flag = 0;
		r = gt9896s_spi_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 */
	gt9896s_cmd_init(dev, &ts_cmd, COMMAND_START_READ_CFG,
			 0, cmd_reg);
	r = gt9896s_send_command(dev, &ts_cmd);
	if (r) {
		ts_err("Failed send read config command");
		goto exit;
	}

	/* wait for config data ready */
	if (gt9896s_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_SPI)
		r = gt9896s_read_config_ys(dev, config_data);

	if (r <= 0)
		ts_err("Failed read config data");

	/* clear command */
	gt9896s_cmd_init(dev, &ts_cmd, TS_CMD_REG_READY, 0, cmd_reg);
	r = gt9896s_send_command(dev, &ts_cmd);
	if (r) {
		ts_err("Failed send read config command");
		goto exit;
	}

exit:
	return r;
}

/**
 * gt9896s_hw_reset - reset device
 *
 * @dev: pointer to touch device
 * Returns 0 - succeed,<0 - failed
 */
int gt9896s_hw_reset(struct gt9896s_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);

	if (dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) {
		/* set spi args & remove GIO hold for ES_CHIP*/
		r = gt9896s_reset_ic_init(dev);
		if (r) {
			ts_err("reset ic init failed, ret %d", r);
			return r;
		}
	}

	/*init dynamic esd*/
	if (dev->reg.esd) {
		r = gt9896s_spi_write(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 r;
}

/**
 * gt9896s_request_handler - handle firmware request
 *
 * @dev: pointer to touch device
 * @request_data: requset information
 * Returns 0 - succeed,<0 - failed
 */
static int gt9896s_request_handler(struct gt9896s_ts_device *dev)
{
	unsigned char buffer[1];
	int r;

	/* read reg.fw_request */
	r = gt9896s_spi_read(dev, dev->reg.fw_request, buffer, 1);
	if (r < 0) {
		ts_debug("spi read fw_request failed, addr 0x%X, r %d",
					dev->reg.fw_request, r);
		return r;
	}

	switch (buffer[0]) {
	case REQUEST_CONFIG:
		ts_info("HW request config");
		r = gt9896s_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 = gt9896s_hw_reset(dev);
		if (r != 0)
			ts_info("request reset, reset faild");
	break;
	case REQUEST_RELOADFW:
		ts_info("HW request reload fw");
		gt9896s_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 = gt9896s_spi_write(dev, dev->reg.fw_request, buffer, 1);
	return r;
}

static void gt9896s_swap_coords(struct gt9896s_ts_device *dev,
		unsigned int *coor_x, unsigned int *coor_y)
{
	unsigned int temp;
	struct gt9896s_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;
}

static void gt9896s_parse_finger_ys(struct gt9896s_ts_device *dev,
	struct gt9896s_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));
		gt9896s_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 void gt9896s_parse_pen_ys(struct gt9896s_ts_device *dev,
	struct gt9896s_pen_data *pen_data, unsigned char *buf, int touch_num)
{
	ts_info("unsupported");
}

static int gt9896s_touch_handler_ys(struct gt9896s_ts_device *dev,
		struct gt9896s_ts_event *ts_event,
		u8 *pre_buf, u32 pre_buf_len)
{
	struct gt9896s_touch_data *touch_data = &ts_event->touch_data;
	struct gt9896s_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;
	static u8 pre_pen_num;

	/* clean event buffer */
	memset(ts_event, 0, sizeof(*ts_event));
	/* copy pre-data to buffer */
	memcpy(buffer, pre_buf, pre_buf_len);

	/* get touch num */
	touch_num = buffer[2] & 0x0F;
	if (unlikely(touch_num > GOODIX_MAX_TOUCH)) {
		touch_num = -EINVAL;
		goto exit_clean_sta;
	}

	/* read all coor data */
	if (unlikely(touch_num > 1)) {
		r = gt9896s_spi_read(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;
	}

	/* check coor data checksum except coor head */
	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;
		}
	}

	/* get touch type */
	if (touch_num > 0)
		point_type = buffer[(touch_num - 1) * BYTES_PER_COORD + IRQ_HEAD_LEN_YS];
	if (touch_num >= 1 &&
		(point_type == TYPE_STYLUS_HOVER || point_type == TYPE_STYLUS)) {
		/* cur type is pen */
		if (pre_finger_num) {
			ts_event->event_type = EVENT_TOUCH;
			gt9896s_parse_finger_ys(dev, touch_data, buffer, 0);
			pre_finger_num = 0;
		} else {
			pre_pen_num = 1;
			ts_event->event_type = EVENT_PEN;
			gt9896s_parse_pen_ys(dev, pen_data, buffer, touch_num);
		}
	} else {
		/* cur type is finger */
		if (pre_pen_num) {
			ts_event->event_type = EVENT_PEN;
			gt9896s_parse_pen_ys(dev, pen_data, buffer, 0);
			pre_pen_num = 0;
		} else {
			ts_event->event_type = EVENT_TOUCH;
			gt9896s_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 gt9896s_event_handler(struct gt9896s_ts_device *dev,
		struct gt9896s_ts_event *ts_event)
{
	int pre_read_len = 0;
	u8 pre_buf[32];
	u8 event_sta;
	int r;

	/* get coor pre_read_len */
	if (dev->ic_type == IC_TYPE_YELLOWSTONE_SPI)
		pre_read_len = IRQ_HEAD_LEN_YS + BYTES_PER_COORD + 2;

	/* read coor head */
	r = gt9896s_spi_read(dev, dev->reg.coor, pre_buf, pre_read_len);
	if (unlikely(r < 0)) {
		ts_debug("spi read coor head failed, addr 0x%x, len %d, r %d",
				dev->reg.coor, pre_read_len, r);
		return r;
	}

	/* check coor head checksum */
	if (dev->ic_type == IC_TYPE_YELLOWSTONE_SPI &&
	    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_SPI)
			gt9896s_touch_handler_ys(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;
		gt9896s_request_handler(dev);
	} else if ((event_sta & GOODIX_GESTURE_EVENT) ==
		   GOODIX_GESTURE_EVENT) {
		/* handle gesture event */
		ts_debug("Gesture event");
	} else {
		ts_debug("unknow event type:0x%x", event_sta);
		r = -EINVAL;
	}

	return r;
}

/**
 * gt9896s_hw_suspend - Let touch deivce stay in lowpower mode.
 * @dev: pointer to gt9896s touch device
 * @return: 0 - succeed, < 0 - failed
 */
static int gt9896s_hw_suspend(struct gt9896s_ts_device *dev)
{
	struct gt9896s_ts_cmd sleep_cmd;
	int r = 0;

	gt9896s_cmd_init(dev, &sleep_cmd, COMMAND_SLEEP, 0, dev->reg.command);
	if (sleep_cmd.initialized) {
		r = gt9896s_send_command(dev, &sleep_cmd);
		if (!r)
			ts_info("Chip in sleep mode");
	} else {
		ts_err("Uninitialized sleep command");
	}
	return r;
}

/**
 * gt9896s_hw_resume - Let touch deivce stay in active  mode.
 * @dev: pointer to gt9896s touch device
 * @return: 0 - succeed, < 0 - failed
 */
static int gt9896s_hw_resume(struct gt9896s_ts_device *dev)
{
	int ret;

	ret = gt9896s_hw_reset(dev);
	if (ret)
		ts_err("%s: hw reset failed, ret %d", __func__, ret);

	return ret;
}

static int gt9896s_esd_check(struct gt9896s_ts_device *dev)
{
	int r;
	u8 data = 0;

	if (dev->reg.esd == 0) {
		ts_err("esd reg is NULL");
		return 0;
	}

	/*check dynamic esd*/
	//TODO:to check reg & value later
	r = dev->hw_ops->read_trans(dev, TS_REG_ESD_TICK_R, &data, 1);
	if (r < 0 || (data == GOODIX_ESD_TICK_WRITE_DATA_YS)) {
		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 gt9896s_ts_hw_ops hw_spi_ops = {
	.dev_prepare = gt9896s_ts_dev_prepare,
	.read = gt9896s_spi_read,
	.write = gt9896s_spi_write,
	.read_trans = gt9896s_spi_read,
	.write_trans = gt9896s_spi_write,
	.reset = gt9896s_hw_reset,
	.event_handler = gt9896s_event_handler,
	.send_config = gt9896s_send_config,
	.read_config = gt9896s_read_config,
	.send_cmd = gt9896s_send_command,
	.read_version = gt9896s_read_version,
	.suspend = gt9896s_hw_suspend,
	.resume = gt9896s_hw_resume,
	.check_hw = gt9896s_esd_check,
};

static void gt9896s_pdev_release(struct device *dev)
{
	ts_info("gt9896s pdev released");
}
#define BOOT_UPDATE_FIRMWARE_NAME novatek_firmware
char novatek_firmware[25];

static void gt9896s_spi_remove(struct spi_device *spi)
{
	if (gt9896s_pdev) {
		platform_device_unregister(gt9896s_pdev);
		gt9896s_pdev = NULL;
	ts_info("GT9896S SPI remove");
	}

	ts_info("GT9896S SPI");
}


#ifdef CONFIG_OF
static const struct of_device_id spi_matches[] = {
	{.compatible = TS_DT_COMPATIBLE,},
	{},
};
#endif

static const struct spi_device_id spi_id_table[] = {
	{TS_DRIVER_NAME, 0},
	{},
};

static struct spi_driver gt9896s_spi_driver = {
	.driver = {
		.name = TS_DRIVER_NAME,
		.owner = THIS_MODULE,
		.bus = &spi_bus_type,
		.of_match_table = spi_matches,
	},
	.id_table = spi_id_table,
	.probe = gt9896s_spi_probe,
	.remove = gt9896s_spi_remove,
};

/* release manully when prob failed */
void gt9896s_ts_dev_release(void)
{
	if (gt9896s_pdev) {
		platform_device_unregister(gt9896s_pdev);
		gt9896s_pdev = NULL;
	}
	spi_unregister_driver(&gt9896s_spi_driver);
}

static int gt9896s_spi_probe(struct spi_device *spi)
{
	struct gt9896s_ts_device *ts_device = NULL;
	int r = 0;

	ts_info("%s IN", __func__);

	/* init spi_device */
	spi->mode            = SPI_MODE_0;
	spi->bits_per_word   = 8;
	spi->max_speed_hz    = 6 * 1000 * 1000;

	/* init ts device data */
	ts_device = devm_kzalloc(&spi->dev,
		sizeof(struct gt9896s_ts_device), GFP_KERNEL);
	if (!ts_device)
		return -ENOMEM;

	/* alloc memory for spi transfer buffer */
	ts_device->tx_buff = kzalloc(GOODIX_SPI_BUFF_MAX_SIZE, GFP_KERNEL);
	ts_device->rx_buff = kzalloc(GOODIX_SPI_BUFF_MAX_SIZE, GFP_KERNEL);
	if (!ts_device->tx_buff || !ts_device->rx_buff) {
		ts_err("%s: out of memory\n", __func__);
		r = -ENOMEM;
		goto err_spi_buf;
	}
	ts_device->name = "Goodix Touch GT9896S";
	ts_device->spi_dev = spi;
	ts_device->dev = &spi->dev;
	ts_device->hw_ops = &hw_spi_ops;

	/* parse devicetree property */
	if (IS_ENABLED(CONFIG_OF) && spi->dev.of_node) {
		r = gt9896s_parse_dt_display(&ts_device->board_data);
		if (r < 0) {
			ts_info("%s OUT, lcm not support", __func__);
			return r;
		}
		r = gt9896s_parse_dt(spi->dev.of_node,
				    &ts_device->board_data);
		if (r < 0) {
			ts_err("failed parse device info form dts, %d", r);
			r = -EINVAL;
			goto err_spi_buf;
		}
		if (gt9896s_find_touch_node == 1) {
			if (strcmp("ft8756_fhdp_dphy_vdo_truly", gt9896s_lcm_buf) == 0) {
				if (ts_device->board_data.panel_max_x == 1080
					&& ts_device->board_data.panel_max_y == 2300) {
					strscpy(panel_config_buf,
						"gt9896s_cfg_6893v02", 20);
					strscpy(panel_firmware_buf,
						"gt9896s_firmware_6893v02", 25);
				} else if (ts_device->board_data.panel_max_x == 1080
					&& ts_device->board_data.panel_max_y == 2280) {
					strscpy(panel_config_buf,
						"gt9896s_cfg_6893v01", 20);
					strscpy(panel_firmware_buf,
						"gt9896s_firmware_6893v01", 25);
				} else {
					ts_info("%s, fault firmware!", gt9896s_lcm_buf);
				}
			} else if ((strcmp("td4330_fhdp_dphy_vdo_truly",
					gt9896s_lcm_buf) == 0) ||
					(strcmp("td4330_fhdp_dphy_cmd_truly",
					gt9896s_lcm_buf) == 0)) {
				if (ts_device->board_data.panel_max_x == 1080
					&& ts_device->board_data.panel_max_y == 2280) {
					strscpy(panel_config_buf,
						"gt9896s_cfg_6893v05", 20);
					strscpy(panel_firmware_buf,
						"gt9896s_firmware_6893v05", 25);
				} else if (ts_device->board_data.panel_max_x == 1080
					&& ts_device->board_data.panel_max_y == 2300) {
					strscpy(panel_config_buf,
						"gt9896s_cfg_6893v02", 20);
					strscpy(panel_firmware_buf,
						"gt9896s_firmware_6893v02", 25);
				} else {
					ts_info("%s, fault firmware!", gt9896s_lcm_buf);
				}
			} else if ((strcmp("nt36672e_fhdp_dphy_vdo_jdi_120hz",
					gt9896s_lcm_buf) == 0) ||
					(strcmp("nt36672e_fhdp_cphy_vdo_jdi_120hz",
					gt9896s_lcm_buf) == 0) ||
					(strcmp("nt36672e_fhdp_dphy_vdo_jdi_144hz",
					gt9896s_lcm_buf) == 0) ||
					(strcmp("nt36672e_fhdp_dphy_vdo_jdi_60hz",
					gt9896s_lcm_buf) == 0)) {
				if (ts_device->board_data.panel_max_x == 1080
					&& ts_device->board_data.panel_max_y == 2400) {
					strscpy(panel_config_buf,
						"gt9896s_cfg_6893v04", 20);
					strscpy(panel_firmware_buf,
						"gt9896s_firmware_6893v04", 25);
				} else {
					ts_info("%s, fault firmware!", gt9896s_lcm_buf);
				}
			}
		}
	} else {
		ts_err("no valid device tree node found");
		r = -ENODEV;
		goto err_spi_buf;
	}

	/* init ts core device */
	gt9896s_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
	if (!gt9896s_pdev) {
		ts_err("kzalloc core dev gt9896s_pdev faliled!");
		r = -ENOMEM;
		goto err_spi_buf;
	}

	/*
	 * you can find this platform dev in
	 * /sys/devices/platfrom/gt9896s_ts.0
	 * gt9896s_pdev->dev.parent = &spi->dev;
	 */
	gt9896s_pdev->name = GOODIX_CORE_DRIVER_NAME;
	gt9896s_pdev->id = 0;
	gt9896s_pdev->num_resources = 0;
	gt9896s_pdev->dev.platform_data = ts_device;
	gt9896s_pdev->dev.release = gt9896s_pdev_release;

	/* register platform device, then the gt9896s_ts_core
	 * module will probe the touch deivce.
	 */
	r = platform_device_register(gt9896s_pdev);
	if (r) {
		ts_err("failed register gt9896s platform device, %d", r);
		goto err_pdev;
	}

	/* register platform driver*/
	r = gt9896s_ts_core_init();
	if (r) {
		ts_err("failed register platform driver, %d", r);
		goto err_pdriver;
	}

	ts_info("%s OUT", __func__);
	return r;

err_pdriver:
	platform_device_unregister(gt9896s_pdev);
err_pdev:
	if (gt9896s_pdev) {
		kfree(gt9896s_pdev);
		gt9896s_pdev = NULL;
	}
err_spi_buf:
	if (ts_device->tx_buff) {
		kfree(ts_device->tx_buff);
		ts_device->tx_buff = NULL;
	}
	if (ts_device->rx_buff) {
		kfree(ts_device->rx_buff);
		ts_device->rx_buff = NULL;
	}
	ts_info("%s OUT, %d", __func__, r);
	return r;
}

static int __init gt9896s_spi_init(void)
{
	ts_info("GT9896S SPI driver init");
	return spi_register_driver(&gt9896s_spi_driver);
}

static void __exit gt9896s_spi_exit(void)
{
	spi_unregister_driver(&gt9896s_spi_driver);
	ts_info("GT9896S SPI driver exit");
}

late_initcall(gt9896s_spi_init);
module_exit(gt9896s_spi_exit);

MODULE_DESCRIPTION("Goodix THP Driver");
MODULE_AUTHOR("Goodix, Inc.");
MODULE_LICENSE("GPL v2");
