lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 25 Oct 2023 16:20:54 +0800
From:   Wei-Shih Lin <frank101417@...il.com>
To:     dmitry.torokhov@...il.com, robh+dt@...nel.org,
        krzysztof.kozlowski+dt@...aro.org, conor+dt@...nel.org
Cc:     linux-input@...r.kernel.org, devicetree@...r.kernel.org,
        linux-kernel@...r.kernel.org
Subject: [PATCH 2/2] Input: Add driver for Novatek NT519XX series touchscreen devices

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@...atek.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

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@...atek.com.tw>
+ * Copyright (C) 2023 Leo LS Chang <Leo_LS_Chang@...atek.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@...atek.com.tw>
+ * Copyright (C) 2023 Leo LS Chang <Leo_LS_Chang@...atek.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@...atek.com.tw>
+ * Copyright (C) 2023 Leo LS Chang <Leo_LS_Chang@...atek.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 },
+};
-- 
2.26.1

Powered by blists - more mailing lists