diff mbox series

[2/2] Input: Add driver for Novatek NT519XX series touchscreen devices

Message ID 20231025082054.1190-3-Weishih_Lin@novatek.com.tw (mailing list archive)
State New
Headers show
Series Add Novatek NT519XX touchcreen driver | expand

Commit Message

Wei-Shih Lin Oct. 25, 2023, 8:20 a.m. UTC
This patch adds support for Novatek NT519XX series touchscreen devices.
Existing Novatek touchscreen driver code developed for Acer Iconia One 7
B1-750 tablet with Novatek IC NT11205 is novatek-nvt-ts.c in the path
drivers/input/touchscreen/. However, this patch supports touch features
for automotive display with Novatek TDDI NT519XX.

Signed-off-by: Wei-Shih Lin <Weishih_Lin@novatek.com.tw>
---
 drivers/input/touchscreen/Kconfig           |  12 +
 drivers/input/touchscreen/Makefile          |   1 +
 drivers/input/touchscreen/nt519xx.c         | 995 ++++++++++++++++++++
 drivers/input/touchscreen/nt519xx.h         | 130 +++
 drivers/input/touchscreen/nt519xx_mem_map.h | 262 ++++++
 5 files changed, 1400 insertions(+)
 create mode 100644 drivers/input/touchscreen/nt519xx.c
 create mode 100644 drivers/input/touchscreen/nt519xx.h
 create mode 100644 drivers/input/touchscreen/nt519xx_mem_map.h

Comments

Krzysztof Kozlowski Oct. 28, 2023, 8:52 a.m. UTC | #1
On 25/10/2023 10:20, Wei-Shih Lin wrote:
> This patch adds support for Novatek NT519XX series touchscreen devices.
> Existing Novatek touchscreen driver code developed for Acer Iconia One 7
> B1-750 tablet with Novatek IC NT11205 is novatek-nvt-ts.c in the path
> drivers/input/touchscreen/. However, this patch supports touch features
> for automotive display with Novatek TDDI NT519XX.
> 


> +
> +static int32_t nvt_ts_resume(struct device *dev)
> +{
> +	if (bTouchIsAwake) {
> +		NVT_LOG("Touch is already resume.\n");
> +		return 0;
> +	}
> +
> +	mutex_lock(&ts->lock);
> +
> +	NVT_LOG("start\n");

Sorry, you cannot have such silly debugs.

> +
> +	nvt_bootloader_reset();
> +	nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN);
> +
> +	nvt_irq_enable(true);
> +
> +	bTouchIsAwake = 1;
> +
> +	mutex_unlock(&ts->lock);
> +
> +	NVT_LOG("end\n");



....

> +#endif
> +
> +static struct i2c_driver nvt_i2c_driver = {
> +	.probe		= nvt_ts_probe,
> +	.remove		= nvt_ts_remove,
> +	.shutdown	= nvt_ts_shutdown,
> +	.id_table	= nvt_ts_id,
> +	.driver = {
> +		.name	= NVT_I2C_NAME,
> +		.owner	= THIS_MODULE,

Drop

> +#ifdef CONFIG_OF

Nope, drop

> +		.of_match_table = nvt_match_table,
> +#endif
> +	},
> +};
> +
> +static int32_t __init nvt_driver_init(void)
> +{
> +	int32_t ret = 0;
> +
> +	NVT_LOG("start\n");

Drop entire init. Open existing code and use it as template.

> +
> +	bTouchIsAwake = 0;
> +
> +	ret = i2c_add_driver(&nvt_i2c_driver);
> +	if (ret) {
> +		NVT_ERR("Failed to add i2c driver!");
> +		goto err_driver;
> +	}
> +
> +	NVT_LOG("end\n");
> +
> +err_driver:
> +	return ret;
> +}
> +
> +static void __exit nvt_driver_exit(void)
> +{
> +	i2c_del_driver(&nvt_i2c_driver);
> +}
> +
> +module_init(nvt_driver_init);
> +module_exit(nvt_driver_exit);
> +
> +MODULE_DESCRIPTION("Novatek Touchscreen Driver");
> +MODULE_LICENSE("GPL");

This driver has very poor quality. Pointing issues here would be even
too much work. Please start from scratch from existing, accepted and
reviewed driver and customize it for your needs. You will notice that it
does not have all this weird code you put here.

Best regards,
Krzysztof
Dmitry Torokhov Oct. 29, 2023, 3:26 a.m. UTC | #2
Hi Wei-Shih,

On Wed, Oct 25, 2023 at 04:20:54PM +0800, Wei-Shih Lin wrote:
> This patch adds support for Novatek NT519XX series touchscreen devices.
> Existing Novatek touchscreen driver code developed for Acer Iconia One 7
> B1-750 tablet with Novatek IC NT11205 is novatek-nvt-ts.c in the path
> drivers/input/touchscreen/. However, this patch supports touch features
> for automotive display with Novatek TDDI NT519XX.

How different the protocol of this part from NT11205? Can the existing
driver be modified to support both variants?

You already got feedback from Krzysztof, on top of his, if we want to
continue with a separate driver:

- it should use standard device properties
- it should use gpiod API
- it looks like it will benefit of regmap's paging support
- helpers like nvt_irq_enable() should not be used - your code should
  know whether itq is enabled or disabled at all times
- all caps are reserved for macros (CTP_I2C_WRITE and others)
- I am sure we have crc8 helpers in the kernel
- please use u8, u16, etc in the kernel code instead of uint8_t,
  uint16_t
- your driver will likely benefit from devm APIs
- no compile-time conditionals like "#if TOUCH_MAX_FINGER_NUM > 1"

Thanks.
diff mbox series

Patch

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c2cbd332af1d..6f84e31e7dbd 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1389,4 +1389,16 @@  config TOUCHSCREEN_HIMAX_HX83112B
 	  To compile this driver as a module, choose M here: the
 	  module will be called himax_hx83112b.
 
+config TOUCHSCREEN_NT519XX
+	tristate "Novatek NT519XX based touchscreens"
+	depends on I2C
+	help
+	  Say Y here if you have a Novatek NT519XX based touchscreen
+	  controller.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nt519xx.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 159cd5136fdb..598d38e4e06a 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -117,3 +117,4 @@  obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW)	+= raspberrypi-ts.o
 obj-$(CONFIG_TOUCHSCREEN_IQS5XX)	+= iqs5xx.o
 obj-$(CONFIG_TOUCHSCREEN_ZINITIX)	+= zinitix.o
 obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B)	+= himax_hx83112b.o
+obj-$(CONFIG_TOUCHSCREEN_NT519XX)	+= nt519xx.o
diff --git a/drivers/input/touchscreen/nt519xx.c b/drivers/input/touchscreen/nt519xx.c
new file mode 100644
index 000000000000..f3cc884c65e9
--- /dev/null
+++ b/drivers/input/touchscreen/nt519xx.c
@@ -0,0 +1,995 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Novatek NT519xx touchscreens
+ *
+ * Copyright (C) 2023 Wei-Shih Lin <Weishih_Lin@novatek.com.tw>
+ * Copyright (C) 2023 Leo LS Chang <Leo_LS_Chang@novatek.com.tw>
+ *
+ * Copyright (C) 2010 - 2023 Novatek, Inc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+
+#include <linux/notifier.h>
+#include <linux/fb.h>
+
+#include "nt519xx.h"
+
+struct nvt_ts_data *ts;
+
+static int nvt_fb_notifier_callback(struct notifier_block *self,
+				    unsigned long event, void *data);
+
+static uint8_t bTouchIsAwake;
+static uint8_t debug_log;
+
+static void nvt_irq_enable(bool enable)
+{
+	struct irq_desc *desc;
+
+	if (enable) {
+		if (!ts->irq_enabled) {
+			enable_irq(ts->client->irq);
+			ts->irq_enabled = true;
+		}
+	} else {
+		if (ts->irq_enabled) {
+			disable_irq(ts->client->irq);
+			ts->irq_enabled = false;
+		}
+	}
+
+	desc = irq_to_desc(ts->client->irq);
+	NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth);
+}
+
+int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf,
+		     uint16_t len)
+{
+	struct i2c_msg msgs[2];
+	int32_t ret = -1;
+	int32_t retries = 0;
+
+	mutex_lock(&ts->xbuf_lock);
+
+	msgs[0].flags = !I2C_M_RD;
+	msgs[0].addr = address;
+	msgs[0].len = 1;
+	msgs[0].buf = &buf[0];
+
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].addr = address;
+	msgs[1].len = len - 1;
+	msgs[1].buf = ts->xbuf;
+
+	while (retries < 5) {
+		ret = i2c_transfer(client->adapter, msgs, 2);
+		if (ret == 2)
+			break;
+		retries++;
+	}
+
+	if (unlikely(retries == 5)) {
+		NVT_ERR("ret=%d\n", ret);
+		ret = -EIO;
+	}
+
+	memcpy(buf + 1, ts->xbuf, len - 1);
+
+	mutex_unlock(&ts->xbuf_lock);
+
+	return ret;
+}
+
+int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf,
+		      uint16_t len)
+{
+	struct i2c_msg msg;
+	int32_t ret = -1;
+	int32_t retries = 0;
+
+	mutex_lock(&ts->xbuf_lock);
+
+	msg.flags = !I2C_M_RD;
+	msg.addr = address;
+	msg.len = len;
+	memcpy(ts->xbuf, buf, len);
+	msg.buf = ts->xbuf;
+
+	while (retries < 5) {
+		ret = i2c_transfer(client->adapter, &msg, 1);
+		if (ret == 1)
+			break;
+		retries++;
+	}
+
+	if (unlikely(retries == 5)) {
+		NVT_ERR("ret=%d\n", ret);
+		ret = -EIO;
+	}
+
+	mutex_unlock(&ts->xbuf_lock);
+
+	return ret;
+}
+
+int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr)
+{
+	uint8_t buf[4] = { 0 };
+
+	buf[0] = 0xFF; // novatek set page command
+	buf[1] = (addr >> 16) & 0xFF;
+	buf[2] = (addr >> 8) & 0xFF;
+
+	return CTP_I2C_WRITE(ts->client, i2c_addr, buf, 3);
+}
+
+int32_t nvt_write_addr(uint32_t addr, uint8_t data)
+{
+	int32_t ret = 0;
+	uint8_t buf[2] = { 0 };
+
+	ret = nvt_set_page(I2C_FW_ADDRESS, addr);
+	if (ret < 0) {
+		NVT_ERR("Set page 0x%06X failed! ret=%d\n", addr, ret);
+		return ret;
+	}
+
+	buf[0] = addr & 0xFF;
+	buf[1] = data;
+	ret = CTP_I2C_WRITE(ts->client, I2C_FW_ADDRESS, buf, 2);
+	if (ret < 0) {
+		NVT_ERR("Write data to 0x%06X failed! ret=%d\n", addr, ret);
+		return ret;
+	}
+	ret = 0;
+
+	return ret;
+}
+
+int32_t nvt_read_reg(struct nvt_ts_reg reg, uint8_t *val)
+{
+	int32_t ret = 0;
+	uint32_t addr = 0;
+	uint8_t bitmask = 0;
+	uint8_t shift = 0;
+	uint8_t buf[8] = { 0 };
+	uint8_t temp = 0;
+
+	addr = reg.addr;
+	bitmask = reg.bitmask;
+	temp = reg.bitmask;
+	shift = 0;
+	while (1) {
+		if ((temp >> shift) & 0x01)
+			break;
+		if (shift == 8) {
+			NVT_ERR("bitmask all bits zero!\n");
+			ret = -1;
+			break;
+		}
+		shift++;
+	}
+
+	nvt_set_page(I2C_FW_ADDRESS, addr);
+	buf[0] = addr & 0xFF;
+	buf[1] = 0x00;
+	ret = CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 2);
+	if (ret < 0) {
+		NVT_ERR("CTP_I2C_READ failed! ret=%d\n", ret);
+		goto nvt_read_register_exit;
+	}
+	*val = (buf[1] & bitmask) >> shift;
+
+nvt_read_register_exit:
+	return ret;
+}
+
+void nvt_sw_reset_idle(void)
+{
+	nvt_write_addr(SWRST_SIF_ADDR, 0xAA);
+
+	usleep_range(15000, 16000);
+
+	nvt_clear_crc_err_flag();
+}
+
+void nvt_bootloader_reset(void)
+{
+	NVT_LOG("start\n");
+
+	nvt_write_addr(SWRST_SIF_ADDR, 0x69);
+
+	msleep(35);
+
+	NVT_LOG("end\n");
+}
+
+bool nvt_check_fw_reset_state(enum rst_complete_state check_reset_state)
+{
+	uint8_t buf[8] = { 0 };
+	bool ret = true;
+	int32_t retry = 0;
+
+	nvt_set_page(I2C_FW_ADDRESS, ts->mmap->EVENT_BUF_ADDR);
+
+	while (1) {
+		usleep_range(10000, 11000);
+
+		buf[0] = EVENT_MAP_RESET_COMPLETE;
+		buf[1] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 6);
+
+		if ((buf[1] >= check_reset_state) &&
+		    (buf[1] <= RESET_STATE_MAX)) {
+			ret = true;
+			break;
+		}
+
+		retry++;
+		if (unlikely(retry > 100)) {
+			NVT_ERR("retry=%d, buf[1]=0x%02X\n", retry, buf[1]);
+			ret = false;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int32_t nvt_check_cascade_chip_numbers(void)
+{
+	uint8_t enb_casc = 0;
+	uint8_t sdloc = 0;
+	uint8_t ic_num = 0;
+	int32_t ret = 0;
+
+	if (ts->cascade_type) {
+		if (ts->cascade_type == CASCADE_ENB_CASC) {
+			if (!ts->mmap->ENB_CASC_REG.addr) {
+				NVT_ERR("ENB_CASC_REG.addr is missing!\n");
+				return (-EIO);
+			}
+
+			nvt_set_page(I2C_FW_ADDRESS,
+				     ts->mmap->ENB_CASC_REG.addr);
+			nvt_read_reg(ts->mmap->ENB_CASC_REG, &enb_casc);
+
+			if (enb_casc == ENB_CASC_1CHIP)
+				ts->cascade_num = CASCADE_1CHIP;
+			else
+				ts->cascade_num = CASCADE_2CHIP;
+
+		} else if (ts->cascade_type == CASCADE_SDLOC) {
+			if (!ts->mmap->SDLOC_REG.addr) {
+				NVT_ERR("SDLOC_REG.addr is missing!\n");
+				return (-EIO);
+			}
+
+			nvt_set_page(I2C_FW_ADDRESS, ts->mmap->SDLOC_REG.addr);
+			nvt_read_reg(ts->mmap->SDLOC_REG, &sdloc);
+
+			switch (sdloc) {
+			case SDLOC_1CHIP:
+				ts->cascade_num = CASCADE_1CHIP;
+				break;
+			case SDLOC_2CHIP:
+				ts->cascade_num = CASCADE_2CHIP;
+				break;
+			case SDLOC_3CHIP_AND_ABOVE:
+				ts->cascade_num = CASCADE_3CHIP;
+				break;
+			default:
+				ts->cascade_num = CASCADE_CHECK_ERR;
+				NVT_ERR("Undefined CASCADE_TYPE_SDLOC: 0x%X!\n",
+					sdloc);
+				ret = (-EIO);
+				break;
+			}
+		} else if (ts->cascade_type == CASCADE_IC_NUM) {
+			if (!ts->mmap->IC_NUM_REG.addr) {
+				NVT_ERR("IC_NUM_REG.addr is missing!\n");
+				return (-EIO);
+			}
+
+			nvt_set_page(I2C_FW_ADDRESS, ts->mmap->IC_NUM_REG.addr);
+			nvt_read_reg(ts->mmap->IC_NUM_REG, &ic_num);
+
+			switch (ic_num) {
+			case IC_NUM_1CHIP:
+				ts->cascade_num = CASCADE_1CHIP;
+				break;
+			case IC_NUM_2CHIP:
+				ts->cascade_num = CASCADE_2CHIP;
+				break;
+			case IC_NUM_3CHIP:
+			case IC_NUM_3CHIP_AND_ABOVE:
+				ts->cascade_num = CASCADE_3CHIP;
+				break;
+			default:
+				ts->cascade_num = CASCADE_CHECK_ERR;
+				NVT_ERR("Undefined CASCADE_TYPE_IC_NUM: 0x%X!\n",
+					ic_num);
+				ret = (-EIO);
+				break;
+			}
+		}
+	} else {
+		ts->cascade_num = NONE_CASCADE_CASE;
+		NVT_LOG("CASCADE_NONE cascade_type: %d, cascade_num: %d\n",
+			ts->cascade_type, ts->cascade_num);
+	}
+
+	return ret;
+}
+
+int32_t nvt_get_fw_info(void)
+{
+	uint8_t buf[64] = { 0 };
+	uint8_t retry_count = 2;
+	int32_t ret = 0;
+
+info_retry:
+	nvt_set_page(I2C_FW_ADDRESS,
+		     ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
+
+	buf[0] = EVENT_MAP_FWINFO;
+	CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 17);
+	if ((buf[1] + buf[2]) != 0xFF) {
+		NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n",
+			buf[1], buf[2]);
+		if (retry_count < 3) {
+			retry_count++;
+			NVT_ERR("retry_count=%d\n", retry_count);
+			goto info_retry;
+		} else {
+			ts->fw_ver = 0;
+			NVT_ERR("Set default fw_ver=0x%02X\n", ts->fw_ver);
+			ret = -1;
+			goto out;
+		}
+	}
+	ts->fw_ver = buf[1];
+	ts->x_num = buf[3];
+	ts->y_num = buf[4];
+	ts->nvt_pid = (uint16_t)((buf[36] << 8) | buf[35]);
+
+	NVT_LOG("fw_ver=0x%02X, x_num=%0d, y_num=%0d, fw_type=0x%02X, PID=0x%04X\n",
+		ts->fw_ver, ts->x_num, ts->y_num, buf[14], ts->nvt_pid);
+
+	ret = 0;
+out:
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static void nvt_parse_dt(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+
+	ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0,
+					       &ts->irq_flags);
+	NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio);
+}
+#else /* CONFIG_OF */
+static void nvt_parse_dt(struct device *dev)
+{
+	ts->irq_gpio = NVTTOUCH_INT_PIN;
+}
+#endif /* CONFIG_OF */
+
+static int nvt_gpio_config(struct nvt_ts_data *ts)
+{
+	int32_t ret = 0;
+
+	if (gpio_is_valid(ts->irq_gpio)) {
+		ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int");
+		if (ret) {
+			NVT_ERR("Failed to request NVT-int GPIO!\n");
+			goto err_request_irq_gpio;
+		}
+	}
+
+	return ret;
+
+err_request_irq_gpio:
+	return ret;
+}
+
+static void nvt_gpio_deconfig(struct nvt_ts_data *ts)
+{
+	if (gpio_is_valid(ts->irq_gpio))
+		gpio_free(ts->irq_gpio);
+}
+
+static uint8_t nvt_calculate_crc8(uint8_t *buf, uint8_t length)
+{
+	uint8_t i = 0, j = 0;
+	uint8_t crc8 = 0;
+	uint8_t poly = 0x1D; // x8 + x4 + x3 + x2 + 1
+
+	for (i = 0; i < length; i++) {
+		crc8 ^= *(buf + i);
+
+		for (j = 0; j < 8; j++) {
+			if (crc8 & 0x80)
+				crc8 = (crc8 << 1) ^ poly;
+			else
+				crc8 = (crc8 << 1);
+		}
+	}
+
+	crc8 ^= (uint8_t)0x00;
+	return crc8;
+}
+
+static int32_t nvt_ts_event_crc8(uint8_t *buf, uint8_t length)
+{
+	uint8_t crc8 = nvt_calculate_crc8(buf, length - 1);
+	uint32_t i = 0;
+
+	/* The lastest item is CRC8 which is reported by FW */
+	if (buf[length - 1] != crc8) {
+		NVT_ERR("CRC8 not match, FW reported: 0x%02X, Calulated CRC8: 0x%02X\n",
+			buf[length - 1], crc8);
+		if (debug_log) {
+			NVT_ERR("length=%d\n", length);
+			for (i = 0; i < length; i++) {
+				NVT_LOG("%02X ", buf[i]);
+				if (i % POINT_DATA_LEN == 0)
+					NVT_LOG("\n");
+			}
+			NVT_LOG("\n");
+		}
+		return -1;
+	}
+	return crc8;
+}
+
+static irqreturn_t nvt_ts_work_func(int irq, void *data)
+{
+	int32_t ret = -1;
+	uint8_t touch_event[DUMMY_BYTES + TOUCH_EVENT_CRC_LEN] = { 0 };
+	uint32_t event_size = ARRAY_SIZE(touch_event);
+	uint32_t position = 0;
+	uint32_t input_x = 0;
+	uint32_t input_y = 0;
+	uint8_t input_id = 0;
+	uint8_t press_id[TOUCH_MAX_FINGER_NUM] = { 0 };
+	int32_t i = 0;
+	int32_t finger_cnt = 0;
+
+	mutex_lock(&ts->lock);
+
+	ret = CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, touch_event, event_size);
+	if (ret < 0) {
+		NVT_ERR("CTP_I2C_READ failed! ret=%d\n", ret);
+		goto nvt_ts_work_func_exit;
+	}
+
+	if (debug_log) {
+		NVT_LOG("Touch event buffer length: %d\n", event_size);
+		for (i = DUMMY_BYTES; i < event_size; i++) {
+			NVT_LOG("%02X ", touch_event[i]);
+			if (i % POINT_DATA_LEN == 0)
+				NVT_LOG("\n");
+		}
+		NVT_LOG("\n");
+	}
+
+	ret = nvt_ts_event_crc8(touch_event + DUMMY_BYTES,
+				event_size - DUMMY_BYTES);
+	if (ret < 0)
+		goto nvt_ts_work_func_exit;
+
+	finger_cnt = 0;
+
+	for (i = 0; i < ts->max_touch_num; i++) {
+		position = DUMMY_BYTES + ASIL_FLAG_LEN + POINT_DATA_LEN * i;
+		input_id = (uint8_t)(touch_event[position] >> 4);
+
+		if ((input_id == 0) || (input_id > ts->max_touch_num))
+			continue;
+
+		/* Finger down (enter or moving) */
+		if (((touch_event[position] & 0x07) == 0x01) ||
+		    ((touch_event[position] & 0x07) == 0x02)) {
+			input_x = (uint32_t)(touch_event[position + 1] << 8) +
+				  (uint32_t)(touch_event[position + 2]);
+			input_y = (uint32_t)(touch_event[position + 3] << 8) +
+				  (uint32_t)(touch_event[position + 4]);
+
+			if ((input_x > TOUCH_MAX_WIDTH) ||
+			    (input_y > TOUCH_MAX_HEIGHT))
+				continue;
+
+			press_id[input_id - 1] = 1;
+			input_mt_slot(ts->input_dev, input_id - 1);
+			input_mt_report_slot_state(ts->input_dev,
+						   MT_TOOL_FINGER, true);
+
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+					 input_x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+					 input_y);
+
+			finger_cnt++;
+		}
+	}
+
+	for (i = 0; i < ts->max_touch_num; i++) {
+		if (press_id[i] != 1) {
+			input_mt_slot(ts->input_dev, i);
+			input_mt_report_slot_state(ts->input_dev,
+						   MT_TOOL_FINGER, false);
+		}
+	}
+
+	input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0));
+
+	input_sync(ts->input_dev);
+
+nvt_ts_work_func_exit:
+	mutex_unlock(&ts->lock);
+
+	return IRQ_HANDLED;
+}
+
+int32_t nvt_clear_crc_err_flag(void)
+{
+	uint8_t buf[2] = { 0x00 };
+	int ret = 0;
+
+	nvt_set_page(I2C_FW_ADDRESS, (uint32_t)BLD_SPE_PUPS_ADDR);
+
+	buf[0] = (uint32_t)BLD_SPE_PUPS_ADDR & 0xFF;
+	buf[1] = 0xA5;
+	ret = CTP_I2C_WRITE(ts->client, I2C_FW_ADDRESS, buf, 2);
+	if (ret < 0) {
+		NVT_LOG("Write 0x%02X to BLD_SPE_PUPS(0x%X) failed, ret=%d!\n",
+			(uint32_t)BLD_SPE_PUPS_ADDR, buf[1], ret);
+		return ret;
+	}
+
+	buf[0] = (uint32_t)BLD_SPE_PUPS_ADDR & 0xFF;
+	buf[1] = 0x00;
+	ret = CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 2);
+	if (ret < 0) {
+		NVT_LOG("Read BLD_SPE_PUPS (0x%X) failed, ret=%d!\n",
+			(uint32_t)BLD_SPE_PUPS_ADDR, ret);
+		return ret;
+	}
+
+	NVT_LOG("BLD_SPE_PUPS(0x%X)=0x%02X\n", (uint32_t)BLD_SPE_PUPS_ADDR,
+		buf[1]);
+
+	if (buf[1] != 0xA5)
+		return (-EIO);
+
+	return ret;
+}
+
+void nvt_stop_crc_reboot(void)
+{
+	uint8_t buf[4] = { 0 };
+	int32_t i = 0;
+
+	/* Read dummy buffer to check CRC fail reboot is happening or not */
+	nvt_set_page(I2C_FW_ADDRESS, (uint32_t)CHIP_VER_TRIM_ADDR);
+
+	/* Read to check if buf is 0xFC which means IC is in CRC reboot */
+	buf[0] = (uint32_t)CHIP_VER_TRIM_ADDR & 0xFF;
+	CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 4);
+
+	if (((buf[1] == 0xFB) && (buf[2] == 0xFB) && (buf[3] == 0xFB)) ||
+	    ((buf[1] == 0xFC) && (buf[2] == 0xFC) && (buf[3] == 0xFC)) ||
+	    ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) {
+		/* IC is in CRC fail reboot loop, needs to be stopped! */
+		for (i = 5; i > 0; i--) {
+			/*
+			 * Reset idle twice to ensure that the cmd will be
+			 * executed in CRC reboot process
+			 */
+			nvt_sw_reset_idle();
+
+			nvt_sw_reset_idle();
+			usleep_range(1000, 2000);
+
+			if (nvt_clear_crc_err_flag() >= 0)
+				break;
+		}
+		if (i == 0)
+			NVT_ERR("CRC auto reboot is not able to be stopped! buf[1]=0x%02X\n",
+				buf[1]);
+	}
+}
+
+static int8_t nvt_ts_check_chip_ver_trim(void)
+{
+	uint8_t buf[8] = { 0 };
+	int32_t i = 0;
+	int32_t j = 0;
+	int32_t k = 0;
+	int32_t found_nvt_chip = 0;
+	int32_t ret = -1;
+
+	nvt_bootloader_reset();
+
+	for (i = 5; i > 0; i--) {
+		nvt_sw_reset_idle();
+
+		nvt_set_page(I2C_FW_ADDRESS, (uint32_t)CHIP_VER_TRIM_ADDR);
+
+		buf[0] = ((uint32_t)CHIP_VER_TRIM_ADDR & 0xFF);
+		buf[1] = 0x00;
+		buf[2] = 0x00;
+		buf[3] = 0x00;
+		buf[4] = 0x00;
+		buf[5] = 0x00;
+		buf[6] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 7);
+		NVT_LOG("EXT_CHIP_ID_RD(0x%X)=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+			(uint32_t)CHIP_VER_TRIM_ADDR, buf[1], buf[2], buf[3],
+			buf[4], buf[5], buf[6]);
+
+		/* Stop CRC check to prevent IC auto reboot */
+		if (((buf[1] == 0xFB) && (buf[2] == 0xFB) &&
+		     (buf[3] == 0xFB)) ||
+		    ((buf[1] == 0xFC) && (buf[2] == 0xFC) &&
+		     (buf[3] == 0xFC)) ||
+		    ((buf[1] == 0xFF) && (buf[2] == 0xFF) &&
+		     (buf[3] == 0xFF))) {
+			nvt_stop_crc_reboot();
+			continue;
+		}
+
+		/* Compare read chip id on supported list */
+		for (j = 0; j < ARRAY_SIZE(trim_id_table); j++) {
+			found_nvt_chip = 0;
+
+			/* Compare each byte */
+			for (k = 0; k < NVT_ID_BYTE_MAX; k++) {
+				if (trim_id_table[j].mask[k]) {
+					if (buf[k + 1] !=
+					    trim_id_table[j].id[k])
+						break;
+				}
+			}
+
+			if (k == NVT_ID_BYTE_MAX)
+				found_nvt_chip = 1;
+
+			if (found_nvt_chip) {
+				ts->mmap = trim_id_table[j].mmap;
+				ts->cascade_type =
+					trim_id_table[j].hwinfo->cascade_type;
+
+				ret = nvt_check_cascade_chip_numbers();
+				if (ret < 0)
+					NVT_ERR("Get cascade chip numbers failed! ret=%d\n",
+						ret);
+
+				/* Find out the memory map of target chip numbers */
+				if (ts->cascade_num ==
+				    trim_id_table[j].hwinfo->cascade_num) {
+					if (ts->cascade_type ==
+					    CASCADE_ENB_CASC) {
+						NVT_LOG("ENB_CASC_REG.addr(0x%X)\n",
+							ts->mmap->ENB_CASC_REG
+								.addr);
+					} else if (ts->cascade_type ==
+						   CASCADE_IC_NUM) {
+						NVT_LOG("IC_NUM_REG.addr(0x%X)\n",
+						ts->mmap->IC_NUM_REG.addr;
+					} else {
+						NVT_LOG("Non cascade case\n");
+					}
+					NVT_LOG("trim_id_table[%d] is matched.\n",
+						j);
+					NVT_LOG("This is NVT touch IC.\n");
+					goto out;
+				}
+			}
+		}
+		usleep_range(10000, 11000);
+	}
+
+	ts->mmap = NULL;
+	ret = -1;
+
+out:
+	return ret;
+}
+
+static int32_t nvt_ts_probe(struct i2c_client *client)
+{
+	int32_t ret = 0;
+
+	ts = kzalloc(sizeof(struct nvt_ts_data), GFP_KERNEL);
+	if (ts == NULL) {
+		NVT_ERR("Failed to allocated memory for nvt ts data!\n");
+		return -ENOMEM;
+	}
+
+	ts->xbuf = kzalloc(NVT_XBUF_LEN, GFP_KERNEL);
+	if (ts->xbuf == NULL) {
+		NVT_ERR("kzalloc for xbuf failed!\n");
+		ret = -ENOMEM;
+		goto err_malloc_xbuf;
+	}
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	nvt_parse_dt(&client->dev);
+
+	ret = nvt_gpio_config(ts);
+	if (ret) {
+		NVT_ERR("Gpio config error!\n");
+		goto err_gpio_config_failed;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		NVT_ERR("i2c_check_functionality() failed! (no I2C_FUNC_I2C)\n");
+		ret = -ENODEV;
+		goto err_check_functionality_failed;
+	}
+
+	mutex_init(&ts->lock);
+	mutex_init(&ts->xbuf_lock);
+
+	/* Need 10ms delay after POR (power on reset) */
+	usleep_range(10000, 11000);
+
+	ret = nvt_ts_check_chip_ver_trim();
+	if (ret) {
+		NVT_ERR("This chip is not identified in mapping table!\n");
+		ret = -EINVAL;
+		goto err_chipvertrim_failed;
+	}
+
+	nvt_bootloader_reset();
+	nvt_check_fw_reset_state(RESET_STATE_INIT);
+	nvt_get_fw_info();
+
+	ts->input_dev = input_allocate_device();
+	if (ts->input_dev == NULL) {
+		NVT_ERR("Allocate input device failed!\n");
+		ret = -ENOMEM;
+		goto err_input_dev_alloc_failed;
+	}
+
+	ts->max_touch_num = TOUCH_MAX_FINGER_NUM;
+
+	ts->int_trigger_type = INT_TRIGGER_TYPE;
+	ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |
+				  BIT_MASK(EV_ABS);
+	ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);
+
+	input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0);
+
+#if TOUCH_MAX_FINGER_NUM > 1
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0,
+			     TOUCH_MAX_WIDTH, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0,
+			     TOUCH_MAX_HEIGHT, 0, 0);
+#endif
+
+	sprintf(ts->phys, "input/ts");
+	ts->input_dev->name = NVT_TS_NAME;
+	ts->input_dev->phys = ts->phys;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		NVT_ERR("Register input device (%s) failed! ret=%d\n",
+			ts->input_dev->name, ret);
+		goto err_input_register_device_failed;
+	}
+
+	client->irq = gpio_to_irq(ts->irq_gpio);
+	if (client->irq) {
+		NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type);
+		ts->irq_enabled = true;
+		ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func,
+					   ts->int_trigger_type | IRQF_ONESHOT,
+					   NVT_I2C_NAME, ts);
+		if (ret != 0) {
+			NVT_ERR("Request irq failed! ret=%d\n", ret);
+			goto err_int_request_failed;
+		} else {
+			nvt_irq_enable(false);
+			NVT_LOG("Request irq %d succeed.\n", client->irq);
+		}
+	}
+
+	ts->fb_notif.notifier_call = nvt_fb_notifier_callback;
+	ret = fb_register_client(&ts->fb_notif);
+	if (ret) {
+		NVT_ERR("Register fb_notifier failed! ret=%d\n", ret);
+		goto err_register_fb_notif_failed;
+	}
+
+	bTouchIsAwake = 1;
+	NVT_LOG("end\n");
+
+	nvt_irq_enable(true);
+
+	return 0;
+
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier!\n");
+err_register_fb_notif_failed:
+	free_irq(client->irq, ts);
+err_int_request_failed:
+	input_unregister_device(ts->input_dev);
+	ts->input_dev = NULL;
+err_input_register_device_failed:
+	if (ts->input_dev) {
+		input_free_device(ts->input_dev);
+		ts->input_dev = NULL;
+	}
+err_input_dev_alloc_failed:
+err_chipvertrim_failed:
+	mutex_destroy(&ts->xbuf_lock);
+	mutex_destroy(&ts->lock);
+err_check_functionality_failed:
+	nvt_gpio_deconfig(ts);
+err_gpio_config_failed:
+	i2c_set_clientdata(client, NULL);
+	kfree(ts->xbuf);
+	ts->xbuf = NULL;
+err_malloc_xbuf:
+	kfree(ts);
+	ts = NULL;
+
+	return ret;
+}
+
+static void nvt_ts_remove(struct i2c_client *client)
+{
+	NVT_LOG("Removing driver...\n");
+
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier!\n");
+
+	nvt_irq_enable(false);
+	free_irq(client->irq, ts);
+
+	mutex_destroy(&ts->xbuf_lock);
+	mutex_destroy(&ts->lock);
+
+	nvt_gpio_deconfig(ts);
+
+	if (ts->input_dev) {
+		input_unregister_device(ts->input_dev);
+		ts->input_dev = NULL;
+	}
+
+	i2c_set_clientdata(client, NULL);
+
+	kfree(ts->xbuf);
+	ts->xbuf = NULL;
+
+	kfree(ts);
+	ts = NULL;
+}
+
+static void nvt_ts_shutdown(struct i2c_client *client)
+{
+	NVT_LOG("Shutdown driver...\n");
+
+	nvt_irq_enable(false);
+
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier!\n");
+}
+
+static int32_t nvt_ts_resume(struct device *dev)
+{
+	if (bTouchIsAwake) {
+		NVT_LOG("Touch is already resume.\n");
+		return 0;
+	}
+
+	mutex_lock(&ts->lock);
+
+	NVT_LOG("start\n");
+
+	nvt_bootloader_reset();
+	nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN);
+
+	nvt_irq_enable(true);
+
+	bTouchIsAwake = 1;
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("end\n");
+
+	return 0;
+}
+
+static int nvt_fb_notifier_callback(struct notifier_block *self,
+				    unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+	struct nvt_ts_data *ts =
+		container_of(self, struct nvt_ts_data, fb_notif);
+
+	if (evdata && evdata->data && event == FB_EVENT_BLANK) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+			nvt_ts_resume(&ts->client->dev);
+		}
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id nvt_ts_id[] = { { NVT_I2C_NAME, 0 }, {} };
+
+#ifdef CONFIG_OF
+static const struct of_device_id nvt_match_table[] = {
+	{
+		.compatible = "novatek,NVT-ts",
+	},
+	{},
+};
+#endif
+
+static struct i2c_driver nvt_i2c_driver = {
+	.probe		= nvt_ts_probe,
+	.remove		= nvt_ts_remove,
+	.shutdown	= nvt_ts_shutdown,
+	.id_table	= nvt_ts_id,
+	.driver = {
+		.name	= NVT_I2C_NAME,
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = nvt_match_table,
+#endif
+	},
+};
+
+static int32_t __init nvt_driver_init(void)
+{
+	int32_t ret = 0;
+
+	NVT_LOG("start\n");
+
+	bTouchIsAwake = 0;
+
+	ret = i2c_add_driver(&nvt_i2c_driver);
+	if (ret) {
+		NVT_ERR("Failed to add i2c driver!");
+		goto err_driver;
+	}
+
+	NVT_LOG("end\n");
+
+err_driver:
+	return ret;
+}
+
+static void __exit nvt_driver_exit(void)
+{
+	i2c_del_driver(&nvt_i2c_driver);
+}
+
+module_init(nvt_driver_init);
+module_exit(nvt_driver_exit);
+
+MODULE_DESCRIPTION("Novatek Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/nt519xx.h b/drivers/input/touchscreen/nt519xx.h
new file mode 100644
index 000000000000..27521416cc6c
--- /dev/null
+++ b/drivers/input/touchscreen/nt519xx.h
@@ -0,0 +1,130 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Driver for Novatek NT519xx touchscreens
+ *
+ * Copyright (C) 2023 Wei-Shih Lin <Weishih_Lin@novatek.com.tw>
+ * Copyright (C) 2023 Leo LS Chang <Leo_LS_Chang@novatek.com.tw>
+ *
+ * Copyright (C) 2010 - 2023 Novatek, Inc.
+ *
+ */
+#ifndef _LINUX_NVT_TOUCH_H
+#define _LINUX_NVT_TOUCH_H
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include "nt519xx_mem_map.h"
+
+#define NVT_DEBUG (1)
+
+/* GPIO Number */
+#define NVTTOUCH_RST_PIN (980)
+#define NVTTOUCH_INT_PIN (943)
+
+/* INT Trigger Mode */
+#define INT_TRIGGER_TYPE (IRQ_TYPE_EDGE_RISING)
+
+/* I2C Driver Info */
+#define NVT_I2C_NAME "NVT-ts"
+#define I2C_FW_ADDRESS (0x01)
+#define I2C_HW_ADDRESS (0x62)
+
+#if NVT_DEBUG
+#define NVT_LOG(fmt, args...)                                                 \
+	dev_err(&ts->client->dev, "[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, \
+		__LINE__, ##args)
+#else
+#define NVT_LOG(fmt, args...)                                                  \
+	dev_info(&ts->client->dev, "[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, \
+		 __LINE__, ##args)
+#endif
+#define NVT_ERR(fmt, args...)                                              \
+	dev_err(&ts->client->dev, "[ERROR][%s] %s %d: " fmt, NVT_I2C_NAME, \
+		__func__, __LINE__, ##args)
+
+/* Input Device Info */
+#define NVT_TS_NAME "NVTCapacitiveTouchScreen"
+
+/* Touch Info */
+#define TOUCH_MAX_WIDTH (1920)
+#define TOUCH_MAX_HEIGHT (1080)
+#define TOUCH_MAX_FINGER_NUM (10)
+
+/* Each data length in event buffer (unit: byte) */
+#define ASIL_FLAG_LEN (1)
+#define POINT_DATA_LEN (7)
+#define BUTTON_STATUS_LEN (2)
+#define TOUCH_EVENT_LEN                                            \
+	(ASIL_FLAG_LEN + (TOUCH_MAX_FINGER_NUM * POINT_DATA_LEN) + \
+	 BUTTON_STATUS_LEN)
+#define ASIL_INFO_LEN (6)
+#define CRC_VALUE_LEN (1)
+#define TOUCH_EVENT_CRC_LEN (TOUCH_EVENT_LEN + ASIL_INFO_LEN + CRC_VALUE_LEN)
+
+#define DUMMY_BYTES (1)
+#define NVT_XBUF_LEN (1025)
+
+struct nvt_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	int8_t phys[32];
+	struct notifier_block fb_notif;
+	uint8_t fw_ver;
+	uint8_t x_num;
+	uint8_t y_num;
+	uint8_t max_touch_num;
+	uint32_t int_trigger_type;
+	int32_t irq_gpio;
+	uint32_t irq_flags;
+	int32_t reset_gpio;
+	uint32_t reset_flags;
+	struct mutex lock;
+	const struct nvt_ts_mem_map *mmap;
+	enum cascade_type cascade_type;
+	enum cascade_chip_num cascade_num;
+	uint16_t nvt_pid;
+	uint8_t *xbuf;
+	struct mutex xbuf_lock;
+	bool irq_enabled;
+};
+
+enum rst_complete_state {
+	RESET_STATE_INIT = 0xA0,
+	RESET_STATE_REK,
+	RESET_STATE_REK_FINISH,
+	RESET_STATE_NORMAL_RUN,
+	RESET_STATE_MAX = 0xAF
+};
+
+enum {
+	EVENT_MAP_HOST_CMD = 0x50,
+	EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51,
+	EVENT_MAP_RESET_COMPLETE = 0x60,
+	EVENT_MAP_FWINFO = 0x78,
+	EVENT_MAP_CASCADE_CHIP_NUMBERS = 0x99,
+	EVENT_MAP_PROJECTID = 0x9A,
+};
+
+extern struct nvt_ts_data *ts;
+
+int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf,
+		     uint16_t len);
+int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf,
+		      uint16_t len);
+void nvt_bootloader_reset(void);
+void nvt_sw_reset_idle(void);
+int32_t nvt_clear_crc_err_flag(void);
+bool nvt_check_fw_reset_state(enum rst_complete_state check_reset_state);
+int32_t nvt_get_fw_info(void);
+int32_t nvt_check_cascade_chip_numbers(void);
+int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr);
+int32_t nvt_write_addr(uint32_t addr, uint8_t data);
+int32_t nvt_read_reg(struct nvt_ts_reg reg, uint8_t *val);
+
+void nvt_stop_crc_reboot(void);
+
+#endif /* _LINUX_NVT_TOUCH_H */
diff --git a/drivers/input/touchscreen/nt519xx_mem_map.h b/drivers/input/touchscreen/nt519xx_mem_map.h
new file mode 100644
index 000000000000..9e9847d73292
--- /dev/null
+++ b/drivers/input/touchscreen/nt519xx_mem_map.h
@@ -0,0 +1,262 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Driver for Novatek NT519xx touchscreens
+ *
+ * Copyright (C) 2023 Wei-Shih Lin <Weishih_Lin@novatek.com.tw>
+ * Copyright (C) 2023 Leo LS Chang <Leo_LS_Chang@novatek.com.tw>
+ *
+ * Copyright (C) 2010 - 2023 Novatek, Inc.
+ *
+ */
+
+#define CHIP_VER_TRIM_ADDR (0xFF004)
+#define SWRST_SIF_ADDR (0xFF0FE)
+#define BLD_SPE_PUPS_ADDR (0xFF135)
+
+enum {
+	BIT0 = 0x01,
+	BIT1 = 0x02,
+	BIT2 = 0x04,
+	BIT3 = 0x08,
+	BIT4 = 0x10,
+	BIT5 = 0x20,
+	BIT6 = 0x40,
+	BIT7 = 0x80
+};
+
+enum cascade_type {
+	CASCADE_NONE = 0x00,
+	CASCADE_ENB_CASC = 0x01,
+	CASCADE_SDLOC = 0x02,
+	CASCADE_IC_NUM = 0x03
+};
+
+enum { ENB_CASC_2CHIP = 0x00, ENB_CASC_1CHIP = 0x01 };
+
+enum { SDLOC_1CHIP = 0x04, SDLOC_2CHIP = 0x06, SDLOC_3CHIP_AND_ABOVE = 0x07 };
+
+enum {
+	IC_NUM_3CHIP = 0x00,
+	IC_NUM_1CHIP = 0x01,
+	IC_NUM_2CHIP = 0x02,
+	IC_NUM_3CHIP_AND_ABOVE = 0x03
+};
+
+enum cascade_chip_num {
+	CASCADE_CHECK_ERR = 0,
+	NONE_CASCADE_CASE = 1,
+	CASCADE_1CHIP = NONE_CASCADE_CASE,
+	CASCADE_2CHIP = 2,
+	CASCADE_3CHIP = 3,
+	CASCADE_4CHIP = 4,
+	CASCADE_5CHIP = 5,
+	CASCADE_CHIP_MAX = CASCADE_5CHIP
+};
+
+struct nvt_ts_reg {
+	uint32_t addr;
+	uint8_t bitmask;
+};
+
+struct nvt_ts_mem_map {
+	uint32_t EVENT_BUF_ADDR;
+	struct nvt_ts_reg ENB_CASC_REG;
+	struct nvt_ts_reg SDLOC_REG;
+	struct nvt_ts_reg IC_NUM_REG;
+};
+
+struct nvt_ts_hw_info {
+	enum cascade_type cascade_type;
+	enum cascade_chip_num cascade_num;
+	uint8_t default_x_num;
+	uint8_t default_y_num;
+	uint16_t default_abs_x_max;
+	uint16_t default_abs_y_max;
+	uint8_t default_max_touch_num;
+	uint8_t default_max_button_num;
+};
+
+static const struct nvt_ts_mem_map NT51900_memory_map = {
+	.EVENT_BUF_ADDR = 0x2A800,
+	.ENB_CASC_REG = { .addr = 0x3F02C, .bitmask = BIT0 },
+};
+
+static const struct nvt_ts_mem_map NT51920_1chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x30500,
+	.ENB_CASC_REG = { .addr = 0x3F02C, .bitmask = BIT0 },
+};
+
+static const struct nvt_ts_mem_map NT51920_2chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x30500,
+	.ENB_CASC_REG = { .addr = 0x3F02C, .bitmask = BIT0 },
+};
+
+static const struct nvt_ts_mem_map NT51923_1chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x94000,
+	.SDLOC_REG = { .addr = 0xFF02C, .bitmask = (BIT2 | BIT1 | BIT0) },
+};
+
+static const struct nvt_ts_mem_map NT51923_2chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x94000,
+	.SDLOC_REG = { .addr = 0xFF02C, .bitmask = (BIT2 | BIT1 | BIT0) },
+};
+
+static const struct nvt_ts_mem_map NT51923_3chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x94000,
+	.SDLOC_REG = { .addr = 0xFF02C, .bitmask = (BIT2 | BIT1 | BIT0) },
+};
+
+static const struct nvt_ts_mem_map NT51926_1chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x96A00,
+	.IC_NUM_REG = { .addr = 0xFF02C, .bitmask = (BIT3 | BIT2) },
+};
+
+static const struct nvt_ts_mem_map NT51926_2chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x96A00,
+	.IC_NUM_REG = { .addr = 0xFF02C, .bitmask = (BIT3 | BIT2) },
+};
+
+static const struct nvt_ts_mem_map NT51926_3chip_memory_map = {
+	.EVENT_BUF_ADDR = 0x96A00,
+	.IC_NUM_REG = { .addr = 0xFF02C, .bitmask = (BIT3 | BIT2) },
+};
+
+static struct nvt_ts_hw_info NT51900_hw_info = {
+	.cascade_type = CASCADE_NONE,
+	.cascade_num = NONE_CASCADE_CASE,
+
+	.default_x_num = 40,
+	.default_y_num = 24,
+	.default_abs_x_max = 1280,
+	.default_abs_y_max = 720,
+	.default_max_button_num = 0,
+};
+
+static struct nvt_ts_hw_info NT51920_1chip_hw_info = {
+	.cascade_type = CASCADE_ENB_CASC,
+	.cascade_num = CASCADE_1CHIP,
+
+	.default_x_num = 48,
+	.default_y_num = 20,
+	.default_abs_x_max = 960,
+	.default_abs_y_max = 540,
+	.default_max_button_num = 0,
+};
+
+static struct nvt_ts_hw_info NT51920_2chip_hw_info = {
+	.cascade_type = CASCADE_ENB_CASC,
+	.cascade_num = CASCADE_2CHIP,
+
+	.default_x_num = 56,
+	.default_y_num = 21,
+	.default_abs_x_max = 2880,
+	.default_abs_y_max = 1080,
+	.default_max_button_num = 0,
+};
+
+static struct nvt_ts_hw_info NT51923_1chip_hw_info = {
+	.cascade_type = CASCADE_SDLOC,
+	.cascade_num = CASCADE_1CHIP,
+
+	.default_x_num = 24,
+	.default_y_num = 60,
+	.default_abs_x_max = 2880,
+	.default_abs_y_max = 1080,
+	.default_max_button_num = 0,
+};
+
+static struct nvt_ts_hw_info NT51923_2chip_hw_info = {
+	.cascade_type = CASCADE_SDLOC,
+	.cascade_num = CASCADE_2CHIP,
+
+	.default_x_num = 48,
+	.default_y_num = 60,
+	.default_abs_x_max = 2880,
+	.default_abs_y_max = 1080,
+	.default_max_button_num = 0,
+};
+
+static struct nvt_ts_hw_info NT51923_3chip_hw_info = {
+	.cascade_type = CASCADE_SDLOC,
+	.cascade_num = CASCADE_3CHIP,
+
+	.default_x_num = 72,
+	.default_y_num = 40,
+	.default_abs_x_max = 2880,
+	.default_abs_y_max = 1620,
+	.default_max_button_num = 0,
+};
+
+static struct nvt_ts_hw_info NT51926_1chip_hw_info = {
+	.cascade_type = CASCADE_IC_NUM,
+	.cascade_num = CASCADE_1CHIP,
+	.default_x_num = 16,
+	.default_y_num = 60,
+	.default_abs_x_max = 2880,
+	.default_abs_y_max = 1080,
+	.default_max_button_num = 0,
+};
+static struct nvt_ts_hw_info NT51926_2chip_hw_info = {
+	.cascade_type = CASCADE_IC_NUM,
+	.cascade_num = CASCADE_2CHIP,
+	.default_x_num = 32,
+	.default_y_num = 60,
+	.default_abs_x_max = 1768,
+	.default_abs_y_max = 828,
+	.default_max_button_num = 0,
+};
+static struct nvt_ts_hw_info NT51926_3chip_hw_info = {
+	.cascade_type = CASCADE_IC_NUM,
+	.cascade_num = CASCADE_3CHIP,
+	.default_x_num = 48,
+	.default_y_num = 60,
+	.default_abs_x_max = 2880,
+	.default_abs_y_max = 1080,
+	.default_max_button_num = 0,
+};
+#define NVT_ID_BYTE_MAX 6
+struct nvt_ts_trim_id_table {
+	uint8_t id[NVT_ID_BYTE_MAX];
+	uint8_t mask[NVT_ID_BYTE_MAX];
+	const struct nvt_ts_mem_map *mmap;
+	const struct nvt_ts_hw_info *hwinfo;
+};
+
+static const struct nvt_ts_trim_id_table trim_id_table[] = {
+	{ .id = { 0x00, 0x00, 0x00, 0x00, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51900_memory_map,
+	  .hwinfo = &NT51900_hw_info },
+	{ .id = { 0x00, 0x00, 0x01, 0x20, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51920_1chip_memory_map,
+	  .hwinfo = &NT51920_1chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x01, 0x20, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51920_2chip_memory_map,
+	  .hwinfo = &NT51920_2chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x00, 0x23, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51923_1chip_memory_map,
+	  .hwinfo = &NT51923_1chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x00, 0x23, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51923_2chip_memory_map,
+	  .hwinfo = &NT51923_2chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x00, 0x23, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51923_3chip_memory_map,
+	  .hwinfo = &NT51923_3chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x00, 0x26, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51926_1chip_memory_map,
+	  .hwinfo = &NT51926_1chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x00, 0x26, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51926_2chip_memory_map,
+	  .hwinfo = &NT51926_2chip_hw_info },
+	{ .id = { 0x00, 0x00, 0x00, 0x26, 0x19, 0x05 },
+	  .mask = { 0, 0, 0, 1, 1, 1 },
+	  .mmap = &NT51926_3chip_memory_map,
+	  .hwinfo = &NT51926_3chip_hw_info },
+};