Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH v1] selftests: hid: tests: test_wacom_generic: add base test for display devices and opaque devices
From: Alex Tran @ 2025-11-17  1:47 UTC (permalink / raw)
  To: jikos, bentiss, shuah
  Cc: linux-input, linux-kselftest, linux-kernel, Alex Tran

Verify Wacom devices set INPUT_PROP_DIRECT appropriately on display devices
and INPUT_PROP_POINTER appropriately on opaque devices. Tests are defined
in the base class and disabled for inapplicable device types.

Signed-off-by: Alex Tran <alex.t.tran@gmail.com>
---
 .../selftests/hid/tests/test_wacom_generic.py       | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py
index 2d6d04f0f..aa2a175f2 100644
--- a/tools/testing/selftests/hid/tests/test_wacom_generic.py
+++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py
@@ -600,15 +600,17 @@ class BaseTest:
 
         def test_prop_direct(self):
             """
-            Todo: Verify that INPUT_PROP_DIRECT is set on display devices.
+            Verify that INPUT_PROP_DIRECT is set on display devices.
             """
-            pass
+            evdev = self.uhdev.get_evdev()
+            assert libevdev.INPUT_PROP_DIRECT in evdev.properties
 
         def test_prop_pointer(self):
             """
-            Todo: Verify that INPUT_PROP_POINTER is set on opaque devices.
+            Verify that INPUT_PROP_POINTER is set on opaque devices.
             """
-            pass
+            evdev = self.uhdev.get_evdev()
+            assert libevdev.INPUT_PROP_POINTER in evdev.properties
 
 
 class PenTabletTest(BaseTest.TestTablet):
@@ -622,6 +624,8 @@ class TouchTabletTest(BaseTest.TestTablet):
 
 
 class TestOpaqueTablet(PenTabletTest):
+    test_prop_direct = None
+
     def create_device(self):
         return OpaqueTablet()
 
@@ -864,6 +868,7 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet):
 
 class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest):
     ContactIds = namedtuple("ContactIds", "contact_id, tracking_id, slot_num")
+    test_prop_pointer = None
 
     def create_device(self):
         return test_multitouch.Digitizer(
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH v2] HID: memory leak in dualshock4_get_calibration_data
From: Eslam Khafagy @ 2025-11-16 15:09 UTC (permalink / raw)
  To: Max Staudt, roderick.colenbrander, jikos, bentiss
  Cc: linux-input, linux-kernel, stable, syzbot+4f5f81e1456a1f645bf8
In-Reply-To: <ddb16ea8-7588-4c0a-9e34-6bd8babd598f@enpas.org>


On 11/16/25 06:34, Max Staudt wrote:
> On 11/16/25 11:27 AM, Eslam Khafagy wrote:
>> function dualshock4_get_calibration_data allocates memory to pointer
>> buf. however the function may exit prematurely due to transfer_failure
>> in this case it does not handle freeing memory.
>>
>> this patch handles memory deallocation at exit.
>
> Reviewed-by: Max Staudt <max@enpas.org>
>
>
> One minor thing that I forgot: I see that the commit message is all 
> lower case. Proper capitalisation helps readability and matches the 
> kernel style. No need to resend this time, but in case you're sending 
> a v3 for any other reason, it'd be great if you picked this up as well :)
>
Oh, ok. but, no plans for v3 for now.
> Thanks for your patch!
>
welcome
> Max
>

^ permalink raw reply

* [PATCH RESEND] HID: pidff: Fix needs_playback check
From: Tomasz Pakuła @ 2025-11-16 14:53 UTC (permalink / raw)
  To: jikos, bentiss; +Cc: oleg, linux-input

Urgent for 6.18 rc period

A small bug made it's way here when rewriting code to Linux quality.
Currently, if an effect is not infinite and a program requests it's
playback with the same number of loops, the play command won't be fired
and if an effect is infinite, the spam will continue.

We want every playback update for non-infinite effects and only some
for infinite (detecting when a program requests stop with 0 which will
be different than previous value which is usually 1 or 255).

Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
---
 drivers/hid/usbhid/hid-pidff.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
index edd61ef50e16..95377c5f6335 100644
--- a/drivers/hid/usbhid/hid-pidff.c
+++ b/drivers/hid/usbhid/hid-pidff.c
@@ -806,8 +806,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
 
 static int pidff_needs_playback(struct pidff_device *pidff, int effect_id, int n)
 {
-	return pidff->effect[effect_id].is_infinite ||
-	       pidff->effect[effect_id].loop_count != n;
+	return !pidff->effect[effect_id].is_infinite ||
+		pidff->effect[effect_id].loop_count != n;
 }
 
 /*
-- 
2.51.2


^ permalink raw reply related

* [PATCH] input: touchscreen: Add ilitek touchscreen driver support
From: 2724853925 @ 2025-11-16 13:49 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg
  Cc: linux-input, linux-kernel, linux-gpio, 2724853925

From: weisicheng <2724853925@qq.com>

Add basic support for Ilitek I2C touchscreen controllers (e.g., ILitek2511, ILitek2701).
This patch includes:
1. Kconfig entry for Ilitek driver configuration
2. Makefile entry to compile the ilitek driver module
3. Core driver files under drivers/input/touchscreen/ilitek/ (I2C communication,
   touch event handling, power management)

The driver is compatible with standard input subsystem and supports:
- Multi-touch detection
- Pressure sensing
- Dynamic power saving mode

Signed-off-by: weisicheng <2724853925@qq.com>
---
 drivers/input/touchscreen/Kconfig             |   13 +
 drivers/input/touchscreen/Makefile            |    1 +
 drivers/input/touchscreen/ilitek/Makefile     |   34 +
 .../input/touchscreen/ilitek/ilitek_common.h  |  264 ++
 .../input/touchscreen/ilitek/ilitek_crypto.c  |  465 +++
 .../input/touchscreen/ilitek/ilitek_crypto.h  |   61 +
 drivers/input/touchscreen/ilitek/ilitek_def.c |  235 ++
 drivers/input/touchscreen/ilitek/ilitek_def.h |  554 +++
 .../input/touchscreen/ilitek/ilitek_main.c    | 2198 ++++++++++
 .../touchscreen/ilitek/ilitek_platform_init.c |  404 ++
 .../touchscreen/ilitek/ilitek_protocol.c      | 3644 +++++++++++++++++
 .../touchscreen/ilitek/ilitek_protocol.h      |  916 +++++
 .../input/touchscreen/ilitek/ilitek_report.c  |  455 ++
 .../input/touchscreen/ilitek/ilitek_report.h  |   78 +
 .../input/touchscreen/ilitek/ilitek_tool.c    | 1156 ++++++
 drivers/input/touchscreen/ilitek/ilitek_ts.h  |  268 ++
 .../input/touchscreen/ilitek/ilitek_update.c  | 1657 ++++++++
 .../input/touchscreen/ilitek/ilitek_update.h  |  199 +
 ...344\272\244\346\265\201\347\250\213 .docx" |  Bin 0 -> 162 bytes
 19 files changed, 12602 insertions(+)
 create mode 100644 drivers/input/touchscreen/ilitek/Makefile
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_common.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_main.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_platform_init.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_tool.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_ts.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.h
 create mode 100644 "drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7d5b72ee07fa..3552629c4d4d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -515,6 +515,19 @@ config TOUCHSCREEN_ILITEK
 	  To compile this driver as a module, choose M here: the
 	  module will be called ilitek_ts_i2c.
 
+config TOUCHSCREEN_ILI2511
+	tristate "Ilitek I2C or SPI Touch ICs"
+	depends on I2C
+	depends on SPI
+	help
+	  Say Y here if you have touchscreen with ILITEK touch IC,
+	  it supports 213X/23XX/25XX and other Lego series.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ilitek.
+
 config TOUCHSCREEN_IPROC
 	tristate "IPROC touch panel driver support"
 	depends on ARCH_BCM_IPROC || COMPILE_TEST
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ab9abd151078..3124c4dd8ee1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX)	+= hynitron_cstxxx.o
 obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X)	+= hynitron-cst816x.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_ILITEK)	+= ilitek_ts_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ILI2511)	+= ilitek/
 obj-$(CONFIG_TOUCHSCREEN_IMAGIS)	+= imagis.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/ilitek/Makefile b/drivers/input/touchscreen/ilitek/Makefile
new file mode 100644
index 000000000000..c912438a3b3e
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/Makefile
@@ -0,0 +1,34 @@
+ccflags-y += -Wall
+
+ifndef __KERNEL__
+ccflags-y += -D__KERNEL__
+endif
+
+#Un-mark below item to enable FW upgrade on boot
+#ccflags-y += -DILITEK_BOOT_UPDATE
+
+ILITEK_INTERFACE = i2c
+#ILITEK_INTERFACE = spi
+
+
+ifeq ($(ILITEK_INTERFACE), spi)
+$(info start to build ilitek SPI driver)
+ccflags-y += -DILITEK_SPI_INTERFACE
+ilitek := ilitek_spi
+else
+$(info start to build ilitek I2C driver)
+ilitek := ilitek_i2c
+endif
+
+$(ilitek)-objs += \
+	ilitek_def.o \
+	ilitek_main.o \
+	ilitek_platform_init.o \
+	ilitek_update.o \
+	ilitek_tool.o \
+	ilitek_protocol.o \
+	ilitek_crypto.o \
+	ilitek_report.o
+
+obj-$(CONFIG_TOUCHSCREEN_ILI2511) += $(ilitek).o
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_common.h b/drivers/input/touchscreen/ilitek/ilitek_common.h
new file mode 100644
index 000000000000..7f058c3e4914
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_common.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _ILITEK_COMMON_H_
+#define _ILITEK_COMMON_H_
+/* Includes of headers ------------------------------------------------------*/
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "ilitek_ts.h"
+#include "ilitek_protocol.h"
+#include "ilitek_update.h"
+
+#include "ilitek_crypto.h"
+#include "ilitek_report.h"
+
+/* Extern define ------------------------------------------------------------*/
+//driver information
+#define DRIVER_VERSION_0				5
+#define DRIVER_VERSION_1				9
+#define DRIVER_VERSION_2				3
+#define DRIVER_VERSION_3				0
+#define CUSTOMER_H_ID					0
+#define CUSTOMER_L_ID					0
+#define TEST_VERSION					0
+
+#define ILITEK_IOCTL_MAX_TRANSFER			5000UL
+
+#define set_arr(arr, idx, val)			\
+	do {					\
+		if ((idx) < ARRAY_SIZE(arr))	\
+			(arr)[(idx)] = (val);	\
+	} while (0)
+
+/* i2c clock rate for rk3288 */
+#if ILITEK_PLAT == ILITEK_PLAT_ROCKCHIP && \
+	KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE
+#define SCL_RATE(rate)	.scl_rate = (rate),
+#else
+#define SCL_RATE(rate)
+#endif
+
+/* netlink */
+#if KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)	\
+	struct netlink_kernel_cfg cfg = {	\
+		.groups = 0,			\
+		.input = func,			\
+	}
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), (cfg_ptr))
+#else
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), THIS_MODULE, (cfg_ptr))
+#endif
+#else
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), 0, (func), NULL, THIS_MODULE)
+#endif
+
+/* input_dev */
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define INPUT_MT_INIT_SLOTS(dev, num)	\
+		input_mt_init_slots((dev), (num), INPUT_MT_DIRECT)
+#else
+#define INPUT_MT_INIT_SLOTS(dev, num)	input_mt_init_slots((dev), (num))
+#endif
+
+/* file_operations ioctl */
+#if KERNEL_VERSION(2, 6, 36) <= LINUX_VERSION_CODE
+#define FOPS_IOCTL	unlocked_ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		long func(struct file *fp, cmd, arg)
+#else
+#define FOPS_IOCTL	ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		s32 func(struct inode *np, struct file *fp,	cmd, arg)
+
+#endif
+
+#if KERNEL_VERSION(6, 3, 0) > LINUX_VERSION_CODE
+#define I2C_PROBE_FUNC(func, client_arg)	\
+	int func(client_arg, const struct i2c_device_id *id)
+#else
+#define I2C_PROBE_FUNC(func, client_arg)	int func(client_arg)
+#endif
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+#define REMOVE_FUNC(func, client_arg)	int func(client_arg)
+#define REMOVE_RETURN(val)		({ __typeof__(val) _val = (val); return _val; })
+#else
+#define REMOVE_FUNC(func, client_arg)	void func(client_arg)
+#define REMOVE_RETURN(val)		(val)
+#endif
+
+#if KERNEL_VERSION(6, 4, 0) > LINUX_VERSION_CODE
+#define CLASS_CREATE(name)	class_create(THIS_MODULE, (name))
+#else
+#define CLASS_CREATE(name)	class_create((name))
+#endif
+
+/* procfs */
+#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
+#define PROC_FOPS_T	file_operations
+#define PROC_READ	read
+#define PROC_WRITE	write
+#define PROC_IOCTL		FOPS_IOCTL
+#define PROC_COMPAT_IOCTL	compat_ioctl
+#define PROC_OPEN	open
+#define PROC_RELEASE	release
+#else
+#define PROC_FOPS_T	proc_ops
+#define PROC_READ	proc_read
+#define PROC_WRITE	proc_write
+#define PROC_IOCTL		proc_ioctl
+#define PROC_COMPAT_IOCTL	proc_compat_ioctl
+#define PROC_OPEN	proc_open
+#define PROC_RELEASE	proc_release
+#endif
+
+#ifdef MTK_UNDTS
+#define ISR_FUNC(func)	void func(void)
+#define ISR_RETURN(val)
+#else
+#define ISR_FUNC(func)	irqreturn_t func(int irq, void *dev_id)
+#define ISR_RETURN(val)	({ __typeof__(val) _val = (val); return _val; })
+#endif
+
+enum ilitek_irq_handle_type {
+	irq_type_normal = 0,
+	irq_type_debug,
+	irq_type_c_model,
+};
+
+struct ilitek_ts_data {
+	void *client;
+	struct device *device;
+	struct ilitek_ts_device *dev;
+
+	/* should > 2K for C-Model */
+	u8 buf[4096];
+
+	struct input_dev *input_dev;
+	struct input_dev *pen_input_dev;
+	struct regulator *vdd;
+	struct regulator *vdd_i2c;
+	struct regulator *vcc_io;
+
+	int irq;
+	int irq_gpio;
+	int reset_gpio;
+	int test_gpio;
+
+	bool system_suspend;
+	bool power_key_triggered;
+
+	u8 irq_trigger_type;
+
+	bool is_touched;
+	bool touch_key_hold_press;
+	int touch_flag[40];
+
+#if 0
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+	struct task_struct *update_thread;
+
+	atomic_t firmware_updating;
+	bool operation_protection;
+	bool unhandle_irq;
+	unsigned int irq_handle_type;
+	unsigned int irq_read_len;
+
+	u8 gesture_status;
+	u8 low_power_status;
+
+	bool esd_check;
+	bool esd_skip;
+	struct workqueue_struct *esd_workq;
+	struct delayed_work esd_work;
+	unsigned long esd_delay;
+
+	/* Mutex for protecting concurrent access */
+	struct mutex ilitek_mutex;
+
+	atomic_t irq_enabled;
+	atomic_t get_INT;
+
+	bool wake_irq_enabled;
+
+	bool irq_registered;
+};
+
+/* Extern macro -------------------------------------------------------------*/
+
+#define CEIL(n, d) (((n) % (d)) ? ((n) / (d)) + 1 : ((n) / (d)))
+
+/* Extern variables ---------------------------------------------------------*/
+
+extern u8 driver_ver[];
+
+extern struct ilitek_ts_data *ts;
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#endif
+/* Extern function prototypes -----------------------------------------------*/
+/* Extern functions ---------------------------------------------------------*/
+void ilitek_resume(void);
+void ilitek_suspend(void);
+int ilitek_main_probe(void *client, struct device *dev);
+int ilitek_main_remove(void *client);
+void ilitek_reset(int delay);
+
+int ilitek_write(u8 *cmd, int len);
+int ilitek_read(u8 *buf, int len);
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len);
+
+void ilitek_irq_enable(void);
+void ilitek_irq_disable(void);
+
+int ilitek_upgrade_firmware(char *filename);
+
+int ilitek_create_tool_node(void);
+int ilitek_remove_tool_node(void);
+
+int ilitek_create_sysfsnode(void);
+void ilitek_remove_sys_node(void);
+
+int ilitek_netlink_init(u8 unit);
+void ilitek_netlink_exit(void);
+
+void ilitek_gpio_dbg(void);
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init);
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y);
+
+int ilitek_create_esd_check_workqueue(void);
+void ilitek_remove_esd_check_workqueue(void);
+
+int ilitek_read_data_and_report(void);
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.c b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
new file mode 100644
index 000000000000..ed33defb57ed
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
@@ -0,0 +1,465 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_crypto.h"
+
+/*
+ * The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+ * The numbers below can be computed dynamically trading ROM for RAM -
+ * This can be useful in (embedded) bootloader applications, where ROM is often limited.
+ */
+static const u8 sbox[256] = {
+	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const u8 rsbox[256] = {
+	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+/*
+ * The round constant word array, contains the values given by
+ * x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+ */
+static const u8 rcon[11] = {
+	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+/* array holding the intermediate results during decryption. */
+typedef u8 state_t[4][4];
+
+struct crypto_aes_ctx {
+	u8 key_dec[240];
+};
+
+static u8 xtime(u8 x)
+{
+	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
+}
+
+static void inv_shift_rows(state_t *state)
+{
+	u8 tmp;
+
+	/* Rotate first row 1 columns to right */
+	tmp = (*state)[3][1];
+	(*state)[3][1] = (*state)[2][1];
+	(*state)[2][1] = (*state)[1][1];
+	(*state)[1][1] = (*state)[0][1];
+	(*state)[0][1] = tmp;
+
+	/* Rotate second row 2 columns to right */
+	tmp = (*state)[0][2];
+	(*state)[0][2] = (*state)[2][2];
+	(*state)[2][2] = tmp;
+	tmp = (*state)[1][2];
+	(*state)[1][2] = (*state)[3][2];
+	(*state)[3][2] = tmp;
+
+	/* Rotate third row 3 columns to right */
+	tmp = (*state)[0][3];
+	(*state)[0][3] = (*state)[1][3];
+	(*state)[1][3] = (*state)[2][3];
+	(*state)[2][3] = (*state)[3][3];
+	(*state)[3][3] = tmp;
+}
+
+static void inv_sub_bytes(state_t *state)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[j][i] = rsbox[(*state)[j][i]];
+}
+
+/*
+ * multiply is used to multiply numbers in the field GF(2^8)
+ * Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+ *       The compiler seems to be able to vectorize the operation better this way.
+ *       See https://github.com/kokke/tiny-AES-c/pull/34
+ */
+static u8 multiply(u8 x, u8 y)
+{
+	return (((y & 1) * x) ^
+		(((y >> 1) & 1) * xtime(x)) ^
+		(((y >> 2) & 1) * xtime(xtime(x))) ^
+		(((y >> 3) & 1) * xtime(xtime(xtime(x)))) ^
+		(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
+}
+
+/*
+ * inv_mix_cols function mixes the columns of the state matrix.
+ */
+static void inv_mix_cols(state_t *state)
+{
+	int i;
+	u8 a, b, c, d;
+
+	for (i = 0; i < 4; ++i) {
+		a = (*state)[i][0];
+		b = (*state)[i][1];
+		c = (*state)[i][2];
+		d = (*state)[i][3];
+
+		(*state)[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^
+			multiply(c, 0x0d) ^ multiply(d, 0x09);
+		(*state)[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^
+			multiply(c, 0x0b) ^ multiply(d, 0x0d);
+		(*state)[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^
+			multiply(c, 0x0e) ^ multiply(d, 0x0b);
+		(*state)[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^
+			multiply(c, 0x09) ^ multiply(d, 0x0e);
+	}
+}
+
+/*
+ * This function adds the round key to state.
+ * The round key is added to the state by an XOR function.
+ */
+static void add_round_key(u8 round, state_t *state, const u8 *key)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[i][j] ^= key[(round * Nb * 4) + (i * Nb) + j];
+}
+
+static void aes_decrypt(state_t *state, const u8 *key)
+{
+	u8 round;
+
+	/* Add the first round key to the state before starting the rounds. */
+	add_round_key(Nr, state, key);
+
+	/*
+	 * There will be Nr rounds.
+	 * The first Nr-1 rounds are identical.
+	 * These Nr rounds are executed in the loop below.
+	 * Last one without InvMixColumn()
+	 */
+	for (round = Nr - 1; ; --round) {
+		inv_shift_rows(state);
+		inv_sub_bytes(state);
+		add_round_key(round, state, key);
+
+		if (!round)
+			break;
+
+		inv_mix_cols(state);
+	}
+}
+
+/*
+ * Produces Nb(Nr+1) round keys, used in each round to decrypt the states.
+ */
+static void aes_expandkey(u8 *round_key, const u8 *key)
+{
+	unsigned int i, j, k;
+	u8 tmp_a[4];
+	u8 tmp_b;
+
+	/* The first round key is the key itself. */
+	for (i = 0; i < Nk; ++i) {
+		round_key[(i * 4) + 0] = key[(i * 4) + 0];
+		round_key[(i * 4) + 1] = key[(i * 4) + 1];
+		round_key[(i * 4) + 2] = key[(i * 4) + 2];
+		round_key[(i * 4) + 3] = key[(i * 4) + 3];
+	}
+
+	/* All other round keys are found from the previous round keys. */
+	for (i = Nk; i < Nb * (Nr + 1); ++i) {
+		k = (i - 1) * 4;
+		tmp_a[0] = round_key[k + 0];
+		tmp_a[1] = round_key[k + 1];
+		tmp_a[2] = round_key[k + 2];
+		tmp_a[3] = round_key[k + 3];
+
+		if (!(i % Nk)) {
+			/*
+			 * Shifts the 4 bytes in a word to the left once.
+			 * [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+			 */
+			tmp_b = tmp_a[0];
+			tmp_a[0] = tmp_a[1];
+			tmp_a[1] = tmp_a[2];
+			tmp_a[2] = tmp_a[3];
+			tmp_a[3] = tmp_b;
+
+			tmp_a[0] = sbox[tmp_a[0]];
+			tmp_a[1] = sbox[tmp_a[1]];
+			tmp_a[2] = sbox[tmp_a[2]];
+			tmp_a[3] = sbox[tmp_a[3]];
+
+			tmp_a[0] = tmp_a[0] ^ rcon[i / Nk];
+		}
+
+		j = i * 4;
+		k = (i - Nk) * 4;
+		round_key[j + 0] = round_key[k + 0] ^ tmp_a[0];
+		round_key[j + 1] = round_key[k + 1] ^ tmp_a[1];
+		round_key[j + 2] = round_key[k + 2] ^ tmp_a[2];
+		round_key[j + 3] = round_key[k + 3] ^ tmp_a[3];
+	}
+}
+
+u8 crypto_key[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			       0x4, 0x5, 0x6, 0x7,
+			       0x8, 0x9, 0xa, 0xb,
+			       0xc, 0xd, 0xe, 0xf };
+
+u8 crypto_iv[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			      0x4, 0x5, 0x6, 0x7,
+			      0x8, 0x9, 0xa, 0xb,
+			      0xc, 0xd, 0xe, 0xf };
+
+void ilitek_decrypt(u8 *buf, u32 len)
+{
+	u8 key[AES_KEY_LEN];
+	u8 iv[AES_KEY_LEN];
+	u8 *tmp = buf;
+	u8 iv_tmp[AES_KEY_LEN];
+	struct crypto_aes_ctx ctx;
+
+	u32 i, j;
+
+	memcpy(key, crypto_key, AES_KEY_LEN);
+	memcpy(iv, crypto_iv, AES_KEY_LEN);
+
+	aes_expandkey(ctx.key_dec, key);
+
+	for (i = 0; i < len; i += AES_KEY_LEN, tmp += AES_KEY_LEN) {
+		memcpy(iv_tmp, tmp, AES_KEY_LEN);
+
+		aes_decrypt((state_t *)tmp, ctx.key_dec);
+		for (j = 0; j < AES_KEY_LEN; j++)
+			tmp[j] ^= iv[j];
+
+		memcpy(iv, iv_tmp, AES_KEY_LEN);
+	}
+}
+
+static void sha256_init(struct ilitek_sha256_ctx *ctx)
+{
+	ctx->datalen = 0;
+	ctx->bitlen[0] = 0;
+	ctx->bitlen[1] = 0;
+
+	/*
+	 * 8 hash value for sha256
+	 * square root of prime number 2,3,5,7,11,13,17,19
+	 * then take 32 bit numbers after the decimal point
+	 */
+	ctx->state[0] = 0x6a09e667;
+	ctx->state[1] = 0xbb67ae85;
+	ctx->state[2] = 0x3c6ef372;
+	ctx->state[3] = 0xa54ff53a;
+	ctx->state[4] = 0x510e527f;
+	ctx->state[5] = 0x9b05688c;
+	ctx->state[6] = 0x1f83d9ab;
+	ctx->state[7] = 0x5be0cd19;
+}
+
+static void sha256_transform(struct ilitek_sha256_ctx *ctx, u8 *data)
+{
+	static u32 key[64] = {
+		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	};
+
+	u32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+	for (i = 0, j = 0; i < 16; ++i, j += 4)
+		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+
+	for (; i < 64; ++i)
+		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+
+	a = ctx->state[0];
+	b = ctx->state[1];
+	c = ctx->state[2];
+	d = ctx->state[3];
+	e = ctx->state[4];
+	f = ctx->state[5];
+	g = ctx->state[6];
+	h = ctx->state[7];
+
+	for (i = 0; i < 64; ++i) {
+		t1 = h + EP1(e) + CH(e, f, g) + key[i] + m[i];
+		t2 = EP0(a) + MAJ(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + t1;
+		d = c;
+		c = b;
+		b = a;
+		a = t1 + t2;
+	}
+
+	ctx->state[0] += a;
+	ctx->state[1] += b;
+	ctx->state[2] += c;
+	ctx->state[3] += d;
+	ctx->state[4] += e;
+	ctx->state[5] += f;
+	ctx->state[6] += g;
+	ctx->state[7] += h;
+}
+
+static void sha256_update(struct ilitek_sha256_ctx *ctx, u8 byte)
+{
+	ctx->data[ctx->datalen] = byte;
+	ctx->datalen++;
+	if (ctx->datalen == 64) {
+		sha256_transform(ctx, ctx->data);
+		DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], 512);
+		ctx->datalen = 0;
+	}
+}
+
+static void sha256_final(struct ilitek_sha256_ctx *ctx, u8 *hash)
+{
+	u32 i;
+
+	i = ctx->datalen;
+
+	/* Pad whatever data is left in the buffer. */
+	if (ctx->datalen < 56) {
+		ctx->data[i++] = 0x80;
+		while (i < 56)
+			ctx->data[i++] = 0x00;
+	} else {
+		ctx->data[i++] = 0x80;
+		while (i < 64)
+			ctx->data[i++] = 0x00;
+		sha256_transform(ctx, ctx->data);
+		memset(ctx->data, 0, 56);
+	}
+
+	/* Append to the padding the total message's length in bits and transform. */
+	DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8);
+	ctx->data[63] = ctx->bitlen[0];
+	ctx->data[62] = ctx->bitlen[0] >> 8;
+	ctx->data[61] = ctx->bitlen[0] >> 16;
+	ctx->data[60] = ctx->bitlen[0] >> 24;
+	ctx->data[59] = ctx->bitlen[1];
+	ctx->data[58] = ctx->bitlen[1] >> 8;
+	ctx->data[57] = ctx->bitlen[1] >> 16;
+	ctx->data[56] = ctx->bitlen[1] >> 24;
+	sha256_transform(ctx, ctx->data);
+
+	/*
+	 * Since this implementation uses little endian byte ordering and SHA uses big endian,
+	 * reverse all the bytes when copying the final state to the output hash.
+	 */
+	for (i = 0; i < 4; ++i) {
+		hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+	}
+}
+
+void get_sha256(u32 start, u32 end, u8 *buf, u32 buf_size, u8 sha256[32])
+{
+	struct ilitek_sha256_ctx ctx;
+	u32 i;
+
+	sha256_init(&ctx);
+
+	if (end >= buf_size || start > buf_size || end < start) {
+		TP_ERR(NULL, "start/end addr: %#x/%#x buf size: %#x OOB\n", start, end, buf_size);
+		return;
+	}
+
+	for (i = start; i <= end && i < buf_size; i++)
+		sha256_update(&ctx, buf[i]);
+
+	sha256_final(&ctx, sha256);
+
+	TP_MSG_ARR(NULL, "sha256:", TYPE_U8, 32, sha256);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.h b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
new file mode 100644
index 000000000000..8085961df031
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_CRYPTO_H__
+#define __ILITEK_CRYPTO_H__
+
+#include "ilitek_def.h"
+
+#define Nb	(4)
+#define Nk	(4)       /* The number of 32 bit words in a key. */
+#define Nr	(10)      /* The number of rounds in AES Cipher. */
+
+#define AES_KEY_LEN      (16) /* Key length in bytes */
+
+extern u8 crypto_key[AES_KEY_LEN];
+extern u8 crypto_iv[AES_KEY_LEN];
+
+#define DBL_INT_ADD(a, b, c)			\
+	do {					\
+		if ((a) > 0xffffffff - (c))	\
+			++(b);			\
+		(a) += (c);			\
+	} while (0)
+
+#define ROTLEFT(a, b)	(((a) << (b)) | ((a) >> (32 - (b))))
+#define ROTRIGHT(a, b)	(((a) >> (b)) | ((a) << (32 - (b))))
+
+#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
+#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
+#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
+
+struct ilitek_sha256_ctx {
+	u8 data[64];
+	u32 datalen;
+	u32 bitlen[2];
+	u32 state[8];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL ilitek_decrypt(u8 *buf, u32 len);
+
+void __DLL get_sha256(u32 start, u32 end,
+		      u8 *buf, u32 buf_size, u8 sha256[32]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.c b/drivers/input/touchscreen/ilitek/ilitek_def.c
new file mode 100644
index 000000000000..e9934cac240d
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_def.h"
+
+int tp_log_level = log_level_msg;
+bool tp_print_en = true;
+FILE *tp_fp;
+
+char g_str[4096];
+msg_t g_msg;
+
+#if defined(__KERNEL__) || defined(__UEFI_DXE__)
+
+int get_time_ms(u32 *t_ms)
+{
+	*t_ms = 0;
+
+	return -EINVAL;
+}
+
+#else
+#ifdef _WIN32
+
+static int gettimeofday(struct timeval *tp, void *tzp)
+{
+	time_t clock;
+	struct tm tm;
+	SYSTEMTIME wtm;
+
+	GetLocalTime(&wtm);
+	tm.tm_year = wtm.wYear - 1900;
+	tm.tm_mon = wtm.wMonth - 1;
+	tm.tm_mday = wtm.wDay;
+	tm.tm_hour = wtm.wHour;
+	tm.tm_min = wtm.wMinute;
+	tm.tm_sec = wtm.wSecond;
+	tm.tm_isdst = -1;
+	clock = mktime(&tm);
+	tp->tv_sec = (long)clock;
+	tp->tv_usec = wtm.wMilliseconds * 1000;
+	return 0;
+}
+
+#endif
+
+int get_time_ms(u32 *t_ms)
+{
+	static u32 time_ms_init;
+	struct timeval t;
+	u32 time_ms;
+
+	gettimeofday(&t, NULL);
+	time_ms = t.tv_sec * 1000 + t.tv_usec / 1000;
+	time_ms_init = (!time_ms_init) ? time_ms : time_ms_init;
+
+	*t_ms = time_ms - time_ms_init;
+
+	return 0;
+}
+
+#endif
+
+void tp_log_arr(char *id, int level, const char *header, const char *tag,
+		int type, int len, void *buf)
+{
+	const int num = 64;
+	int i, idx = 0;
+	u32 time_ms;
+	int error;
+
+	if (level > tp_log_level || !buf)
+		return;
+
+	error = get_time_ms(&time_ms);
+
+	do {
+		_memset(g_str, 0, sizeof(g_str));
+
+		if (!error)
+			_sprintf(g_str, 0, "[%7u.%03u]",
+				time_ms / 1000, time_ms % 1000);
+
+		if (id)
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 "[" PFMT_C8 "] " PFMT_C8 " ",
+				 header, id, tag);
+		else
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 " " PFMT_C8 " ",
+				 header, tag);
+
+		for (i = 0; i < num && idx < len; i++, idx++) {
+			switch (type) {
+			default:
+			case TYPE_U8:
+				_sprintf(g_str, _strlen(g_str), "%02x-",
+					((u8 *)buf)[idx]);
+				break;
+			case TYPE_INT:
+				_sprintf(g_str, _strlen(g_str), "%d-",
+					((int *)buf)[idx]);
+				break;
+			}
+		}
+		_sprintf(g_str, _strlen(g_str) - 1, ", len: [%d/%d]\n",
+			idx, len);
+
+		if (tp_print_en)
+			TP_PRINTF(PFMT_C8, g_str);
+		if (g_msg)
+			g_msg(level, g_str);
+		TP_LOG(tp_fp, g_str);
+	} while (idx < len);
+}
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items)
+{
+	int error = 0;
+
+	MUTEX_INIT(q->mutex);
+
+	MUTEX_LOCK(q->mutex);
+
+	do {
+		q->item_size = item_size;
+		q->curr_size = 0;
+		q->max_size = max_items;
+
+		q->buf = (u8 *)CALLOC(max_items, item_size);
+		if (!q->buf) {
+			error = -ENOMEM;
+			break;
+		}
+
+		q->push_ptr = q->buf;
+		q->pop_ptr = q->buf;
+		q->end_ptr = q->buf + (max_items - 1) * item_size;
+	} while (false);
+
+	MUTEX_UNLOCK(q->mutex);
+
+	return error;
+}
+
+void queue_exit(struct queue *q)
+{
+	if (q->buf)
+		CFREE(q->buf);
+	MUTEX_EXIT(q->mutex);
+}
+
+void queue_push(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	/* Stop push data when queue is full */
+	if (q->curr_size >= q->max_size)
+		goto release_push_lock;
+
+	q->curr_size++;
+	if (q->push_ptr == q->end_ptr)
+		q->push_ptr = q->buf;
+	else
+		q->push_ptr += q->item_size;
+
+	if (q->push_ptr == q->pop_ptr)
+		TP_ERR(NULL, "[Warn]Queue overload, queue size: %u\n", q->curr_size);
+
+release_push_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void queue_pop(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	if (!q->curr_size)
+		goto release_pop_lock;
+
+	q->curr_size--;
+	if (q->pop_ptr == q->end_ptr)
+		q->pop_ptr = q->buf;
+	else
+		q->pop_ptr += q->item_size;
+
+release_pop_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void set_print_en(bool enable)
+{
+	tp_print_en = enable;
+}
+
+void set_log_level(int level)
+{
+	tp_log_level = level;
+}
+
+int set_log_fopen(WCHAR *filename)
+{
+	int error;
+
+	if (tp_fp)
+		return -EINVAL;
+
+	error = WFOPEN(&tp_fp, filename, "w+");
+	if (error < 0) {
+		tp_fp = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+void set_log_fclose(void)
+{
+	if (!tp_fp)
+		return;
+
+	_fclose(tp_fp);
+	tp_fp = NULL;
+}
+
+void set_log_fwrite(char *str)
+{
+	TP_LOG(tp_fp, str);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.h b/drivers/input/touchscreen/ilitek/ilitek_def.h
new file mode 100644
index 000000000000..d6f2077d7760
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.h
@@ -0,0 +1,554 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_DEF_H__
+#define __ILITEK_DEF_H__
+
+#define COMMONFLOW_CODE_VERSION		0x00000301
+
+/*
+ * Windows
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+#define __MAYBE_UNUSED
+
+#ifdef _WINDLL
+#define __DLL __declspec(dllexport)
+#else
+#define __DLL __declspec(dllimport)
+#endif
+
+#define __PACKED__
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	sprintf_s((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size)	strncpy_s((dst), (dst_size), (src), (n))
+#define _strcasecmp(l, r)		_stricmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat_s((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)		sscanf_s(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy_s((dst), (dst_size), (src))
+#define _strtok(str, del, next_token)	strtok_s((str), (del), (next_token))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				wchar_t
+#define WSTRING				wstring
+#define WCSCPY(dst, src, dst_size)	wcscpy_s((dst), (dst_size), (src))
+#define WCSCASECMP(str, tag)		_wcsicmp((str), (L##tag))
+#define WCSRCHR(str, ch)		wcsrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	swprintf_s((buf), (size), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)					\
+	((!((*(pfp)) = _wfsopen((filename), (const wchar_t *)(L##mode), \
+				_SH_DENYWR))) ? -EFAULT : 0)
+#define WACCESS(filename, mode)		_waccess((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)					\
+	do {							\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_U8TEXT);		\
+		fwprintf((fp), (fmt), ##__VA_ARGS__);		\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_TEXT);		\
+	} while (false)
+
+#define PFMT_C16			"%ls"
+#define PFMT_C8				"%hs"
+
+#include <codecvt>
+#define TO_WCHAR(x)	\
+	std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t > ().from_bytes(x).c_str()
+
+#define _localtime(ptm, ptime)		localtime_s((ptm), (ptime))
+
+#define _fopen(pfp, filename, mode)	\
+	(fopen_s((pfp), (filename), (const char *)(mode)))
+#define _fclose(pfp)			fclose((pfp))
+
+#define MUTEX_T				HANDLE
+#define MUTEX_INIT(x)			((x) = CreateMutex(NULL, false, NULL))
+#define MUTEX_LOCK(x)			(WaitForSingleObject((x), INFINITE))
+#define MUTEX_UNLOCK(x)			(ReleaseMutex((x)))
+#define MUTEX_EXIT(x)			(CloseHandle((x)))
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+/*
+ * UEFI Driver
+ */
+#elif defined(__UEFI_DXE__)
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+
+#define UEFI_MAX_STR_SIZE	0x1000
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	AsciiSPrint((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) \
+	AsciiStrnCpyS(dst, dst_size, src, (UINTN)n)
+#define _strcasecmp(l, r)		AsciiStriCmp((l), (r))
+#define _strcat(dst, src, dst_size)	\
+	AsciiStrCatS((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)
+#define _strlen(str)			AsciiStrnLenS((str), UEFI_MAX_STR_SIZE)
+#define _strcpy(dst, src, dst_size)	\
+	_strncpy((dst), (src), _strlen(src), (dst_size))
+#define _strtok(str, del, next_token)
+
+static __MAYBE_UNUSED char *_strrchr(const char *s, char c)
+{
+	char *found = NULL;
+
+	do {
+		if (*s == c)
+			found = (char *)s;
+	} while (*s++ != '\0');
+
+	return found;
+}
+
+#define _memset(ptr, ch, size)		\
+	SetMem((ptr), (UINTN)(size), (UINT8)(ch))
+#define _memcpy(dst, src, size)		\
+	CopyMem((dst), (src), (UINTN)(size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		_strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	(-EINVAL)
+#define WACCESS(filename, mode)		(-EINVAL)
+#define WFPRINTF(fp, fmt, ...)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%a"
+#define PFMT_C8				"%a"
+#define PFMT_U8				"%u"
+#define PFMT_U16			"%u"
+#define PFMT_X8				"%02x"
+#define PFMT_X16			"%04x"
+#define PFMT_X64			"%016llx"
+
+#define _localtime(ptm, ptime)
+#define _fclose(pfp)
+
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define MALLOC(size)		AllocatePool(size)
+#define CALLOC(num, size)	AllocateZeroPool(num * size)
+#define FREE(ptr)			\
+	do {				\
+		FreePool((ptr));	\
+		(ptr) = NULL;		\
+	} while (0)
+
+#define CFREE(ptr)		FREE(ptr)
+#define TP_PRINTF(fmt, ...)	DebugPrint(DEBUG_INFO, fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+/*
+ * Linux-Based System
+ */
+#else
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+#define _sprintf(buf, idx, fmt, ...)	sprintf((buf) + (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) strncpy((dst), (src), (n))
+#define _strcasecmp(l, r)		strcasecmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat((dst), (src))
+#define _sscanf(str, fmt, ...)		sscanf(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy((dst), (src))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define WTEXT(str)			str
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	_fopen(pfp, filename, mode)
+#define WACCESS(filename, mode)		_access((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)		fprintf((fp), (fmt), ##__VA_ARGS__)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%s"
+#define PFMT_C8				"%s"
+
+#define _localtime(ptm, ptime)						\
+		do {							\
+			struct tm *__tm__;				\
+									\
+			__tm__ = localtime((ptime));			\
+			_memcpy((ptm), __tm__, sizeof(struct tm));	\
+		} while (false)
+
+#ifdef __KERNEL__
+#define FILE				void
+#define _fopen(pfp, filename, mode)	(-EINVAL)
+#define _fclose(pfp)
+#define _access(filename, mode)		(-EINVAL)
+
+#define _strtok(str, del, next_token)
+
+#include <linux/spinlock.h>
+#define MUTEX_T				spinlock_t
+#define MUTEX_INIT(x)			spin_lock_init(&(x))
+#define MUTEX_LOCK(x)			spin_lock(&(x))
+#define MUTEX_UNLOCK(x)			spin_unlock(&(x))
+#define MUTEX_EXIT(x)
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#define MALLOC(size)		kmalloc(size, GFP_KERNEL)
+#define CALLOC(num, size)	vmalloc(num * size)
+#define FREE(ptr)		\
+	do {			\
+		kfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		\
+	do {			\
+		vfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+
+#define TP_PRINTF(fmt, ...)	printk(fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+#else
+#include <sys/time.h>
+#include <unistd.h>
+
+#define _fopen(pfp, filename, mode)	\
+	((!((*(pfp)) = fopen((filename), (mode)))) ? -EFAULT : 0)
+#define _fclose(pfp)			fclose((pfp))
+#define _access(filename, mode)		access((filename), (mode))
+
+#define _strtok(str, del, next_token)	strtok((str), (del))
+
+#ifdef __UEFI_APP__
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+#else
+#include <pthread.h>
+#define MUTEX_T				pthread_mutex_t
+#define MUTEX_INIT(x)			(pthread_mutex_init(&(x), NULL))
+#define MUTEX_LOCK(x)			(pthread_mutex_lock(&(x)))
+#define MUTEX_UNLOCK(x)			(pthread_mutex_unlock(&(x)))
+#define MUTEX_EXIT(x)			(pthread_mutex_destroy(&(x)))
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#define TP_PRINTF(fmt, ...)							\
+	__android_log_print(ANDROID_LOG_INFO, "ILITEK COMMON", "[%s][%d]" fmt,	\
+			    __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _WIN32 */
+
+#define U82U64(byte, order)	\
+	((u64)((u64)(byte) << ((order) * 8)))
+
+#ifndef PFMT_C16
+#define PFMT_C16			"%ls"
+#endif
+#ifndef PFMT_C8
+#define PFMT_C8				"%hs"
+#endif
+#ifndef PFMT_U8
+#define PFMT_U8				"%hhu"
+#endif
+#ifndef PFMT_U16
+#define PFMT_U16			"%hu"
+#endif
+#ifndef PFMT_X8
+#define PFMT_X8				"%hhx"
+#endif
+#ifndef PFMT_X16
+#define PFMT_X16			"%hx"
+#endif
+
+#ifndef PFMT_X64
+#define PFMT_X64			"%llx"
+#endif
+
+#ifndef TP_PRINTF
+#define TP_PRINTF(fmt, ...)				\
+	do {						\
+		printf(fmt, ##__VA_ARGS__);		\
+		fflush(stdout);				\
+	} while (0)
+#endif
+
+#ifndef TP_LOG
+#define TP_LOG(fp, str)				\
+	do {					\
+		if (!fp)			\
+			break;			\
+		fprintf((fp), PFMT_C8, (str));	\
+		fflush((fp));			\
+	} while (0)
+#endif
+
+#ifndef TP_PRINT
+#define TP_PRINT(_id, level, need_tag, tag, fmt, ...)			\
+	do {								\
+		char *__id__ = (_id);					\
+		u32 __time_ms__;					\
+									\
+		if (level > tp_log_level)				\
+			break;						\
+									\
+		g_str[0] = '\0';					\
+									\
+		if (need_tag) {						\
+			if (!get_time_ms(&__time_ms__))			\
+				_sprintf(g_str, _strlen(g_str),		\
+					"[%7u.%03u]",			\
+					__time_ms__ / 1000,		\
+					__time_ms__ % 1000);		\
+			_sprintf(g_str, _strlen(g_str), PFMT_C8, tag);	\
+		}							\
+									\
+		if (__id__) {						\
+			_sprintf(g_str, _strlen(g_str),			\
+				 "[" PFMT_C8 "] " fmt,			\
+				 __id__, ##__VA_ARGS__);		\
+		} else {						\
+			_sprintf(g_str, _strlen(g_str), " " fmt,	\
+				 ##__VA_ARGS__);			\
+		}							\
+									\
+		if (tp_print_en)					\
+			TP_PRINTF(PFMT_C8, g_str);			\
+		if (g_msg)						\
+			g_msg(level, g_str);				\
+		TP_LOG(tp_fp, g_str);					\
+	} while (0)
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d)	(((n) + (d) - 1) / (d))
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x)		((void)(x))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a)		((sizeof(a) / sizeof(*(a))))
+#endif
+
+#ifndef MIN
+#define MIN(l, r)		(((l) > (r)) ? (r) : (l))
+#endif
+
+#ifndef MAX
+#define MAX(l, r)		(((l) > (r)) ? (l) : (r))
+#endif
+
+#define EILICOMM		200
+#define EILIBUSY		201
+#define EILITIME		202
+#define EILIPROTO		203
+
+enum ilitek_log_level {
+	log_level_none = -1,	/* no log displayed */
+	log_level_err = 0,	/* critical errors */
+	log_level_warn,		/* warnings */
+	log_level_tag,		/* special-required tags */
+	log_level_info,		/* important/UI messages */
+	log_level_msg,		/* non-important messages */
+	log_level_dbg,		/* debugging messages */
+	log_level_pkt,		/* tx/rx packets */
+
+	log_level_max,		/* sentinel */
+};
+
+#define _TP_ERR(fmt, ...)	\
+	TP_PRINT(NULL, log_level_err, false, "", fmt, ##__VA_ARGS__)
+#define _TP_WARN(fmt, ...)	\
+	TP_PRINT(NULL, log_level_warn, false, "", fmt, ##__VA_ARGS__)
+#define _TP_TAG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_tag, false, "", fmt, ##__VA_ARGS__)
+#define _TP_INFO(fmt, ...)	\
+	TP_PRINT(NULL, log_level_info, false, "", fmt, ##__VA_ARGS__)
+#define _TP_MSG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_msg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_DBG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_dbg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_PKT(fmt, ...)	\
+	TP_PRINT(NULL, log_level_pkt, false, "", fmt, ##__VA_ARGS__)
+
+
+#define TP_ERR(id, fmt, ...)	\
+	TP_PRINT(id, log_level_err, true, "[ILITEK][ERR]", fmt, ##__VA_ARGS__)
+#define TP_WARN(id, fmt, ...)	\
+	TP_PRINT(id, log_level_warn, true, "[ILITEK][WARN]", fmt, ##__VA_ARGS__)
+#define TP_TAG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_tag, true, "[ILITEK][TAG]", fmt, ##__VA_ARGS__)
+#define TP_INFO(id, fmt, ...)	\
+	TP_PRINT(id, log_level_info, true, "[ILITEK][INFO]", fmt, ##__VA_ARGS__)
+#define TP_MSG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_msg, true, "[ILITEK][MSG]", fmt, ##__VA_ARGS__)
+#define TP_DBG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_dbg, true, "[ILITEK][DBG]", fmt, ##__VA_ARGS__)
+#define TP_PKT(id, fmt, ...)	\
+	TP_PRINT(id, log_level_pkt, true, "[ILITEK][PKT]", fmt, ##__VA_ARGS__)
+
+enum ilitek_array_type {
+	TYPE_U8 = 0,
+	TYPE_INT,
+	TYPE_U32,
+};
+
+#define TP_ERR_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_err, "[ILITEK][ERR]", tag, type, len, buf)
+#define TP_WARN_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_warn, "[ILITEK][WARN]", tag, type, len, buf)
+#define TP_TAG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_tag, "[ILITEK][TAG]", tag, type, len, buf)
+#define TP_INFO_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_info, "[ILITEK][INFO]", tag, type, len, buf)
+#define TP_MSG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_msg, "[ILITEK][MSG]", tag, type, len, buf)
+#define TP_DBG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_dbg, "[ILITEK][DBG]", tag, type, len, buf)
+#define TP_PKT_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_pkt, "[ILITEK][PKT]", tag, type, len, buf)
+
+extern int tp_log_level;
+extern bool tp_print_en;
+extern char g_str[4096];
+extern FILE *tp_fp;
+
+typedef void (*msg_t)(int, char *);
+extern msg_t g_msg;
+
+struct queue {
+	u32 curr_size;
+	u32 max_size;
+	u8 *buf;
+	u8 *push_ptr;
+	u8 *pop_ptr;
+	u8 *end_ptr;
+	u32 item_size;
+
+	MUTEX_T mutex;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL tp_log_arr(char *id, int level, const char *header,
+		      const char *tag, int type, int len, void *buf);
+
+int __DLL get_time_ms(u32 *t_ms);
+void __DLL set_print_en(bool enable);
+void __DLL set_log_level(int level);
+int __DLL set_log_fopen(WCHAR *filename);
+void __DLL set_log_fclose(void);
+void __DLL set_log_fwrite(char *str);
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items);
+void queue_exit(struct queue *q);
+void queue_push(struct queue *q);
+void queue_pop(struct queue *q);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_main.c b/drivers/input/touchscreen/ilitek/ilitek_main.c
new file mode 100644
index 000000000000..9bb1fe6aa4b7
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_main.c
@@ -0,0 +1,2198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+struct ilitek_ts_data *ts;
+
+u8 driver_ver[] = {
+	DRIVER_VERSION_0, DRIVER_VERSION_1, DRIVER_VERSION_2, DRIVER_VERSION_3,
+	CUSTOMER_H_ID, CUSTOMER_L_ID, TEST_VERSION,
+};
+
+static bool checksum_failed_release = ILITEK_CHECKSUM_FAILED_RELEASE;
+module_param(checksum_failed_release, bool, 0664);
+MODULE_PARM_DESC(checksum_failed_release,
+		 "When packet's checksum is wrong, (default)release all touch point or ignore the packet");
+
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+extern struct tpd_device *tpd;
+#ifdef ILITEK_ENABLE_DMA
+static u8 *I2CDMABuf_va;
+static dma_addr_t I2CDMABuf_pa;
+#endif
+#endif
+
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+struct wake_lock ilitek_wake_lock;
+#endif
+
+#ifdef ILITEK_TUNING_MESSAGE
+static struct sock *ilitek_netlink_sock;
+bool ilitek_debug_flag;
+static u_int ilitek_pid = 100, ilitek_seq = 23;
+#endif
+
+static void __maybe_unused ilitek_udp_reply(void *payload, int size)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int len = NLMSG_SPACE(size);
+	int ret;
+	int pid = ilitek_pid, seq = ilitek_seq;
+
+	TP_DBG(NULL, "[%s] ilitek_debug_flag: %d\n", __func__, ilitek_debug_flag);
+	if (!ilitek_debug_flag)
+		return;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		TP_ERR(NULL, "alloc skb error\n");
+		return;
+	}
+
+	nlh = nlmsg_put(skb, pid, seq, 0, size, 0);
+	if (!nlh)
+		goto nlmsg_failure;
+
+	nlh->nlmsg_flags = 0;
+	memcpy(NLMSG_DATA(nlh), payload, size);
+
+	NETLINK_CB(skb).portid = 0;	/* from kernel */
+	NETLINK_CB(skb).dst_group = 0;	/* unicast */
+
+	ret = netlink_unicast(ilitek_netlink_sock, skb, pid, MSG_DONTWAIT);
+	if (ret < 0)
+		TP_ERR(NULL, "ilitek send failed, ret: %d\n", ret);
+	return;
+
+nlmsg_failure:
+	kfree_skb(skb);
+
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void __maybe_unused udp_receive(struct sk_buff *skb)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	int count = 0, ret = 0, i = 0;
+	u8 *data;
+	struct nlmsghdr *nlh;
+
+	nlh = (struct nlmsghdr *)skb->data;
+	ilitek_pid = NETLINK_CREDS(skb)->pid;
+	ilitek_seq = nlh->nlmsg_seq;
+
+	TP_DBG(NULL, "netlink received, pid: %d, seq: %d\n",
+		     ilitek_pid, ilitek_seq);
+
+	data = (u8 *) NLMSG_DATA(nlh);
+	count = nlmsg_len(nlh);
+	if (!strcmp(data, "Open!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = true;
+		ilitek_udp_reply(data, sizeof("Open!"));
+	} else if (!strcmp(data, "Close!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = false;
+	} else if (!strcmp(data, "Wifi_Paint_Start") ||
+		   !strcmp(data, "Daemon_Debug_Start")) {
+		ilitek_debug_flag = true;
+	} else if (!strcmp(data, "Wifi_Paint_End") ||
+		   !strcmp(data, "Daemon_Debug_End")) {
+		ilitek_debug_flag = false;
+	}
+
+
+	TP_DBG(NULL, "count = %d  data[count -3] = %d data[count -2] = %c\n",
+	       count, data[count - 3], data[count - 2]);
+	for (i = 0; i < count; i++)
+		//TP_MSG(NULL, "data[%d] = 0x%x\n", i, data[i]);
+	if (data[count - 2] == 'I' && (count == 20 || count == 52) &&
+	    data[0] == 0x77 && data[1] == 0x77) {
+		TP_DBG(NULL, "IOCTL_WRITE CMD = %d\n", data[2]);
+		switch (data[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", data[3]);
+			if (data[3] == 0)
+				ilitek_debug_flag = false;
+			else if (data[3] == 1)
+				ilitek_debug_flag = true;
+			break;
+		case 15:
+			if (data[3] == 0) {
+				ilitek_irq_disable();
+				TP_DBG(NULL, "ilitek_irq_disable.\n");
+			} else {
+				ilitek_irq_enable();
+				TP_DBG(NULL, "ilitek_irq_enable.\n");
+			}
+			break;
+		case 16:
+			ts->operation_protection = data[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 8:
+			TP_MSG(NULL, "get driver version\n");
+			ilitek_udp_reply(driver_ver, 7);
+			break;
+		case 18:
+			TP_DBG(NULL, "firmware update write 33 bytes data\n");
+			ret = ilitek_write(&data[3], 33);
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			if (ret < 0)
+				data[0] = 1;
+			else
+				data[0] = 0;
+			ilitek_udp_reply(data, 1);
+			return;
+		default:
+			return;
+		}
+	} else if (data[count - 2] == 'W') {
+		ret = ilitek_write(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+		if (ret < 0)
+			data[0] = 1;
+		else
+			data[0] = 0;
+		ilitek_udp_reply(data, 1);
+	} else if (data[count - 2] == 'R') {
+		ret = ilitek_read(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		if (ret < 0)
+			data[count - 2] = 1;
+		else
+			data[count - 2] = 0;
+		ilitek_udp_reply(data, count - 1);
+	}
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void ilitek_esd_check(struct work_struct *work)
+{
+	int retry = 3;
+	static bool is_first_run = true;
+	static u32 protocol_ver;
+
+	/*
+	 * update protocol version at the first run
+	 */
+	if (is_first_run) {
+		is_first_run = false;
+		protocol_ver = ts->dev->protocol.ver;
+		TP_MSG(NULL, "[ESD] firstly loading protocol ver: %x as ref.\n",
+			protocol_ver);
+	}
+
+	if (ts->operation_protection || ts->esd_skip) {
+		TP_MSG(NULL, "[ESD] operation_protection: %hhu, esd_skip: %hhu\n",
+			ts->operation_protection, ts->esd_skip);
+		goto skip_return;
+	}
+
+	mutex_lock(&ts->ilitek_mutex);
+
+	for (; retry-- > 0;) {
+		if (api_protocol_set_cmd(ts->dev, GET_PTL_VER, NULL) < 0) {
+			TP_ERR(NULL, "[ESD] i2c comm. failed\n");
+			continue;
+		}
+
+		if (protocol_ver != ts->dev->protocol.ver) {
+			TP_ERR(NULL, "unexpected ptl ver (referance)%x vs. %x\n",
+				protocol_ver, ts->dev->protocol.ver);
+			continue;
+		}
+
+		goto pass_return;
+	}
+
+	ilitek_reset(ts->dev->reset_time);
+
+pass_return:
+	mutex_unlock(&ts->ilitek_mutex);
+
+skip_return:
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+}
+
+void ilitek_irq_enable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	enable_irq(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 1);
+}
+
+void ilitek_irq_disable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (!atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	disable_irq_nosync(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 0);
+}
+
+#ifdef ILITEK_ENABLE_DMA
+static int ilitek_dma_i2c_read(u8 *buf, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int err;
+
+
+	if (len < 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_recv(client, buf, len);
+	}
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+	err = i2c_master_recv(client, (u8 *)I2CDMABuf_pa, len);
+	if (err < 0)
+		return err;
+
+	memcpy(buf, I2CDMABuf_va, len);
+
+	return 0;
+}
+
+static int ilitek_dma_i2c_write(u8 *cmd, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+
+	if (len <= 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_send(client, cmd, len);
+	}
+
+	memcpy(I2CDMABuf_va, cmd, len);
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+
+	return i2c_master_send(client, (u8 *)I2CDMABuf_pa, len);
+}
+#endif
+
+static int ilitek_i2c_transfer(struct i2c_msg *msgs, int cnt)
+{
+	int err = 0;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int count = ILITEK_I2C_RETRY_COUNT;
+
+#ifdef ILITEK_ENABLE_DMA
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		count = ILITEK_I2C_RETRY_COUNT;
+		while (count-- >= 0) {
+			msgs[i].ext_flag = 0;
+			if (msgs[i].flags == I2C_M_RD)
+				err = ilitek_dma_i2c_read(msgs[i].buf, msgs[i].len);
+			else if (!msgs[i].flags)
+				err = ilitek_dma_i2c_write(msgs[i].buf, msgs[i].len);
+
+			if (err < 0) {
+				TP_ERR(NULL, "i2c[0x%hx] dma tx/rx failed, err: %d\n",
+					msgs[i].addr, err);
+				mdelay(20);
+				continue;
+			}
+
+			break;
+		}
+	}
+#else
+	while (count-- >= 0) {
+		err = i2c_transfer(client->adapter, msgs, cnt);
+		if (err < 0) {
+			TP_ERR(NULL, "i2c[0x%hx] tx/rx failed, err: %d\n",
+				msgs[0].addr, err);
+			mdelay(20);
+			continue;
+		}
+		break;
+	}
+#endif
+
+	return err;
+}
+
+static int __maybe_unused ilitek_i2c_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error;
+
+	/*
+	 * Default ILITEK_BL_ADDR. is firstly used.
+	 * if communication failed, change between BL addr. and
+	 * other addr. defined by DTS, then retry.
+	 */
+	static unsigned short addr = ILITEK_BL_ADDR;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	struct i2c_msg msgs[2] = {
+		{.addr = addr, .flags = 0, .len = w_len,
+		 .buf = cmd, SCL_RATE(400000)},
+		{.addr = addr, .flags = I2C_M_RD, .len = r_len,
+		 .buf = buf, SCL_RATE(400000)}
+	};
+
+	/*
+	 * IMPORTANT: If I2C repeat start is required, please check with ILITEK.
+	 */
+	if (w_len > 0 && r_len > 0 && !delay_ms) {
+		if (ilitek_i2c_transfer(msgs, 2) < 0) {
+			/* try another i2c addr. (default: 0x41) */
+			addr = (addr == ILITEK_BL_ADDR) ?
+				client->addr : ILITEK_BL_ADDR;
+			msgs[0].addr = msgs[1].addr = addr;
+
+			return ilitek_i2c_transfer(msgs, 2);
+		}
+
+		return 0;
+	}
+
+	if (w_len > 0 && ilitek_i2c_transfer(msgs, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		error = ilitek_i2c_transfer(msgs, 1);
+		if (error < 0)
+			return error;
+	}
+
+	if (delay_ms > 0)
+		mdelay(delay_ms);
+
+	if (r_len > 0 && ilitek_i2c_transfer(msgs + 1, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		return ilitek_i2c_transfer(msgs + 1, 1);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_i2c_write(u8 *cmd, int len)
+{
+	return ilitek_i2c_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_i2c_read(u8 *buf, int len)
+{
+	return ilitek_i2c_write_and_read(NULL, 0, 0, buf, len);
+}
+
+static int __maybe_unused ilitek_spi_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error = 0;
+	u8 *wbuf, *rbuf;
+	struct spi_device *spi = (struct spi_device *)ts->client;
+	struct spi_transfer xfer = {
+		.len = r_len + 4,
+		.speed_hz = ((struct spi_device *)ts->client)->max_speed_hz,
+	};
+	struct spi_message msg;
+
+	if (w_len > 0 && r_len > 0) {
+		error = ilitek_spi_write_and_read(cmd, w_len, delay_ms,
+						  NULL, 0);
+		if (error < 0)
+			return error;
+
+		return ilitek_spi_write_and_read(NULL, 0, 0, buf, r_len);
+	}
+
+	wbuf = CALLOC(4096, sizeof(u8));
+	rbuf = CALLOC(4096, sizeof(u8));
+
+	if (!wbuf || !rbuf) {
+		error = -ENOMEM;
+		goto exit;
+	}
+
+	xfer.tx_buf = wbuf;
+	xfer.rx_buf = rbuf;
+
+	wbuf[1] = 0xAA;
+
+	/* wbuf[0] set as 0x83 for spi data read */
+	if (r_len > 0) {
+		wbuf[0] = 0x83;
+		memset(wbuf + 2, 0, xfer.len - 2);
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfer, &msg);
+		error = spi_sync(spi, &msg);
+		if (error < 0)
+			goto exit;
+
+		TP_DBG(NULL, "[rbuf]: %*phD, len: %d\n", xfer.len, rbuf, xfer.len);
+
+		memcpy(buf, rbuf + 4, r_len);
+	} else if (w_len > 0) {
+		wbuf[0] = 0x82;
+		wbuf[2] = cmd[0];
+		wbuf[3] = 0;
+		memcpy(wbuf + 4, cmd + 1, w_len - 1);
+
+		TP_DBG(NULL, "[wbuf]: %*phD, len: %d\n", 3 + w_len, wbuf, 3 + w_len);
+
+		error = spi_write(spi, wbuf, 3 + w_len);
+		if (error < 0)
+			goto exit;
+
+		if (delay_ms > 0)
+			mdelay(delay_ms);
+	}
+
+exit:
+	CFREE(wbuf);
+	CFREE(rbuf);
+
+	return (error < 0) ? error : 0;
+}
+
+static int __maybe_unused ilitek_spi_write(u8 *cmd, int len)
+{
+	return ilitek_spi_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_spi_read(u8 *buf, int len)
+{
+	return ilitek_spi_write_and_read(NULL, 0, 0, buf, len);
+}
+
+int ilitek_write(u8 *cmd, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write(cmd, len);
+#else
+	error = ilitek_i2c_write(cmd, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_read(u8 *buf, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_read(buf, len);
+#else
+	error = ilitek_i2c_read(buf, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#else
+	error = ilitek_i2c_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+void __maybe_unused ilitek_gpio_dbg(void)
+{
+#if defined(ILITEK_GPIO_DEBUG)
+	gpio_direction_output(ts->test_gpio, 0);
+	mdelay(1);
+	gpio_direction_output(ts->test_gpio, 1);
+#endif
+}
+
+void ilitek_reset(int delay)
+{
+	TP_MSG(NULL, "reset_gpio: %d, delay: %d\n", ts->reset_gpio, delay);
+
+	ilitek_irq_disable();
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK && defined(MTK_UNDTS)
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ZERO);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(delay);
+#elif ILITEK_PLAT == ILITEK_PLAT_MTK
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 0);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#else
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 0);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#endif
+
+	ilitek_irq_enable();
+}
+
+static int ilitek_free_gpio(void)
+{
+
+#ifndef MTK_UNDTS
+	if (gpio_is_valid(ts->reset_gpio)) {
+		TP_MSG(NULL, "reset_gpio is valid so free\n");
+		gpio_free(ts->reset_gpio);
+	}
+	if (gpio_is_valid(ts->irq_gpio)) {
+		TP_MSG(NULL, "irq_gpio is valid so free\n");
+		gpio_free(ts->irq_gpio);
+	}
+#endif
+
+#if defined(ILITEK_GPIO_DEBUG)
+	if (gpio_is_valid(ts->test_gpio)) {
+		TP_MSG(NULL, "test_gpio is valid so free\n");
+		gpio_free(ts->test_gpio);
+	}
+#endif
+
+	return 0;
+}
+
+static int ilitek_request_pen_input_dev(void)
+{
+	int error;
+	struct input_dev *input;
+
+	int x_min = ts->dev->screen_info.pen_x_min;
+	int y_min = ts->dev->screen_info.pen_y_min;
+	int x_max = ts->dev->screen_info.pen_x_max;
+	int y_max = ts->dev->screen_info.pen_y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	TP_DBG(NULL, "registering pen input device\n");
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOOL_PEN, input->keybit);		/* In Range */
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);	/* Invert */
+	__set_bit(BTN_STYLUS, input->keybit);		/* Barrel Switch */
+	__set_bit(BTN_TOUCH, input->keybit);		/* Tip Switch */
+
+	input->name = "ILITEK STYLUS";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
+
+	input_set_abs_params(input, ABS_PRESSURE,
+			     ts->dev->screen_info.pressure_min,
+			     ts->dev->screen_info.pressure_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_X,
+			     ts->dev->screen_info.x_tilt_min,
+			     ts->dev->screen_info.x_tilt_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_Y,
+			     ts->dev->screen_info.y_tilt_min,
+			     ts->dev->screen_info.y_tilt_max, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "register pen device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+
+	ts->pen_input_dev = input;
+
+	return 0;
+}
+
+static int ilitek_request_input_dev(void)
+{
+	int error;
+	int i;
+	struct input_dev *input;
+
+#ifdef ILITEK_USE_MTK_INPUT_DEV
+	input = tpd->dev;
+	if (!input)
+		return -ENOMEM;
+#ifdef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
+			input_set_capability(input, EV_KEY,
+					     tpd_dts_data.tpd_key_local[i]);
+	}
+#endif
+#else
+	int x_min = ts->dev->screen_info.x_min;
+	int y_min = ts->dev->screen_info.y_min;
+	int x_max = ts->dev->screen_info.x_max;
+	int y_max = ts->dev->screen_info.y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+#endif
+
+	TP_DBG(NULL, "registering touch input device\n");
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	INPUT_MT_INIT_SLOTS(input, MAX(2, ts->dev->tp_info.max_fingers));
+#else
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0,
+			     ts->dev->tp_info.max_fingers, 0, 0);
+#endif
+
+#ifdef ILITEK_REPORT_PRESSURE
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+#endif
+
+	for (i = 0; i < ts->dev->tp_info.key_num; i++)
+		set_bit(ts->dev->key.info.keys[i].id & KEY_MAX, input->keybit);
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+#ifndef ILITEK_USE_MTK_INPUT_DEV
+	input->name = ILITEK_TS_NAME;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x_min = 0; y_min = 0;
+	x_max = TOUCH_SCREEN_X_MAX; y_max = TOUCH_SCREEN_Y_MAX;
+#endif
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 32767, 0, 0);
+	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 32767, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "input_register_device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+#endif
+
+	ts->input_dev = input;
+
+	if (ts->dev->tp_info.pen_modes)
+		ilitek_request_pen_input_dev();
+
+	return 0;
+}
+
+static int ilitek_touch_down(int id, int x, int y, int p, int h, int w)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x = (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX /
+		(ts->dev->screen_info.x_max - ts->dev->screen_info.x_min);
+	y = (y - ts->dev->screen_info.y_min) * TOUCH_SCREEN_Y_MAX /
+		(ts->dev->screen_info.y_max - ts->dev->screen_info.y_min);
+#endif
+
+	input_report_key(input, BTN_TOUCH, 1);
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_mt_slot(input, id);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+#endif
+	input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+	input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+	input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, h);
+	input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, w);
+#ifdef ILITEK_REPORT_PRESSURE
+	input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+#endif
+#ifndef ILITEK_TOUCH_PROTOCOL_B
+	input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id);
+	input_mt_sync(input);
+#endif
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(x, y, 1);
+			TP_DBG(NULL, "tpd_button(x, y, 1) = tpd_button(%d, %d, 1)\n", x, y);
+		}
+	}
+#endif
+#endif
+#endif
+	return 0;
+}
+
+static int ilitek_touch_release(int id)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	if (ts->touch_flag[id] == 1) {
+		TP_DBG(NULL, "release point id = %d\n", id);
+		input_mt_slot(input, id);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+#else
+	input_report_key(input, BTN_TOUCH, 0);
+	input_mt_sync(input);
+#endif
+	set_arr(ts->touch_flag, id, 0);
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() ||
+		    RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(0, 0, 0);
+			TP_DBG(NULL, "tpd_button(x, y, 0) = tpd_button(0, 0, 0)\n");
+		}
+	}
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int ilitek_touch_release_all_point(void)
+{
+	struct input_dev *input = ts->input_dev;
+	int i = 0;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_report_key(input, BTN_TOUCH, 0);
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		ilitek_touch_release(i);
+#else
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		set_arr(ts->touch_flag, i, 0);
+	ilitek_touch_release(0);
+#endif
+	ts->is_touched = false;
+	input_sync(input);
+	return 0;
+}
+
+static int ilitek_check_key_down(int x, int y)
+{
+	int j;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if ((x >= ts->dev->key.info.keys[j].x &&
+		     x <= ts->dev->key.info.keys[j].x +
+			ts->dev->key.info.x_len) &&
+		    (y >= ts->dev->key.info.keys[j].y &&
+		     y <= ts->dev->key.info.keys[j].y +
+			ts->dev->key.info.y_len)) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 1);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				x = tpd_dts_data.tpd_key_dim_local[j].key_x;
+				y = tpd_dts_data.tpd_key_dim_local[j].key_y;
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key down\n",
+					j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_down(0, x, y, 10, 128, 1);
+			}
+#else
+			x = touch_key_point_maping_array[j].point_x;
+			y = touch_key_point_maping_array[j].point_y;
+			ilitek_touch_down(0, x, y, 10, 128, 1);
+#endif
+#endif
+			ts->dev->key.clicked[j] = true;
+			ts->touch_key_hold_press = true;
+			ts->is_touched = true;
+			TP_DBG(NULL, "Key, Keydown ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int ilitek_check_key_release(int x, int y, int check_point)
+{
+	int j = 0;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if (!ts->dev->key.clicked[j])
+			continue;
+
+		if (check_point) {
+			if (x < ts->dev->key.info.keys[j].x ||
+			    x > ts->dev->key.info.keys[j].x + ts->dev->key.info.x_len ||
+			    y < ts->dev->key.info.keys[j].y ||
+			    y > ts->dev->key.info.keys[j].y + ts->dev->key.info.y_len) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+				input_report_key(ts->input_dev,
+						 ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+				if (tpd_dts_data.use_tpd_button) {
+					TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+					ilitek_touch_release(0);
+				}
+#else
+				ilitek_touch_release(0);
+#endif
+#endif
+				ts->dev->key.clicked[j] = false;
+				ts->touch_key_hold_press = false;
+				TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+					ts->dev->key.info.keys[j].id, x, y,
+					ts->dev->key.clicked[j]);
+				break;
+			}
+		} else {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_release(0);
+			}
+#else
+			ilitek_touch_release(0);
+#endif
+#endif
+			ts->dev->key.clicked[j] = false;
+			ts->touch_key_hold_press = false;
+			TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+int event_spacing;
+static u8 finger_state;
+static int start_x;
+static int start_y;
+static int current_x;
+static int current_y;
+
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+static struct timeval start_event_time;
+#else
+unsigned long start_event_time_jiffies;
+#endif
+
+static int ilitek_get_time_diff(void)
+{
+	int diff_milliseconds = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+	struct timeval time_now;
+
+	do_gettimeofday(&time_now);
+	diff_milliseconds += (time_now.tv_sec - start_event_time.tv_sec) * 1000;
+
+	if (time_now.tv_usec < start_event_time.tv_usec) {
+		diff_milliseconds -= 1000;
+		diff_milliseconds += (1000 * 1000 + time_now.tv_usec - start_event_time.tv_usec) / 1000;
+	} else
+		diff_milliseconds += (time_now.tv_usec - start_event_time.tv_usec) / 1000;
+
+	if (diff_milliseconds < (-10000))
+		diff_milliseconds = 10000;
+	TP_MSG(NULL, "time_now.tv_sec = %d start_event_time.tv_sec = %d time_now.tv_usec = %d start_event_time.tv_usec = %d diff_milliseconds = %d\n",
+			(int)time_now.tv_sec, (int)start_event_time.tv_sec, (int)time_now.tv_usec, (int)start_event_time.tv_usec, diff_milliseconds);
+#else
+	diff_milliseconds = jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_event_time_jiffies);
+	TP_MSG(NULL, "jiffies_to_msecs(jiffies) = %u jiffies_to_msecs(start_event_time_jiffies) = %u diff_milliseconds = %d\n", jiffies_to_msecs(jiffies),
+			jiffies_to_msecs(start_event_time_jiffies), diff_milliseconds);
+#endif
+	return diff_milliseconds;
+}
+
+static u8 ilitek_double_click_touch(int finger_id, int x, int y,
+					 u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_id > 0) {
+		finger_state = 0;
+		goto out;
+	}
+	if (finger_state == 0 || finger_state == 5) {
+
+		finger_state = 1;
+		start_x = x;
+		start_y = y;
+		current_x = 0;
+		current_y = 0;
+		event_spacing = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+		do_gettimeofday(&start_event_time);
+#else
+		start_event_time_jiffies = jiffies;
+#endif
+	} else if (finger_state == 1) {
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 4;
+	} else if (finger_state == 2) {
+		finger_state = 3;
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > (DOUBLE_CLICK_ONE_CLICK_USED_TIME + DOUBLE_CLICK_NO_TOUCH_TIME))
+			finger_state = 0;
+	} else if (finger_state == 3) {
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_TOTAL_USED_TIME) {
+			start_x = current_x;
+			start_y = current_y;
+			finger_state = 4;
+		}
+	}
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+static u8 ilitek_double_click_release(u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_state == 1) {
+		finger_state = 2;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 0;
+	}
+	if (finger_state == 3) {
+		event_spacing = ilitek_get_time_diff();
+		if ((event_spacing < DOUBLE_CLICK_TOTAL_USED_TIME && event_spacing > 50) && (ABSSUB(current_x, start_x) < DOUBLE_CLICK_DISTANCE)
+				&& ((ABSSUB(current_y, start_y) < DOUBLE_CLICK_DISTANCE))) {
+			finger_state = 5;
+			goto out;
+		} else
+			finger_state = 0;
+	} else if (finger_state == 4)
+		finger_state = 0;
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y)
+{
+	struct input_dev *input = ts->input_dev;
+
+	if (ts->gesture_status == Gesture_Double_Click) {
+		if (touch) {
+			finger_state = ilitek_double_click_touch(idx, x, y, finger_state);
+			return;
+		}
+		finger_state = ilitek_double_click_release(finger_state);
+
+		if (finger_state != 5)
+			return;
+	}
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_timeout(&ilitek_wake_lock, 5 * HZ);
+#endif
+
+	/* prevent power key being triggered multiple times */
+	if (ts->power_key_triggered)
+		return;
+
+	input_report_key(input, KEY_POWER, 1);
+	input_sync(input);
+	input_report_key(input, KEY_POWER, 0);
+	input_sync(input);
+
+	ts->power_key_triggered = true;
+}
+
+static void ilitek_report_touch_event(struct touch_data *touch, void *_private)
+{
+	struct input_dev *input = ts->input_dev;
+	u8 i, released_cnt = 0;
+
+	u16 x_max = ts->dev->screen_info.x_max;
+	u16 x_min = ts->dev->screen_info.x_min;
+	u16 y_max = ts->dev->screen_info.y_max;
+	u16 y_min = ts->dev->screen_info.y_min;
+
+	/*
+	 * ISR may be activated after registering irq and
+	 * before creating input_dev
+	 */
+	if (!input) {
+		TP_ERR(NULL, "input_dev is not registerred\n");
+		return;
+	}
+
+	for (i = 0; i < touch->cnt; i++) {
+		if (!touch->finger[i].status) {
+			released_cnt++;
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+			ilitek_touch_release(touch->finger[i].id);
+#endif
+			continue;
+		}
+
+		set_arr(ts->touch_flag, touch->finger[i].id, 1);
+
+		touch->finger[i].x = (ILITEK_REVERT_X) ?
+			x_max - touch->finger[i].x + x_min : touch->finger[i].x;
+		touch->finger[i].y = (ILITEK_REVERT_Y) ?
+			y_max - touch->finger[i].y + y_min : touch->finger[i].y;
+
+#if ILITEK_ROTATE_FLAG
+		swap(touch->finger[i].x, touch->finger[i].y);
+#endif
+
+		if (ts->system_suspend) {
+			TP_MSG(NULL, "system is suspend not report point\n");
+			ilitek_gesture_handle(true, i, touch->finger[i].x,
+					      touch->finger[i].y);
+			continue;
+		}
+
+		if (!ts->is_touched)
+			ilitek_check_key_down(touch->finger[i].x,
+					      touch->finger[i].y);
+
+		if (!ts->touch_key_hold_press) {
+			ts->is_touched = true;
+			ilitek_touch_down(touch->finger[i].id,
+					  touch->finger[i].x,
+					  touch->finger[i].y,
+					  touch->finger[i].pressure,
+					  touch->finger[i].height,
+					  touch->finger[i].width);
+		} else {
+			ilitek_check_key_release(touch->finger[i].x,
+						 touch->finger[i].y, 1);
+		}
+	}
+
+	if (touch->cnt == released_cnt) {
+		if (ts->is_touched)
+			ilitek_touch_release_all_point();
+
+		ilitek_check_key_release(0, 0, 0);
+		ts->is_touched = false;
+
+		if (ts->system_suspend)
+			ilitek_gesture_handle(false, 0, 0, 0);
+	}
+
+	input_sync(input);
+}
+
+static void ilitek_report_pen_event(struct pen_data *pen, void *_private)
+{
+	static int curr_tool = BTN_TOOL_PEN;
+	struct input_dev *pen_input = ts->pen_input_dev;
+	int tool;
+
+	u16 x_max = ts->dev->screen_info.pen_x_max;
+	u16 x_min = ts->dev->screen_info.pen_x_min;
+	u16 y_max = ts->dev->screen_info.pen_y_max;
+	u16 y_min = ts->dev->screen_info.pen_y_min;
+
+	if (!pen_input)
+		return;
+
+	tool = (pen->pen.in_range && pen->pen.invert) ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	if (curr_tool != tool) {
+		input_report_key(pen_input, curr_tool, 0);
+		input_sync(pen_input);
+		curr_tool = tool;
+	}
+
+	pen->pen.x = (ILITEK_REVERT_X) ?
+		x_max - pen->pen.x + x_min : pen->pen.x;
+	pen->pen.y = (ILITEK_REVERT_Y) ?
+		y_max - pen->pen.y + y_min : pen->pen.y;
+
+#if ILITEK_ROTATE_FLAG
+	swap(pen->pen.x, pen->pen.y);
+#endif
+
+	input_report_key(pen_input, BTN_TOUCH,
+			 pen->pen.tip_sw || pen->pen.eraser);
+	input_report_key(pen_input, curr_tool, pen->pen.in_range);
+	input_report_key(pen_input, BTN_STYLUS, pen->pen.barrel_sw);
+	input_event(pen_input, EV_ABS, ABS_X, pen->pen.x);
+	input_event(pen_input, EV_ABS, ABS_Y, pen->pen.y);
+	input_event(pen_input, EV_ABS, ABS_PRESSURE, pen->pen.pressure);
+	input_event(pen_input, EV_ABS, ABS_TILT_X, pen->pen.x_tilt);
+	input_event(pen_input, EV_ABS, ABS_TILT_Y, pen->pen.y_tilt);
+
+	input_sync(pen_input);
+}
+
+
+static void ilitek_report_buf(u8 *buf, int size,
+			      bool is_last, void *_private)
+{
+	UNUSED(is_last);
+
+	ilitek_udp_reply(buf, size);
+}
+
+int ilitek_read_data_and_report(void)
+{
+	int error;
+	u8 i, count;
+	struct ilitek_report report;
+
+	memset(&report, 0, sizeof(report));
+	report.cb.report_touch_event = ilitek_report_touch_event;
+	report.cb.report_pen_event = ilitek_report_pen_event;
+	report.cb.report_buf = ilitek_report_buf;
+
+	switch (ts->irq_handle_type) {
+	case irq_type_c_model:
+		memset(ts->buf, 0, sizeof(ts->buf));
+
+		for (i = 0, count = 1; i < count; i++) {
+			ilitek_read(ts->buf, ts->irq_read_len);
+			ilitek_udp_reply(ts->buf, ts->irq_read_len);
+			count = ts->buf[ts->irq_read_len - 1];
+		}
+
+		break;
+
+	case irq_type_debug:
+	case irq_type_normal:
+	default:
+		error = ilitek_report_update(ts->dev, &report);
+		if (error < 0) {
+			if (error == -EILIPROTO && !checksum_failed_release)
+				break;
+
+			if (ts->is_touched) {
+				ilitek_touch_release_all_point();
+				ilitek_check_key_release(0, 0, 0);
+			}
+
+			return error;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+static ISR_FUNC(ilitek_i2c_isr)
+{
+	int error;
+
+	TP_DBG(NULL, "%s\n", __func__);
+
+	atomic_set(&ts->get_INT, 1);
+	ilitek_gpio_dbg();
+
+	ts->esd_skip = true;
+
+	if (atomic_read(&ts->firmware_updating)) {
+		TP_DBG(NULL, "firmware_updating return\n");
+		goto exit;
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_disable();
+#endif
+
+	if (!ts->unhandle_irq) {
+		mutex_lock(&ts->ilitek_mutex);
+		error = ilitek_read_data_and_report();
+		if (error < 0)
+			TP_ERR(NULL, "process error\n");
+		mutex_unlock(&ts->ilitek_mutex);
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_enable();
+#endif
+
+exit:
+	ts->esd_skip = false;
+
+	ISR_RETURN(IRQ_HANDLED);
+}
+
+static int ilitek_request_irq(void)
+{
+	int error;
+
+#ifdef MTK_UNDTS
+	mt_set_gpio_mode(ILITEK_IRQ_GPIO, GPIO_CTP_EINT_PIN_M_EINT);
+	mt_set_gpio_dir(ILITEK_IRQ_GPIO, GPIO_DIR_IN);
+	mt_set_gpio_pull_enable(ILITEK_IRQ_GPIO, GPIO_PULL_ENABLE);
+	mt_set_gpio_pull_select(ILITEK_IRQ_GPIO, GPIO_PULL_UP);
+
+	mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+	mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, ilitek_i2c_isr, 1);
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	struct device_node *node;
+
+	node = of_find_matching_node(NULL, touch_of_match);
+	if (node)
+		ts->irq = irq_of_parse_and_map(node, 0);
+#else
+	ts->irq = gpio_to_irq(ts->irq_gpio);
+#endif
+
+	TP_MSG(NULL, "ts->irq: %d\n", ts->irq);
+	if (ts->irq <= 0)
+		return -EINVAL;
+
+	error = request_threaded_irq(ts->irq, NULL, ilitek_i2c_isr,
+				     ts->irq_trigger_type | IRQF_ONESHOT,
+				     "ilitek_touch_irq", ts);
+	if (error) {
+		TP_ERR(NULL, "request threaded irq failed, err: %d\n", error);
+		return error;
+	}
+#endif
+
+	ts->irq_registered = true;
+	atomic_set(&ts->irq_enabled, 1);
+
+	return 0;
+}
+
+static int ilitek_read_fw(char *filename, unsigned char *buf, int size, void *data)
+{
+	int error, fw_size;
+	const struct firmware *fw;
+	struct device *device = (struct device *)data;
+
+	error = request_firmware(&fw, filename, device);
+	if (error) {
+		TP_ERR(NULL, "request fw: %s failed, err:%d\n", filename, error);
+		return error;
+	}
+
+	if (size < fw->size) {
+		fw_size = -EFBIG;
+		goto release_fw;
+	}
+
+	fw_size = fw->size;
+	memcpy(buf, fw->data, fw->size);
+
+release_fw:
+	release_firmware(fw);
+
+	return fw_size;
+}
+
+struct ilitek_update_callback update_cb = {
+	.read_fw = ilitek_read_fw,
+	.update_progress = NULL,
+	.update_fw_file_info = NULL,
+
+	.slave_update_notify = NULL,
+	.update_fw_ic_info = NULL,
+};
+
+int ilitek_upgrade_firmware(char *filename)
+{
+	int error;
+	struct ilitek_fw_handle *handle;
+	struct ilitek_fw_settings setting;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	atomic_set(&ts->firmware_updating, 1);
+	ts->operation_protection = true;
+
+	handle = ilitek_update_init(ts->dev, false, &update_cb, ts->device);
+
+	setting.force_update = false;
+	setting.fw_check_only = false;
+	setting.fw_ver_check = false;
+	setting.retry = 3;
+	ilitek_update_setting(handle, &setting);
+
+	error = ilitek_update_load_fw(handle, filename);
+	if (error < 0)
+		goto err_return;
+	error = ilitek_update_start(handle);
+	if (error < 0)
+		goto err_return;
+
+err_return:
+	ilitek_update_exit(handle);
+
+	ts->operation_protection = false;
+	atomic_set(&ts->firmware_updating, 0);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return error;
+}
+
+
+static int __maybe_unused ilitek_update_thread(void *arg)
+{
+#ifdef ILITEK_BOOT_UPDATE
+	int error;
+
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (kthread_should_stop()) {
+		TP_MSG(NULL, "ilitek_update_thread, stop\n");
+		return -1;
+	}
+
+	mdelay(100);
+
+	error = ilitek_upgrade_firmware("ilitek.ili");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.hex");
+		if (error < 0) {
+			error = ilitek_upgrade_firmware("ilitek.bin");
+			if (error < 0)
+				return error;
+		}
+	}
+
+	error = ilitek_request_input_dev();
+	if (error)
+		return (error < 0) ? error : -EFAULT;
+#endif
+
+	return 0;
+}
+
+void ilitek_suspend(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	ts->esd_skip = true;
+	if (ts->esd_check && ts->esd_workq)
+		cancel_delayed_work_sync(&ts->esd_work);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ts->wake_irq_enabled = (enable_irq_wake(ts->irq) == 0);
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_set_idle(ts->dev, true) < 0)
+				TP_ERR(NULL, "enable Idle mode failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	} else {
+		/*
+		 * Must disable irq before sleep cmd,
+		 * Avoid getting into ISR handling (and do i2c read),
+		 * after sending sleep cmd.
+		 */
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_protocol_set_cmd(ts->dev, SET_IC_SLEEP,
+						 NULL) < 0)
+				TP_ERR(NULL, "set tp sleep failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->power_key_triggered = false;
+	ts->system_suspend = true;
+}
+
+void ilitek_resume(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_set_idle(ts->dev, false);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+
+		if (ts->gesture_status == Gesture_Double_Click)
+			finger_state = 0;
+
+		if (ts->wake_irq_enabled) {
+			disable_irq_wake(ts->irq);
+			ts->wake_irq_enabled = false;
+		}
+	} else {
+		if (ts->dev->protocol.flag == PTL_V3) {
+			/*
+			 * If ILITEK_SLEEP is defined and FW support wakeup cmd,
+			 * the hw reset can be mark.
+			 */
+			ilitek_reset(ts->dev->reset_time);
+		}
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_protocol_set_cmd(ts->dev, SET_IC_WAKE, NULL);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->esd_skip = false;
+	if (ts->esd_check && ts->esd_workq)
+		queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	ilitek_touch_release_all_point();
+	ilitek_check_key_release(0, 0, 0);
+
+	ts->system_suspend = false;
+
+	ilitek_irq_enable();
+}
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+int ilitek_resume_allwin(struct i2c_client *client)
+{
+	ilitek_resume();
+	return 0;
+}
+#endif
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+static int __maybe_unused ilitek_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+#ifdef CONFIG_QCOM_DRM
+	struct msm_drm_notifier *ev_data = data;
+#else
+	struct fb_event *ev_data = data;
+#endif
+	int *blank;
+
+	TP_MSG(NULL, "FB EVENT event: %lu\n", event);
+
+#ifdef CONFIG_QCOM_DRM
+	if (!ev_data || (ev_data->id != 0))
+		return 0;
+#endif
+	if (ev_data && ev_data->data && event == ILITEK_EVENT_BLANK) {
+		blank = ev_data->data;
+		TP_MSG(NULL, "blank: %d\n", *blank);
+		if (*blank == ILITEK_BLANK_POWERDOWN) {
+			ilitek_suspend();
+		} else if (*blank == ILITEK_BLANK_UNBLANK ||
+			   *blank == ILITEK_BLANK_NORMAL) {
+			ilitek_resume();
+		}
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void __maybe_unused ilitek_early_suspend(struct early_suspend *h)
+{
+	ilitek_suspend();
+}
+
+static void __maybe_unused ilitek_late_resume(struct early_suspend *h)
+{
+	ilitek_resume();
+}
+#endif
+#endif
+
+static void ilitek_get_gpio_num(void)
+{
+#ifdef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	TP_MSG(NULL, "(config_info.wakeup_gpio.gpio) = %d (config_info.int_number) = %d\n", (config_info.wakeup_gpio.gpio), (config_info.int_number));
+	ts->reset_gpio = (config_info.wakeup_gpio.gpio);
+	ts->irq_gpio = (config_info.int_number);
+#else
+#ifdef CONFIG_OF
+	ts->reset_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,reset-gpio", 0);
+	if (ts->reset_gpio < 0)
+		TP_ERR(NULL, "reset_gpio = %d\n", ts->reset_gpio);
+	ts->irq_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,irq-gpio", 0);
+	if (ts->irq_gpio < 0)
+		TP_ERR(NULL, "irq_gpio = %d\n", ts->irq_gpio);
+#endif
+#endif
+#else
+	ts->reset_gpio = ILITEK_RESET_GPIO;
+	ts->irq_gpio = ILITEK_IRQ_GPIO;
+#endif
+
+	TP_MSG(NULL, "reset_gpio = %d irq_gpio = %d\n", ts->reset_gpio, ts->irq_gpio);
+
+
+#if defined(ILITEK_GPIO_DEBUG)
+	do {
+		ts->test_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,test-gpio", 0);
+		if (ts->test_gpio < 0) {
+			TP_ERR(NULL, "test_gpio: %d\n", ts->test_gpio);
+			break;
+		}
+
+		TP_MSG(NULL, "test_gpio: %d\n", ts->test_gpio);
+
+		if (gpio_request(ts->test_gpio, "ilitek-test-gpio")) {
+			TP_ERR(NULL, "request test_gpio failed\n");
+			break;
+		}
+
+		gpio_direction_output(ts->test_gpio, 1);
+
+	} while (0);
+#endif
+}
+
+static int ilitek_request_gpio(void)
+{
+	int ret = 0;
+
+	ts->irq_gpio = -ENODEV;
+	ts->reset_gpio = -ENODEV;
+
+	ilitek_get_gpio_num();
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+	if (ts->reset_gpio > 0) {
+		ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio so free retry\n");
+			gpio_free(ts->reset_gpio);
+			ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request reset_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio\n");
+		} else {
+			ret = gpio_direction_output(ts->reset_gpio, 1);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction output rest gpio err\n");
+		}
+	}
+	if (ts->irq_gpio > 0) {
+		ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio so free retry\n");
+			gpio_free(ts->irq_gpio);
+			ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request irq_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio\n");
+		} else {
+			ret = gpio_direction_input(ts->irq_gpio);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction input irq gpio err\n");
+		}
+	}
+#endif
+	return ret;
+}
+
+int ilitek_create_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to create esd workqueue\n");
+
+	INIT_DELAYED_WORK(&ts->esd_work, ilitek_esd_check);
+	ts->esd_workq = create_singlethread_workqueue("ilitek_esd_wq");
+	if (!ts->esd_workq)
+		return -ENOMEM;
+
+	ts->esd_skip = false;
+	ts->esd_delay = 2 * HZ;
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	return 0;
+}
+
+void ilitek_remove_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to remove esd workqueue\n");
+
+	if (ts->esd_workq) {
+		cancel_delayed_work_sync(&ts->esd_work);
+		destroy_workqueue(ts->esd_workq);
+		ts->esd_workq = NULL;
+	}
+}
+
+static int ilitek_register_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+	int error;
+
+	ts->fb_notif.notifier_call = ilitek_notifier_callback;
+
+#ifdef CONFIG_QCOM_DRM
+	error = msm_drm_register_client(&ts->fb_notif);
+#else
+	error = fb_register_client(&ts->fb_notif);
+#endif
+	if (error)
+		TP_ERR(NULL, "register fb_notifier failed, err: %d\n", error);
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	ts->early_suspend.suspend = ilitek_early_suspend;
+	ts->early_suspend.resume = ilitek_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+#endif /* ILITEK_PLAT != ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	device_enable_async_suspend(ts->device);
+	pm_runtime_set_active(ts->device);
+	pm_runtime_get(ts->device);
+	pm_runtime_enable(ts->device);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+#ifdef CONFIG_QCOM_DRM
+	msm_drm_unregister_client(&ts->fb_notif);
+#else
+	fb_unregister_client(&ts->fb_notif);
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+}
+
+int ilitek_netlink_init(u8 unit)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	NETLINK_KERNEL_CFG_DECLARE(cfg, udp_receive);
+
+	if (ilitek_netlink_sock)
+		ilitek_netlink_exit();
+
+	ilitek_netlink_sock = NETLINK_KERNEL_CREATE(unit, &cfg, udp_receive);
+
+	if (!ilitek_netlink_sock) {
+		TP_ERR(NULL, "netlink_kernel_create failed\n");
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+#endif
+	return -EPERM;
+}
+
+void ilitek_netlink_exit(void)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	if (ilitek_netlink_sock)
+		netlink_kernel_release(ilitek_netlink_sock);
+
+	ilitek_netlink_sock = NULL;
+#endif
+}
+
+static int __maybe_unused ilitek_alloc_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	I2CDMABuf_va = (u8 *) dma_alloc_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+	if (!I2CDMABuf_va) {
+		TP_ERR(NULL, "ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n");
+		I2CDMABuf_va = (u8 *) dma_alloc_coherent(NULL, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+		if (!I2CDMABuf_va) {
+			TP_ERR(NULL, "ilitek [TPD] NULL dma_alloc_coherent error\n");
+			return -ENOMEM;
+		}
+	}
+	memset(I2CDMABuf_va, 0, ILITEK_DMA_SIZE);
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_free_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	if (I2CDMABuf_va) {
+		dma_free_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE,
+				  I2CDMABuf_va, I2CDMABuf_pa);
+
+		I2CDMABuf_va = NULL;
+		I2CDMABuf_pa = 0;
+
+	}
+#endif
+	return 0;
+}
+
+static int __maybe_unused ilitek_power_on(bool status)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int error;
+
+	TP_MSG(NULL, "%s\n", status ? "POWER ON" : "POWER OFF");
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	input_set_power_enable(&(config_info.input_type), status);
+#else
+
+	if (status) {
+		if (ts->vdd) {
+			error = regulator_enable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_enable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	} else {
+		if (ts->vdd) {
+			error = regulator_disable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_disable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	}
+
+#ifdef MTK_UNDTS
+	if (status)
+		hwPowerOn(PMIC_APP_CAP_TOUCH_VDD, VOL_3300, "TP");
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_request_regulator(struct ilitek_ts_data *ts)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int ret = 0;
+	char *vdd_name = "vdd";
+	char *vcc_i2c_name = "vcc_i2c";
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	vdd_name = "vtouch";
+	ts->vdd = regulator_get(tpd->tpd_dev, vdd_name);
+	tpd->reg = ts->vdd;
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+	}
+#elif ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	ts->vdd = regulator_get(ts->device, vdd_name);
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+
+	}
+
+	ts->vdd_i2c = regulator_get(ts->device, vcc_i2c_name);
+	if (IS_ERR(ts->vdd_i2c)) {
+		TP_ERR(NULL, "regulator_get vdd_i2c fail\n");
+		ts->vdd_i2c = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd_i2c, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set i2c to 3000~3300mv.\n");
+	}
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+#endif /* ILITEK_ENABLE_REGULATOR_POWER_ON */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_regulator(void)
+{
+#if defined(ILITEK_ENABLE_REGULATOR_POWER_ON) && ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	if (ts->vdd)
+		regulator_put(ts->vdd);
+	if (ts->vdd_i2c)
+		regulator_put(ts->vdd_i2c);
+#endif
+}
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init)
+{
+	if (init) {
+		device_init_wakeup(ts->device, 1);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+		wake_lock_init(&ilitek_wake_lock, WAKE_LOCK_SUSPEND, "ilitek wakelock");
+#endif
+		return;
+	}
+
+	device_init_wakeup(ts->device, 0);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_destroy(&ilitek_wake_lock);
+#endif
+}
+
+static int _ilitek_write_then_read(unsigned char *wbuf, int wlen,
+				   unsigned char *rbuf, int rlen, void *data)
+{
+	return ilitek_write_and_read(wbuf, wlen, 1, rbuf, rlen);
+}
+
+static int ilitek_read_interrupt_in(unsigned char *rbuf, int rlen,
+				    unsigned int timeout_ms, void *data)
+{
+	return ilitek_write_and_read(NULL, 0, 0, rbuf, rlen);
+}
+
+static void _ilitek_init_ack(unsigned int tout_ms, void *data)
+{
+	UNUSED(tout_ms);
+
+	ilitek_irq_enable();
+	ts->unhandle_irq = true;
+	atomic_set(&ts->get_INT, 0);
+}
+
+static int _ilitek_wait_ack(u8 cmd, unsigned int tout_ms, void *data)
+{
+	unsigned int t_ms = 0;
+	int tmp, error = -ETIME;
+
+	UNUSED(cmd);
+
+	do {
+		tmp = atomic_read(&ts->get_INT);
+		if (tmp) {
+			error = 0;
+			break;
+		}
+
+		udelay(1000);
+		t_ms++;
+	} while (t_ms < tout_ms);
+
+	ts->unhandle_irq = false;
+	ilitek_irq_disable();
+
+	return error;
+}
+
+static void _ilitek_delay(unsigned int delay_ms)
+{
+	mdelay(delay_ms);
+}
+
+static int _ilitek_reset(unsigned int delay_ms, void *data)
+{
+	/* return error if no reset gpio found */
+	if (ts->reset_gpio < 0)
+		return -ENODEV;
+
+	ilitek_reset(delay_ms);
+	return 0;
+}
+
+struct ilitek_ts_callback dev_cb = {
+	.write_then_read = _ilitek_write_then_read,
+	.read_interrupt_in = ilitek_read_interrupt_in,
+	.init_ack = _ilitek_init_ack,
+	.wait_ack = _ilitek_wait_ack,
+	.hw_reset = _ilitek_reset,
+	.re_enum = NULL,
+	.delay_ms = _ilitek_delay,
+	.msg = NULL,
+
+	.write_then_read_direct = NULL,
+	.mode_switch_notify = NULL,
+};
+
+int ilitek_main_probe(void *client, struct device *device)
+{
+	struct ilitek_ts_settings setting;
+
+	TP_MSG(NULL, "driver version: %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu\n",
+		driver_ver[0], driver_ver[1], driver_ver[2], driver_ver[3],
+		driver_ver[4], driver_ver[5], driver_ver[6]);
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts) {
+		TP_ERR(NULL, "allocate ts failed\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	ts->device = device;
+
+	mutex_init(&ts->ilitek_mutex);
+	ts->unhandle_irq = false;
+
+	ilitek_alloc_dma();
+	ilitek_request_regulator(ts);
+	ilitek_power_on(true);
+	ilitek_request_gpio();
+
+	ilitek_reset(600);
+
+	ts->dev = ilitek_dev_init(interface_i2c, "0", false, &dev_cb, ts);
+	if (!ts->dev)
+		goto err_free_gpio;
+
+	memset(&setting, 0, sizeof(setting));
+	setting.sensor_id_mask = ILITEK_SENSOR_ID_MASK;
+	ilitek_dev_setting(ts->dev, &setting);
+
+	if (api_update_ts_info(ts->dev) < 0)
+		goto err_dev_exit;
+
+	ts->irq_trigger_type = (ts->dev->protocol.flag == PTL_V6) ?
+		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+	if (ilitek_request_irq())
+		goto err_dev_exit;
+
+#ifdef ILITEK_BOOT_UPDATE
+	ts->update_thread = kthread_run(ilitek_update_thread, NULL,
+					"ilitek_update_thread");
+	if (IS_ERR(ts->update_thread))
+		goto err_free_irq;
+#else
+	if (ilitek_request_input_dev())
+		goto err_free_irq;
+#endif
+
+	ilitek_register_resume_suspend();
+	ilitek_create_sysfsnode();
+	ilitek_create_tool_node();
+	ilitek_netlink_init(NETLINK_USERSOCK);
+
+	ts->esd_check = ILITEK_ESD_CHECK_ENABLE;
+	if (ts->esd_check)
+		ilitek_create_esd_check_workqueue();
+
+	ts->gesture_status = ILITEK_GESTURE_DEFAULT;
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, true);
+
+	ts->low_power_status = ILITEK_LOW_POWER_DEFAULT;
+
+	return 0;
+
+err_free_irq:
+	free_irq(ts->irq, ts);
+
+err_dev_exit:
+	ilitek_dev_exit(ts->dev);
+
+err_free_gpio:
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+	kfree(ts);
+
+	return -ENODEV;
+}
+
+int ilitek_main_remove(void *client)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (!ts)
+		return 0;
+
+	ilitek_netlink_exit();
+
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, false);
+
+	ilitek_remove_esd_check_workqueue();
+	ilitek_remove_tool_node();
+	ilitek_remove_sys_node();
+	ilitek_release_resume_suspend();
+
+	if (ts->pen_input_dev)
+		input_unregister_device(ts->pen_input_dev);
+
+	if (ts->input_dev)
+		input_unregister_device(ts->input_dev);
+
+#ifndef MTK_UNDTS
+	free_irq(ts->irq, ts);
+#endif
+
+	ilitek_dev_exit(ts->dev);
+
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+
+	kfree(ts);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_platform_init.c b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
new file mode 100644
index 000000000000..0b7f97ab7746
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+
+#define TPD_OK (0)
+
+#ifdef MTK_UNDTS
+static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
+static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
+struct touch_vitual_key_map_t touch_key_point_maping_array[] = { {key_1}, {key_2}, {key_3}, {key_4} };
+
+static struct i2c_board_info __initdata ilitek_i2c_tpd = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41)
+};
+#endif
+
+/* probe function is used for matching and initializing input device */
+static int /*__devinit*/ tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	TP_MSG(NULL, "TPD probe\n");
+
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -1;
+	}
+
+	ret = ilitek_main_probe(client, &client->dev);
+	if (ret == 0)	// If probe is success, then enable the below flag.
+		tpd_load_status = 1;
+
+	TP_MSG(NULL, "TPD probe done\n");
+	return TPD_OK;
+}
+
+static int tpd_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	strcpy(info->type, TPD_DEVICE);
+	return TPD_OK;
+}
+
+static int /*__devexit*/ tpd_remove(struct i2c_client *client)
+{
+	TP_MSG(NULL, "TPD removed\n");
+	return ilitek_main_remove(client);
+}
+
+/* The I2C device list is used for matching I2C device and I2C device driver. */
+static const struct i2c_device_id tpd_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+
+MODULE_DEVICE_TABLE(i2c, tpd_device_id);
+
+const struct of_device_id touch_dt_match_table[] = {
+	{ .compatible = "mediatek,cap_touch",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, touch_dt_match_table);
+
+static struct i2c_driver tpd_i2c_driver = {
+
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.of_match_table = of_match_ptr(touch_dt_match_table),
+	},
+	.probe = tpd_probe,
+	.remove = tpd_remove,
+	.id_table = tpd_device_id,
+	.detect = tpd_detect,
+};
+
+static int tpd_local_init(void)
+{
+	TP_MSG(NULL, "TPD init device driver\n");
+
+	if (i2c_add_driver(&tpd_i2c_driver) != 0) {
+		TP_ERR(NULL, "Unable to add i2c driver.\n");
+		return -1;
+	}
+	if (tpd_load_status == 0) {
+		TP_ERR(NULL, "Add error touch panel driver.\n");
+		i2c_del_driver(&tpd_i2c_driver);
+		return -1;
+	}
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button)
+		tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, tpd_dts_data.tpd_key_dim_local);
+
+#else
+	tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);	// initialize tpd button data
+#endif
+	tpd_type_cap = 1;
+	TP_MSG(NULL, "TPD init done\n");
+	return TPD_OK;
+}
+
+#ifdef MTK_UNDTS
+static void tpd_resume(struct early_suspend *h)
+{
+#else
+static void tpd_resume(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD wake up\n");
+	ilitek_resume();
+	TP_MSG(NULL, "TPD wake up done\n");
+}
+
+#ifdef MTK_UNDTS
+static void tpd_suspend(struct early_suspend *h)
+{
+#else
+static void tpd_suspend(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD enter sleep\n");
+	ilitek_suspend();
+	TP_MSG(NULL, "TPD enter sleep done\n");
+}
+
+static struct tpd_driver_t tpd_device_driver = {
+	.tpd_device_name = ILITEK_TS_NAME,
+	.tpd_local_init = tpd_local_init,
+	.suspend = tpd_suspend,
+	.resume = tpd_resume,
+	.tpd_have_button = 1,
+};
+
+static int __init ilitek_touch_driver_init(void)
+{
+	TP_MSG(NULL, "touch panel driver init\n");
+
+#ifdef MTK_UNDTS
+	i2c_register_board_info(2, &ilitek_i2c_tpd, 1);
+#else
+	tpd_get_dts_info();
+#endif
+	if (tpd_driver_add(&tpd_device_driver) < 0)
+		TP_ERR(NULL, "TPD add TP driver failed\n");
+
+	return 0;
+
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "touch panel driver exit\n");
+	tpd_driver_remove(&tpd_device_driver);
+}
+
+#else
+
+#ifdef ILITEK_SPI_INTERFACE
+static int ilitek_touch_driver_probe(struct spi_device *spi)
+{
+	int error;
+
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		TP_ERR(NULL, "SPI setup failed, err: %d\n", error);
+		return error;
+	}
+
+	TP_MSG(NULL, "SPI start probe, max_speed_hz: %d\n", spi->max_speed_hz);
+
+	return ilitek_main_probe(spi, &spi->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct spi_device *spi)
+{
+	REMOVE_RETURN(ilitek_main_remove(spi));
+}
+#else
+static I2C_PROBE_FUNC(ilitek_touch_driver_probe, struct i2c_client *client)
+{
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -ENODEV;
+	}
+
+	TP_MSG(NULL, "ILITEK client->addr: 0x%x, client->irq: %d\n",
+		client->addr, client->irq);
+
+	return ilitek_main_probe(client, &client->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct i2c_client *client)
+{
+	REMOVE_RETURN(ilitek_main_remove(client));
+}
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id ilitek_touch_match_table[] = {
+	{.compatible = "tchip,ilitek",},
+	{},
+};
+#endif
+
+static const struct i2c_device_id ilitek_touch_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+MODULE_DEVICE_TABLE(i2c, ilitek_touch_device_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ilitekts_acpi_id[] = {
+	{"ILIT2901", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
+#endif
+
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+static int __maybe_unused ilitek_ts_suspend(struct device *dev)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+static int __maybe_unused ilitek_ts_resume(struct device *dev)
+{
+	ilitek_resume();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_ts_suspend, ilitek_ts_resume);
+#endif
+
+#ifdef ILITEK_SPI_INTERFACE
+static struct spi_driver ilitek_touch_device_driver = {
+#else
+static struct i2c_driver ilitek_touch_device_driver = {
+#endif
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = ilitek_touch_match_table,
+#endif
+#ifdef CONFIG_ACPI
+		.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
+#endif
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+		.pm = &ilitek_pm_ops,
+#endif
+	},
+
+	.probe = ilitek_touch_driver_probe,
+	.remove = ilitek_touch_driver_remove,
+
+#if !defined(ILITEK_SPI_INTERFACE)
+	.id_table = ilitek_touch_device_id,
+#endif
+};
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+static const unsigned short normal_i2c[2] = { 0x41, I2C_CLIENT_END };
+struct ctp_config_info config_info = {
+	.input_type = CTP_TYPE,
+	.name = NULL,
+	.int_number = 0,
+};
+
+static int twi_id;
+static int screen_max_x;
+static int screen_max_y;
+static int revert_x_flag;
+static int revert_y_flag;
+static int exchange_x_y_flag;
+static int ctp_get_system_config(void)
+{
+	twi_id = config_info.twi_id;
+	screen_max_x = config_info.screen_max_x;
+	screen_max_y = config_info.screen_max_y;
+	TP_MSG(NULL, "Ilitek: screen_max_x = %d\n", screen_max_x);
+	revert_x_flag = config_info.revert_x_flag;
+	revert_y_flag = config_info.revert_y_flag;
+	exchange_x_y_flag = config_info.exchange_x_y_flag;
+	if ((screen_max_x == 0) || (screen_max_y == 0)) {
+		TP_ERR(NULL, "%s:read config error!\n", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+int ctp_ts_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+
+	if (twi_id == adapter->nr) {
+		strlcpy(info->type, ILITEK_TS_NAME, I2C_NAME_SIZE);
+		return 0;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static struct i2c_board_info i2c_info_dev = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41),
+	.platform_data = NULL,
+};
+
+static int add_ctp_device(void)
+{
+	struct i2c_adapter *adap;
+	//script_parser_fetch("ctp_para", "ctp_twi_id", &twi_id, 1);
+	adap = i2c_get_adapter(twi_id);
+	i2c_new_device(adap, &i2c_info_dev);
+	return 0;
+}
+
+static int ilitek_init_allwin(void)
+{
+
+	int ret = 0;
+
+	if (input_fetch_sysconfig_para(&(config_info.input_type))) {
+		TP_ERR(NULL, "Ilitek:  ctp_fetch_sysconfig_para err.\n");
+		return -1;
+	} else {
+		ret = input_init_platform_resource(&(config_info.input_type));
+		if (0 != ret)
+			TP_ERR(NULL, "Ilitek: ctp_ops.init_platform_resource err.\n");
+	}
+
+	if (config_info.ctp_used == 0) {
+		TP_ERR(NULL, "Ilitek: *** if use ctp,please put the sys_config.fex ctp_used set to 1.\n");
+		return -1;
+	}
+
+	if (ctp_get_system_config() < 0)
+		TP_ERR(NULL, "Ilitek: %s:read config fail!\n", __func__);
+
+	add_ctp_device();
+	ilitek_touch_device_driver.address_list = normal_i2c;
+	ilitek_touch_device_driver.detect = ctp_ts_detect;
+	return 0;
+
+}
+
+#endif
+static int __init ilitek_touch_driver_init(void)
+{
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	if (ilitek_init_allwin() < 0) {
+		TP_ERR(NULL, "ilitek_init_allwin failed.\n");
+		return -ENODEV;
+	}
+#endif
+
+	TP_MSG(NULL, "add ILITEK touch device driver\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	return spi_register_driver(&ilitek_touch_device_driver);
+#else
+	return i2c_add_driver(&ilitek_touch_device_driver);
+#endif
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "remove touch device driver i2c driver.\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	spi_unregister_driver(&ilitek_touch_device_driver);
+#else
+	i2c_del_driver(&ilitek_touch_device_driver);
+#endif
+}
+#endif
+
+module_init(ilitek_touch_driver_init);
+module_exit(ilitek_touch_driver_exit);
+MODULE_AUTHOR("ILITEK");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.c b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
new file mode 100644
index 000000000000..945bab688405
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
@@ -0,0 +1,3644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_protocol.h"
+
+typedef int (*protocol_func_t)(struct ilitek_ts_device *, void *);
+
+struct protocol_map {
+	u8 cmd;
+	u8 flag;
+	protocol_func_t func;
+	const char *desc;
+};
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} touch_fmts[touch_fmt_max];
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} pen_fmts[pen_fmt_max];
+
+#define X(_cmd, _protocol, _cmd_id, _api) \
+	static int _api(struct ilitek_ts_device *, void *);
+ILITEK_CMD_MAP
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api) {_cmd, _protocol, _api, #_cmd_id},
+struct protocol_map protocol_maps[] = { ILITEK_CMD_MAP };
+#undef X
+
+u16 le16(const u8 *p)
+{
+	return p[0] | p[1] << 8;
+}
+
+u16 be16(const u8 *p)
+{
+	return p[1] | p[0] << 8;
+}
+
+u32 le32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val += (p[bytes] << (8 * bytes));
+
+	return val;
+}
+
+u32 be32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val = (val << 8) | (*p++);
+
+	return val;
+}
+
+static bool is_2501x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "25011") ||
+	    !strcmp(dev->mcu_info.ic_name, "25012"))
+		return true;
+
+	return false;
+}
+
+bool is_29xx(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "2900") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901") ||
+	    !strcmp(dev->mcu_info.ic_name, "2910") ||
+	    !strcmp(dev->mcu_info.ic_name, "2911") ||
+	    !strcmp(dev->mcu_info.ic_name, "2531") ||
+	    !strcmp(dev->mcu_info.ic_name, "2532") ||
+	    !strcmp(dev->mcu_info.ic_name, "2921") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901M") ||
+	    is_2501x(handle))
+		return true;
+
+	return false;
+}
+
+bool _is_231x(char *ic_name)
+{
+	if (!strcmp(ic_name, "2312") || !strcmp(ic_name, "2315"))
+		return true;
+
+	return false;
+}
+
+bool is_231x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	return _is_231x(dev->mcu_info.ic_name);
+}
+
+bool has_hw_key(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle || !dev->tp_info.key_num)
+		return false;
+
+	if (dev->key.info.mode == key_hw ||
+	    dev->key.info.mode == key_hsw)
+		return true;
+
+	return false;
+}
+
+u8 get_protocol_ver_flag(u32 ver)
+{
+	if (((ver >> 16) & 0xFF) == 0x3 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_6 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_7)
+		return PTL_V3;
+
+	if (((ver >> 16) & 0xFF) == 0x6 ||
+	    (ver & 0xFFFF00) == BL_PROTOCOL_V1_8)
+		return PTL_V6;
+
+	return PTL_ANY;
+}
+
+void grid_reset(struct grids *grid)
+{
+	grid->mc.need_update = false;
+	grid->sc_x.need_update = false;
+	grid->sc_y.need_update = false;
+	grid->pen_x.need_update = false;
+	grid->pen_y.need_update = false;
+
+	grid->key_mc.need_update = false;
+	grid->key_x.need_update = false;
+	grid->key_y.need_update = false;
+
+	grid->self.need_update = false;
+
+	if (grid->mc.data)
+		_memset(grid->mc.data, 0,
+		       grid->mc.X * grid->mc.Y * sizeof(s32));
+	if (grid->sc_x.data)
+		_memset(grid->sc_x.data, 0,
+		grid->sc_x.X * grid->sc_x.Y * sizeof(s32));
+	if (grid->sc_y.data)
+		_memset(grid->sc_y.data, 0,
+		grid->sc_y.X * grid->sc_y.Y * sizeof(s32));
+	if (grid->pen_x.data)
+		_memset(grid->pen_x.data, 0,
+		grid->pen_x.X * grid->pen_x.Y * sizeof(s32));
+	if (grid->pen_y.data)
+		_memset(grid->pen_y.data, 0,
+		grid->pen_y.X * grid->pen_y.Y * sizeof(s32));
+
+	if (grid->key_mc.data)
+		_memset(grid->key_mc.data, 0,
+		grid->key_mc.X * grid->key_mc.Y * sizeof(s32));
+	if (grid->key_x.data)
+		_memset(grid->key_x.data, 0,
+		grid->key_x.X * grid->key_x.Y * sizeof(s32));
+	if (grid->key_y.data)
+		_memset(grid->key_y.data, 0,
+		grid->key_y.X * grid->key_y.Y * sizeof(s32));
+
+	if (grid->self.data)
+		_memset(grid->self.data, 0,
+		grid->self.X * grid->self.Y * sizeof(s32));
+
+	grid->dmsg.pen_need_update = false;
+	grid->dmsg.touch_need_update = false;
+	_memset(grid->dmsg.touch, 0, sizeof(grid->dmsg.touch));
+	_memset(grid->dmsg.pen, 0, sizeof(grid->dmsg.pen));
+}
+
+void grid_free(struct grids *grid)
+{
+	if (grid->mc.data)
+		CFREE(grid->mc.data);
+	if (grid->sc_x.data)
+		CFREE(grid->sc_x.data);
+	if (grid->sc_y.data)
+		CFREE(grid->sc_y.data);
+	if (grid->pen_x.data)
+		CFREE(grid->pen_x.data);
+	if (grid->pen_y.data)
+		CFREE(grid->pen_y.data);
+
+	if (grid->key_mc.data)
+		CFREE(grid->key_mc.data);
+	if (grid->key_x.data)
+		CFREE(grid->key_x.data);
+	if (grid->key_y.data)
+		CFREE(grid->key_y.data);
+
+	if (grid->self.data)
+		CFREE(grid->self.data);
+}
+
+int grid_alloc(void *handle, struct grids *grid)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int X, Y, key;
+
+	_memset(grid, 0, sizeof(*grid));
+
+	if (!dev)
+		return -EINVAL;
+
+	X = dev->tp_info.x_ch;
+	Y = dev->tp_info.y_ch;
+	key = dev->tp_info.key_num;
+
+	grid->mc.data = (s32 *)CALLOC(X * Y, sizeof(s32));
+	grid->sc_x.data = (s32 *)CALLOC(X, sizeof(s32));
+	grid->sc_y.data = (s32 *)CALLOC(Y, sizeof(s32));
+	grid->pen_x.data = (s32 *)CALLOC(X * 8, sizeof(s32));
+	grid->pen_y.data = (s32 *)CALLOC(Y * 8, sizeof(s32));
+	grid->key_mc.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_x.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_y.data = (s32 *)CALLOC(1, sizeof(s32));
+	grid->self.data = (s32 *)CALLOC(4, sizeof(s32));
+	if (!grid->mc.data || !grid->sc_x.data || !grid->sc_y.data ||
+	    !grid->pen_x.data || !grid->pen_y.data || !grid->key_mc.data ||
+	    !grid->key_x.data || !grid->key_y.data || !grid->self.data)
+		goto err_free;
+
+	grid->mc.X = X; grid->mc.Y = Y;
+	grid->sc_x.X = X; grid->sc_x.Y = 1;
+	grid->sc_y.X = 1; grid->sc_y.Y = Y;
+	grid->pen_x.X = X; grid->pen_x.Y = 8;
+	grid->pen_y.X = 8; grid->pen_y.Y = Y;
+
+	grid->key_mc.X = key; grid->key_mc.Y = 1;
+	grid->key_x.X = key; grid->key_x.Y = 1;
+	grid->key_y.X = 1; grid->key_y.Y = 1;
+
+	grid->self.X = 4, grid->self.Y = 1;
+
+	grid_reset(grid);
+
+	return 0;
+
+err_free:
+	grid_free(grid);
+
+	return -ENOMEM;
+}
+
+static u16 update_crc(u16 crc, u8 newbyte)
+{
+	char i;
+		const u16 crc_poly = 0x8408;
+
+	crc ^= newbyte;
+
+	for (i = 0; i < 8; i++) {
+		if (crc & 0x01)
+			crc = (crc >> 1) ^ crc_poly;
+		else
+			crc = crc >> 1;
+	}
+
+	return crc;
+}
+
+u16 get_crc(u32 start, u32 end,
+	    u8 *buf, u32 buf_size)
+{
+	u16 crc = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		crc = update_crc(crc, buf[i]);
+
+	return crc;
+}
+
+u32 get_checksum(u32 start, u32 end,
+		 u8 *buf, u32 buf_size)
+{
+	u32 sum = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		sum += buf[i];
+
+	return sum;
+}
+
+bool is_checksum_matched(u8 checksum, int start, int end,
+			 u8 *buf, int buf_size)
+{
+	u8 check;
+
+	check = ~(get_checksum(start, end, buf, buf_size)) + 1;
+	if (check != checksum) {
+		TP_ERR_ARR(NULL, "[data]", TYPE_U8, end - start, buf + start);
+		TP_ERR(NULL, "checksum : 0x%02x/0x%02x not matched\n",
+			check, checksum);
+		return false;
+	}
+
+	return true;
+}
+
+bool support_mcu_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060009))
+		return false;
+
+	return true;
+}
+
+bool support_sensor_id(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060004))
+		return false;
+
+	return true;
+}
+
+bool support_production_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_fwid(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010802) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_power_status(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x06000a))
+		return false;
+
+	return true;
+}
+
+int bridge_set_int_monitor(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf3;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int bridge_set_test_mode(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf2;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int reset_helper(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool need_re_enum = true;
+
+	if (dev->_interface == interface_i2c) {
+		/* sw reset if no reset-gpio found */
+		if (!dev->cb.hw_reset ||
+		    dev->cb.hw_reset(dev->reset_time, dev->_private) < 0)
+			return api_protocol_set_cmd(dev, SET_SW_RST,
+						    &need_re_enum);
+
+		return 0;
+	}
+
+	return api_protocol_set_cmd(dev, SET_SW_RST, &need_re_enum);
+}
+
+static int re_enum_helper(struct ilitek_ts_device *dev, u8 enum_type)
+{
+	int error;
+	int retry = 5;
+
+	if (!dev->cb.re_enum)
+		return -EINVAL;
+
+	do {
+		error = dev->cb.re_enum(enum_type, dev->_private);
+		if (!error)
+			return 0;
+
+		TP_WARN(dev->id, "re-enum failed, error: %d, retry: %d\n", error, retry);
+		dev->cb.delay_ms(500);
+	} while (!dev->setting.no_retry && retry--);
+
+	TP_ERR(dev->id, "re-enum retry failed\n");
+
+	return -ENODEV;
+}
+
+int read_interrupt_in(void *handle, u8 *buf, int rlen,
+		      unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_interrupt_in)
+		return -EINVAL;
+
+	error = dev->cb.read_interrupt_in(buf, rlen, timeout_ms,
+					   dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[int-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		 unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_ctrl_in)
+		return -EINVAL;
+
+	if (dev->quirks & QUIRK_BRIDGE) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA4;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = 0;
+		dev->wbuf[4] = (rlen + 6) & 0xFF;
+		dev->wbuf[5] = ((rlen + 6) >> 8) & 0xFF;
+		dev->wbuf[6] = cmd;
+
+		error = dev->cb.write_then_read_direct(dev->wbuf, 64,
+							dev->rbuf, 64, dev->_private);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.read_ctrl_in(buf, rlen, timeout_ms, dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[ctrl-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int write_then_read(void *handle, u8 *cmd, int wlen,
+		    u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[wbuf]:", TYPE_U8, wlen, cmd);
+
+	if (!wlen && (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+	    dev->quirks & QUIRK_BRIDGE)) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA3;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = rlen;
+
+		error = write_then_read_direct(dev, dev->wbuf, 64, NULL, 0);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.write_then_read(cmd, wlen, buf, rlen, dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[rbuf]:", TYPE_U8, rlen, buf);
+
+	return (error < 0) ? error : 0;
+}
+
+int write_then_read_direct(void *handle, u8 *cmd, int wlen,
+			   u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read_direct)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-wbuf]:", TYPE_U8, wlen, cmd);
+
+	error = dev->cb.write_then_read_direct(cmd, wlen, buf, rlen,
+					       dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-rbuf]:", TYPE_U8, rlen, buf);
+
+	return error;
+}
+
+int write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct ilitek_ts_callback *cb = &dev->cb;
+
+	u8 ack_cmd;
+
+	TP_DBG(dev->id, "cmd: 0x" PFMT_X8 ", tout_ms: %d\n",
+		cmd[0], timeout_ms);
+
+	if (dev->quirks & QUIRK_WAIT_ACK_DELAY) {
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		cb->delay_ms(dev->setting.wait_ack_delay);
+		return 0;
+	}
+
+	if (dev->setting.no_INT_ack) {
+		/* prevent bridge int handling flow affecting the following read */
+		if (dev->quirks & QUIRK_BRIDGE)
+			bridge_set_int_monitor(dev, false);
+
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		/*
+		* for no-INT-ack flow, add delay to prevent
+		* interrupting FW flow too soon, while FW should
+		* be handling previous write command. ex. 0xcd/ 0xc3
+		*/
+		cb->delay_ms(5);
+
+		goto check_busy;
+	}
+
+	if (!cb->init_ack || !cb->wait_ack)
+		return -EINVAL;
+
+	cb->init_ack(timeout_ms, dev->_private);
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, true);
+
+	error = write_then_read(dev, cmd, wlen, NULL, 0);
+	if (error < 0)
+		return error;
+
+	ack_cmd = (cmd[0] == CMD_ACCESS_SLAVE) ? cmd[2] : cmd[0];
+	error = cb->wait_ack(ack_cmd, timeout_ms, dev->_private);
+
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, false);
+
+	/* cmd[0] should be ILITEK cmd code */
+	if (error < 0) {
+		TP_WARN(dev->id, "wait 0x" PFMT_X8 " ack %d ms timeout, err: %d\n",
+			cmd[0], timeout_ms, error);
+
+		if (dev->setting.no_retry)
+			return -EILITIME;
+
+		goto check_busy;
+	}
+
+	return 0;
+
+check_busy:
+	return api_check_busy(dev, timeout_ms, 10);
+}
+
+/* Common APIs */
+static int api_protocol_get_scrn_res(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_screen_info *screen_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 28);
+	if (error < 0)
+		return error;
+
+	screen_info = (struct ilitek_screen_info *)dev->rbuf;
+
+	dev->screen_info.x_min = screen_info->x_min;
+	dev->screen_info.y_min = screen_info->y_min;
+	dev->screen_info.x_max = screen_info->x_max;
+	dev->screen_info.y_max = screen_info->y_max;
+
+	TP_DBG(dev->id, "screen x: " PFMT_U16 "~" PFMT_U16 ", screen y: " PFMT_U16 "~" PFMT_U16 "\n",
+		dev->screen_info.x_min, dev->screen_info.x_max,
+		dev->screen_info.y_min, dev->screen_info.y_max);
+
+	dev->screen_info.pressure_min = 0;
+	dev->screen_info.pressure_max = 0;
+	dev->screen_info.x_tilt_min = 0;
+	dev->screen_info.x_tilt_max = 0;
+	dev->screen_info.y_tilt_min = 0;
+	dev->screen_info.y_tilt_max = 0;
+	if (dev->protocol.ver > 0x60006) {
+		dev->screen_info.pressure_min = screen_info->pressure_min;
+		dev->screen_info.pressure_max = screen_info->pressure_max;
+		dev->screen_info.x_tilt_min = screen_info->x_tilt_min;
+		dev->screen_info.x_tilt_max = screen_info->x_tilt_max;
+		dev->screen_info.y_tilt_min = screen_info->y_tilt_min;
+		dev->screen_info.y_tilt_max = screen_info->y_tilt_max;
+
+		dev->screen_info.pen_x_min = screen_info->pen_x_min;
+		dev->screen_info.pen_y_min = screen_info->pen_y_min;
+		dev->screen_info.pen_x_max = screen_info->pen_x_max;
+		dev->screen_info.pen_y_max = screen_info->pen_y_max;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v3 *tp_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 15);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v3 *)dev->rbuf;
+
+	dev->tp_info.block_num = 2;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+
+	dev->tp_info.support_modes = tp_info->support_modes;
+	if (dev->tp_info.support_modes > 3 || !dev->tp_info.support_modes)
+		dev->tp_info.support_modes = 1;
+
+	TP_DBG(dev->id, "touch ch.(start/end) x: " PFMT_U8 "/" PFMT_U8 ", y: " PFMT_U8 "/" PFMT_U8 "\n",
+		tp_info->touch_start_y, tp_info->touch_end_y,
+		tp_info->touch_start_x, tp_info->touch_end_x);
+
+	if (dev->tp_info.key_num) {
+		/* check v3 key is virtual or hw keys */
+		dev->key.info.mode =
+			(tp_info->touch_start_y == 0xff &&
+			 tp_info->touch_end_y == 0xff &&
+			 tp_info->touch_start_x == 0xff &&
+			 tp_info->touch_end_x == 0xff) ?
+			key_hw : key_vitual;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v6 *tp_info;
+	u8 i;
+
+#define X(_enum, _code, _name) {_code, _name},
+	const struct {
+		const int code;
+		const char *str;
+	} pen_modes[] = { STYLUS_MODES };
+#undef X
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 21);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v6 *)dev->rbuf;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+	dev->tp_info.ic_num = tp_info->ic_num;
+	dev->tp_info.format = tp_info->format;
+	dev->tp_info.support_modes = tp_info->support_modes;
+	dev->tp_info.die_num = tp_info->die_num;
+
+	if (dev->tp_info.format == 5)
+		api_protocol_set_cmd(dev, GET_CRYPTO_INFO, NULL);
+
+	if (dev->tp_info.ic_num > ARRAY_SIZE(dev->ic)) {
+		TP_ERR(dev->id, "invalid ic_num: " PFMT_U8 "\n", dev->tp_info.ic_num);
+		return -EINVAL;
+	}
+	TP_MSG(dev->id, "[Panel Information] Chip count: %u\n",
+		dev->tp_info.ic_num);
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max tp: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	if (dev->protocol.ver < 0x60003)
+		return 0;
+
+	dev->tp_info.block_num = tp_info->block_num;
+	TP_MSG(dev->id, "[Panel Information] Block Number: " PFMT_U8 "\n",
+		dev->tp_info.block_num);
+
+	if (dev->protocol.ver < 0x60007)
+		return 0;
+
+	dev->tp_info.pen_modes = tp_info->pen_modes;
+
+	_memset(dev->pen_mode, 0, sizeof(dev->pen_mode));
+	if (!dev->tp_info.pen_modes)
+		_strcpy(dev->pen_mode, "Disable",
+			sizeof(dev->pen_mode));
+	for (i = 0; i < ARRAY_SIZE(pen_modes); i++) {
+		if (!(tp_info->pen_modes & pen_modes[i].code))
+			continue;
+
+		if (_strlen(dev->pen_mode))
+			_strcat(dev->pen_mode, ",", sizeof(dev->pen_mode));
+
+		_strcat(dev->pen_mode, pen_modes[i].str,
+			sizeof(dev->pen_mode));
+	}
+
+	TP_DBG(dev->id, "pen_modes: " PFMT_U8 "\n", dev->tp_info.pen_modes);
+	TP_MSG(dev->id, "[Panel Information] Pen Mode: " PFMT_C8 "\n",
+		dev->pen_mode);
+
+	dev->tp_info.pen_format = tp_info->pen_format;
+	dev->tp_info.pen_x_resolution = tp_info->pen_x_resolution;
+	dev->tp_info.pen_y_resolution = tp_info->pen_y_resolution;
+	TP_MSG(dev->id, "[Panel Information] Pen Format: 0x" PFMT_X8 "\n",
+		dev->tp_info.pen_format);
+	TP_MSG(dev->id, "[Panel Information] Pen X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.pen_x_resolution,
+		dev->tp_info.pen_y_resolution);
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+#define X(_enum, _id, _size, _cnt)	\
+	pen_fmts[_id].size = _size;	\
+	pen_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_PEN_REPORT_FORMAT;
+#undef X
+
+	if (dev->protocol.flag == PTL_V3)
+		error = api_protocol_get_tp_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		error = api_protocol_get_tp_info_v6(dev, data);
+	else
+		return -EINVAL;
+
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max fingers: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	switch (dev->tp_info.format) {
+	case touch_fmt_0x1:
+	case touch_fmt_0x2:
+	case touch_fmt_0x3:
+	case touch_fmt_0x4:
+	case touch_fmt_0x10:
+		if (dev->setting.default_format_enabled)
+			goto default_fmt_enabled;
+
+		dev->fmt.touch_size = touch_fmts[dev->tp_info.format].size;
+		dev->fmt.touch_max_cnt = touch_fmts[dev->tp_info.format].max_cnt;
+		break;
+
+default_fmt_enabled:
+	default:
+	case touch_fmt_0x11:
+	case touch_fmt_0x0:
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+		break;
+	}
+
+	switch (dev->tp_info.pen_format) {
+	case pen_fmt_0x1:
+	case pen_fmt_0x2:
+		dev->fmt.pen_size = pen_fmts[dev->tp_info.pen_format].size;
+		dev->fmt.pen_max_cnt = pen_fmts[dev->tp_info.pen_format].max_cnt;
+		break;
+	default:
+	case pen_fmt_0x0:
+		dev->fmt.pen_size = pen_fmts[pen_fmt_0x0].size;
+		dev->fmt.pen_max_cnt = pen_fmts[pen_fmt_0x0].max_cnt;
+		break;
+	}
+
+	TP_MSG(dev->id, "[Panel Information] X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_resolution, dev->tp_info.y_resolution);
+	TP_MSG(dev->id, "[Panel Information] X/Y channel: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_ch, dev->tp_info.y_ch);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Fingers\n",
+		dev->tp_info.max_fingers);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Keys\n",
+		dev->tp_info.key_num);
+
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " modes\n",
+		dev->tp_info.support_modes);
+
+	TP_DBG(dev->id, "touch format: 0x" PFMT_X8 ", size: %u bytes, max cnt: %u per packet\n",
+		dev->tp_info.format, dev->fmt.touch_size,
+		dev->fmt.touch_max_cnt);
+
+	if (dev->tp_info.key_num > 0) {
+		error = api_protocol_set_cmd(dev, GET_KEY_INFO, NULL);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v3 *key_info;
+	unsigned int i;
+
+	UNUSED(data);
+
+	/* Only i2c interface has key for V3 */
+	if (dev->_interface != interface_i2c)
+		return 0;
+
+	if (dev->tp_info.key_num > 20) {
+		TP_ERR(dev->id, "key count: " PFMT_U8 " invalid\n", dev->tp_info.key_num);
+		return -EINVAL;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 29);
+	if (error < 0)
+		return error;
+
+	for (i = 0; dev->tp_info.key_num > 5U &&
+	     i < DIV_ROUND_UP(dev->tp_info.key_num, 5U) - 1U; i++) {
+		TP_MSG(dev->id, "read keyinfo again, i: %u\n", i);
+		error = write_then_read(dev, NULL, 0,
+					dev->rbuf + 29 + 5 * i, 25);
+		if (error < 0)
+			return error;
+	}
+
+	key_info = (struct ilitek_key_info_v3 *)dev->rbuf;
+	dev->key.info.x_len = be16(key_info->x_len);
+	dev->key.info.y_len = be16(key_info->y_len);
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = be16(key_info->keys[i].x);
+		dev->key.info.keys[i].y = be16(key_info->keys[i].y);
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v6 *key_info;
+	unsigned int i, offset;
+
+	UNUSED(data);
+
+	if (dev->tp_info.key_num > ARRAY_SIZE(dev->key.info.keys)) {
+		TP_ERR(dev->id, "exception keycount " PFMT_U8 " > %d\n", dev->tp_info.key_num,
+			(int)ARRAY_SIZE(dev->key.info.keys));
+		return -EINVAL;
+	}
+
+	switch (dev->_interface) {
+	case interface_i2c:
+		if (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+		    dev->quirks & QUIRK_BRIDGE) {
+			error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+			if (error < 0)
+				return error;
+			error = read_ctrl_in(dev, CMD_GET_KEY_INFO,
+					     dev->rbuf,
+					     5 + dev->tp_info.key_num * 5, 2000);
+			if (error < 0)
+				return error;
+			offset = (dev->quirks & QUIRK_BRIDGE) ? 1 : 0;
+		} else {
+			error = write_then_read(dev, dev->wbuf, 1,
+						dev->rbuf, 5 + dev->tp_info.key_num * 5);
+			if (error < 0)
+				return error;
+			offset = 0;
+		}
+		break;
+
+	case interface_usb:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 6;
+		break;
+	case interface_hid_over_i2c:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 4;
+		break;
+	default: return -EINVAL;
+	};
+
+	key_info = (struct ilitek_key_info_v6 *)(dev->rbuf + offset);
+	dev->key.info.mode = key_info->mode;
+	TP_MSG(dev->id, "[Panel Information] key mode: " PFMT_U8 "\n", dev->key.info.mode);
+
+	dev->key.info.x_len = key_info->x_len;
+	dev->key.info.y_len = key_info->y_len;
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = key_info->keys[i].x;
+		dev->key.info.keys[i].y = key_info->keys[i].y;
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_key_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_key_info_v6(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_get_ptl_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->protocol.flag = PTL_V6;
+	dev->reset_time = 1000;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->protocol.ver = (dev->rbuf[0] << 16) + (dev->rbuf[1] << 8) +
+			     dev->rbuf[2];
+	TP_MSG(dev->id, "[Protocol Version]: %x.%x.%x\n",
+		(dev->protocol.ver >> 16) & 0xFF,
+		(dev->protocol.ver >> 8) & 0xFF,
+		dev->protocol.ver & 0xFF);
+
+	dev->protocol.flag = get_protocol_ver_flag(dev->protocol.ver);
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->reset_time = 200;
+		break;
+	case PTL_V6:
+		dev->reset_time = 600;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol ver.: 0x%x\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_fw_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->fw_ver, dev->rbuf, 8);
+
+	if (dev->ic[0].mode == BL_MODE) {
+		TP_MSG_ARR(dev->id, "[BL Firmware Version]", TYPE_U8,
+			   8, dev->fw_ver);
+	} else {
+		TP_MSG_ARR(dev->id, "[FW Version]", TYPE_U8, 4, dev->fw_ver);
+		TP_MSG_ARR(dev->id, "[Customer Version]", TYPE_U8,
+			   4, dev->fw_ver + 4);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < ic_num; i++) {
+		dev->ic[i].mode = dev->rbuf[i * 2];
+
+		if (dev->ic[i].mode == AP_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "AP");
+		else if (dev->ic[i].mode == BL_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "BL");
+		else
+			_sprintf(dev->ic[i].mode_str, 0, "UNKNOWN");
+	}
+
+	TP_MSG(dev->id, "[Current Mode] Master: 0x" PFMT_X8 " " PFMT_C8 "\n",
+		dev->ic[0].mode, dev->ic[0].mode_str);
+	for (i = 1; i < ic_num; i++)
+		TP_MSG(dev->id, "[Current Mode] Slave[" PFMT_U8 "]: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, dev->ic[i].mode, dev->ic[i].mode_str);
+
+	return 0;
+}
+
+static int api_protocol_power_status(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 func, lvd_level_sel;
+
+	if (!data)
+		return -EFAULT;
+
+	if (!support_power_status(dev)) {
+		_memset(&dev->pwr, 0, sizeof(dev->pwr));
+		return 0;
+	}
+
+	func = ((*(u16 *)data) >> 8) & 0xff;
+	lvd_level_sel = (*(u16 *)data) & 0xff;
+
+	dev->wbuf[1] = func;
+	switch (func) {
+	/* get level select */
+	case 0x02:
+		dev->wbuf[2] = lvd_level_sel;
+		return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+
+	/* clear flag */
+	case 0x00:
+		return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+
+	/* get flag */
+	case 0x01:
+		error = write_then_read(dev, dev->wbuf, 2,
+					dev->rbuf, 4);
+		if (error < 0)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev->pwr.header = be16(dev->rbuf);
+	dev->pwr.vdd33_lvd_flag = dev->rbuf[2];
+	dev->pwr.vdd33_lvd_level_sel = dev->rbuf[3];
+
+	TP_DBG(dev->id, "[Power-Status] header: 0x" PFMT_X16 ", flag: 0x" PFMT_X8 ", level_sel: 0x" PFMT_X8 "\n",
+		dev->pwr.header, dev->pwr.vdd33_lvd_flag,
+		dev->pwr.vdd33_lvd_level_sel);
+
+	return 0;
+}
+
+static int api_protocol_get_sensor_id(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_sensor_id(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->sensor.header = be16(dev->rbuf);
+	dev->sensor.id = dev->rbuf[2];
+
+	TP_MSG(dev->id, "[Sensor ID] header: 0x" PFMT_X16 ", id: 0x" PFMT_X8 "\n",
+		dev->sensor.header,
+		(u8)(dev->sensor.id & dev->setting.sensor_id_mask));
+
+	return 0;
+}
+
+static int api_protocol_get_product_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_production_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->product_info, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[Production Info]", TYPE_U8, 8, dev->product_info);
+
+	return 0;
+}
+
+static int api_protocol_get_fwid(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_fwid(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->customer_id = le16(dev->rbuf);
+	dev->fwid = le16(dev->rbuf + 2);
+
+	TP_MSG(dev->id, "[Customer ID] 0x%04x\n", dev->customer_id);
+	TP_MSG(dev->id, "[FWID] 0x%04x\n", dev->fwid);
+
+	return 0;
+}
+
+static int api_protocol_get_crypto_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	u16 __MAYBE_UNUSED crypto_ver;
+	u32 crypto_opt;
+
+	UNUSED(data);
+
+	/*
+	 * encrypted report format should be supported after AP v6.0.8
+	 * set report format to 0 if protocol version not matched or
+	 * crypto info say it's not supported.
+	 */
+	if (dev->protocol.ver < 0x060008 ||
+	    write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6) < 0) {
+		dev->tp_info.format = 0;
+		return 0;
+	}
+
+	crypto_ver = le16(dev->rbuf);
+	crypto_opt = le32(dev->rbuf + 2, 4);
+
+	TP_MSG(dev->id, "[Encrypt Ver.] 0x%x\n", crypto_ver);
+	TP_MSG(dev->id, "[Encrypt Options] 0x%x\n", crypto_opt);
+
+	if (!(crypto_opt & 1))
+		dev->tp_info.format = 0;
+
+	return 0;
+}
+
+static int api_protocol_get_hid_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060009)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6);
+	if (error < 0)
+		return error;
+
+	_memcpy(&dev->hid_info, dev->rbuf, sizeof(dev->hid_info));
+
+	TP_MSG(dev->id, "vid/pid/rev: 0x%x/0x%x/0x%x\n",
+		dev->hid_info.vid, dev->hid_info.pid, dev->hid_info.rev);
+
+	return 0;
+}
+
+static bool is_special_char(char c)
+{
+	return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+		(c >= '0' && c <= '9')) ? false : true;
+}
+
+static int api_protocol_get_mcu_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_ver {
+		u16 ic_name;
+		u8 df_start_addr[3];
+		u8 df_size;
+
+		char module_name[26];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, use GET_MCU_VER (0x61) cmd
+	 */
+	if (dev->protocol.flag == PTL_V6 && support_mcu_info(dev)) {
+		error = api_protocol_set_cmd(dev, GET_MCU_INFO, NULL);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1,
+					dev->rbuf, 32);
+		if (error < 0)
+			return error;
+
+		parser = (struct mcu_ver *)dev->rbuf;
+
+		_memset(dev->mcu_info.ic_name, 0,
+			sizeof(dev->mcu_info.ic_name));
+		_sprintf(dev->mcu_info.ic_name, 0, "%04x", parser->ic_name);
+
+		_memset(dev->mcu_info.module_name, 0,
+			sizeof(dev->mcu_info.module_name));
+		_memcpy(dev->mcu_info.module_name, parser->module_name,
+			sizeof(parser->module_name));
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		if (is_29xx(dev)) {
+			/* modify reset time to 100ms for 29xx ICs */
+			dev->reset_time = 100;
+
+			/* set mm_addr for bin file update */
+			dev->mcu_info.mm_addr =
+				is_2501x(dev) ? MM_ADDR_2501X : MM_ADDR_29XX;
+			dev->mcu_info.min_addr = START_ADDR_29XX;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		} else {
+			dev->mcu_info.mm_addr = MM_ADDR_LEGO;
+			dev->mcu_info.min_addr = START_ADDR_LEGO;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		}
+	}
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+	if (!strcmp(dev->mcu_info.ic_name, "2133"))
+		_sprintf(dev->mcu_info.ic_name, 0, "2132S");
+
+	_memset(dev->mcu_info.ic_full_name, 0,
+		sizeof(dev->mcu_info.ic_full_name));
+	_sprintf(dev->mcu_info.ic_full_name, 0,
+		"ILI" PFMT_C8, dev->mcu_info.ic_name);
+
+	TP_MSG(dev->id, "[MCU Kernel Version] " PFMT_C8 "\n",
+		dev->mcu_info.ic_full_name);
+	TP_MSG(dev->id, "[Module Name]: [" PFMT_C8 "]\n",
+		dev->mcu_info.module_name);
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_info {
+		char ic_name[5];
+		char mask_ver[2];
+		u8 mm_addr[3];
+		char module_name[18];
+		u8 reserve[4];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd only support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, return 0 to skip this command.
+	 */
+	if (dev->protocol.flag != PTL_V6 || !support_mcu_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	if (error < 0)
+		return error;
+
+	parser = (struct mcu_info *)dev->rbuf;
+
+	_memset(dev->mcu_info.ic_name, 0, sizeof(dev->mcu_info.ic_name));
+	_memcpy(dev->mcu_info.ic_name, parser->ic_name,
+		sizeof(parser->ic_name));
+
+	_memcpy(dev->mcu_info.module_name, parser->module_name,
+		sizeof(parser->module_name));
+	dev->mcu_info.mm_addr = le32(parser->mm_addr, 3);
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_fs_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->sine.start & 0xFF;
+	dev->wbuf[2] = freq->sine.start >> 8;
+	dev->wbuf[3] = freq->sine.end & 0xFF;
+	dev->wbuf[4] = freq->sine.end >> 8;
+	dev->wbuf[5] = freq->sine.step;
+	dev->wbuf[6] = freq->mc_swcap.start & 0xFF;
+	dev->wbuf[7] = freq->mc_swcap.start >> 8;
+	dev->wbuf[8] = freq->mc_swcap.end & 0xFF;
+	dev->wbuf[9] = freq->mc_swcap.end >> 8;
+	dev->wbuf[10] = freq->mc_swcap.step;
+	dev->wbuf[11] = freq->sc_swcap.start & 0xFF;
+	dev->wbuf[12] = freq->sc_swcap.start >> 8;
+	dev->wbuf[13] = freq->sc_swcap.end & 0xFF;
+	dev->wbuf[14] = freq->sc_swcap.end >> 8;
+	dev->wbuf[15] = freq->sc_swcap.step;
+	dev->wbuf[16] = freq->frame_cnt & 0xFF;
+	dev->wbuf[17] = freq->frame_cnt >> 8;
+	dev->wbuf[18] = freq->scan_type;
+
+	if (dev->protocol.ver < 0x60005)
+		return write_then_read(dev, dev->wbuf, 19, NULL, 0);
+
+	do {
+		if (dev->protocol.ver < 0x60009) {
+			error = write_then_read(dev, dev->wbuf, 19,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[16] = freq->dump1.start & 0xFF;
+		dev->wbuf[17] = freq->dump1.start >> 8;
+		dev->wbuf[18] = freq->dump1.end & 0xFF;
+		dev->wbuf[19] = freq->dump1.end >> 8;
+		dev->wbuf[20] = freq->dump1.step;
+		dev->wbuf[21] = freq->dump1_val;
+		dev->wbuf[22] = freq->dump2.start & 0xFF;
+		dev->wbuf[23] = freq->dump2.start >> 8;
+		dev->wbuf[24] = freq->dump2.end & 0xFF;
+		dev->wbuf[25] = freq->dump2.end >> 8;
+		dev->wbuf[26] = freq->dump2.step;
+		dev->wbuf[27] = freq->dump2_val;
+		dev->wbuf[28] = freq->frame_cnt & 0xFF;
+		dev->wbuf[29] = freq->frame_cnt >> 8;
+		dev->wbuf[30] = freq->scan_type;
+
+		if (dev->protocol.ver < 0x6000a) {
+			error = write_then_read(dev, dev->wbuf, 31,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[31] = freq->mc_frame_cnt & 0xFF;
+		dev->wbuf[32] = freq->mc_frame_cnt >> 8;
+		dev->wbuf[33] = freq->dump_frame_cnt & 0xFF;
+		dev->wbuf[34] = freq->dump_frame_cnt >> 8;
+
+		error = write_then_read(dev, dev->wbuf, 35, dev->rbuf, 5);
+
+	} while (0);
+
+	if (error < 0)
+		return error;
+
+	freq->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != 0x5a || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			freq->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_short_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct short_settings *_short = (struct short_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[short info] dump1: 0x" PFMT_X8 ", dump2: 0x" PFMT_X8 ", vref: 0x" PFMT_X8 ", postidle: 0x" PFMT_X16 "\n",
+		_short->dump_1, _short->dump_2,
+		_short->v_ref_L, _short->post_idle);
+
+	dev->wbuf[1] = _short->dump_1;
+	dev->wbuf[2] = _short->dump_2;
+	dev->wbuf[3] = _short->v_ref_L;
+	dev->wbuf[4] = _short->post_idle & 0xFF;
+	dev->wbuf[5] = (_short->post_idle >> 8) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 6, NULL, 0);
+}
+
+static int api_protocol_set_open_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct open_settings *open = (struct open_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id,
+		"[open info] freq.: " PFMT_U16 ", gain: 0x" PFMT_X8 ", gain_rfb: 0x" PFMT_X8
+		", afe_res_sel: 0x" PFMT_X8 ", mc_fsel: 0x" PFMT_X8 "\n",
+		open->freq, open->gain, open->gain_rfb,
+		open->afe_res_sel, open->mc_fsel);
+
+	dev->wbuf[wlen++] = open->freq & 0xFF;
+	dev->wbuf[wlen++] = (open->freq >> 8) & 0xFF;
+	dev->wbuf[wlen++] = open->gain;
+	dev->wbuf[wlen++] = open->gain_rfb;
+	dev->wbuf[wlen++] = open->afe_res_sel;
+	dev->wbuf[wlen++] = open->mc_fsel;
+
+	if (dev->protocol.ver > 0x060009) {
+		TP_DBG(dev->id, "[open info] frame: " PFMT_U16 "\n",
+			open->frame);
+
+		dev->wbuf[wlen++] = open->frame & 0xFF;
+		dev->wbuf[wlen++] = (open->frame >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_charge_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error, i;
+	struct charge_curve_settings *curve =
+		(struct charge_curve_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "charge-curve info. scan mode: 0x" PFMT_U8 "\n",
+		curve->scan_mode);
+
+	dev->wbuf[1] = curve->scan_mode;
+	dev->wbuf[2] = curve->dump.start & 0xFF;
+	dev->wbuf[3] = (curve->dump.start >> 8) & 0xFF;
+	dev->wbuf[4] = curve->dump.end & 0xFF;
+	dev->wbuf[5] = (curve->dump.end >> 8) & 0xFF;
+	dev->wbuf[6] = curve->dump.step;
+	dev->wbuf[7] = curve->dump.post_idle & 0xFF;
+	dev->wbuf[8] = (curve->dump.post_idle >> 8) & 0xFF;
+	dev->wbuf[9] = curve->dump.fix_val & 0xFF;
+	dev->wbuf[10] = (curve->dump.fix_val >> 8) & 0xFF;
+	dev->wbuf[11] = curve->charge.start & 0xFF;
+	dev->wbuf[12] = (curve->charge.start >> 8) & 0xFF;
+	dev->wbuf[13] = curve->charge.end & 0xFF;
+	dev->wbuf[14] = (curve->charge.end >> 8) & 0xFF;
+	dev->wbuf[15] = curve->charge.step;
+	dev->wbuf[16] = curve->charge.post_idle & 0xFF;
+	dev->wbuf[17] = (curve->charge.post_idle >> 8) & 0xFF;
+	dev->wbuf[18] = curve->charge.fix_val & 0xFF;
+	dev->wbuf[19] = (curve->charge.fix_val >> 8) & 0xFF;
+	dev->wbuf[20] = curve->c_sub & 0xFF;
+	dev->wbuf[21] = (curve->c_sub >> 8) & 0xFF;
+	dev->wbuf[22] = curve->frame_cnt & 0xFF;
+	dev->wbuf[23] = (curve->frame_cnt >> 8) & 0xFF;
+
+	for (i = 0; i < (int)ARRAY_SIZE(curve->pt); i++) {
+		dev->wbuf[24 + i * 4] = curve->pt[i].x & 0xFF;
+		dev->wbuf[24 + i * 4 + 1] = (curve->pt[i].x >> 8) & 0xFF;
+		dev->wbuf[24 + i * 4 + 2] = curve->pt[i].y & 0xFF;
+		dev->wbuf[24 + i * 4 + 3] = (curve->pt[i].y >> 8) & 0xFF;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 60, dev->rbuf, 5);
+	if (error < 0)
+		return error;
+
+	curve->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != AP_MODE || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			curve->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_p2p_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct p2p_settings *p2p = (struct p2p_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[p2p info] frame_cnt.: " PFMT_U16 ", type: 0x" PFMT_X8 "\n",
+		p2p->frame_cnt, p2p->type);
+
+	dev->wbuf[wlen++] = p2p->frame_cnt & 0xFF;
+	dev->wbuf[wlen++] = (p2p->frame_cnt >> 8) & 0xFF;
+
+	if (dev->protocol.ver > 0x060009) {
+		dev->wbuf[wlen++] = p2p->type & 0xFF;
+		dev->wbuf[wlen++] = p2p->freq & 0xFF;
+		dev->wbuf[wlen++] = (p2p->freq >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_pen_fs_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->pen.mode;
+	dev->wbuf[2] = freq->pen.start & 0xFF;
+	dev->wbuf[3] = freq->pen.start >> 8;
+	dev->wbuf[4] = freq->pen.end & 0xFF;
+	dev->wbuf[5] = freq->pen.end >> 8;
+	dev->wbuf[6] = freq->frame_cnt & 0xFF;
+	dev->wbuf[7] = freq->frame_cnt >> 8;
+
+	error = write_then_read(dev, dev->wbuf, 8, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int api_protocol_get_core_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->core_ver, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[CoreVersion]", TYPE_U8, 4, dev->core_ver);
+
+	return 0;
+}
+
+static int api_protocol_get_tuning_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->tuning_ver, dev->rbuf, 4);
+
+	TP_MSG_ARR(dev->id, "[TurningVersion]", TYPE_U8, 4, dev->tuning_ver);
+
+	return 0;
+}
+
+static int api_protocol_set_sw_reset(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	int wlen = 1;
+	bool need_re_enum = (data) ? *(bool *)data : false;
+	bool force_reset = (!data) ? true : false;
+
+	/* make sure touch report in default I2C-HID mode after force reset */
+	if (dev->_interface == interface_hid_over_i2c && !force_reset)
+		return 0;
+
+	dev->wbuf[1] = 0;
+	error = write_then_read(dev, dev->wbuf, wlen, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(dev->reset_time);
+
+	if (dev->_interface == interface_usb && need_re_enum)
+		return re_enum_helper(dev, enum_sw_reset);
+
+	return 0;
+}
+
+static int api_protocol_get_sys_busy(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	if (data)
+		*(u8 *)data = 0;
+
+	_memset(dev->rbuf, 0, 64);
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 1);
+	if (error < 0)
+		return error;
+
+	if (data)
+		*(u8 *)data = dev->rbuf[0];
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	/*
+	 * No need to get/print AP CRC by 0xC7 in BL mode,
+	 * and 2501x ICs would get wrong crc in BL.
+	 */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1,
+				dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	TP_MSG(dev->id, "[FW CRC] Master: 0x%x\n", dev->ic[0].crc[0]);
+
+	for (i = 1; i < ic_num; i++) {
+		dev->ic[i].crc[0] = le16(dev->rbuf + 2 * i);
+		TP_MSG(dev->id, "[FW CRC] Slave[" PFMT_U8 "]: 0x%x\n",
+			i, dev->ic[i].crc[0]);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error, rlen;
+
+	UNUSED(data);
+
+	rlen = (is_231x(dev)) ? 4 : 2;
+
+	if (dev->_interface == interface_i2c) {
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		dev->cb.delay_ms(600);
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	}
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	if (is_231x(dev))
+		dev->ic[0].crc[0] |= (le16(dev->rbuf + 2) << 16);
+
+	TP_MSG(dev->id, "[Check Code] AP: 0x%x\n", dev->ic[0].crc[0]);
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_ap_crc_v6(dev, data);
+	else if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_ap_crc_v3(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_set_mode_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 mode = dev->wbuf[1];
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * Bridge with V3 IC need to set bridge into/out test mode additionally.
+	 */
+	if (dev->quirks & QUIRK_BRIDGE)
+		return bridge_set_test_mode(dev, (mode) ? true : false);
+
+	return 0;
+}
+
+static int api_protocol_write_enable(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool in_ap = (data) ? *(bool *)data : true;
+
+	error = write_then_read(dev, dev->wbuf,
+				(in_ap) ? 3 : 10, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * V3 need AP/BL mode switch delay
+	 */
+	if (in_ap)
+		dev->cb.delay_ms(is_231x(dev) ? 1000 : 100);
+	else
+		dev->cb.delay_ms(10);
+
+	return 0;
+}
+
+static int api_protocol_write_data_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 33, NULL, 0);
+}
+
+static int api_protocol_get_df_crc(struct ilitek_ts_device *dev,  void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->ic[0].crc[1] = 0;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[1] = le16(dev->rbuf + 2) << 16 | le16(dev->rbuf);
+	TP_MSG(dev->id, "[Check Code] Data: 0x%x\n", dev->ic[0].crc[1]);
+
+	return 0;
+}
+
+static int api_protocol_set_mode_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_get_crc_by_addr(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 start, end, t_ms;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		start = le32(dev->wbuf + 2, 3);
+		end = le32(dev->wbuf + 5, 3);
+		t_ms = ((end - start) / 4096 + 1) * TOUT_CD * TOUT_CD_RATIO;
+
+		error = write_then_wait_ack(dev, dev->wbuf, 8, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_get_crc_by_num(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 t_ms = (dev->wbuf[2] == 0) ? TOUT_CF_BLOCK_0 : TOUT_CF_BLOCK_N;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		error = write_then_wait_ack(dev, dev->wbuf, 3, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_read_flash(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 code = *(u32 *)data;
+	bool prepare;
+	int rlen;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		if ((dev->protocol.ver & 0xFFFF00) == BL_PROTOCOL_V1_7 &&
+		     dev->fw_ver[3] < 3) {
+			TP_ERR(dev->id, "BL: 0x%x, FW: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 " not support cmd: 0x" PFMT_X8 "\n",
+				dev->protocol.ver, dev->fw_ver[0],
+				dev->fw_ver[1], dev->fw_ver[2], dev->fw_ver[3],
+				dev->wbuf[0]);
+			return -EINVAL;
+		}
+
+		return write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	}
+
+	if (!data)
+		return -EINVAL;
+
+	prepare = (code >> 16) ? true : false;
+	rlen = code & 0xFFFF;
+
+	if (prepare) {
+		error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+		dev->cb.delay_ms(100);
+
+		return error;
+	}
+
+	if (dev->_interface == interface_i2c)
+		error = write_then_read(dev, dev->wbuf, 2, dev->rbuf, rlen);
+	else
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+
+	return error;
+}
+
+static int api_protocol_set_flash_addr(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 addr = *(u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		dev->wbuf[3] = addr & 0xFF;
+		dev->wbuf[2] = (addr >> 8) & 0xFF;
+		dev->wbuf[1] = (addr >> 16) & 0xFF;
+
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		return 0;
+	}
+
+	dev->wbuf[1] = addr & 0xFF;
+	dev->wbuf[2] = (addr >> 8) & 0xFF;
+	dev->wbuf[3] = (addr >> 16) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 4, NULL, 0);
+}
+
+static int api_protocol_set_data_len(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_set_flash_enable(struct ilitek_ts_device *dev,
+					 void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	int wlen, rlen;
+	bool in_ap = ((type & 0x1) != 0) ? true : false;
+	bool is_slave = ((type & 0x2) != 0) ? true : false;
+
+	u32 set_start, set_end, get_start, get_end;
+
+	if (!is_slave) {
+		wlen = (in_ap) ? 3 : 9;
+		rlen = (in_ap || dev->protocol.ver < 0x010803) ? 0 : 6;
+
+		set_start = le32(dev->wbuf + 3, 3);
+		set_end = le32(dev->wbuf + 6, 3);
+
+		error = write_then_read(dev, dev->wbuf, wlen,
+					dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+
+		if (in_ap || dev->protocol.ver < 0x010803)
+			return 0;
+
+		get_start = le32(dev->rbuf, 3);
+		get_end = le32(dev->rbuf + 3, 3);
+
+		if (set_start != get_start || set_end != get_end) {
+			TP_ERR(dev->id, "start/end addr.: 0x%x/0x%x vs. 0x%x/0x%x not match\n",
+				set_start, set_end, get_start, get_end);
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	error = write_then_wait_ack(dev, dev->wbuf, 9,
+				    TOUT_CC_SLAVE * TOUT_CC_SLAVE_RATIO);
+	if (error < 0)
+		return error;
+	dev->cb.delay_ms(2000);
+
+	return (dev->_interface == interface_usb) ?
+		re_enum_helper(dev, enum_sw_reset) : 0;
+}
+
+static int api_protocol_write_data_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, TOUT_C3 * TOUT_C3_RATIO);
+}
+
+static int api_protocol_write_data_m2v(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, 30000);
+}
+
+static int api_protocol_access_slave(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_slave_access *access;
+
+	if (!data)
+		return -EINVAL;
+
+	access = (struct ilitek_slave_access *)data;
+
+	dev->wbuf[1] = access->slave_id;
+	dev->wbuf[2] = access->func;
+	_memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (access->func) {
+	case CMD_GET_AP_CRC:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 4);
+		*((u32 *)access->data) = le32(dev->rbuf, 4);
+		break;
+
+	case CMD_GET_MCU_MOD:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 1);
+		*((u8 *)access->data) = dev->rbuf[0];
+		break;
+
+	case CMD_GET_FW_VER:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 8);
+		_memcpy((u8 *)access->data, dev->rbuf, 8);
+
+		break;
+
+	case CMD_WRITE_ENABLE:
+		dev->wbuf[3] = ((u8 *)access->data)[0];
+		dev->wbuf[4] = ((u8 *)access->data)[1];
+		dev->wbuf[5] = ((u8 *)access->data)[2];
+		dev->wbuf[6] = ((u8 *)access->data)[3];
+		dev->wbuf[7] = ((u8 *)access->data)[4];
+		dev->wbuf[8] = ((u8 *)access->data)[5];
+
+		error = write_then_wait_ack(dev, dev->wbuf, 9, 5000);
+		break;
+
+	default:
+		error = write_then_wait_ack(dev, dev->wbuf, 3, 5000);
+		break;
+	};
+
+	return error;
+}
+
+static int api_protocol_set_ap_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, false, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_bl_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, true, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_idle(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_set_sleep(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_wakeup(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_func_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool get = (data) ? *(bool *)data : true;
+
+	if (!data)
+		return -EINVAL;
+
+	if (get) {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+		if (error < 0)
+			return error;
+
+		dev->func.header = be16(dev->rbuf);
+		dev->func.mode = dev->rbuf[2];
+		TP_MSG(dev->id, "[FW Mode] 0x" PFMT_X8 "\n", dev->func.mode);
+
+		return 0;
+	}
+
+	if (dev->protocol.flag == PTL_V3) {
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+		return 0;
+	} else if (dev->protocol.flag == PTL_V6) {
+		error = write_then_wait_ack(dev, dev->wbuf, 4,
+					    TOUT_68 * TOUT_68_RATIO);
+		if (error < 0)
+			return error;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int api_protocol_c_model_info(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060008)
+		return write_then_read(dev, dev->wbuf, 12, NULL, 0);
+
+	return write_then_read(dev, dev->wbuf, 18, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct tuning_para_settings tuning =
+		*(struct tuning_para_settings *)data;
+	u32 wlen;
+
+	int header;
+	int tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = tuning.func;
+	dev->wbuf[2] = tuning.ctrl;
+	dev->wbuf[3] = tuning.type;
+
+	if (tuning.func == 0x0) {
+		wlen = 4;
+		tout_ms = TOUT_65_READ * TOUT_65_READ_RATIO;
+
+		switch (tuning.ctrl) {
+		case 0x3: case 0x5: case 0x10:
+			wlen += tuning.len;
+			tout_ms = TOUT_65_WRITE * TOUT_65_WRITE_RATIO;
+
+			//TODO: add memory range check
+			_memcpy(dev->wbuf + 4, tuning.buf, tuning.len);
+			break;
+		}
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	switch (tuning.ctrl) {
+	case 0x2: case 0x4:
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = read_ctrl_in(dev, CMD_TUNING_PARA_V6,
+				     dev->rbuf, 1024, 5000);
+		if (error < 0)
+			return error;
+
+		header = (dev->_interface == interface_i2c) ? 5 : 6;
+		header = (dev->quirks & QUIRK_BRIDGE) ? 6 : header;
+
+		_memcpy(tuning.buf, dev->rbuf + header, tuning.len);
+
+		break;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_cdc_init_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	int wlen;
+	struct cdc_settings *set = (struct cdc_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (set->is_freq) {
+		dev->wbuf[1] = 0x0F;
+		dev->wbuf[2] = set->freq.sine.start;
+		dev->wbuf[3] = set->freq.sine.end;
+		dev->wbuf[4] = set->freq.sine.step;
+
+		error = write_then_read(dev, dev->wbuf, 5, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(200);
+	} else {
+		dev->wbuf[1] = set->cmd;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = set->config & 0xFF;
+		wlen = 4;
+
+		if (set->config & 0xFF00) {
+			dev->wbuf[3] = (set->config >> 8) & 0xFF;
+			dev->wbuf[4] = set->config & 0xFF;
+			wlen = 5;
+		}
+
+		error = write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(10);
+	}
+
+	return api_check_busy(dev, 15000, 10);
+}
+
+static int api_protocol_get_cdc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_wait_ack(dev, dev->wbuf, 1, TOUT_F2 * TOUT_F2_RATIO);
+}
+
+static int api_protocol_set_cdc_init_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	struct cdc_settings *set = (struct cdc_settings *)data;
+	int wlen = 1, tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[wlen++] = set->cmd;
+
+	if (set->is_freq) {
+		tout_ms = set->freq.sine.steps * TOUT_F1_FREQ_MC;
+		tout_ms += (set->freq.mc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.sc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump1.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump2.steps * TOUT_F1_FREQ_SC);
+
+		tout_ms += (set->freq.pen.steps * TOUT_F1_FREQ_MC);
+
+		tout_ms *= MAX(set->freq.frame_cnt, 1);
+		tout_ms *= TOUT_F1_FREQ_RATIO;
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (set->is_p2p) {
+		tout_ms = MAX(set->p2p.frame_cnt, 1) *
+			TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (dev->protocol.ver > 0x60008)
+		dev->wbuf[wlen++] = set->config & 0xFF;
+
+	tout_ms = TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+	if (set->is_curve) {
+		tout_ms = set->curve.dump.steps * TOUT_F1_CURVE;
+		tout_ms += (set->curve.charge.steps * TOUT_F1_CURVE);
+		tout_ms *= MAX(set->curve.frame_cnt, 1);
+		tout_ms *= TOUT_F1_CURVE_RATIO;
+	} else if (set->is_short) {
+		tout_ms = TOUT_F1_SHORT * TOUT_F1_SHORT_RATIO;
+	} else if (set->is_open) {
+		tout_ms = MAX(set->open.frame, 1) *
+			TOUT_F1_OPEN * TOUT_F1_OPEN_RATIO;
+	} else if (set->is_key && dev->protocol.ver < 0x6000a) {
+		tout_ms = TOUT_F1_KEY;
+	}
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+}
+
+static int api_protocol_get_cdc_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u32 *cdc_info = (u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	*cdc_info = le32(dev->rbuf, 4);
+
+	return 0;
+}
+
+int api_protocol_set_cmd(void *handle, u8 idx, void *data)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev || idx >= ARRAY_SIZE(protocol_maps))
+		return -EINVAL;
+
+	if (!(dev->protocol.flag & protocol_maps[idx].flag) &&
+	    protocol_maps[idx].flag != PTL_ANY) {
+		TP_ERR(dev->id, "Unexpected cmd: " PFMT_C8 " for 0x" PFMT_X8 " only, now is 0x" PFMT_X8 "\n",
+			protocol_maps[idx].desc, protocol_maps[idx].flag,
+			dev->protocol.flag);
+		return -EINVAL;
+	}
+
+	dev->wbuf[0] = protocol_maps[idx].cmd;
+	error = protocol_maps[idx].func(dev, data);
+	if (error < 0) {
+		TP_ERR(dev->id, "failed to execute cmd: 0x" PFMT_X8 " " PFMT_C8 ", err: %d\n",
+			protocol_maps[idx].cmd, protocol_maps[idx].desc, error);
+		return error;
+	}
+
+	return 0;
+}
+
+int api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	u8 cmd = 0;
+
+	if (!force && dev->fw_mode == mode)
+		return 0;
+
+	_memset(dev->wbuf, 0, sizeof(dev->wbuf));
+
+	if (dev->protocol.flag == PTL_V3) {
+		/* V3 only support suspend and normal mode */
+		if (mode != mode_normal &&
+		    mode != mode_suspend &&
+		    mode != mode_test)
+			return -EPROTONOSUPPORT;
+		dev->wbuf[1] = (mode == mode_normal) ? 0x00 : 0x01;
+		cmd = SET_TEST_MOD;
+	} else if (dev->protocol.flag == PTL_V6) {
+		dev->wbuf[1] = mode;
+		dev->wbuf[2] = (eng) ? 0x01 : 0x00;
+		cmd = SET_MOD_CTRL;
+	}
+
+	error = api_protocol_set_cmd(dev, cmd, NULL);
+	if (error < 0)
+		return error;
+
+	/* switch from test to normal mode should wait 1 sec. delay */
+	if (dev->protocol.flag == PTL_V6 &&
+	    dev->fw_mode == mode_test && mode == mode_normal)
+		dev->cb.delay_ms(1000);
+	else
+		dev->cb.delay_ms(100);
+
+	dev->fw_mode = mode;
+
+	return 0;
+}
+
+u16 api_get_block_crc_by_addr(void *handle, u8 type,
+			      u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = start;
+	dev->wbuf[3] = (start >> 8) & 0xFF;
+	dev->wbuf[4] = (start >> 16) & 0xFF;
+	dev->wbuf[5] = end & 0xFF;
+	dev->wbuf[6] = (end >> 8) & 0xFF;
+	dev->wbuf[7] = (end >> 16) & 0xFF;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+u16 api_get_block_crc_by_num(void *handle, u8 type,
+			     u8 block_num)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = block_num;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+int api_set_data_len(void *handle, u16 data_len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[1] = data_len & 0xFF;
+	dev->wbuf[2] = (data_len >> 8) & 0xFF;
+
+	return api_protocol_set_cmd(dev, SET_DATA_LEN, NULL);
+}
+
+int api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 type;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = start & 0xFF;
+	dev->wbuf[4] = (start >> 8) & 0xFF;
+	dev->wbuf[5] = start >> 16;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (end >> 8) & 0xFF;
+	dev->wbuf[8] = end >> 16;
+
+	type = (in_ap) ? 0x1 : 0x0;
+	type |= (is_slave) ? 0x2 : 0x0;
+
+	return api_protocol_set_cmd(dev, SET_FLASH_EN, &type);
+}
+
+int api_write_data_v6(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V6, &wlen);
+}
+
+int api_access_slave(void *handle, u8 id, u8 func, void *data)
+{
+	struct ilitek_slave_access access;
+
+	access.slave_id = id;
+	access.func = func;
+	access.data = data;
+
+	return api_protocol_set_cmd(handle, ACCESS_SLAVE, &access);
+}
+
+int api_write_enable_v3(void *handle, bool in_ap, bool write_ap,
+			u32 end, u32 checksum)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = (write_ap) ? 0x0 : 0x1;
+	dev->wbuf[4] = (end >> 16) & 0xFF;
+	dev->wbuf[5] = (end >> 8) & 0xFF;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (checksum >> 16) & 0xFF;
+	dev->wbuf[8] = (checksum >> 8) & 0xFF;
+	dev->wbuf[9] = checksum & 0xFF;
+
+	return api_protocol_set_cmd(dev, WRITE_ENABLE, &in_ap);
+}
+
+int api_write_data_v3(void *handle)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V3, NULL);
+}
+
+int api_check_busy(void *handle, int timeout_ms, int delay_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 busy;
+
+	/* retry 2 times at least */
+	int i = MAX(DIV_ROUND_UP(timeout_ms, delay_ms), 2);
+
+	_memset(dev->wbuf, 0, 64);
+
+	while (i--) {
+		api_protocol_set_cmd(dev, GET_SYS_BUSY, &busy);
+		if (busy == ILITEK_TP_SYSTEM_READY)
+			return 0;
+
+		/* delay ms for each check busy */
+		dev->cb.delay_ms(delay_ms);
+
+		/* if caller set no_retry then skip check busy retry */
+		if (dev->setting.no_retry)
+			break;
+	}
+
+	TP_WARN(dev->id, "check busy timeout: %d ms, state: 0x" PFMT_X8 "\n",
+		timeout_ms, busy);
+
+	return -EILIBUSY;
+}
+
+int api_to_bl_mode(void *handle, bool to_bl,
+		   u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+
+	do {
+		if (api_protocol_set_cmd(dev, GET_MCU_MOD, NULL) < 0)
+			continue;
+
+		if (dev->ic[0].mode == target_mode)
+			goto success_change_mode;
+
+		if (to_bl) {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, true, false,
+						     0, 0) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_BL_MODE, NULL);
+		} else {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, false, false,
+						     start, end) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_AP_MODE, NULL);
+		}
+
+		switch (dev->_interface) {
+		case interface_hid_over_i2c:
+		case interface_i2c:
+			dev->cb.delay_ms(1000 + 100 * cnt);
+			break;
+		case interface_usb:
+			re_enum_helper(dev, enum_ap_bl);
+			break;
+		}
+	} while (!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+
+	/* update fw ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+
+	/* update protocol ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	return 0;
+}
+
+int api_set_idle(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = (enable) ? 1 : 0;
+	return api_protocol_set_cmd(dev, SET_MCU_IDLE, NULL);
+}
+
+int api_set_func_mode(void *handle, u8 mode)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	bool get = false;
+
+	_memset(dev->wbuf, 0, 64);
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->wbuf[1] = 0x55;
+		dev->wbuf[2] = 0xAA;
+		break;
+	case PTL_V6:
+		dev->wbuf[1] = 0x5A;
+		dev->wbuf[2] = 0xA5;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol: %x, flag: " PFMT_U8 "",
+			dev->protocol.ver, dev->protocol.flag);
+		return -EINVAL;
+	}
+	dev->wbuf[3] = mode;
+
+	if (dev->protocol.ver < 0x30400) {
+		TP_ERR(dev->id, "protocol: 0x%x not support\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	error = api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+	if (error < 0)
+		return error;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		return error;
+
+	return (dev->func.mode == mode) ? 0 : -EFAULT;
+}
+
+int api_get_func_mode(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool get = true;
+
+	return api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+}
+
+int api_erase_data_v3(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+
+	TP_INFO(dev->id, "erase data flash for " PFMT_C8 ", mode: " PFMT_X8 "\n",
+		dev->mcu_info.ic_name, dev->ic[0].mode);
+
+	if (is_231x(dev)) {
+		/* V3 231x only support erase data flash in AP mode */
+		if (dev->ic[0].mode != AP_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, true, false, 0, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(100);
+
+		dev->wbuf[1] = 0x02;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V3, NULL);
+		if (error < 0)
+			return error;
+
+		switch (dev->_interface) {
+		case interface_usb:
+			return re_enum_helper(dev, enum_ap_bl);
+		default:
+			dev->cb.delay_ms(1500);
+			break;
+		}
+	} else {
+		/* V3 251x only support erase data flash in BL mode */
+		if (dev->ic[0].mode != BL_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, false, false, 0xf01f, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		_memset(dev->wbuf + 1, 0xFF, 32);
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(500);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v3(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 addr, end = start + len, copied;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += 32, copied += 32) {
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, READ_FLASH, NULL);
+		if (error < 0)
+			return error;
+
+		_memcpy(buf + copied, dev->rbuf, 32);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v6(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 code, addr, end = start + len, copied;
+	u16 data_len;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += data_len, copied += data_len) {
+		if (end - addr > 1024)
+			data_len = 2048;
+		else if (end - addr > 256)
+			data_len = 1024;
+		else if (end - addr > 64)
+			data_len = 256;
+		else
+			data_len = 64;
+
+		error = api_set_data_len(dev, data_len);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x1; code = 1 << 16;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x0; code = data_len & 0xFFFF;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		if (dev->_interface == interface_hid_over_i2c)
+			_memcpy(buf + copied, dev->rbuf + 5, data_len);
+		else
+			_memcpy(buf + copied, dev->rbuf, data_len);
+	}
+
+	return 0;
+}
+
+int api_read_flash(void *handle, u8 *buf,
+		   u32 start, u32 len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (dev->protocol.flag == PTL_V3)
+		return api_read_flash_v3(dev, buf, start, len);
+
+	return api_read_flash_v6(dev, buf, start, len);
+}
+
+int _api_read_mp_result(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)(&dev->mp);
+	u32 addr = (is_29xx(dev)) ? 0x2e000 : 0x3e000;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return api_read_flash_v6(dev, buf, addr, 1000);
+
+	/* 1000 bytes data/ 2 bytes crc/ 1 bytes checksum */
+	tuning.len = 1003;
+	tuning.buf = buf;
+
+	tuning.func = 0x0; tuning.ctrl = 0x4; tuning.type = 0x10;
+	error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+	if (error < 0)
+		return error;
+
+	tuning.func = 0x1; tuning.ctrl = 0x4; tuning.type = 0x10;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_read_mp_result(void *handle)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_read_mp_result(handle);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+int _api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)mp;
+	u16 crc;
+
+	crc = get_crc(0, 1000, buf, sizeof(struct mp_station));
+	mp->crc = crc;
+
+	tuning.func = 0x0;
+	tuning.ctrl = 0x5;
+	tuning.type = 0x10;
+	tuning.buf = buf;
+	tuning.len = 1002;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_write_mp_result(handle, mp);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+static void mp_result(const char *item, u8 data)
+{
+	if (data == 1)
+		TP_INFO(NULL, PFMT_C8 " Result: PASS\n", item);
+	else if (data == 2)
+		TP_INFO(NULL, PFMT_C8 " Result: NG\n", item);
+	else
+		TP_INFO(NULL, PFMT_C8 " Result: N/A\n", item);
+}
+
+void api_decode_mp_result(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct mp_station *mp = &dev->mp;
+	int i, j;
+	struct mp_station_old *_mp;
+	char module_name[32];
+	char bar_code[256];
+
+	TP_DBG_ARR(NULL, "[MpResult Raw]:", TYPE_U8, 1002, (u8 *)mp);
+
+	TP_MSG(NULL, "mp result ver: 0x%08x\n", mp->info.mp_result_ver);
+
+	/* For Old Mp Result Format */
+	if (mp->info.mp_result_ver == 0xFFFFFFFF ||
+	    mp->info.mp_result_ver < 0x01000000) {
+		_mp = (struct mp_station_old *)mp;
+
+		for (i = 0; i < 9; i++) {
+			TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+			if (!_mp->station[i].week ||
+			    _mp->station[i].week == 0xFF) {
+				TP_INFO(NULL, "No Test\n");
+				continue;
+			}
+
+			TP_INFO(NULL, "Week of year : " PFMT_U8 "\n",
+				_mp->station[i].week);
+			TP_INFO(NULL, "Year : 20%02u\n",
+				_mp->station[i].year);
+			TP_INFO_ARR(NULL, "Firmware Version : ",
+				   TYPE_U8, 8, _mp->station[i].fw_ver);
+
+			_memset(module_name, 0, sizeof(module_name));
+			_memcpy(module_name, _mp->station[i].module,
+				sizeof(_mp->station[i].module));
+			module_name[sizeof(_mp->station[i].module) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].module);
+			     j++) {
+				if ((u8)module_name[j] == 0xFF) {
+					module_name[j] = '\0';
+					break;
+				}
+			}
+			TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n",
+				module_name);
+
+			mp_result("Short Test", _mp->station[i].short_test);
+			mp_result("Open Test", _mp->station[i].open_test);
+			mp_result("Self Cap Test", _mp->station[i].self_test);
+			mp_result("Uniformity Test",
+				_mp->station[i].uniform_test);
+			mp_result("DAC Test", _mp->station[i].dac_test);
+			mp_result("Key Raw Test", _mp->station[i].key_test);;
+			mp_result("Painting Test", _mp->station[i].paint_test);
+			mp_result("MicroOpen Test", _mp->station[i].mopen_test);
+			mp_result("GPIO Test", _mp->station[i].gpio_test);
+			mp_result("Final", _mp->station[i].final_result);
+
+			_memset(bar_code, 0, sizeof(bar_code));
+			_memcpy(bar_code, _mp->station[i].bar_code,
+				sizeof(_mp->station[i].bar_code));
+			bar_code[sizeof(_mp->station[i].bar_code) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].bar_code);
+			     j++) {
+				if ((u8)bar_code[j] == 0xFF) {
+					bar_code[j] = '\0';
+					break;
+				}
+			}
+
+			TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+			TP_INFO(NULL, "Customer ID : 0x%04x\n",
+				_mp->station[i].custom_id);
+			TP_INFO(NULL, "FWID : 0x%04x\n",
+				_mp->station[i].fwid);
+		}
+
+		return;
+	}
+
+	TP_INFO(NULL, "[MP Result]\n");
+	TP_INFO(NULL, "***Customer Info***\n");
+
+	TP_INFO(NULL, "MPResult Version:%02X.%02X.%02X.%02X\n",
+		(mp->info.mp_result_ver >> 24) & 0xFF,
+		(mp->info.mp_result_ver >> 16) & 0xFF,
+		(mp->info.mp_result_ver >> 8) & 0xFF,
+		mp->info.mp_result_ver & 0xFF);
+	TP_INFO(NULL, "Customer ID : 0x%04X\n", mp->info.customer_id);
+	TP_INFO(NULL, "FW ID : 0x%04X\n", mp->info.fwid);
+
+	for (i = 0; i < (int)ARRAY_SIZE(mp->station); i++) {
+		TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+		if (!mp->station[i].week || mp->station[i].week == 0xFF) {
+			TP_INFO(NULL, "No Test\n");
+			continue;
+		}
+
+		TP_INFO(NULL, "Week of year : %d\n", mp->station[i].week);
+		TP_INFO(NULL, "Year : 20%02d\n", mp->station[i].year);
+		TP_INFO(NULL, "Firmware Version : 0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+			mp->station[i].fw_ver[7], mp->station[i].fw_ver[6],
+			mp->station[i].fw_ver[5], mp->station[i].fw_ver[4],
+			mp->station[i].fw_ver[3], mp->station[i].fw_ver[2],
+			mp->station[i].fw_ver[1], mp->station[i].fw_ver[0]);
+
+		_memset(module_name, 0, sizeof(module_name));
+		_memcpy(module_name, mp->station[i].module,
+			sizeof(mp->station[i].module));
+		module_name[sizeof(mp->station[i].module) + 1] = '\0';
+		for (j = 0; j < (int)sizeof(mp->station[i].module); j++) {
+			if ((u8)module_name[j] == 0xFF) {
+				module_name[j] = '\0';
+				break;
+			}
+		}
+		TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n", module_name);
+
+		mp_result("Short Test", mp->station[i].short_test);
+		mp_result("Open Test", mp->station[i].open_test);
+		mp_result("Self Cap Test", mp->station[i].self_test);
+		mp_result("Uniformity Test", mp->station[i].uniform_test);
+		mp_result("DAC Test", mp->station[i].dac_test);
+		mp_result("Key Raw Test", mp->station[i].key_test);;
+		mp_result("Painting Test", mp->station[i].paint_test);
+		mp_result("MicroOpen Test", mp->station[i].mopen_test);
+		mp_result("GPIO Test", mp->station[i].gpio_test);
+		mp_result("Final", mp->station[i].final_result);
+
+		TP_INFO(NULL, "Tool Version : " PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "\n",
+			mp->station[i].tool_ver[7], mp->station[i].tool_ver[6],
+			mp->station[i].tool_ver[5], mp->station[i].tool_ver[4],
+			mp->station[i].tool_ver[3], mp->station[i].tool_ver[2],
+			mp->station[i].tool_ver[1], mp->station[i].tool_ver[0]);
+
+		_memset(bar_code, 0, sizeof(bar_code));
+		_memcpy(bar_code, mp->station[i].bar_code,
+			sizeof(mp->station[i].bar_code));
+		bar_code[sizeof(mp->station[i].bar_code) + 1] = '\0';
+		TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+		TP_INFO(NULL, "Customer ID : 0x%04x\n",
+			mp->station[i].custom_id);
+		TP_INFO(NULL, "FWID : 0x%04x\n", mp->station[i].fwid);
+	}
+}
+
+int api_read_tuning(void *handle, u8 *buf, int rlen)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	int got, need;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return -EINVAL;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	for (got = 0, need = rlen; need > 0; got += 1024, need -= 1024) {
+		tuning.len = MIN(need, 1024);
+		tuning.buf = buf + got;
+
+		tuning.func = 0x0; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+
+		tuning.func = 0x1; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+	}
+
+	TP_DBG_ARR(dev->id, "[tuning]:", TYPE_U8, rlen, buf);
+
+	return api_set_ctrl_mode(dev, mode_normal, false, true);
+}
+
+int api_write_data_m2v(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_M2V, &wlen);
+}
+
+int api_to_bl_mode_m2v(void *handle, bool to_bl)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+	u8 mode;
+
+	if (dev->_interface != interface_usb)
+		return -EINVAL;
+
+	do {
+		dev->cb.delay_ms(100);//Reed Add : 20230927
+
+		if (api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) < 0)
+			continue;
+
+		if (mode == target_mode)
+			goto success_change_mode;
+
+		dev->cb.delay_ms(300);//Reed Add : 20230927
+
+		if (to_bl && api_access_slave(dev, 0x80, CMD_SET_BL_MODE,
+					      NULL) < 0)
+			continue;
+		else if (!to_bl && api_access_slave(dev, 0x80, CMD_SET_AP_MODE,
+						    NULL) < 0)
+			continue;
+
+		do {
+			dev->cb.delay_ms(100);//Reed Add : 20230927
+			if (!api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) &&
+			    mode == target_mode)
+				goto success_change_mode;
+			dev->cb.delay_ms(5000);
+		} while (!dev->setting.no_retry && cnt++ < retry);
+		break;
+	} while (!!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "M2V current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "M2V current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		mode, (to_bl) ? "BL" : "AP");
+
+	return 0;
+}
+
+int api_get_ic_crc(void *handle, u8 final_fw_mode)
+{
+	int error;
+	u8 i;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		break;
+	case PTL_V6:
+		for (i = 1; i < dev->tp_info.block_num; i++) {
+			dev->ic[0].crc[i] = api_get_block_crc_by_num(dev,
+				CRC_CALCULATE, i);
+		}
+		break;
+	default:
+		error = -EINVAL; break;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, final_fw_mode, false, true);
+
+	return error;
+}
+
+void api_print_ts_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 i;
+
+	TP_INFO(dev->id, "[Protocol Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X\n",
+		(dev->protocol.ver >> 16) & 0xff,
+		(dev->protocol.ver >> 8) & 0xff,
+		dev->protocol.ver & 0xff);
+
+	TP_INFO(dev->id, "[Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[0], dev->fw_ver[1],
+		dev->fw_ver[2], dev->fw_ver[3]);
+	TP_INFO(dev->id, "[Customer Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[4], dev->fw_ver[5],
+		dev->fw_ver[6], dev->fw_ver[7]);
+
+	TP_INFO(dev->id, "[Kernel Version]\n");
+	if (dev->protocol.flag == PTL_V6 &&
+	    support_mcu_info(dev)) {
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X (" PFMT_C8 ")\n",
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1],
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[4], dev->mcu_info.ic_name);
+	} else {
+		TP_INFO(dev->id, "0x%c%c.0x%c%c\n",
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1]);
+	}
+
+	TP_INFO(dev->id, "[Current Mode]\n");
+	TP_INFO(dev->id, "Master : " PFMT_C8 "\n", dev->ic[0].mode_str);
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	for (i = 1; i < dev->tp_info.ic_num; i++)
+		TP_INFO(dev->id, "Slave : " PFMT_C8 "\n", dev->ic[i].mode_str);
+
+	TP_INFO(dev->id, "[Module Name]\n");
+	TP_INFO(dev->id, PFMT_C8 "\n", dev->mcu_info.module_name);
+
+	TP_INFO(dev->id, "[Core Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->core_ver[0], dev->core_ver[1],
+		dev->core_ver[2], dev->core_ver[3]);
+
+	if (is_231x(dev)) {
+		switch (dev->core_ver[1]) {
+		case 0x00:
+			TP_INFO(dev->id, "(Dual Interface)\n"); break;
+		case 0x01:
+			TP_INFO(dev->id, "(USB Interface)\n"); break;
+		case 0x02:
+			TP_INFO(dev->id, "(I2C Interface)\n"); break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[Tuning Version]\n");
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+			dev->tuning_ver[0], dev->tuning_ver[1],
+			dev->tuning_ver[2], dev->tuning_ver[3]);
+	}
+
+	TP_INFO(dev->id, "[Panel Information]\n");
+	TP_INFO(dev->id, "X resolution : " PFMT_U16 "\n", dev->tp_info.x_resolution);
+	TP_INFO(dev->id, "Y resolution : " PFMT_U16 "\n", dev->tp_info.y_resolution);
+	TP_INFO(dev->id, "AA X Channel : " PFMT_U16 "\n", dev->tp_info.x_ch);
+	TP_INFO(dev->id, "AA Y Channel : " PFMT_U16 "\n", dev->tp_info.y_ch);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Fingers\n", dev->tp_info.max_fingers);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Touch Keys\n", dev->tp_info.key_num);
+
+	if (dev->tp_info.key_num) {
+		switch (dev->key.info.mode) {
+		case key_disable:
+			TP_INFO(dev->id, "Key Mode : NO_Key\n");
+			break;
+		case key_hw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_1\n");
+			break;
+		case key_hsw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_2\n");
+			break;
+		case key_vitual:
+			TP_INFO(dev->id, "Key Mode : Virtual_Key\n");
+			break;
+		case key_fw_disable:
+			TP_INFO(dev->id, "Key Mode : FW_disable\n");
+			break;
+		default:
+			TP_INFO(dev->id, "Key Mode : Unknown(0x" PFMT_X8 ")\n",
+				dev->key.info.mode);
+			break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "Support Pen Type : " PFMT_C8 "\n",
+			dev->pen_mode);
+		TP_INFO(dev->id, "Chip Counts : " PFMT_U8 "\n", dev->tp_info.ic_num);
+		TP_INFO(dev->id, "Report Format : " PFMT_U8 "\n", dev->tp_info.format);
+		TP_INFO(dev->id, "Block Number : " PFMT_U8 "\n", dev->tp_info.block_num);
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[FW CRC]\n");
+		TP_INFO(dev->id, "Master : 0x%04X\n", dev->ic[0].crc[0]);
+		for (i = 1; i < dev->tp_info.ic_num; i++)
+			TP_INFO(dev->id, "Slave : 0x%04X\n", dev->ic[i].crc[0]);
+	} else {
+		TP_INFO(dev->id, "[Check Code]\n");
+		TP_INFO(dev->id, "AP : 0x%08X\n", dev->ic[0].crc[0]);
+		TP_INFO(dev->id, "DATA : 0x%08X\n", dev->ic[0].crc[1]);
+	}
+}
+
+void api_read_then_print_m2v_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 m2v_mode;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	api_set_ctrl_mode(dev, mode_suspend, false, true);
+	api_access_slave(dev, 0x80, CMD_GET_FW_VER, m2v_fw_ver);
+	api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &m2v_mode);
+	api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &m2v_checksum);
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	TP_INFO(dev->id, "[M2V Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+		m2v_fw_ver[0], m2v_fw_ver[1], m2v_fw_ver[2], m2v_fw_ver[3],
+		m2v_fw_ver[4], m2v_fw_ver[5], m2v_fw_ver[6], m2v_fw_ver[7]);
+
+	TP_INFO(dev->id, "[M2V Current Mode]\n");
+	TP_INFO(dev->id, "Mode : " PFMT_C8 " Mode\n",
+		(m2v_mode == AP_MODE) ? "AP" : "BL");
+
+	TP_INFO(dev->id, "[M2V FW CheckSum]\n");
+	TP_INFO(dev->id, "FW CheckSum : 0x%08X\n", m2v_checksum);
+}
+
+int api_update_ts_info(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	/*
+	 * previous set suspend mode is in V6 format.
+	 * set suspend mode again if device protocol is checked as V3.
+	 */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_PRODUCT_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_FWID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_SENSOR_ID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_HID_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	/* BL mode should perform FW upgrade afterward */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	/* V3 need to get DF CRC */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_TUNING_VER, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_CORE_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_SCRN_RES, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->tp_info.ic_num > 1) {
+		error = api_protocol_set_cmd(dev, GET_AP_CRC,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_MCU_MOD,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	return error;
+}
+
+void __ilitek_get_info(void *handle, struct ilitek_common_info *info)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!info || !dev)
+		return;
+
+	_memcpy(info, &dev->quirks, sizeof(struct ilitek_common_info));
+}
+
+void ilitek_dev_set_quirks(void *handle, u32 quirks)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	dev->quirks = quirks;
+}
+
+void ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->sys, sys, sizeof(struct ilitek_sys_info));
+}
+
+void ilitek_dev_setting(void *handle, struct ilitek_ts_settings *setting)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->setting, setting, sizeof(struct ilitek_ts_settings));
+
+	if (dev->setting.default_format_enabled) {
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+	}
+
+	TP_MSG(dev->id, "no-retry: %d, no-INT-ack: %d\n",
+		dev->setting.no_retry, dev->setting.no_INT_ack);
+}
+
+void ilitek_dev_bind_callback(void *handle, struct ilitek_ts_callback *callback)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (callback) {
+		_memcpy(&dev->cb, callback, sizeof(struct ilitek_ts_callback));
+		if (dev->cb.msg)
+			g_msg = dev->cb.msg;
+	}
+}
+
+void *ilitek_dev_init(u8 _interface, const char *id,
+		      bool need_update_ts_info,
+		      struct ilitek_ts_callback *callback, void *_private)
+{
+	struct ilitek_ts_device *dev;
+
+	dev = (struct ilitek_ts_device *)MALLOC(sizeof(*dev));
+	if (!dev)
+		return NULL;
+
+	TP_MSG(NULL, "commonflow code version: 0x%x\n",
+		COMMONFLOW_CODE_VERSION);
+
+	TP_DBG(NULL, "sizeof(ilitek_ts_device): %u\n",
+		(unsigned int)sizeof(struct ilitek_ts_device));
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(dev, 0, sizeof(*dev));
+
+	_strcpy(dev->id, id, sizeof(dev->id));
+	ilitek_dev_bind_callback(dev, callback);
+
+	dev->_interface = _interface;
+	dev->_private = _private;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	dev->fw_mode = mode_unknown;
+
+	if (need_update_ts_info && api_update_ts_info(dev) < 0) {
+		ilitek_dev_exit(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+void ilitek_dev_exit(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/*
+	 * LIBUSB would kill /dev/hidraw* and make system stop handling
+	 * device's usb. sw reset is required to re-enum usb then /dev/hidraw*
+	 * would be created and system would start to handle touch event.
+	 */
+	if (dev->quirks & QUIRK_LIBUSB || dev->setting.sw_reset_at_last)
+		api_protocol_set_cmd(dev, SET_SW_RST, NULL);
+
+	if (dev)
+		FREE(dev);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.h b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
new file mode 100644
index 000000000000..ad9c5c18cd36
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
@@ -0,0 +1,916 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_PROTOCOL_H__
+#define __ILITEK_PROTOCOL_H__
+
+#include "ilitek_def.h"
+
+/* quirks definition */
+#define QUIRK_WAIT_ACK_DELAY		0x1
+#define QUIRK_BRIDGE			0x2
+#define QUIRK_DAEMON_I2C		0x4
+#define QUIRK_WIFI_ITS_I2C		0x8
+#define QUIRK_LIBUSB			0x10
+
+#define START_ADDR_LEGO			0x3000
+#define START_ADDR_29XX			0x4000
+#define END_ADDR_LEGO			0x40000
+
+#define MM_ADDR_LEGO			0x3020
+#define MM_ADDR_29XX			0x4020
+#define MM_ADDR_2501X			0x4038
+
+#define DF_START_ADDR_LEGO		0x3C000
+#define DF_START_ADDR_29XX		0x2C000
+
+#define ILITEK_TP_SYSTEM_READY		0x50
+
+#define CRC_CALCULATE			0
+#define CRC_GET				1
+
+#define ILTIEK_MAX_BLOCK_NUM		20
+
+#define PTL_ANY				0x00
+#define PTL_V3				0x03
+#define PTL_V6				0x06
+
+#define BL_PROTOCOL_V1_8		0x10800
+#define BL_PROTOCOL_V1_7		0x10700
+#define BL_PROTOCOL_V1_6		0x10600
+
+#define TOUT_CF_BLOCK_0			2500
+#define TOUT_CF_BLOCK_N			500
+#define TOUT_F1_SHORT			1600
+#define TOUT_F1_OPEN			12
+#define TOUT_F1_FREQ_MC			2
+#define TOUT_F1_FREQ_SC			1
+#define TOUT_F1_CURVE			13
+#define TOUT_F1_KEY			400
+#define TOUT_F1_OTHER			27
+#define TOUT_F2				7
+#define TOUT_CD				27
+#define TOUT_C3				100
+#define TOUT_65_WRITE			135
+#define TOUT_65_READ			3
+#define TOUT_68				24
+#define TOUT_CC_SLAVE			16000
+
+#define TOUT_F1_SHORT_RATIO		2
+#define TOUT_F1_OPEN_RATIO		3
+#define TOUT_F1_FREQ_RATIO		3
+#define TOUT_F1_CURVE_RATIO		3
+#define TOUT_F1_OTHER_RATIO		3
+#define TOUT_F2_RATIO			3
+#define TOUT_CD_RATIO			3
+#define TOUT_C3_RATIO			3
+#define TOUT_65_WRITE_RATIO		3
+#define TOUT_65_READ_RATIO		3
+#define TOUT_68_RATIO			3
+#define TOUT_CC_SLAVE_RATIO		2
+
+#define AP_MODE		0x5A
+#define BL_MODE		0x55
+
+#define STYLUS_MODES			\
+	X(STYLUS_WGP,	0x1,	"WGP")	\
+	X(STYLUS_USI,	0x2,	"USI")	\
+	X(STYLUS_MPP,	0x4,	"MPP")
+
+#define ILITEK_CMD_MAP							\
+	X(0x20, PTL_ANY, GET_TP_INFO, api_protocol_get_tp_info)		\
+	X(0x21, PTL_ANY, GET_SCRN_RES, api_protocol_get_scrn_res)	\
+	X(0x22, PTL_ANY, GET_KEY_INFO, api_protocol_get_key_info)	\
+	X(0x30, PTL_ANY, SET_IC_SLEEP, api_protocol_set_sleep)		\
+	X(0x31, PTL_ANY, SET_IC_WAKE, api_protocol_set_wakeup)		\
+	X(0x34, PTL_ANY, SET_MCU_IDLE, api_protocol_set_idle)		\
+	X(0x40, PTL_ANY, GET_FW_VER, api_protocol_get_fw_ver)		\
+	X(0x42, PTL_ANY, GET_PTL_VER, api_protocol_get_ptl_ver)		\
+	X(0x43, PTL_ANY, GET_CORE_VER, api_protocol_get_core_ver)	\
+	X(0x60, PTL_ANY, SET_SW_RST, api_protocol_set_sw_reset)		\
+	X(0x61, PTL_ANY, GET_MCU_VER, api_protocol_get_mcu_ver)		\
+	X(0x68, PTL_ANY, SET_FUNC_MOD, api_protocol_set_func_mode)	\
+	X(0x80, PTL_ANY, GET_SYS_BUSY, api_protocol_get_sys_busy)	\
+	X(0xC0, PTL_ANY, GET_MCU_MOD, api_protocol_get_mcu_mode)	\
+	X(0xC1, PTL_ANY, SET_AP_MODE, api_protocol_set_ap_mode)		\
+	X(0xC2, PTL_ANY, SET_BL_MODE, api_protocol_set_bl_mode)		\
+	X(0xC5, PTL_ANY, READ_FLASH, api_protocol_read_flash)		\
+	X(0xC7, PTL_ANY, GET_AP_CRC, api_protocol_get_ap_crc)		\
+	X(0xC8, PTL_ANY, SET_ADDR, api_protocol_set_flash_addr)		\
+									\
+	/* v3 only cmds */						\
+	X(0x25, PTL_V3, GET_CDC_INFO_V3, api_protocol_get_cdc_info_v3)	\
+	X(0x63, PTL_V3, TUNING_PARA_V3, api_protocol_tuning_para_v3)	\
+	X(0xC3, PTL_V3, WRITE_DATA_V3, api_protocol_write_data_v3)	\
+	X(0xC4, PTL_V3, WRITE_ENABLE, api_protocol_write_enable)	\
+	X(0xCA, PTL_V3, GET_DF_CRC, api_protocol_get_df_crc)		\
+	X(0xF2, PTL_V3, SET_TEST_MOD, api_protocol_set_mode_v3)		\
+	X(0xF3, PTL_V3, INIT_CDC_V3, api_protocol_set_cdc_init_v3)	\
+									\
+	/* v6 only cmds */						\
+	X(0x24, PTL_V6, POWER_STATUS, api_protocol_power_status)	\
+	X(0x27, PTL_V6, GET_SENSOR_ID, api_protocol_get_sensor_id)	\
+	X(0x44, PTL_V6, GET_TUNING_VER, api_protocol_get_tuning_ver)	\
+	X(0x45, PTL_V6, GET_PRODUCT_INFO, api_protocol_get_product_info)\
+	X(0x46, PTL_V6, GET_FWID, api_protocol_get_fwid)		\
+	X(0x47, PTL_V6, GET_CRYPTO_INFO, api_protocol_get_crypto_info)	\
+	X(0x48, PTL_V6, GET_HID_INFO, api_protocol_get_hid_info)	\
+	X(0x62, PTL_V6, GET_MCU_INFO, api_protocol_get_mcu_info)	\
+	X(0x65, PTL_V6, TUNING_PARA_V6, api_protocol_tuning_para_v6)	\
+	X(0x69, PTL_V6, SET_FS_INFO, api_protocol_set_fs_info)		\
+	X(0x6A, PTL_V6, SET_SHORT_INFO, api_protocol_set_short_info)	\
+	X(0x6B, PTL_V6, C_MODEL_INFO, api_protocol_c_model_info)	\
+	X(0x6C, PTL_V6, SET_P2P_INFO, api_protocol_set_p2p_info)	\
+	X(0x6D, PTL_V6, SET_OPEN_INFO, api_protocol_set_open_info)	\
+	X(0x6E, PTL_V6, SET_CHARGE_INFO, api_protocol_set_charge_info)	\
+	X(0x6F, PTL_V6, SET_PEN_FS_INFO, api_protocol_set_pen_fs_info)	\
+	X(0xB0, PTL_V6, WRITE_DATA_M2V, api_protocol_write_data_m2v)	\
+	X(0xC3, PTL_V6, WRITE_DATA_V6, api_protocol_write_data_v6)	\
+	X(0xC9, PTL_V6, SET_DATA_LEN, api_protocol_set_data_len)	\
+	X(0xCB, PTL_V6, ACCESS_SLAVE, api_protocol_access_slave)	\
+	X(0xCC, PTL_V6, SET_FLASH_EN, api_protocol_set_flash_enable)	\
+	X(0xCD, PTL_V6, GET_BLK_CRC_ADDR, api_protocol_get_crc_by_addr)	\
+	X(0xCF, PTL_V6, GET_BLK_CRC_NUM, api_protocol_get_crc_by_num)	\
+	X(0xF0, PTL_V6, SET_MOD_CTRL, api_protocol_set_mode_v6)		\
+	X(0xF1, PTL_V6, INIT_CDC_V6, api_protocol_set_cdc_init_v6)	\
+	X(0xF2, PTL_V6, GET_CDC_V6, api_protocol_get_cdc_v6)
+
+
+#define X(_cmd, _protocol, _cmd_id, _api)	_cmd_id,
+enum ilitek_cmd_ids {
+	ILITEK_CMD_MAP
+	/* ALWAYS keep at the end */
+	MAX_CMD_CNT
+};
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api)	CMD_##_cmd_id = _cmd,
+enum ilitek_cmds { ILITEK_CMD_MAP };
+#undef X
+
+enum ilitek_hw_interfaces {
+	interface_i2c = 0,
+	interface_hid_over_i2c,
+	interface_usb,
+};
+
+enum ilitek_fw_modes {
+	mode_unknown = -1,
+	mode_normal = 0,
+	mode_test,
+	mode_debug,
+	mode_suspend,
+};
+
+enum ilitek_key_modes {
+	key_disable = 0,
+	key_hw = 1,
+	key_hsw = 2,
+	key_vitual = 3,
+	key_fw_disable = 0xff,
+};
+
+#define ILITEK_TOUCH_REPORT_FORMAT			\
+	X(touch_fmt_0x0,	0x0, 5, 10)	\
+	X(touch_fmt_0x1,	0x1, 6, 10)	\
+	X(touch_fmt_0x2,	0x2, 10, 5)	\
+	X(touch_fmt_0x3,	0x3, 10, 5)	\
+	X(touch_fmt_0x4,	0x4, 10, 5)	\
+	X(touch_fmt_0x10,	0x10, 10, 6)	\
+	X(touch_fmt_0x11,	0x11, 5, 10)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_touch_fmts {
+	ILITEK_TOUCH_REPORT_FORMAT
+	touch_fmt_max = 0x100,
+};
+#undef X
+
+#define ILITEK_PEN_REPORT_FORMAT		\
+	X(pen_fmt_0x0, 0x0, 12, 1)	\
+	X(pen_fmt_0x1, 0x1, 18, 1)	\
+	X(pen_fmt_0x2, 0x2, 22, 1)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_pen_fmts {
+	ILITEK_PEN_REPORT_FORMAT
+	pen_fmt_max = 0x100,
+};
+#undef X
+
+struct ilitek_slave_access {
+	u8 slave_id;
+	u8 func;
+	void *data;
+};
+
+struct tuning_para_settings {
+	u8 func;
+	u8 ctrl;
+	u8 type;
+
+	u8 *buf;
+	u32 len;
+};
+
+struct reports {
+	bool touch_need_update;
+	bool pen_need_update;
+
+	u8 touch[64];
+	u8 pen[64];
+};
+
+struct grid_data {
+	bool need_update;
+	unsigned int X, Y;
+
+	s32 *data;
+};
+
+struct grids {
+	struct grid_data mc;
+	struct grid_data sc_x;
+	struct grid_data sc_y;
+	struct grid_data pen_x;
+	struct grid_data pen_y;
+
+	struct grid_data key_mc;
+	struct grid_data key_x;
+	struct grid_data key_y;
+
+	struct grid_data self;
+
+	/* touch/pen debug message along with frame update */
+	struct reports dmsg;
+};
+
+enum ilitek_enum_type {
+	enum_ap_bl = 0,
+	enum_sw_reset,
+};
+
+typedef void (*update_grid_t)(u32, u32, struct grids *, void *);
+typedef void (*update_report_rate_t)(unsigned int);
+
+typedef int (*write_then_read_t)(u8 *, int, u8 *, int, void *);
+typedef int (*read_ctrl_in_t)(u8 *, int, unsigned int, void *);
+typedef int (*read_interrupt_in_t)(u8 *, int, unsigned int, void *);
+typedef void (*init_ack_t)(unsigned int, void *);
+typedef int (*wait_ack_t)(u8, unsigned int, void *);
+typedef int (*hw_reset_t)(unsigned int, void *);
+typedef int (*re_enum_t)(u8, void *);
+typedef void (*delay_ms_t)(unsigned int);
+
+
+typedef int (*write_then_read_direct_t)(u8 *, int, u8 *, int, void *);
+typedef void (*mode_switch_notify_t)(bool, bool, void *);
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_fmt {
+	u8 id : 6;
+	u8 status : 1;
+	u8 reserve : 1;
+	u16 x;
+	u16 y;
+	u8 pressure;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ touch_iwb_fmt {
+	u8 status : 3;
+	u8 reserve : 5;
+	u8 id : 6;
+	u8 reserve_1 : 2;
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ pen_fmt {
+	union __PACKED__ {
+		u8 modes;
+		struct __PACKED__ {
+			u8 tip_sw : 1;
+			u8 barrel_sw : 1;
+			u8 eraser : 1;
+			u8 invert : 1;
+			u8 in_range : 1;
+			u8 reserve : 3;
+		};
+	};
+	u16 x;
+	u16 y;
+	u16 pressure;
+	s16 x_tilt;
+	s16 y_tilt;
+
+	u8 battery;
+
+	union __PACKED__ {
+		/* usi v1.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 width;
+			u8 style;
+		} usi_1;
+
+		/* usi v2.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 color_24[3];
+			u8 no_color;
+			u8 width;
+			u8 style;
+		} usi_2;
+	};
+};
+
+struct __PACKED__ ilitek_report_fmt_info {
+	u32 touch_size;
+	u32 touch_max_cnt;
+
+	u32 pen_size;
+	u32 pen_max_cnt;
+};
+
+struct __PACKED__ ilitek_screen_info {
+	u16 x_min;
+	u16 y_min;
+	u16 x_max;
+	u16 y_max;
+	u16 pressure_min;
+	u16 pressure_max;
+	s16 x_tilt_min;
+	s16 x_tilt_max;
+	s16 y_tilt_min;
+	s16 y_tilt_max;
+	u16 pen_x_min;
+	u16 pen_y_min;
+	u16 pen_x_max;
+	u16 pen_y_max;
+};
+
+struct __PACKED__ ilitek_tp_info_v6 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u16 x_ch;
+	u16 y_ch;
+	u8 max_fingers;
+	u8 key_num;
+	u8 ic_num;
+	u8 support_modes;
+	u8 format;
+	u8 die_num;
+	u8 block_num;
+	u8 pen_modes;
+	u8 pen_format;
+	u16 pen_x_resolution;
+	u16 pen_y_resolution;
+};
+
+struct __PACKED__ ilitek_tp_info_v3 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u8 x_ch;
+	u8 y_ch;
+	u8 max_fingers;
+	u8 reserve;
+	u8 key_num;
+	u8 reserve_1;
+	u8 touch_start_y;
+	u8 touch_end_y;
+	u8 touch_start_x;
+	u8 touch_end_x;
+	u8 support_modes;
+};
+
+struct __PACKED__ ilitek_key_info_v6 {
+	u8 mode;
+	u16 x_len;
+	u16 y_len;
+
+	struct __PACKED__ _ilitek_key_info_v6 {
+		u8 id;
+		u16 x;
+		u16 y;
+	} keys[50];
+};
+
+struct __PACKED__ ilitek_key_info_v3 {
+	u8 x_len[2];
+	u8 y_len[2];
+
+	struct __PACKED__ _ilitek_key_info_v3 {
+		u8 id;
+		u8 x[2];
+		u8 y[2];
+	} keys[20];
+};
+
+struct __PACKED__ ilitek_ts_kernel_info {
+	char ic_name[6];
+	char mask_ver[2];
+	u32 mm_addr;
+	u32 min_addr;
+	u32 max_addr;
+	char module_name[32];
+
+	char ic_full_name[16];
+};
+
+struct __PACKED__ ilitek_key_info {
+	struct ilitek_key_info_v6 info;
+	bool clicked[50];
+};
+
+struct __PACKED__ ilitek_power_status {
+	u16 header;
+	u8 vdd33_lvd_flag;
+	u8 vdd33_lvd_level_sel;
+};
+
+struct __PACKED__ ilitek_sensor_id {
+	u16 header;
+	u8 id;
+};
+
+struct __PACKED__ ilitek_func_mode {
+	u16 header;
+	u8 mode;
+};
+
+struct __PACKED__ ilitek_ts_protocol {
+	u32 ver;
+	u8 flag;
+};
+
+struct __PACKED__ ilitek_ts_ic {
+	u8 mode;
+	u32 crc[ILTIEK_MAX_BLOCK_NUM];
+
+	char mode_str[32];
+};
+
+struct __PACKED__ ilitek_hid_info {
+	u16 pid;
+	u16 vid;
+	u16 rev;
+};
+
+struct __PACKED__ freq_category {
+	u32 start;
+	u32 end;
+	u32 step;
+	u32 steps;
+
+		u32 size;
+	char limit[1024];
+
+	u8 mode;
+
+	s32 *data;
+};
+
+struct __PACKED__ freq_settings {
+	bool prepared;
+
+	unsigned int frame_cnt;
+
+	/* add from v6.0.A */
+	unsigned int mc_frame_cnt;
+	unsigned int dump_frame_cnt;
+
+	unsigned int scan_type;
+
+	struct freq_category sine;
+	struct freq_category mc_swcap;
+	struct freq_category sc_swcap;
+	struct freq_category pen;
+
+	struct freq_category dump1;
+	struct freq_category dump2;
+	u8 dump1_val;
+	u8 dump2_val;
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ short_settings {
+	bool prepared;
+
+	u8 dump_1;
+	u8 dump_2;
+	u8 v_ref_L;
+	u16 post_idle;
+};
+
+struct __PACKED__ open_settings {
+	bool prepared;
+
+	u16 freq;
+	u8 gain;
+	u8 gain_rfb;
+	u8 afe_res_sel;
+	u8 mc_fsel;
+	u16 frame;
+};
+
+struct __PACKED__ p2p_settings {
+	bool prepared;
+
+	u16 frame_cnt;
+	u8 type;
+
+	/* add from v6.0.A */
+	u16 freq;
+};
+
+struct __PACKED__ charge_curve_sweep {
+	u16 start;
+	u16 end;
+	u8 step;
+	u16 post_idle;
+	u16 fix_val;
+
+	u16 steps;
+};
+
+struct __PACKED__ charge_curve_settings {
+	bool prepared;
+
+	u8 scan_mode;
+
+	struct charge_curve_sweep dump;
+	struct charge_curve_sweep charge;
+
+	u16 c_sub;
+	u16 frame_cnt;
+
+	struct __PACKED__ charge_curve_point {
+		u16 x;
+		u16 y;
+		u16 *dump_max;
+		u16 *dump_avg;
+		u16 *charge_max;
+		u16 *charge_avg;
+	} pt[9];
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ cdc_settings {
+	u8 cmd;
+	u16 config;
+
+	bool skip_checksum;
+
+	/* freq. */
+	struct freq_settings freq;
+	/* short */
+	struct short_settings _short;
+	/* open */
+	struct open_settings open;
+	/* p2p */
+	struct p2p_settings p2p;
+	/* charge curve */
+	struct charge_curve_settings curve;
+
+	/* status only writable by CDC commonflow */
+	bool is_key;
+	bool is_p2p;
+	bool is_freq;
+	bool is_curve;
+	bool is_short;
+	bool is_open;
+	bool is_16bit;
+	bool is_sign;
+	bool is_fast_mode;
+	unsigned int total_bytes;
+
+	/* error code during cdc data collection */
+	s32 error;
+};
+
+struct __PACKED__ mp_station_old {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test:2;
+		u8 open_test:2;
+		u8 self_test:2;
+		u8 uniform_test:2;
+
+		u8 dac_test:2;
+		u8 key_test:2;
+		u8 final_result:2;
+		u8 paint_test:2;
+
+		u8 mopen_test:2;
+		u8 gpio_test:2;
+		u8 reserve_1:4;
+
+		char bar_code[28];
+		u8 reserve_2[35];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[10];
+};
+
+struct __PACKED__ mp_station {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test : 2;
+		u8 open_test : 2;
+		u8 self_test : 2;
+		u8 uniform_test : 2;
+
+		u8 dac_test : 2;
+		u8 key_test : 2;
+		u8 final_result : 2;
+		u8 paint_test : 2;
+
+		u8 mopen_test : 2;
+		u8 gpio_test : 2;
+		u8 reserve : 4;
+
+		u8 tool_ver[8];
+		char bar_code[135];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[5];
+
+	struct __PACKED__ {
+		u8 reserve_1[91];
+		u32 mp_result_ver;
+		u16 customer_id;
+		u16 fwid;
+		u8 reserve_2;
+	} info;
+
+	u16 crc;
+};
+
+struct __PACKED__ ilitek_ts_settings {
+	bool no_retry;
+	bool no_INT_ack;
+
+	bool sw_reset_at_last;
+
+	u8 sensor_id_mask;
+
+	/* only used for QUIRK_WAIT_ACK_DELAY */
+	u32 wait_ack_delay;
+
+	/*
+	 * engineer mode would likely report default format
+	 * ex. IWB-format
+	 */
+	bool default_format_enabled;
+};
+
+struct __PACKED__ ilitek_sys_info {
+	u16 pid;
+};
+
+struct __PACKED__ ilitek_ts_callback {
+	/* Please don't use "repeated start" for I2C interface */
+	write_then_read_t write_then_read;
+	read_ctrl_in_t read_ctrl_in;
+	read_interrupt_in_t read_interrupt_in;
+	init_ack_t init_ack;
+	wait_ack_t wait_ack;
+	hw_reset_t hw_reset;
+	re_enum_t re_enum;
+	delay_ms_t delay_ms;
+	msg_t msg;
+
+	/* write cmd without adding any hid header */
+	write_then_read_direct_t write_then_read_direct;
+	/* notify caller after AP/BL mode switch command */
+	mode_switch_notify_t mode_switch_notify;
+};
+
+struct __PACKED__ ilitek_common_info {
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen;
+	struct ilitek_tp_info_v6 tp;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu;
+	struct ilitek_hid_info hid;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+};
+
+struct __PACKED__ ilitek_ts_device {
+	void *_private;
+	char id[64];
+	u32 reset_time;
+
+	struct ilitek_ts_settings setting;
+
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen_info;
+	struct ilitek_tp_info_v6 tp_info;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu_info;
+	struct ilitek_hid_info hid_info;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+
+	u8 fw_mode;
+	struct mp_station mp;
+
+	u8 wbuf[4096];
+	u8 rbuf[4096];
+	struct ilitek_ts_callback cb;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16 __DLL le16(const u8 *p);
+u16 __DLL be16(const u8 *p);
+u32 __DLL le32(const u8 *p, int bytes);
+u32 __DLL be32(const u8 *p, int bytes);
+
+bool __DLL is_29xx(void *handle);
+
+bool __DLL _is_231x(char *ic_name);
+bool __DLL is_231x(void *handle);
+
+bool __DLL has_hw_key(void *handle);
+
+u8 __DLL get_protocol_ver_flag(u32 ver);
+
+int __DLL grid_alloc(void *handle, struct grids *grid);
+void __DLL grid_free(struct grids *grid);
+void __DLL grid_reset(struct grids *grid);
+
+u16 __DLL get_crc(u32 start, u32 end,
+		  u8 *buf, u32 buf_size);
+
+u32 __DLL get_checksum(u32 start, u32 end,
+		       u8 *buf, u32 buf_size);
+
+bool __DLL is_checksum_matched(u8 checksum, int start, int end,
+			       u8 *buf, int buf_size);
+
+bool __DLL support_sensor_id(void *handle);
+bool __DLL support_production_info(void *handle);
+bool __DLL support_fwid(void *handle);
+bool __DLL support_mcu_info(void *handle);
+bool __DLL support_power_status(void *handle);
+int __DLL write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms);
+
+int __DLL bridge_set_int_monitor(void *handle, bool enable);
+int __DLL bridge_set_test_mode(void *handle, bool enable);
+
+int __DLL reset_helper(void *handle);
+
+int __DLL write_then_read(void *handle, u8 *cmd, int wlen,
+			  u8 *buf, int rlen);
+int __DLL write_then_read_direct(void *handle, u8 *cmd, int wlen,
+				 u8 *buf, int rlen);
+int __DLL read_interrupt_in(void *handle, u8 *buf, int rlen,
+			    unsigned int timeout_ms);
+int __DLL read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		       unsigned int timeout_ms);
+
+void __DLL __ilitek_get_info(void *handle,
+			     struct ilitek_common_info *info);
+
+void __DLL ilitek_dev_set_quirks(void *handle, u32 quirks);
+void __DLL ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys);
+void __DLL ilitek_dev_setting(void *handle,
+			      struct ilitek_ts_settings *setting);
+
+void __DLL ilitek_dev_bind_callback(void *handle,
+				    struct ilitek_ts_callback *callback);
+
+void __DLL *ilitek_dev_init(u8 _interface, const char *id,
+			    bool need_update_ts_info,
+			    struct ilitek_ts_callback *callback,
+			    void *_private);
+void __DLL ilitek_dev_exit(void *handle);
+
+void __DLL api_print_ts_info(void *handle);
+void __DLL api_read_then_print_m2v_info(void *handle);
+
+int __DLL api_update_ts_info(void *handle);
+
+int __DLL api_protocol_set_cmd(void *handle, u8 idx, void *data);
+int __DLL api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force);
+
+u16 __DLL api_get_block_crc_by_addr(void *handle, u8 type,
+				    u32 start, u32 end);
+u16 __DLL api_get_block_crc_by_num(void *handle, u8 type,
+				   u8 block_num);
+
+int __DLL api_set_data_len(void *handle, u16 data_len);
+int __DLL api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			      u32 start, u32 end);
+int __DLL api_write_data_v6(void *handle, int wlen);
+int __DLL api_access_slave(void *handle, u8 id, u8 func, void *data);
+int __DLL api_check_busy(void *handle, int timeout_ms, int delay_ms);
+int __DLL api_write_enable_v3(void *handle, bool in_ap,
+			      bool write_ap, u32 end, u32 checksum);
+int __DLL api_write_data_v3(void *handle);
+
+int __DLL api_to_bl_mode(void *handle, bool bl, u32 start, u32 end);
+
+int __DLL api_write_data_m2v(void *handle, int wlen);
+int __DLL api_to_bl_mode_m2v(void *handle, bool to_bl);
+
+int __DLL api_set_idle(void *handle, bool enable);
+int __DLL api_set_func_mode(void *handle, u8 mode);
+int __DLL api_get_func_mode(void *handle);
+
+int __DLL api_erase_data_v3(void *handle);
+
+int __DLL api_read_flash(void *handle, u8 *buf,
+			 u32 start_addr, u32 len);
+
+int __DLL _api_read_mp_result(void *handle);
+int __DLL api_read_mp_result(void *handle);
+int __DLL _api_write_mp_result(void *handle, struct mp_station *mp);
+int __DLL api_write_mp_result(void *handle, struct mp_station *mp);
+void __DLL api_decode_mp_result(void *handle);
+
+int __DLL api_read_tuning(void *handle, u8 *buf, int rlen);
+
+int __DLL api_get_ic_crc(void *handle, u8 final_fw_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.c b/drivers/input/touchscreen/ilitek/ilitek_report.c
new file mode 100644
index 000000000000..dbe8abfd64ef
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_report.h"
+#include "ilitek_crypto.h"
+
+static bool is_debug_packet_id(u8 id, u8 _interface)
+{
+	return ((_interface == interface_i2c && id == 0xdb) ||
+		(_interface == interface_usb && id == 0xaa));
+}
+
+static bool is_pen_packet_id(u8 id)
+{
+	return (id == 0x0c || id == 0x0d);
+}
+
+static void touch_decode(struct ilitek_ts_device *dev,
+			 struct ilitek_report *report,
+			 u8 *buf, u8 cnt)
+{
+	struct touch_fmt *parser, *finger;
+	struct touch_iwb_fmt *parser_iwb;
+	u8 i, j, offset;
+	u32 size = dev->fmt.touch_size;
+
+	offset = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	for (i = 0; i < cnt && i < dev->tp_info.max_fingers; i++) {
+		finger = &report->touch.finger[i];
+
+		do {
+			if (dev->protocol.flag == PTL_V3 &&
+			    dev->_interface == interface_i2c) {
+				finger->id = i;
+				finger->status = buf[1 + offset + i * 5] >> 7;
+				finger->x = ((buf[1 + offset + i * 5] & 0x3F) << 8) +
+					buf[2 + offset + i * 5];
+				finger->y = ((buf[3 + offset + i * 5] & 0x3F) << 8) +
+					buf[4 + offset + i * 5];
+				finger->pressure = buf[5 + offset + i * 5];
+				finger->height = 128;
+				finger->width = 1;
+
+				break;
+			}
+
+			parser = (struct touch_fmt *)
+				(buf + 1 + i * size);
+			parser_iwb = (struct touch_iwb_fmt *)
+				(buf + 1 + i * size);
+
+			finger->id = parser->id;
+			finger->status = parser->status;
+			finger->x = parser->x;
+			finger->y = parser->y;
+
+			if (dev->setting.default_format_enabled)
+				break;
+
+			switch (dev->tp_info.format) {
+			case touch_fmt_0x1:
+				finger->pressure = parser->pressure;
+				break;
+			case touch_fmt_0x2:
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x3:
+				finger->pressure = parser->pressure;
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x4:
+				finger->id = parser_iwb->id;
+				finger->status =
+					(parser_iwb->status == 0x7) ? 1 : 0;
+				finger->x = parser_iwb->x;
+				finger->y = parser_iwb->y;
+				finger->width = parser_iwb->width;
+				finger->height = parser_iwb->height;
+				break;
+			case touch_fmt_0x10:
+				finger->width = le16(buf + i * size + 6);
+				finger->height = le16(buf + i * size + 8);
+				finger->algo = buf[i * size + 10];
+				report->touch.algo = 0;
+				break;
+			case touch_fmt_0x11:
+				finger->id =
+					(!finger->id) ? 0 : finger->id - 1;
+				break;
+			}
+		} while (false);
+
+		TP_DBG(dev->id,
+		       "[touch-report] id:%hhu, status:%hhu, x:%hu, y:%hu, p:%hhu, w:%hu, h:%hu, algo: 0x%hhx\n",
+			finger->id, finger->status, finger->x, finger->y,
+			finger->pressure, finger->width, finger->height,
+			finger->algo);
+
+		if (finger->id >= dev->tp_info.max_fingers) {
+			TP_ERR(dev->id, "invalid touch id: %hhu >= %hhu\n",
+				finger->id, dev->tp_info.max_fingers);
+			return;
+		}
+
+		/*
+		 * if x/y within key's range, skip touch range check.
+		 */
+		for (j = 0; j < dev->tp_info.key_num; j++) {
+			if ((finger->x < dev->key.info.keys[j].x &&
+			     finger->x > dev->key.info.keys[j].x +
+				     dev->key.info.x_len) &&
+			    (finger->y < dev->key.info.keys[j].y &&
+			     finger->y > dev->key.info.keys[j].y +
+				     dev->key.info.y_len))
+				continue;
+
+			goto skip_touch_range_check;
+		}
+
+		if (finger->status &&
+		    (finger->x - 1 > dev->screen_info.x_max ||
+		     finger->y - 1 > dev->screen_info.y_max ||
+		     finger->x < dev->screen_info.x_min ||
+		     finger->y < dev->screen_info.y_min)) {
+			TP_ERR(dev->id, "Point[%d]: (%d, %d), Limit: (%d:%d, %d:%d) OOB\n",
+				finger->id, finger->x, finger->y,
+				dev->screen_info.x_min, dev->screen_info.x_max,
+				dev->screen_info.y_min, dev->screen_info.y_max);
+			return;
+		}
+
+skip_touch_range_check:
+		continue;
+	}
+
+	report->touch.cnt = i;
+
+	/*
+	 * report touch event callback,
+	 * which includes actual count of finger report just parsed above.
+	 */
+	if (report->cb.report_touch_event)
+		report->cb.report_touch_event(&report->touch, report->_private);
+}
+
+static void pen_decode(struct ilitek_ts_device *dev,
+		       struct ilitek_report *report,
+		       u8 *buf)
+{
+	struct pen_fmt *parser = (struct pen_fmt *)(buf + 1);
+	struct pen_fmt *pen = &report->pen.pen;
+
+	report->pen.cnt = buf[61];
+	report->pen.algo = buf[62];
+
+	memcpy(pen, parser, sizeof(struct pen_fmt));
+
+	switch (dev->tp_info.pen_format) {
+	default:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery);
+		break;
+	case 1:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_1.barrel_pressure,
+			pen->usi_1.idx, pen->usi_1.color, pen->usi_1.width,
+			pen->usi_1.style);
+		break;
+	case 2:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color24: %u, no_color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_2.barrel_pressure,
+			pen->usi_2.idx, le32(pen->usi_2.color_24, 3),
+			pen->usi_2.no_color, pen->usi_2.width,
+			pen->usi_2.style);
+		break;
+	}
+
+	/* report pen event callback */
+	if (report->cb.report_pen_event)
+		report->cb.report_pen_event(&report->pen, report->_private);
+}
+
+static void dmsg_decode(struct ilitek_ts_device *dev,
+			struct ilitek_report *report,
+			u8 *buf, int buf_size)
+{
+	if ((int)buf[1] >= buf_size)
+		return;
+
+	buf[buf[1]] = '\0';
+	TP_DBG(dev->id, "%s\n", (char *)(buf + 2));
+
+	if (report->cb.report_dmsg)
+		report->cb.report_dmsg((char *)(buf + 2),
+					buf_size - 2, report->_private);
+}
+
+static void report_buf(struct ilitek_report *report, u8 *buf,
+		       bool is_last)
+{
+	if (!report->cb.report_buf)
+		return;
+
+	report->cb.report_buf(buf, 64, is_last, report->_private);
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v3(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+
+	u8 cnt;
+	int idx = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	UNUSED(buf_size);
+
+	if (dev->_interface == interface_i2c) {
+		if (dev->quirks & QUIRK_BRIDGE) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+
+			if (buf[0] != 0x03 || buf[1] != 0xa3 || buf[2] != 0x10)
+				return -EAGAIN;
+
+			/* move algo byte to index 62 */
+			buf[62] = buf[35];
+		} else if (dev->quirks & QUIRK_DAEMON_I2C ||
+			   dev->quirks & QUIRK_BRIDGE ||
+			   dev->quirks & QUIRK_WIFI_ITS_I2C) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+		} else {
+			dev->wbuf[0] = 0x10;
+			error = write_then_read(dev, dev->wbuf, 1, buf, 32);
+			if (error < 0)
+				return error;
+
+			report->touch.algo = buf[31];
+			buf[31] = 0;
+
+			if (buf[0] == 2) {
+				error = write_then_read(dev, NULL, 0,
+							 buf + 31, 20);
+				if (error < 0)
+					return error;
+			}
+
+			/* move algo byte to index 62 */
+			buf[62] = report->touch.algo;
+		}
+
+		switch (dev->rbuf[idx]) {
+		default:
+		case 1:
+			cnt = 6;
+			break;
+		case 0:
+			cnt = 0;
+			break;
+		case 2:
+			cnt = 10;
+			break;
+		}
+	} else {
+		error = read_interrupt_in(dev, buf, 64, 1000);
+		if (error < 0)
+			return error;
+
+		cnt = buf[55];
+
+		/* move algo byte to index 62 */
+		buf[62] = buf[56];
+	}
+
+	report->touch.algo = buf[62];
+
+	report->touch.dbg_size = 64;
+	memcpy(report->touch.dbg, buf, 64);
+
+	report_buf(report, buf, true);
+
+	return cnt;
+}
+
+static bool is_iwb_fmt(struct ilitek_ts_device *dev)
+{
+	return dev->tp_info.format == touch_fmt_0x4 &&
+		!dev->setting.default_format_enabled;
+}
+
+static bool need_skip_checksum(struct ilitek_ts_device *dev,
+			       struct ilitek_report *report,
+			       u8 packet_id, bool is_first)
+{
+	/*
+	 * don't check checksum for below situation:
+	 *   1. debug packet
+	 *   2. normal mode pen packet w/ HID.
+	 *   3. IWB format packet
+	 */
+
+	if (report->skip_checksum)
+		return true;
+
+	if (is_first) {
+		if (is_debug_packet_id(packet_id, dev->_interface))
+			return true;
+
+		if ((dev->_interface == interface_usb ||
+		     dev->_interface == interface_hid_over_i2c) &&
+		    packet_id == 0x0d)
+			return true;
+	}
+
+	if (is_iwb_fmt(dev))
+		return true;
+
+	return false;
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v6(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+	u8 i, cnt;
+	u8 size = dev->fmt.touch_size, max_cnt = dev->fmt.touch_max_cnt;
+	u8 tmp[64];
+
+	UNUSED(buf_size);
+
+	error = read_interrupt_in(dev, tmp, 64, 1000);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.format == 5)
+		ilitek_decrypt(tmp + 1, 48);
+
+	/* set packet id to 0x48 forcely for using BRIDGE finger report */
+	if (dev->quirks & QUIRK_BRIDGE && !is_pen_packet_id(tmp[0]))
+		tmp[0] = 0x48;
+
+	if (!need_skip_checksum(dev, report, tmp[0], true) &&
+	    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+		return -EILIPROTO;
+
+	report->pen.algo = tmp[62];
+	report->touch.algo = tmp[62];
+	report->pen.dbg_size = 64;
+	report->touch.dbg_size = 64;
+	memcpy(report->pen.dbg, tmp, 64);
+	memcpy(report->touch.dbg, tmp, 64);
+	memcpy(buf, tmp, 64);
+
+	/*
+	 * no need to check contact count byte for debug packet and pen packet.
+	 */
+	if (is_pen_packet_id(tmp[0]) ||
+	    is_debug_packet_id(tmp[0], dev->_interface)) {
+		report_buf(report, tmp, true);
+		return 0;
+	}
+
+	cnt = is_iwb_fmt(dev) ? tmp[55] : tmp[61];
+	for (i = 1; i < DIV_ROUND_UP(cnt, max_cnt); i++) {
+		report_buf(report, tmp, false);
+
+		error = read_interrupt_in(dev, tmp, 64, 1000);
+		if (error < 0)
+			return error;
+
+		if (dev->tp_info.format == 5)
+			ilitek_decrypt(tmp + 1, 48);
+
+		if (!need_skip_checksum(dev, report, tmp[0], false) &&
+		    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+			return -EILIPROTO;
+
+		/* copy and skip the first rid byte */
+		memcpy(buf + i * size * max_cnt + 1, tmp + 1, 63);
+	}
+	report_buf(report, tmp, true);
+
+	return cnt;
+}
+
+int ilitek_report_update(void *handle, struct ilitek_report *report)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt;
+
+	if (!dev)
+		return -EINVAL;
+
+	/* initial report event */
+	memset(&report->touch, 0, sizeof(report->touch));
+	memset(&report->pen, 0, sizeof(report->pen));
+
+	memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (dev->protocol.flag) {
+	default: return -EPERM;
+	case PTL_V3:
+		cnt = report_get_raw_v3(dev, report, dev->rbuf,
+					 sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		break;
+
+	case PTL_V6:
+		cnt = report_get_raw_v6(dev, report, dev->rbuf,
+					sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		/* pen packet (V6 only) */
+		if (is_pen_packet_id(dev->rbuf[0])) {
+			pen_decode(dev, report, dev->rbuf);
+			return 0;
+		}
+
+		break;
+	}
+
+	/* debug message packet */
+	if (is_debug_packet_id(dev->rbuf[0], dev->_interface)) {
+		dmsg_decode(dev, report, dev->rbuf, sizeof(dev->rbuf));
+		return 0;
+	}
+
+	touch_decode(dev, report, dev->rbuf, cnt);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.h b/drivers/input/touchscreen/ilitek/ilitek_report.h
new file mode 100644
index 000000000000..e49333f9618b
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_REPORT_H__
+#define __ILITEK_REPORT_H__
+
+#include "ilitek_def.h"
+#include "ilitek_protocol.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_data {
+	struct touch_fmt finger[40];
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+struct __PACKED__ pen_data {
+	struct pen_fmt pen;
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return touch event */
+typedef void(*report_touch_event_t)(struct touch_data *, void *);
+/* return pen event */
+typedef void(*report_pen_event_t)(struct pen_data *, void *);
+/* return debug msg */
+typedef void(*report_dmsg_t)(char *, int, void *);
+/* return raw data buf */
+typedef void(*report_buf_t)(u8 *, int, bool, void *);
+
+struct ilitek_report_callback {
+	report_touch_event_t report_touch_event;
+	report_pen_event_t report_pen_event;
+	report_dmsg_t report_dmsg;
+	report_buf_t report_buf;
+};
+
+struct ilitek_report {
+	struct touch_data touch;
+	struct pen_data pen;
+	struct ilitek_report_callback cb;
+
+	bool skip_checksum;
+
+	void *_private;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	int __DLL ilitek_report_update(void *handle, struct ilitek_report *report);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_tool.c b/drivers/input/touchscreen/ilitek/ilitek_tool.c
new file mode 100644
index 000000000000..689c1c09d397
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_tool.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+#include <linux/firmware.h>
+
+#include <linux/miscdevice.h>
+
+static struct proc_dir_entry *ilitek_proc;
+static struct proc_dir_entry *ilitek_proc_entry;
+
+#define ILITEK_IOCTL_BASE                       100
+#define ILITEK_IOCTL_I2C_WRITE_DATA             _IOWR(ILITEK_IOCTL_BASE, 0, uint64_t)
+#define ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT      _IOWR(ILITEK_IOCTL_BASE, 0, uint32_t)
+#define ILITEK_IOCTL_I2C_WRITE_LENGTH           _IOWR(ILITEK_IOCTL_BASE, 1, int32_t)
+#define ILITEK_IOCTL_I2C_READ_DATA              _IOWR(ILITEK_IOCTL_BASE, 2, uint64_t)
+#define ILITEK_IOCTL_I2C_READ_DATA_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 2, uint32_t)
+#define ILITEK_IOCTL_I2C_READ_LENGTH            _IOWR(ILITEK_IOCTL_BASE, 3, int32_t)
+#define ILITEK_IOCTL_DRIVER_INFORMATION		_IOWR(ILITEK_IOCTL_BASE, 8, int32_t)
+#define ILITEK_IOCTL_USB_UPDATE_RESOLUTION      _IOWR(ILITEK_IOCTL_BASE, 9, int32_t)
+#define ILITEK_IOCTL_I2C_INT_FLAG	        _IOWR(ILITEK_IOCTL_BASE, 10, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE                 _IOWR(ILITEK_IOCTL_BASE, 11, int32_t)
+#define ILITEK_IOCTL_STOP_READ_DATA             _IOWR(ILITEK_IOCTL_BASE, 12, int32_t)
+#define ILITEK_IOCTL_START_READ_DATA            _IOWR(ILITEK_IOCTL_BASE, 13, int32_t)
+#define ILITEK_IOCTL_I2C_SWITCH_IRQ		_IOWR(ILITEK_IOCTL_BASE, 15, int32_t)
+#define ILITEK_IOCTL_UPDATE_FLAG		_IOWR(ILITEK_IOCTL_BASE, 16, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE_FW		_IOWR(ILITEK_IOCTL_BASE, 18, int32_t)
+#define ILITEK_IOCTL_RESET			_IOWR(ILITEK_IOCTL_BASE, 19, int32_t)
+#define ILITEK_IOCTL_INT_STATUS			_IOWR(ILITEK_IOCTL_BASE, 20, int32_t)
+
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#define ILITEK_IOCTL_DEBUG_SWITCH		_IOWR(ILITEK_IOCTL_BASE, 21, int32_t)
+#endif
+
+#define ILITEK_IOCTL_I2C_INT_CLR		_IOWR(ILITEK_IOCTL_BASE, 22, int32_t)
+#define ILITEK_IOCTL_I2C_INT_POLL		_IOWR(ILITEK_IOCTL_BASE, 23, uint64_t)
+#define ILITEK_IOCTL_I2C_INT_POLL_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 23, uint32_t)
+#define ILITEK_IOCTL_I2C_ISR_TYPE		_IOWR(ILITEK_IOCTL_BASE, 24, uint32_t)
+#define ILITEK_IOCTL_I2C_NETLINK		_IOWR(ILITEK_IOCTL_BASE, 25, uint16_t)
+
+
+#define ILITEK_DEVICE_NODE_PERMISSON			0666
+
+static s32 ilitek_file_open(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = true;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static s32 ilitek_file_close(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = false;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static ssize_t ilitek_file_write(struct file *filp,
+				 const char *buf, size_t size, loff_t *f_pos)
+{
+	s32 ret = 0, count = 0;
+	u8 buffer[512];
+	u32 *data;
+	char *token = NULL, *cur = NULL;
+
+	if (size > sizeof(buffer)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(buffer));
+		return -EINVAL;
+	}
+
+	ret = copy_from_user(buffer, buf, size);
+	if (ret < 0) {
+		TP_ERR(NULL, "copy data from user space, failed");
+		return -1;
+	}
+
+	token = cur = buffer;
+
+	data = kcalloc(size, sizeof(u32), GFP_KERNEL);
+
+	while ((token = strsep(&cur, ",")) != NULL) {
+		//data[count] = str2hex(token);
+		sscanf(token, "%x", &data[count]);
+		TP_MSG(NULL, "data[%d] = %x\n", count, data[count]);
+		count++;
+	}
+
+	if (buffer[size - 2] == 'I' && (size == 20 || size == 52) && buffer[0] == 0x77 && buffer[1] == 0x77) {
+
+		TP_MSG(NULL, "IOCTL_WRITE CMD = %d\n", buffer[2]);
+		switch (buffer[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+#ifdef ILITEK_TUNING_MESSAGE
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", buffer[3]);
+			if (buffer[3] == 0) {
+				ilitek_debug_flag = false;
+			} else if (buffer[3] == 1) {
+				ilitek_debug_flag = true;
+			}
+			break;
+#endif
+		case 15:
+			if (buffer[3] == 0)
+				ilitek_irq_disable();
+			else
+				ilitek_irq_enable();
+
+			break;
+		case 16:
+			ts->operation_protection = buffer[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 18:
+			ilitek_irq_disable();
+			mutex_lock(&ts->ilitek_mutex);
+			ret = ilitek_write(&buffer[3], 33);
+			mutex_unlock(&ts->ilitek_mutex);
+			ilitek_irq_enable();
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+
+			return ret;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	if (buffer[size - 2] == 'W') {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write(buffer, size - 2);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			return ret;
+		}
+	} else if (!strncmp(buffer, "unhandle_irq", strlen("unhandle_irq"))) {
+		ts->unhandle_irq = !ts->unhandle_irq;
+		TP_MSG(NULL, "ts->unhandle_irq = %d.\n", ts->unhandle_irq);
+	} else if (!strncmp(buffer, "dbg_pkt", strlen("dbg_pkt"))) {
+		set_log_level(log_level_pkt);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_pkt);
+	} else if (!strncmp(buffer, "dbg_debug", strlen("dbg_debug"))) {
+		set_log_level(log_level_dbg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_dbg);
+	} else if (!strncmp(buffer, "dbg_info", strlen("dbg_info"))) {
+		set_log_level(log_level_msg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_msg);
+	} else if (!strncmp(buffer, "dbg_err", strlen("dbg_err"))) {
+		set_log_level(log_level_err);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_err);
+	} else if (!strncmp(buffer, "dbg_num", strlen("dbg_num"))) {
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", tp_log_level);
+	}
+#ifdef ILITEK_TUNING_MESSAGE
+	else if (!strncmp(buffer, "truning_dbg_flag", strlen("truning_dbg_flag"))) {
+		ilitek_debug_flag = !ilitek_debug_flag;
+		TP_MSG(NULL, " %s debug_flag message(%X).\n", ilitek_debug_flag ? "Enabled" : "Disabled", ilitek_debug_flag);
+	}
+#endif
+	else if (!strncmp(buffer, "irq_status", strlen("irq_status"))) {
+		TP_MSG(NULL, "gpio_get_value(i2c.irq_gpio) = %d.\n", gpio_get_value(ts->irq_gpio));
+	} else if (!strncmp(buffer, "enable", strlen("enable"))) {
+		ilitek_irq_enable();
+		TP_MSG(NULL, "irq enable\n");
+	} else if (!strncmp(buffer, "disable", strlen("disable"))) {
+		ilitek_irq_disable();
+		TP_MSG(NULL, "irq disable\n");
+	} else if (!strncmp(buffer, "info", strlen("info"))) {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		api_update_ts_info(ts->dev);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+	} else if (!strncmp(buffer, "reset", strlen("reset"))) {
+		ilitek_reset(ts->dev->reset_time);
+	}
+
+	TP_DBG(NULL, "ilitek return count = %zu\n", size);
+	kfree(data);
+	return size;
+}
+
+static FOPS_IOCTL_FUNC(ilitek_file_ioctl, uint32_t cmd, unsigned long arg)
+{
+	static u8 *buffer;
+	static unsigned long len;
+	s32 ret = 0;
+	int tmp;
+
+	buffer = kmalloc(ILITEK_IOCTL_MAX_TRANSFER, GFP_KERNEL);
+	memset(buffer, 0, ILITEK_IOCTL_MAX_TRANSFER);
+
+	switch (cmd) {
+	case ILITEK_IOCTL_I2C_WRITE_DATA:
+	case ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid write len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(buffer, (u8 *)arg, len)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, len, 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write failed, cmd: %x\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_I2C_READ_DATA:
+	case ILITEK_IOCTL_I2C_READ_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid read len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(NULL, 0, 0, buffer, len);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c read failed, buf: %x\n", buffer[0]);
+			break;
+		}
+
+		if (copy_to_user((u8 *)arg, buffer, len)) {
+			ret = -EFAULT;
+			TP_ERR(NULL, "copy data to user space, failed\n");
+		}
+		break;
+	case ILITEK_IOCTL_I2C_WRITE_LENGTH:
+	case ILITEK_IOCTL_I2C_READ_LENGTH:
+		len = arg;
+		break;
+	case ILITEK_IOCTL_DRIVER_INFORMATION:
+		memcpy(buffer, driver_ver, 7);
+		if (copy_to_user((u8 *)arg, buffer, 7))
+			ret = -EFAULT;
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE:
+		break;
+	case ILITEK_IOCTL_I2C_INT_FLAG:
+		buffer[0] = !(gpio_get_value(ts->irq_gpio));
+		if (copy_to_user((u8 *)arg, buffer, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_FLAG = %d.\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_START_READ_DATA:
+		ilitek_irq_enable();
+		ts->unhandle_irq = false;
+		TP_MSG(NULL, "enable_irq and ts->unhandle_irq = false.\n");
+		break;
+	case ILITEK_IOCTL_STOP_READ_DATA:
+		ilitek_irq_disable();
+		ts->unhandle_irq = true;
+		TP_MSG(NULL, "disable_irq and ts->unhandle_irq = true.\n");
+		break;
+	case ILITEK_IOCTL_RESET:
+		ilitek_reset(ts->dev->reset_time);
+		break;
+	case ILITEK_IOCTL_INT_STATUS:
+		if (put_user(gpio_get_value(ts->irq_gpio), (s32 *)arg))
+			ret = -EFAULT;
+		break;
+#ifdef ILITEK_TUNING_MESSAGE
+	case ILITEK_IOCTL_DEBUG_SWITCH:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+		TP_MSG(NULL, "ilitek The debug_flag = %d.\n", buffer[0]);
+		if (buffer[0] == 0)
+			ilitek_debug_flag = false;
+		else if (buffer[0] == 1)
+			ilitek_debug_flag = true;
+		break;
+#endif
+	case ILITEK_IOCTL_I2C_SWITCH_IRQ:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		if (buffer[0] == 0)
+			ilitek_irq_disable();
+		else
+			ilitek_irq_enable();
+
+		break;
+	case ILITEK_IOCTL_UPDATE_FLAG:
+		ts->operation_protection = arg;
+		TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE_FW:
+		if (copy_from_user(buffer, (u8 *)arg, 35)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, buffer[34], 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write, failed\n");
+
+		break;
+	case ILITEK_IOCTL_I2C_INT_CLR:
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_CLR, set get_INT false\n");
+		atomic_set(&ts->get_INT, 0);
+		break;
+	case ILITEK_IOCTL_I2C_INT_POLL:
+	case ILITEK_IOCTL_I2C_INT_POLL_COMPAT:
+		tmp = atomic_read(&ts->get_INT);
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_POLL, get_INT: %d\n", tmp);
+
+		if (copy_to_user((u8 *)arg, &tmp, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+		}
+		break;
+	case ILITEK_IOCTL_I2C_ISR_TYPE:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_ISR_TYPE, set ISR type: %lu\n", arg);
+		ts->irq_handle_type = (arg >> 16);
+		ts->irq_read_len = arg & 0xFFFF;
+		break;
+	case ILITEK_IOCTL_I2C_NETLINK:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_NETLINK, set netlink: %s with ETH: %hhu\n",
+			(arg >> 8) ? "ON" : "OFF", (u8)(arg & 0xFF));
+
+		if (arg >> 8)
+			ret = ilitek_netlink_init(arg & 0xFF);
+		else
+			ilitek_netlink_exit();
+
+		break;
+	default:
+		TP_ERR(NULL, "unrecognized ioctl cmd: 0x%04x\n", cmd);
+		ret = -EINVAL;
+		break;
+	}
+
+	kfree(buffer);
+	return (ret < 0) ? ret : 0;
+}
+
+static ssize_t ilitek_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	u8 *tmp;
+	s32 ret;
+
+	if (count > 8192)
+		count = 8192;
+
+	tmp = kmalloc(count, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = ilitek_read(tmp, count);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+	if (ret < 0) {
+		TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		goto err_free;
+	}
+
+	if (copy_to_user(buf, tmp, count)) {
+		ret = -EFAULT;
+		goto err_free;
+	}
+
+err_free:
+	kfree(tmp);
+
+	return ret > 0 ? count : ret;
+}
+
+/* compat ioctl for 32/64 bit user program compatibility */
+#ifdef CONFIG_COMPAT
+static long ilitek_file_compat_ioctl(struct file *fp, uint32_t cmd,
+				     unsigned long arg)
+{
+	return ilitek_file_ioctl(fp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+
+static struct file_operations ilitek_fops = {
+	.FOPS_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = ilitek_file_compat_ioctl,
+#endif
+
+	.read = ilitek_file_read,
+	.write = ilitek_file_write,
+	.open = ilitek_file_open,
+	.release = ilitek_file_close,
+};
+
+static struct PROC_FOPS_T ilitek_proc_fops = {
+	.PROC_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.PROC_COMPAT_IOCTL = ilitek_file_compat_ioctl,
+#endif
+
+	.PROC_READ = ilitek_file_read,
+	.PROC_WRITE = ilitek_file_write,
+	.PROC_OPEN = ilitek_file_open,
+	.PROC_RELEASE = ilitek_file_close,
+};
+
+static ssize_t ilitek_driver_version_read(struct file *fp, char __user *buf,
+					  size_t size, loff_t *off)
+{
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	cnt = scnprintf(str, sizeof(str), "driver-version-tag: [%*phD]\n",
+			7, driver_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_drv_version = {
+	.PROC_READ = ilitek_driver_version_read,
+};
+
+static ssize_t ilitek_update_fw_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int error;
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.bin");
+		if (error < 0)
+			cnt += scnprintf(str, sizeof(str),
+					 "upgrade failed, err: %d\n", error);
+	} else {
+		cnt += scnprintf(str, sizeof(str),
+				 "upgrade success, fw version: %*phD\n",
+				 8, ts->dev->fw_ver);
+	}
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwupdate = {
+	.PROC_READ = ilitek_update_fw_read,
+};
+
+static ssize_t ilitek_firmware_version_read(struct file *fp, char __user *buf,
+					    size_t size, loff_t *off)
+{
+	int error;
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (error < 0)
+		cnt = scnprintf(str, sizeof(str), "read failed, err: %d\n",
+				error);
+	else
+		cnt = scnprintf(str, sizeof(str), "fw-version-tag: [%*phD]\n",
+				8, ts->dev->fw_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwversion = {
+	.PROC_READ = ilitek_firmware_version_read,
+};
+
+static ssize_t ilitek_console_write(struct file *filp, const char *buf,
+				    size_t size, loff_t *f_pos)
+{
+	int error;
+	char tmp[256], str[256];
+	char *ptr, *cur = str;
+	size_t wlen, rlen, _wlen = 0;
+	u8 cmd[64], data[64];
+
+	if (size > sizeof(tmp)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(tmp));
+		return -EINVAL;
+	}
+
+	error = copy_from_user(tmp, buf, size);
+	if (error < 0) {
+		TP_ERR(NULL, "copy_from_user failed, err: %d", error);
+		return error;
+	}
+
+	memset(str, 0, sizeof(str));
+	sscanf(tmp, "%zu:%zu:%s", &wlen, &rlen, str);
+	TP_MSG(NULL, "wlen: %zu, rlen: %zu, command: %s\n", wlen, rlen, str);
+
+	while ((ptr = strsep(&cur, "-")) && _wlen < sizeof(cmd))
+		sscanf(ptr, "%hhx", &cmd[_wlen++]);
+
+	if (wlen > 0 && wlen != _wlen) {
+		TP_ERR(NULL, "write cmd length: %zu not match with %s\n",
+			wlen, str);
+		return -EINVAL;
+	} else if (rlen > sizeof(data)) {
+		TP_ERR(NULL, "invalid read cmd length: %zu > %zu too large\n",
+			rlen, sizeof(data));
+		return -EINVAL;
+	}
+
+	if (wlen > 0)
+		TP_MSG(NULL, "[write]: %*phD, len: %zu\n",
+			(int)wlen, cmd, wlen);
+
+	error = ilitek_write_and_read(cmd, wlen, 1, data, rlen);
+	if (error < 0)
+		return error;
+
+	if (rlen > 0)
+		TP_MSG(NULL, "[read]: %*phD, len: %zu\n",
+			(int)rlen, data, rlen);
+
+	return size;
+}
+
+static struct PROC_FOPS_T ilitek_fops_console = {
+	.PROC_WRITE = ilitek_console_write,
+};
+
+static ssize_t ilitek_func_mode_write(struct file *fp, const char *buf,
+				      size_t size, loff_t *off)
+{
+	int error, cnt;
+	char str[64];
+	u8 func_mode;
+
+	cnt = MIN(size, sizeof(str));
+	memset(str, 0, sizeof(str));
+	if (copy_from_user(str, buf, cnt))
+		return -EFAULT;
+
+	sscanf(str, "%hhu", &func_mode);
+	TP_MSG(NULL, "set func mode: %hhu, support max: %hhu modes\n",
+		func_mode, ts->dev->tp_info.support_modes);
+
+	if (func_mode >= ts->dev->tp_info.support_modes)
+		return -EINVAL;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	error |= api_set_func_mode(ts->dev, func_mode);
+	error |= api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return (error < 0) ? error : cnt;
+}
+
+static ssize_t ilitek_func_mode_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 i;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_protocol_set_cmd(ts->dev, GET_TP_INFO, NULL);
+	api_get_func_mode(ts->dev);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "function mode: ");
+	for (i = 0; i < ts->dev->tp_info.support_modes; i++) {
+		if (i == ts->dev->func.mode)
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "[%hhu] ", i);
+		else
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "%hhu ", i);
+	}
+	cnt += scnprintf(str + strlen(str), sizeof(str) - cnt, "\n");
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_func_mode = {
+	.PROC_READ = ilitek_func_mode_read,
+	.PROC_WRITE = ilitek_func_mode_write,
+};
+
+static ssize_t ilitek_crypto_key_write(struct file *fp, const char *buf,
+				       size_t size, loff_t *off)
+{
+	u8 crypto[AES_KEY_LEN * 2];
+
+	if (size != sizeof(crypto) ||
+	    copy_from_user(crypto, buf, sizeof(crypto)))
+		return -EFAULT;
+
+	TP_MSG(NULL, "set crypto_key: %*phD\n", AES_KEY_LEN, crypto);
+	TP_MSG(NULL, "set crypto_iv: %*phD\n", AES_KEY_LEN,
+		crypto + AES_KEY_LEN);
+
+	memcpy(crypto_key, crypto, AES_KEY_LEN);
+	memcpy(crypto_iv, crypto + AES_KEY_LEN, AES_KEY_LEN);
+
+	return sizeof(crypto);
+}
+
+static ssize_t ilitek_crypto_key_read(struct file *fp, char __user *buf,
+				      size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "key: %*phD\n",
+			 AES_KEY_LEN, crypto_key);
+	cnt += scnprintf(str + strlen(str), sizeof(str), "iv: %*phD\n",
+			 AES_KEY_LEN, crypto_iv);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_crypto_key = {
+	.PROC_READ = ilitek_crypto_key_read,
+	.PROC_WRITE = ilitek_crypto_key_write,
+};
+
+static ssize_t ilitek_setmode_0_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 0);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_0 = {
+	.PROC_READ = ilitek_setmode_0_read,
+};
+
+static ssize_t ilitek_setmode_1_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 1);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_1 = {
+	.PROC_READ = ilitek_setmode_1_read,
+};
+
+static ssize_t ilitek_setmode_2_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 2);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_2 = {
+	.PROC_READ = ilitek_setmode_2_read,
+};
+
+static ssize_t ilitek_driver_version_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "driver-version-tag: [%*phD]\n",
+			 7, driver_ver);
+}
+
+static DEVICE_ATTR(driver_version, 0664, ilitek_driver_version_show, NULL);
+
+static ssize_t ilitek_eds_check_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	if (ts->esd_check)
+		return scnprintf(buf, PAGE_SIZE, "[enable] disable\n");
+
+	return scnprintf(buf, PAGE_SIZE, "enable [disable]\n");
+}
+
+static ssize_t ilitek_esd_check_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	TP_MSG(NULL, "set esd check: %hhx to %s\n", ts->esd_check, buf);
+
+	if (!strncmp(buf, "enable", 6) && !ts->esd_check) {
+		ilitek_create_esd_check_workqueue();
+		ts->esd_check = true;
+	} else if (!strncmp(buf, "disable", 7) && ts->esd_check) {
+		ilitek_remove_esd_check_workqueue();
+		ts->esd_check = false;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(esd_check, 0664,
+		   ilitek_eds_check_show,
+		   ilitek_esd_check_store);
+
+
+static ssize_t ilitek_low_power_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->low_power_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_low_power_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	int i;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set low power: %hhx to %s\n", ts->low_power_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		ts->low_power_status = modes[i].id;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(low_power, 0664,
+		   ilitek_low_power_show,
+		   ilitek_low_power_store);
+
+static ssize_t ilitek_gesture_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->gesture_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_gesture_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int i;
+	u8 type = ts->gesture_status;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set gesture: %hhx to %s\n", ts->gesture_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		type = modes[i].id;
+	}
+
+	if (!ts->gesture_status && type)
+		ilitek_register_gesture(ts, true);
+	else if (ts->gesture_status && !type)
+		ilitek_register_gesture(ts, false);
+
+	ts->gesture_status = type;
+
+	return size;
+}
+static DEVICE_ATTR(gesture, 0664, ilitek_gesture_show, ilitek_gesture_store);
+
+static ssize_t ilitek_firmware_version_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	int ret;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (ret < 0) {
+		TP_ERR(NULL, "read tp info failed, err: %d\n", ret);
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n", ret);
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "fw-version-tag: [%*phD]\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(firmware_version, 0664, ilitek_firmware_version_show, NULL);
+
+static ssize_t ilitek_module_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_protocol_set_cmd(ts->dev, GET_MCU_VER, NULL);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	if (error < 0)
+		TP_ERR(NULL, "read mcu ver. failed, err: %d\n", error);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+out:
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE, "module-name-tag: [%s]\n",
+			 ts->dev->mcu_info.module_name);
+}
+
+static DEVICE_ATTR(product_id, 0664, ilitek_module_name_show, NULL);
+
+static ssize_t ilitek_update_fw_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0)
+		error = ilitek_upgrade_firmware("ilitek.bin");
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "upgrade failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE,
+			 "upgrade success, current fw version: %*phD\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(update_fw, 0664, ilitek_update_fw_show, NULL);
+
+static struct attribute *ilitek_sysfs_attrs_ctrl[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_gesture.attr,
+	&dev_attr_low_power.attr,
+	&dev_attr_esd_check.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static struct attribute_group ilitek_attribute_group[] = {
+	{ .attrs = ilitek_sysfs_attrs_ctrl },
+};
+
+int ilitek_create_sysfsnode(void)
+{
+	int error;
+
+	error = sysfs_create_group(&ts->device->kobj, ilitek_attribute_group);
+	if (error < 0) {
+		TP_ERR(NULL, "sysfs_create_group failed, err: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+void ilitek_remove_sys_node(void)
+{
+	sysfs_remove_group(&ts->device->kobj, ilitek_attribute_group);
+}
+
+static struct miscdevice ilitek_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ilitek_ctrl",
+	.fops = &ilitek_fops,
+	.mode = 0666,
+};
+
+int ilitek_create_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+	misc_register(&ilitek_misc);
+
+	ilitek_proc = proc_create("ilitek_ctrl", ILITEK_DEVICE_NODE_PERMISSON,
+				  NULL, &ilitek_proc_fops);
+
+	if (!ilitek_proc)
+		TP_ERR(NULL, "proc_create(ilitek_ctrl, ILITEK_DEVICE_NODE_PERMISSON, NULL, &ilitek_fops) fail\n");
+
+	ilitek_proc_entry = proc_mkdir("ilitek", NULL);
+	if (!ilitek_proc_entry) {
+		TP_ERR(NULL, "Error, failed to creat procfs.\n");
+		return -EINVAL;
+	}
+
+	if (!proc_create("driver_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_drv_version))
+		TP_ERR(NULL, "failed to create procfs driver_version.\n");
+
+	if (!proc_create("firmware_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwversion))
+		TP_ERR(NULL, "failed to create procfs firmware_version.\n");
+
+	if (!proc_create("console", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_console))
+		TP_ERR(NULL, "failed to create procfs console.\n");
+
+	if (!proc_create("update_fw", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwupdate))
+		TP_ERR(NULL, "failed to create procfs update_fw.\n");
+
+	if (!proc_create("func_mode", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_func_mode))
+		TP_ERR(NULL, "failed to create procfs func_mode.\n");
+
+	if (!proc_create("crypto_key", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_crypto_key))
+		TP_ERR(NULL, "failed to create procfs crypto_key.\n");
+
+	/*
+	 * below setmode_X is historical setting for some customer need
+	 * to set function mode by cat procfs node.
+	 * please make sure it's not risky to modifiy below.
+	 */
+	if (!proc_create("setmode_0", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_0))
+		TP_ERR(NULL, "failed to create procfs setmode_0.\n");
+	if (!proc_create("setmode_1", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_1))
+		TP_ERR(NULL, "failed to create procfs setmode_1.\n");
+	if (!proc_create("setmode_2", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_2))
+		TP_ERR(NULL, "failed to create procfs setmode_2.\n");
+#endif
+
+	return 0;
+}
+
+int ilitek_remove_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+
+	misc_deregister(&ilitek_misc);
+
+	if (ilitek_proc) {
+		TP_MSG(NULL, "remove procfs ilitek_ctrl.\n");
+		remove_proc_entry("ilitek_ctrl", NULL);
+		ilitek_proc = NULL;
+	}
+
+	if (ilitek_proc_entry) {
+		TP_MSG(NULL, "remove procfs inode\n");
+		remove_proc_entry("driver_version", ilitek_proc_entry);
+		remove_proc_entry("firmware_version", ilitek_proc_entry);
+		remove_proc_entry("console", ilitek_proc_entry);
+		remove_proc_entry("update_fw", ilitek_proc_entry);
+		remove_proc_entry("func_mode", ilitek_proc_entry);
+		remove_proc_entry("crypto_key", ilitek_proc_entry);
+
+		remove_proc_entry("setmode_0", ilitek_proc_entry);
+		remove_proc_entry("setmode_1", ilitek_proc_entry);
+		remove_proc_entry("setmode_2", ilitek_proc_entry);
+
+		remove_proc_entry("ilitek", NULL);
+		ilitek_proc_entry = NULL;
+	}
+#endif
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_ts.h b/drivers/input/touchscreen/ilitek/ilitek_ts.h
new file mode 100644
index 000000000000..9ee0b13e08cd
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_ts.h
@@ -0,0 +1,268 @@
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ *
+ */
+
+#ifndef __ILITEK_TS_H__
+#define __ILITEK_TS_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include <linux/init.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+//#if defined(CONFIG_FB)
+#if 0
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#define ILITEK_BLANK_POWERDOWN			FB_BLANK_POWERDOWN
+#define	ILITEK_BLANK_UNBLANK			FB_BLANK_UNBLANK
+#define	ILITEK_EVENT_BLANK			FB_EVENT_BLANK
+#define	ILITEK_BLANK_NORMAL			FB_BLANK_NORMAL
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#define ILITEK_BL_ADDR				0x41
+#define ILITEK_SENSOR_ID_MASK			0xff
+
+#define ILITEK_PLAT_QCOM			1
+#define ILITEK_PLAT_MTK				2
+#define ILITEK_PLAT_ROCKCHIP			3
+#define ILITEK_PLAT_ALLWIN			4
+#define ILITEK_PLAT_AMLOGIC			5
+#define ILITEK_PLAT				ILITEK_PLAT_ROCKCHIP
+//#define CONFIG_QCOM_DRM
+#if ILITEK_PLAT == ILITEK_PLAT_QCOM
+#ifdef CONFIG_QCOM_DRM
+#include <linux/msm_drm_notify.h>
+#define ILITEK_BLANK_POWERDOWN			MSM_DRM_BLANK_POWERDOWN
+#define ILITEK_BLANK_UNBLANK			MSM_DRM_BLANK_UNBLANK
+#define ILITEK_EVENT_BLANK			MSM_DRM_EVENT_BLANK
+#define ILITEK_BLANK_NORMAL			MSM_DRM_BLANK_UNBLANK
+#endif
+#endif
+
+#define ILITEK_GESTURE_TYPES			\
+	X(Disable, 0, "disable")		\
+	X(Single_Click, 1, "single-click")	\
+	X(Double_Click, 2, "double-click")
+#define ILITEK_GESTURE_DEFAULT			Gesture_Disable
+
+#define X(_type, _id, _str)	Gesture_##_type = _id,
+enum Gesture_Type {
+	ILITEK_GESTURE_TYPES
+};
+#undef X
+
+#define ILITEK_LOW_POWER_TYPES		\
+	X(Sleep,	0, "sleep")	\
+	X(Idle,		1, "idle")	\
+	X(PowerOff,	2, "poweroff")
+#define ILITEK_LOW_POWER_DEFAULT		Low_Power_Sleep
+
+#define X(_type, _id, _str)	Low_Power_##_type = _id,
+enum Low_Power_Type {
+	ILITEK_LOW_POWER_TYPES
+};
+#undef X
+
+#define ILITEK_CHECKSUM_FAILED_RELEASE		true
+
+#define ILITEK_TOUCH_PROTOCOL_B
+//#define ILITEK_REPORT_PRESSURE
+//#define ILITEK_USE_LCM_RESOLUTION
+//#define ILITEK_ISR_PROTECT
+
+#define ILITEK_TUNING_MESSAGE
+#define ILITEK_REGISTER_SUSPEND_RESUME
+#define ILITEK_ESD_CHECK_ENABLE			0
+
+#define ILITEK_TOOL
+
+#define ILITEK_ROTATE_FLAG			0
+#define ILITEK_REVERT_X				0
+#define ILITEK_REVERT_Y				0
+#define TOUCH_SCREEN_X_MAX			1080	//LCD_WIDTH
+#define TOUCH_SCREEN_Y_MAX			1920	//LCD_HEIGHT
+#define ILITEK_RESOLUTION_MAX			16384
+//#define ILITEK_ENABLE_REGULATOR_POWER_ON
+#define ILITEK_GET_GPIO_NUM
+
+#define ILITEK_GET_TIME_FUNC_WITH_TIME		0
+#define ILITEK_GET_TIME_FUNC_WITH_JIFFIES	1
+#define ILITEK_GET_TIME_FUNC			ILITEK_GET_TIME_FUNC_WITH_JIFFIES
+
+#define DOUBLE_CLICK_DISTANCE			1000
+#define DOUBLE_CLICK_ONE_CLICK_USED_TIME	800
+#define DOUBLE_CLICK_NO_TOUCH_TIME		1000
+#define DOUBLE_CLICK_TOTAL_USED_TIME		(DOUBLE_CLICK_NO_TOUCH_TIME + (DOUBLE_CLICK_ONE_CLICK_USED_TIME * 2))
+
+//#define ILITEK_WAKELOCK_SUPPORT
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+#include <linux/wakelock.h>
+#endif
+
+#define ILITEK_TS_NAME				"ilitek_ts"
+
+#define ABSSUB(a, b) ((a > b) ? (a - b) : (b - a))
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+//#define MTK_UNDTS //no use dts and for mtk old version
+
+#define ILITEK_ENABLE_DMA
+#define ILITEK_DMA_SIZE				4096
+#define ILITEK_USE_MTK_INPUT_DEV
+
+#if define(ILITEK_USE_MTK_INPUT_DEV) && !defined(ILITEK_USE_LCM_RESOLUTION)
+#define ILITEK_USE_LCM_RESOLUTION
+#endif
+
+#ifdef ILITEK_GET_GPIO_NUM
+#undef ILITEK_GET_GPIO_NUM
+#endif
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include <linux/namei.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#ifdef MTK_UNDTS
+
+#define TPD_KEY_COUNT   4
+#define key_1           {60, 17000}	//auto define
+#define key_2           {180, 17000}
+#define key_3           {300, 17000}
+#define key_4           {420, 17000}
+
+#define TPD_KEYS        {KEY_MENU, KEY_HOMEPAGE, KEY_BACK, KEY_SEARCH}	//change for you panel key info
+#define TPD_KEYS_DIM    {{key_1, 50, 30 }, {key_2, 50, 30 }, {key_3, 50, 30 }, {key_4, 50, 30 } }
+
+struct touch_vitual_key_map_t {
+	int point_x;
+	int point_y;
+};
+
+extern struct touch_vitual_key_map_t touch_key_point_maping_array[];
+
+#include <mach/mt_pm_ldo.h>
+#include <cust_eint.h>
+#include "cust_gpio_usage.h"
+#include <mach/mt_gpio.h>
+#include <mach/mt_typedefs.h>
+#include <pmic_drv.h>
+#include <mach/mt_boot.h>
+#include <linux/dma-mapping.h>
+
+#else
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_MTK_BOOT
+#include "mt_boot_common.h"
+#endif
+
+#endif /* MTK_UNDTS */
+
+#include "tpd.h"
+extern struct tpd_device *tpd;
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#include <linux/irq.h>
+#include <linux/init-input.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+extern struct ctp_config_info config_info;
+#endif
+
+
+#ifndef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef MTK_UNDTS
+#define ILITEK_IRQ_GPIO				GPIO_CTP_EINT_PIN
+#define ILITEK_RESET_GPIO			GPIO_CTP_RST_PIN
+#else
+#define ILITEK_IRQ_GPIO				GTP_INT_PORT
+#define ILITEK_RESET_GPIO			GTP_RST_PORT
+#endif
+#elif ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#define ILITEK_IRQ_GPIO				config_info.int_number
+#define ILITEK_RESET_GPIO			config_info.wakeup_gpio.gpio
+#else
+#define ILITEK_IRQ_GPIO				9
+#define ILITEK_RESET_GPIO			10
+#endif
+#endif
+
+#define ILITEK_I2C_RETRY_COUNT			3
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+extern int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg);
+extern int ilitek_resume_allwin(struct i2c_client *client);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.c b/drivers/input/touchscreen/ilitek/ilitek_update.c
new file mode 100644
index 000000000000..d7e873a3c204
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_update.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifndef __KERNEL__
+static int hex_to_bin(u8 ch)
+{
+	u8 cu = ch & 0xdf;
+	return -1 +
+		((ch - '0' +  1) & (unsigned)((ch - '9' - 1) &
+		('0' - 1 - ch)) >> 8) +
+		((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) &
+		('A' - 1 - cu)) >> 8);
+}
+
+static int hex2bin(u8 *dst, const u8 *src, size_t count)
+{
+	int hi = 0, lo = 0;
+
+	while (count--) {
+		hi = hex_to_bin(*src++);
+		lo = hex_to_bin(*src++);
+		if (hi < 0 || lo < 0) {
+			TP_ERR(NULL, "hex_to_bin failed, hi: %d, lo: %d\n",
+				hi, lo);
+			return -EINVAL;
+		}
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
+#endif
+
+static u32 get_tag_addr(u32 start, u32 end,
+			const u8 *buf, unsigned int buf_size,
+			const u8 *tag, unsigned int tag_size)
+{
+	unsigned int i;
+
+	for (i = start; i <= end - tag_size && i < buf_size - tag_size; i++) {
+		if (!memcmp(buf + i, tag, tag_size))
+			return i + tag_size + 1;
+	}
+
+	return end;
+}
+
+static u32 get_endaddr(u32 start, u32 end, const u8 *buf,
+		       unsigned int buf_size, bool is_AP)
+{
+	u32 addr;
+	u8 tag[32];
+	const u8 ap_tag[] = "ILITek AP CRC   ";
+	const u8 blk_tag[] = "ILITek END TAG  ";
+
+	_memset(tag, 0xFF, sizeof(tag));
+	_memcpy(tag + 16, (is_AP) ? ap_tag : blk_tag, 16);
+
+	addr = get_tag_addr(start, end, buf, buf_size, tag, sizeof(tag));
+	TP_DBG(NULL, "find tag in start/end: 0x%x/0x%x, tag addr: 0x%x\n",
+		start, end, addr);
+
+	return addr;
+}
+
+static int decode_mm(struct ilitek_fw_handle *fw, u32 addr,
+		     u8 *buf, u32 buf_size)
+{
+	u8 i;
+	union mapping_info *mapping;
+
+	TP_INFO(NULL, "------------Memory Mapping information------------\n");
+	TP_INFO(NULL, "memory-mapping-info addr: 0x%x\n", addr);
+
+	mapping = (union mapping_info *)(buf + addr);
+	_memset(fw->file.ic_name, 0, sizeof(fw->file.ic_name));
+
+	switch (mapping->mapping_ver[2]) {
+	case 0x2:
+		_memcpy(fw->file.ic_name, mapping->ic_name,
+			sizeof(mapping->ic_name));
+		break;
+	default:
+	case 0x1:
+		_sprintf(fw->file.ic_name, 0, "%02x%02x",
+			mapping->ic_name[1], mapping->ic_name[0]);
+		break;
+	}
+
+	if (!strcmp(fw->file.ic_name, "2133"))
+		_sprintf(fw->file.ic_name, 0, "2132S");
+
+	if (fw->dev && strcmp(fw->dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " not matched\n",
+			fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EINVAL;
+	}
+
+	TP_MSG(NULL, "Hex Mapping Ver.: 0x%x\n",
+		le32(mapping->mapping_ver, 3));
+	TP_MSG(NULL, "Hex Protocol: 0x%x\n",
+		le32(mapping->protocol_ver, 3));
+	TP_MSG(NULL, "Hex MCU Ver.: " PFMT_C8 "\n", fw->file.ic_name);
+
+	_memset(fw->file.fw_ver, 0, sizeof(fw->file.fw_ver));
+	fw->file.fwid = 0xffff;
+
+	fw->file.mm_addr = addr;
+	switch (addr) {
+	case 0x4038:
+	case 0x4020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x2C007];
+		fw->file.fw_ver[5] = buf[0x2C006];
+		fw->file.fw_ver[6] = buf[0x2C005];
+		fw->file.fw_ver[7] = buf[0x2C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x3020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x3C007];
+		fw->file.fw_ver[5] = buf[0x3C006];
+		fw->file.fw_ver[6] = buf[0x3C005];
+		fw->file.fw_ver[7] = buf[0x3C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x2020:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x2033];
+		fw->file.fw_ver[1] = buf[0x2032];
+		fw->file.fw_ver[2] = buf[0x2031];
+		fw->file.fw_ver[3] = buf[0x2030];
+		fw->file.fw_ver[4] = buf[0xF004];
+		fw->file.fw_ver[5] = buf[0xF005];
+		fw->file.fw_ver[6] = buf[0xF006];
+		fw->file.fw_ver[7] = buf[0xF007];
+
+		/* for V3 251x IC, get AP crc and DF checksum */
+		fw->file.blocks[0].check = get_crc(fw->file.blocks[0].start,
+			fw->file.blocks[0].end - 1, buf, buf_size);
+
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+
+	case 0x500:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x52D];
+		fw->file.fw_ver[1] = buf[0x52C];
+		fw->file.fw_ver[2] = buf[0x52B];
+		fw->file.fw_ver[3] = buf[0x52A];
+		fw->file.fw_ver[4] = buf[0x1F404];
+		fw->file.fw_ver[5] = buf[0x1F405];
+		fw->file.fw_ver[6] = buf[0x1F406];
+		fw->file.fw_ver[7] = buf[0x1F407];
+
+		/* for V3 231x IC, get AP checksum and DF checksum */
+		fw->file.blocks[0].check = get_checksum(
+			fw->file.blocks[0].start,
+			fw->file.blocks[0].end + 1, buf, buf_size);
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+	default:
+		fw->file.mm_size = 0;
+		break;
+	}
+
+	TP_MSG(NULL, "file fwid: 0x%04x\n", fw->file.fwid);
+
+	TP_INFO(NULL, "File FW Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[0], fw->file.fw_ver[1],
+		fw->file.fw_ver[2], fw->file.fw_ver[3]);
+	TP_INFO(NULL, "File Customer Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[4], fw->file.fw_ver[5],
+		fw->file.fw_ver[6], fw->file.fw_ver[7]);
+
+	if (le32(mapping->mapping_ver, 3) < 0x10000)
+		goto memory_mapping_end;
+
+	TP_INFO(NULL, "File Tuning Version: %02x-%02x-%02x-%02x\n",
+		mapping->_lego.tuning_ver[3], mapping->_lego.tuning_ver[2],
+		mapping->_lego.tuning_ver[1], mapping->_lego.tuning_ver[0]);
+
+	if (mapping->_lego.block_num > ARRAY_SIZE(fw->file.blocks)) {
+		TP_ERR(NULL, "Unexpected block num: " PFMT_U8 " > %u\n",
+			mapping->_lego.block_num,
+			(unsigned int)ARRAY_SIZE(fw->file.blocks));
+		goto memory_mapping_end;
+	}
+
+	fw->file.block_num = mapping->_lego.block_num;
+
+	TP_MSG(NULL, "Total " PFMT_U8 " blocks\n", fw->file.block_num);
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].start =
+			le32(mapping->_lego.blocks[i].addr, 3);
+		fw->file.blocks[i].end = (i == fw->file.block_num - 1) ?
+			le32(mapping->_lego.end_addr, 3) :
+			le32(mapping->_lego.blocks[i + 1].addr, 3);
+
+		/*
+		 * get end addr. of block,
+		 * i.e. address of block's final byte of crc.
+		 */
+		fw->file.blocks[i].end = get_endaddr(
+			fw->file.blocks[i].start, fw->file.blocks[i].end,
+			buf, buf_size, i == 0);
+
+		fw->file.blocks[i].check = get_crc(fw->file.blocks[i].start,
+			fw->file.blocks[i].end - 1,
+			buf, buf_size);
+
+		TP_MSG(NULL, "Block[%u], start:0x%x end:0x%x, crc:0x%x\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			fw->file.blocks[i].check);
+	}
+
+memory_mapping_end:
+	TP_INFO(NULL, "--------------------------------------------------\n");
+
+	return 0;
+}
+
+static int decode_hex(struct ilitek_fw_handle *fw, u8 *hex,
+		      u32 start, u32 end,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	u8 info[4], data[16];
+	unsigned int i, len, addr, type, exaddr = 0;
+	u32 mapping_info_addr = 0;
+
+	/* m2v hex has another block at the end of hex file */
+	u8 j = (fw->m2v) ? fw->file.block_num : 0;
+
+	fw->file.blocks[j].start = (~0U);
+	fw->file.blocks[j].end = 0x0;
+	fw->file.blocks[j].check = 0x0;
+	fw->file.blocks[j + 1].start = (~0U);
+	fw->file.blocks[j + 1].end = 0x0;
+	fw->file.blocks[j + 1].check = 0x0;
+
+	for (i = start; i < end; i++) {
+		/* filter out non-hexadecimal characters */
+		if (hex_to_bin(hex[i]) < 0)
+			continue;
+
+		error = hex2bin(info, hex + i, sizeof(info));
+		if (error < 0)
+			return error;
+
+		len = info[0];
+		addr = be32(info + 1, 2);
+		type = info[3];
+
+		error = hex2bin(data, hex + i + 8, len);
+		if (error < 0)
+			return error;
+
+		switch (type) {
+		case 0xAC:
+			mapping_info_addr = be32(data, len);
+			break;
+
+		case 0xAD:
+			fw->file.blocks[1].start = be32(data, len);
+			_memset(buf + fw->file.blocks[1].start, 0, 0x1000);
+			break;
+
+		case 0xBA:
+			if (be32(data, len) != 2U)
+				break;
+
+			TP_MSG(NULL, "start to decode M2V part of hex file\n");
+			fw->m2v = true;
+			////Reed Add : 20230721��������2326��������Decode_mm,�ټ�������M2V���֡���
+			if (mapping_info_addr)
+				decode_mm(fw, mapping_info_addr, buf, buf_size);
+			return decode_hex(fw, hex, i + 10 + len * 2 + 1, end,
+					  fw->m2v_buf, ILITEK_FW_BUF_SIZE);
+
+		case 0x01:
+			goto success_return;
+
+		case 0x02:
+			exaddr = be32(data, len) << 4;
+			break;
+
+		case 0x04:
+			exaddr = be32(data, len) << 16;
+			break;
+
+		case 0x05:
+			TP_MSG(NULL, "hex data type: 0x%x, start linear address: 0x%x\n",
+				type, be32(data, len));
+			break;
+
+		case 0x00:
+			addr += exaddr;
+
+			if (addr + len > buf_size) {
+				TP_ERR(NULL, "hex addr: 0x%x, buf size: 0x%x OOB\n",
+					addr + len, buf_size);
+				return -ENOBUFS;
+			}
+			_memcpy(buf + addr, data, len);
+
+			fw->file.blocks[j].start =
+				MIN(fw->file.blocks[j].start, addr);
+
+			if (addr + len < fw->file.blocks[j + 1].start) {
+				fw->file.blocks[j].end =
+					MAX(fw->file.blocks[j].end,
+					    addr + len - 1);
+				fw->file.blocks[j].check += get_checksum(
+					0, len, data, sizeof(data));
+			} else {
+				fw->file.blocks[j + 1].end =
+					MAX(fw->file.blocks[j + 1].end,
+					    addr + len - 1);
+				fw->file.blocks[j + 1].check += get_checksum(
+					0, len, data, sizeof(data));
+			}
+
+			break;
+		default:
+			TP_ERR(NULL, "unexpected type:0x%x in hex, len:%u, addr:0x%x\n",
+				type, len, addr);
+			return -EINVAL;
+		}
+
+		i = i + 10 + len * 2;
+	}
+
+success_return:
+	if (fw->m2v)
+		fw->m2v_checksum = fw->file.blocks[fw->file.block_num].check;
+	if (mapping_info_addr)
+		return decode_mm(fw, mapping_info_addr, fw->file.buf, buf_size);
+
+	return 0;
+}
+
+static int decode_bin(struct ilitek_fw_handle *fw,
+		      u8 *bin, u32 bin_size,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 mapping_info_addr;
+
+	if (!dev) {
+		TP_ERR(NULL, "offline decode bin file is not supported\n");
+		return -EINVAL;
+	}
+
+	if (bin_size > buf_size) {
+		TP_ERR(dev->id, "bin file size: 0x%x, buf size: 0x%x OOB\n",
+			bin_size, buf_size);
+		return -ENOBUFS;
+	}
+	_memcpy(buf, bin, bin_size);
+
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		return error;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		return error;
+
+	switch (dev->protocol.flag) {
+	case PTL_V6:
+		mapping_info_addr = dev->mcu_info.mm_addr;
+		break;
+
+	case PTL_V3:
+		/*
+		 * For 231x: AP checksum and DF checksum, DF start addr: 0x1f000
+		 * For 251x: AP crc and DF checksum, DF start addr: 0xf000
+		 */
+		if (is_231x(dev)) {
+			mapping_info_addr = 0x500;
+
+			fw->file.blocks[1].start = 0x1f000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x0;
+
+			/* +2 to get end addr. of last byte of 4 bytes checksum */
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true) + 2;
+
+		} else {
+			mapping_info_addr = 0x2020;
+			fw->file.blocks[1].start = 0xf000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x2000;
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true);
+		}
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * take the whole "buf" into decode_mm, "buf" should be
+	 * properly initialized, and the size should be
+	 * larger than "bin", which reduce OOB issue.
+	 */
+	return decode_mm(fw, mapping_info_addr, buf, buf_size);
+}
+
+#ifdef ILITEK_BOOT_UPDATE
+#include "ilitek_fw.h"
+
+static int decode_ili(struct ilitek_fw_handle *fw,
+		      u8 *buf, u32 buf_size)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 *ili_buf = CTPM_FW;
+	int size = sizeof(CTPM_FW);
+	u8 id;
+
+#if defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		if (!support_sensor_id(dev)) {
+			TP_WARN(dev->id,
+				"protocol: 0x%x, mode: 0x" PFMT_X8 ", sensor-id not supported, take default fw(id: 0x%x)\n",
+				dev->protocol.ver, dev->ic[0].mode,
+				ILITEK_BOOT_UPDATE_DEF_ID);
+			fw->file.id = ILITEK_BOOT_UPDATE_DEF_ID;
+			break;
+		}
+
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id >= ARRAY_SIZE(ili_arr)) {
+			TP_ERR(dev->id, "invalid sensor id: " PFMT_U8 " >= %d\n",
+				dev->sensor.id, (int)ARRAY_SIZE(ili_arr));
+			return -EINVAL;
+		}
+
+		fw->file.id = id;
+		ili_buf = ili_arr[id].buf;
+		size = ili_arr[id].size;
+		break;
+	case ili_default:
+		break;
+	default:
+		TP_ERR(dev->id, "unexpected ili-select-type: %u\n",
+			__ili_select_type__);
+		return -EINVAL;
+	}
+#endif
+
+	if (!ili_buf || size < 32)
+		return -EINVAL;
+
+	fw->setting.fw_ver_check = true;
+	fw->setting.fw_ver_policy = 0;
+	_memcpy(fw->setting.fw_ver, ili_buf + 18, 8);
+
+	TP_MSG_ARR(dev->id, "IC  fw ver:", TYPE_U8, 8, dev->fw_ver);
+	TP_MSG_ARR(dev->id, "Hex fw ver:", TYPE_U8, 8, fw->setting.fw_ver);
+
+	return decode_bin(fw, ili_buf + 32, size - 32, buf, buf_size);
+}
+#endif
+
+static bool need_retry(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 id;
+
+#if defined(ILITEK_BOOT_UPDATE) && defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	if (fw->file.type != fw_ili)
+		return false;
+
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id == fw->file.id)
+			break;
+
+		/* reload correct fw if sensor-id not matched with default fw */
+		TP_MSG(dev->id, "sensor id: 0x%x, file: 0x%x not matched, "
+			"reload fw again\n",
+			id, fw->file.id);
+
+		if (ilitek_update_load_fw(fw, "ilitek.ili") < 0) {
+			TP_ERR(dev->id, "reload ilitek.ili failed\n");
+			return false;
+		}
+
+		/* MUST DISABLE fw ver check for the second try */
+		fw->setting.fw_ver_check = false;
+
+		return true;
+	default:
+		break;
+	}
+#endif
+
+	UNUSED(dev);
+	UNUSED(id);
+
+	return false;
+}
+
+static int decode_firmware(struct ilitek_fw_handle *fw, WCHAR *filename)
+{
+	int error;
+	int size = 0;
+	u8 *buf;
+	WCHAR *file_ext;
+
+	/* initialization */
+	_memset(fw->file.buf, 0xFF, fw->file.buf_size);
+	_memset(fw->m2v_buf, 0xFF, ILITEK_FW_BUF_SIZE);
+	fw->m2v = false;
+	/*
+	 * set block num 2 for V3 AP and Data Flash as default,
+	 * for V6, block num would be updated after decoding memory mapping.
+	 */
+	fw->file.block_num = 2;
+	fw->file.blocks[0].start = (~0U);
+	fw->file.blocks[0].end = 0x0;
+	fw->file.blocks[0].check = 0x0;
+	fw->file.blocks[1].start = (~0U);
+	fw->file.blocks[1].end = 0x0;
+	fw->file.blocks[1].check = 0x0;
+
+	TP_MSG(NULL, "start to read fw file: " PFMT_C16 "\n", filename);
+
+	buf = (u8 *)CALLOC(ILITEK_FW_FILE_SIZE, 1);
+	if (!buf)
+		return -ENOMEM;
+
+	file_ext = WCSRCHR(filename, '.');
+	if (!file_ext)
+		return -ENOENT;
+
+	/* no need to read .ili file */
+	if (WCSCASECMP(file_ext, ".ili")) {
+		if (!fw->cb.read_fw) {
+			error = -EFAULT;
+			TP_ERR(NULL, "read fw callback not registered\n");
+			goto err_free;
+		}
+
+		size = fw->cb.read_fw(filename, buf, ILITEK_FW_FILE_SIZE,
+				      fw->_private);
+
+		error = size;
+		if (error < 0) {
+			TP_ERR(NULL, "read fw file: " PFMT_C16 " failed, err: %d\n",
+				filename, error);
+			goto err_free;
+		}
+	}
+
+	if (!WCSCASECMP(file_ext, ".hex")) {
+		fw->file.type = fw_hex;
+		error = decode_hex(fw, buf, 0, size, fw->file.buf,
+				   fw->file.buf_size);
+	} else if (!WCSCASECMP(file_ext, ".bin")) {
+		fw->file.type = fw_bin;
+		error = decode_bin(fw, buf, size, fw->file.buf,
+				   fw->file.buf_size);
+	}
+#ifdef ILITEK_BOOT_UPDATE
+	else if (!WCSCASECMP(file_ext, ".ili")) {
+		fw->file.type = fw_ili;
+		error = decode_ili(fw, fw->file.buf, fw->file.buf_size);
+	}
+#endif
+	else {
+		error = -EINVAL;
+	}
+
+err_free:
+	CFREE(buf);
+
+	return error;
+}
+
+static bool need_fw_update_v3(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool fw_file_has_data_flash =
+		(fw->file.blocks[1].start < fw->file.blocks[1].end);
+	bool need = false;
+
+	TP_INFO(dev->id, "------------V3 AP/DF Info.------------\n");
+
+	fw->file.blocks[0].check_match = (fw->setting.force_update) ?
+		false : dev->ic[0].crc[0] == fw->file.blocks[0].check;
+
+	TP_INFO(dev->id, "AP block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+		fw->file.blocks[0].start, fw->file.blocks[0].end,
+		dev->ic[0].crc[0], fw->file.blocks[0].check,
+		(fw->file.blocks[0].check_match) ? "matched" : "not matched");
+
+	fw->file.blocks[1].check_match = true;
+	if (fw_file_has_data_flash) {
+		fw->file.blocks[1].check_match = (fw->setting.force_update) ?
+			false : dev->ic[0].crc[1] == fw->file.blocks[1].check;
+
+		TP_INFO(dev->id, "DF block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(fw->file.blocks[1].check_match) ?
+			"matched" : "not matched");
+	} else if (!is_231x(dev)) {
+		/*
+		 * for 251x ICs, if no data flash in fw file,
+		 * need to switch to BL mode then erase data flash forcely.
+		 */
+		need = true;
+	}
+
+	TP_INFO(dev->id, "--------------------------------------\n");
+
+	need |= (!fw->file.blocks[0].check_match ||
+		 !fw->file.blocks[1].check_match);
+
+	if (dev->ic[0].mode == BL_MODE)
+		return true;
+
+	return need;
+}
+
+static bool need_fw_update_v6(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	bool need = false;
+
+	TP_INFO(dev->id, "------------Lego Block Info.------------\n");
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].check_match = (fw->setting.force_update) ?
+			false : (dev->ic[0].crc[i] == fw->file.blocks[i].check);
+
+		need = (!fw->file.blocks[i].check_match) ? true : need;
+
+		TP_INFO(dev->id, "Block[" PFMT_U8 "]: Start/End Addr.: 0x%x/0x%x, IC/File CRC: 0x%x/0x%x " PFMT_C8 "\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			dev->ic[0].crc[i], fw->file.blocks[i].check,
+			(fw->file.blocks[i].check_match) ?
+			"matched" : "not matched");
+	}
+
+	/* check BL mode firstly before AP-cmd related varaible, ex: ic_num */
+	if (dev->ic[0].mode == BL_MODE) {
+		need = true;
+		goto force_return;
+	}
+
+	for (i = 1; i < dev->tp_info.ic_num; i++) {
+		TP_INFO(dev->id, "Master/Slave[" PFMT_U8 "] CRC: 0x%x/0x%x " PFMT_C8 ", Slave Mode: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, fw->file.blocks[0].check, dev->ic[i].crc[0],
+			(fw->file.blocks[0].check == dev->ic[i].crc[0]) ?
+			"matched" : "not matched",
+			dev->ic[i].mode, dev->ic[i].mode_str);
+
+		if (dev->ic[i].crc[0] == fw->file.blocks[0].check &&
+			dev->ic[i].mode == AP_MODE)
+			continue;
+		need = true;
+	}
+
+	if (fw->m2v) {
+		api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &fw->m2v_checksum);
+
+		fw->file.blocks[fw->file.block_num].check_match =
+			fw->file.blocks[fw->file.block_num].check ==
+			fw->m2v_checksum;
+		TP_INFO(dev->id, "M2V IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->m2v_checksum,
+			fw->file.blocks[fw->file.block_num].check,
+			(fw->file.blocks[fw->file.block_num].check_match) ?
+			"matched" : "not matched");
+
+		fw->m2v_need_update =
+			!fw->file.blocks[fw->file.block_num].check_match ||
+			fw->setting.force_update;
+		need |= fw->m2v_need_update;
+	}
+
+force_return:
+	TP_INFO(dev->id, "----------------------------------------\n");
+
+	return need;
+}
+
+static bool need_fw_update(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool need = false;
+
+	struct ilitek_fw_settings *set = &fw->setting;
+	u64 dev_fw_ver, file_fw_ver;
+
+	if (dev->protocol.flag == PTL_V3)
+		need = need_fw_update_v3(fw);
+	else if (dev->protocol.flag == PTL_V6)
+		need = need_fw_update_v6(fw);
+
+	if (fw->cb.update_fw_ic_info)
+		fw->cb.update_fw_ic_info(false,
+					 dev->fw_ver, dev->ic[0].crc,
+					 fw->file.block_num, fw->_private);
+
+	if (set->force_update)
+		return true;
+	else if (set->fw_check_only)
+		return false;
+
+	if (set->fw_ver_check && dev->ic[0].mode == AP_MODE) {
+		TP_INFO(dev->id, "IC FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			dev->fw_ver[0], dev->fw_ver[1], dev->fw_ver[2],
+			dev->fw_ver[3], dev->fw_ver[4], dev->fw_ver[5],
+			dev->fw_ver[6], dev->fw_ver[7]);
+		TP_INFO(dev->id, "File FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			set->fw_ver[0], set->fw_ver[1],
+			set->fw_ver[2], set->fw_ver[3],
+			set->fw_ver[4], set->fw_ver[5],
+			set->fw_ver[6], set->fw_ver[7]);
+
+		dev_fw_ver =
+			U82U64(dev->fw_ver[0], 7) + U82U64(dev->fw_ver[1], 6) +
+			U82U64(dev->fw_ver[2], 5) + U82U64(dev->fw_ver[3], 4) +
+			U82U64(dev->fw_ver[4], 3) + U82U64(dev->fw_ver[5], 2) +
+			U82U64(dev->fw_ver[6], 1) + U82U64(dev->fw_ver[7], 0);
+		file_fw_ver =
+			U82U64(set->fw_ver[0], 7) + U82U64(set->fw_ver[1], 6) +
+			U82U64(set->fw_ver[2], 5) + U82U64(set->fw_ver[3], 4) +
+			U82U64(set->fw_ver[4], 3) + U82U64(set->fw_ver[5], 2) +
+			U82U64(set->fw_ver[6], 1) + U82U64(set->fw_ver[7], 0);
+
+		TP_MSG(dev->id, "IC fw ver: 0x" PFMT_X64 ", File fw ver: 0x" PFMT_X64 "\n",
+			(long long unsigned int)dev_fw_ver,
+			(long long unsigned int)file_fw_ver);
+
+		if (file_fw_ver > dev_fw_ver) {
+			TP_INFO(dev->id, "IC FW version is older than File FW version\n");
+			return true;
+		} else if (file_fw_ver == dev_fw_ver) {
+			TP_INFO(dev->id, "File FW version is the same, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_same) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_same) ?
+				need : false;
+		} else {
+			TP_INFO(dev->id, "File FW version is older, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				need : false;
+		}
+	}
+
+	return need;
+}
+
+static int update_master(struct ilitek_fw_handle *fw, int idx, u32 len)
+{
+	int error = 0;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+	u16 file_crc;
+	int retry = 3;
+
+	TP_MSG(dev->id, "updating block[%d], data len: %u, start/end addr: 0x%x/0x%x\n",
+		idx, len, fw->file.blocks[idx].start, fw->file.blocks[idx].end);
+
+err_retry:
+	if ((dev->setting.no_retry && error < 0) || retry-- < 0)
+		return (error < 0) ? error : -EINVAL;
+
+	error = api_write_enable_v6(dev, false, false,
+				     fw->file.blocks[idx].start,
+				     fw->file.blocks[idx].end);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[idx].start;
+	     i < fw->file.blocks[idx].end; i += len) {
+		/*
+		 * check end addr. of data write buffer is within valid range.
+		 */
+		if (i + len > END_ADDR_LEGO) {
+			TP_ERR(dev->id, "block[%d] write addr. 0x%x + 0x%x > 0x%x OOB\n",
+				idx, i, len, END_ADDR_LEGO);
+			return -EINVAL;
+		}
+
+		_memcpy(dev->wbuf + 1, fw->file.buf + i, len);
+		error = api_write_data_v6(dev, len + 1);
+
+		if (error < 0)
+			goto err_retry;
+
+		fw->progress_curr = MIN(i + len - fw->file.blocks[idx].offset,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "block[%d] update progress: " PFMT_U8 "%%\n",
+			idx, fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	file_crc = get_crc(fw->file.blocks[idx].start,
+			   fw->file.blocks[idx].end - 1,
+			   fw->file.buf, fw->file.buf_size);
+	dev->ic[0].crc[idx] =
+		api_get_block_crc_by_addr(dev, CRC_GET,
+					  fw->file.blocks[idx].start,
+					  fw->file.blocks[idx].end);
+
+	TP_INFO(dev->id, "block[%d]: start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+		idx, fw->file.blocks[idx].start, fw->file.blocks[idx].end,
+		dev->ic[0].crc[idx], file_crc,
+		(file_crc == dev->ic[0].crc[idx]) ?
+		"matched" : "not matched");
+
+	if (file_crc != dev->ic[0].crc[idx]) {
+		error = -EINVAL;
+		goto err_retry;
+	}
+
+	return 0;
+}
+
+static int update_slave(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[0].crc[0] == dev->ic[i].crc[0] &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "updating slave, master/slave[" PFMT_U8 "] crc: 0x%x/0x%x\n",
+			i, dev->ic[0].crc[0], dev->ic[i].crc[0]);
+		error = api_access_slave(dev, 0x3, CMD_WRITE_DATA_V6, NULL);
+		if (error < 0)
+			return error;
+		error = api_write_enable_v6(dev, false, true,
+					    fw->file.blocks[0].start,
+					    fw->file.blocks[0].end);
+		if (error < 0)
+			return error;
+
+		goto success_return;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[i].mode == AP_MODE &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "changing slave[" PFMT_U8 "]: 0x" PFMT_X8 " to AP mode\n",
+			i, dev->ic[i].mode);
+		error = api_access_slave(dev, 0x3, CMD_SET_AP_MODE, NULL);
+		if (error < 0)
+			return error;
+		break;
+	}
+
+success_return:
+	return 0;
+}
+
+static int update_M3_M2V(struct ilitek_fw_handle *fw, u32 len)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 i;
+	u8 buf[6];
+	u32 AdressRange, M2V_Buf_Len, CheckSumRange;
+	u16 AddDataCount;
+
+	u8 j = fw->file.block_num;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	//Reed Add : 20230721
+	AdressRange = fw->file.blocks[j].end - fw->file.blocks[j].start;
+	M2V_Buf_Len = fw->file.blocks[j].end - fw->file.blocks[j].start + 1;
+	CheckSumRange = fw->file.blocks[j].check;
+	AddDataCount = len - M2V_Buf_Len % len;
+	AddDataCount = (AddDataCount >= 8) ? AddDataCount : AddDataCount + len;
+	AdressRange += AddDataCount;
+	CheckSumRange += AddDataCount * 0xFF;
+
+	error = api_to_bl_mode_m2v(dev, true);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_set_data_len(dev, len);
+	if (error < 0)
+		return error;
+
+	TP_INFO(dev->id, "updating M2V, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+		fw->file.blocks[j].start, fw->file.blocks[j].end,
+		fw->file.blocks[j].check);
+
+	//Reed Add : 20230721
+	buf[0] = (AdressRange >> 16) & 0xFF;
+	buf[1] = (AdressRange >> 8) & 0xFF;
+	buf[2] = AdressRange & 0xFF;
+	buf[3] = (CheckSumRange >> 16) & 0xFF;
+	buf[4] = (CheckSumRange >> 8) & 0xFF;
+	buf[5] = CheckSumRange & 0xFF;
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+	error = api_access_slave(dev, 0x80, CMD_WRITE_ENABLE, buf);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[j].start;
+	     i < fw->file.blocks[j].end; i += len) {
+		_memcpy(dev->wbuf + 1, fw->m2v_buf + i, len);
+
+		error = api_write_data_m2v(dev, len + 1);
+		if (error < 0)
+			return error;
+
+		fw->progress_curr = MIN(fw->progress_curr + len,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "m2v update progress: " PFMT_U8 "%%\n", fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	error = api_to_bl_mode_m2v(dev, false);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_access_slave(dev, 0x80, CMD_GET_FW_VER, fw->m2v_fw_ver);
+	if (error < 0)
+		return error;
+
+	TP_MSG_ARR(dev->id, "update M2V success, fw version:", TYPE_U8,
+		   8, fw->m2v_fw_ver);
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_8(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	error = api_set_data_len(dev, fw->update_len);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		if (fw->file.blocks[i].check_match)
+			continue;
+
+		error = update_master(fw, i, fw->update_len);
+		if (error < 0) {
+			TP_ERR(dev->id, "Upgrade Block:" PFMT_U8 " failed, err: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	error = api_to_bl_mode(dev, false, fw->file.blocks[0].start,
+			       fw->file.blocks[0].end);
+	if (error < 0)
+		return error;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	/* get tp info. for updating ic_num */
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.ic_num > 1) {
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(true, fw->_private);
+		error = update_slave(fw);
+
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(false, fw->_private);
+
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade slave failed, err: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	if (fw->m2v && fw->m2v_need_update) {
+		error = update_M3_M2V(fw, 1024);
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade m2v slave failed, err: %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_7(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+
+	/*
+	 * Erase data initially for the case that hex w/o data flash section.
+	 * Due to historical factor, please double confirm before modify here.
+	 */
+	error = api_erase_data_v3(dev);
+	if (error < 0)
+		return error;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false,
+					     fw->file.blocks[1].end + 1,
+					     fw->file.blocks[1].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start;
+		     i <= fw->file.blocks[1].end; i += 32) {
+			_memset(dev->wbuf + 1, 0, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->cb.delay_ms(50);
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[1] = (le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+
+		TP_INFO(dev->id, "DF block, start/end addr.: 0x%x/0x%x, ic/file checkksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(dev->ic[0].crc[1] == fw->file.blocks[1].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[1] != fw->file.blocks[1].check)
+			return -EFAULT;
+	}
+
+	/*
+	 * Update AP code forcely if AP crc/checksum not match or
+	 * Data Flash has been updated.
+	 */
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true,
+					     fw->file.blocks[0].end + 1,
+					     fw->file.blocks[0].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start;
+		     i <= fw->file.blocks[0].end; i += 32) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[0] =
+			(le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+		TP_INFO(dev->id, "AP block, start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			dev->ic[0].crc[0], fw->file.blocks[0].check,
+			(dev->ic[0].crc[0] == fw->file.blocks[0].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[0] != fw->file.blocks[0].check)
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_6(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	struct ilitek_ts_callback *cb = &dev->cb;
+	unsigned int i, j;
+
+	u32 bytes, end, check;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[1].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[1].end + 1) % 32) : 31;
+		end = fw->file.blocks[1].end + bytes;
+		check = fw->file.blocks[1].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) DF start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[1].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start, j = 0;
+		     i <= fw->file.blocks[1].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+	}
+
+	/*
+	* Update AP code forcely if AP crc/checksum not match or
+	* Data Flash has been updated.
+	*/
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[0].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[0].end + 1) % 32) : 31;
+		end = fw->file.blocks[0].end + bytes;
+		check = fw->file.blocks[0].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) AP start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[0].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start, j = 0;
+			i <= fw->file.blocks[0].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[0].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+
+#ifdef ILITEK_BOOT_UPDATE
+		/*
+		 * .ili file fill DF section with default 0xFF, but not 0
+		 * which make flow stuck in BL mode.
+		 * so HW-reset is need before switching to AP mode.
+		 * remove HW-reset after fixing .ili converter bug.
+		 */
+		reset_helper(dev);
+#endif
+	}
+
+	return 0;
+}
+
+static void update_progress(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	unsigned int last_end = 0, last_offset = 0;
+
+	fw->progress = 0;
+	fw->progress_max = 0;
+	fw->progress_curr = 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		fw->progress_max +=
+			(fw->file.blocks[1].end > fw->file.blocks[1].start) ?
+			fw->file.blocks[1].end - fw->file.blocks[1].start : 0;
+		fw->progress_max +=
+			(fw->file.blocks[0].end > fw->file.blocks[0].start) ?
+			fw->file.blocks[0].end - fw->file.blocks[0].start : 0;
+		break;
+
+	case PTL_V6:
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->file.blocks[i].check_match)
+				continue;
+
+			fw->progress_max +=
+				fw->file.blocks[i].end -
+				fw->file.blocks[i].start;
+			last_offset += fw->file.blocks[i].start - last_end;
+			fw->file.blocks[i].offset = last_offset;
+
+			last_end = fw->file.blocks[i].end;
+		}
+
+		if (fw->m2v_need_update) {
+			fw->progress_max +=
+				fw->file.blocks[fw->file.block_num].end -
+				fw->file.blocks[fw->file.block_num].start;
+		}
+
+		break;
+	}
+}
+
+void *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			 struct ilitek_update_callback *cb, void *_private)
+{
+	struct ilitek_fw_handle *fw;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)_dev;
+
+	if (need_update_ts_info && dev && api_update_ts_info(dev) < 0)
+		return NULL;
+
+	fw = (struct ilitek_fw_handle *)MALLOC(sizeof(*fw));
+	if (!fw)
+		return NULL;
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(fw, 0, sizeof(*fw));
+	fw->dev = (dev) ? dev : NULL;
+
+	/* initial update-len to default UPDATE_LEN */
+	fw->update_len = UPDATE_LEN;
+
+	fw->dev = dev;
+	fw->_private = _private;
+	fw->file.buf_size = ILITEK_FW_BUF_SIZE;
+	fw->file.buf = (u8 *)CALLOC(fw->file.buf_size, 1);
+	if (!fw->file.buf)
+		goto err_free_fw;
+
+	fw->m2v_buf = (u8 *)CALLOC(ILITEK_FW_BUF_SIZE, 1);
+	if (!fw->m2v_buf)
+		goto err_free_fw_buf;
+
+	if (cb)
+		_memcpy(&fw->cb, cb, sizeof(*cb));
+
+	return fw;
+
+err_free_fw_buf:
+	CFREE(fw->file.buf);
+err_free_fw:
+	FREE(fw);
+
+	return NULL;
+}
+
+void ilitek_update_exit(void *handle)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	if (fw->file.buf)
+		CFREE(fw->file.buf);
+
+	if (fw->m2v_buf)
+		CFREE(fw->m2v_buf);
+
+	if (fw)
+		FREE(fw);
+}
+
+void ilitek_update_set_data_length(void *handle, u16 len)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!fw)
+		return;
+
+	fw->update_len = len;
+}
+
+int ilitek_update_load_fw(void *handle, WCHAR *fw_name)
+{
+	int error;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	u32 i;
+
+	if (!handle)
+		return -EINVAL;
+
+	error = decode_firmware(fw, fw_name);
+	if (error < 0)
+		return error;
+
+	if (fw->cb.update_fw_file_info)
+		fw->cb.update_fw_file_info(&fw->file, fw->_private);
+
+	if (!fw->dev)
+		return 0;
+
+	/* for Lego and V6 IC, check block's start/end address validity */
+	if (fw->dev->protocol.flag == PTL_V6) {
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->dev->mcu_info.min_addr <=
+				fw->file.blocks[i].start &&
+			    fw->dev->mcu_info.max_addr >
+				fw->file.blocks[i].end)
+				continue;
+
+			if (!(fw->file.blocks[i].start % 0x1000))
+				continue;
+
+			TP_ERR(fw->dev->id, "Block[%u] addr. OOB (0x%x <= 0x%x/0x%x < 0x%x) or invalid start addr\n",
+				i, fw->dev->mcu_info.min_addr,
+				fw->file.blocks[i].start,
+				fw->file.blocks[i].end,
+				fw->dev->mcu_info.max_addr);
+			return -EINVAL;
+		}
+	}
+
+	TP_MSG(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " matched\n",
+		fw->dev->mcu_info.ic_name, fw->file.ic_name);
+
+	return 0;
+}
+
+int ilitek_update_start(void *handle)
+{
+	int error;
+	s8 retry = 0;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+	struct ilitek_ts_device *dev;
+
+	if (!handle)
+		return -EINVAL;
+	dev = fw->dev;
+
+	/*
+	 * Some platform (ITS-Bridge) might change touch controller
+	 * after loading fw file, get panel info. forcely and
+	 * re-check the ic/file are matched.
+	 */
+	error = api_update_ts_info(dev);
+	if (error < 0 || strcmp(dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "get ic info failed, err: %d or ic/file (" PFMT_C8 "/" PFMT_C8 ") not matched\n",
+			error, fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EPERM;
+	}
+
+	TP_INFO(dev->id, "[ilitek_update_start] start\n");
+
+	do {
+		TP_DBG(dev->id, "retry: %hhd, retry_limit: %hhd\n",
+			retry, fw->setting.retry);
+		if (retry)
+			reset_helper(dev);
+
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			continue;
+
+		if (!need_fw_update(fw)) {
+			if (is_231x(dev))
+				goto erase_data_flash_231x;
+			goto success_return;
+		}
+
+		update_progress(fw);
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(0, fw->_private);
+
+		error = api_to_bl_mode(dev, true, 0, 0);
+		if (error < 0)
+			continue;
+
+		TP_INFO_ARR(dev->id, "[BL Firmware Version]",
+			    TYPE_U8, 8, dev->fw_ver);
+		TP_INFO(dev->id, "[ilitek_update_start] start to program\n");
+
+		switch (dev->protocol.ver & 0xFFFF00) {
+		case BL_PROTOCOL_V1_8:
+			error = ilitek_update_BL_v1_8(fw);
+			break;
+		case BL_PROTOCOL_V1_7:
+			error = ilitek_update_BL_v1_7(fw);
+			break;
+		case BL_PROTOCOL_V1_6:
+			error = ilitek_update_BL_v1_6(fw);
+			break;
+		default:
+			TP_ERR(dev->id, "BL protocol ver: 0x%x not supported\n",
+				dev->protocol.ver);
+			continue;
+		}
+		if (error < 0)
+			continue;
+
+		error = api_to_bl_mode(dev, false,
+				       fw->file.blocks[0].start,
+				       fw->file.blocks[0].end);
+		if (error < 0)
+			continue;
+
+erase_data_flash_231x:
+		/*
+		 * If no data flash section in firmware file,
+		 * 231x need to erase data flash after change to AP mode.
+		 */
+		if (fw->file.blocks[1].end < fw->file.blocks[1].start &&
+		    is_231x(dev)) {
+			error = api_erase_data_v3(dev);
+			if (error < 0)
+				continue;
+		}
+
+success_return:
+		error = api_update_ts_info(dev);
+		if (error < 0)
+			continue;
+
+		error = api_set_ctrl_mode(dev, mode_normal, false, true);
+		if (error < 0)
+			continue;
+
+		if (fw->cb.update_fw_ic_info)
+			fw->cb.update_fw_ic_info(true,
+						 dev->fw_ver, dev->ic[0].crc,
+						 fw->file.block_num,
+						 fw->_private);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(100, fw->_private);
+
+		if (need_retry(fw))
+			continue;
+
+		TP_INFO(dev->id, "[ilitek_update_start] success\n");
+
+		return 0;
+	} while (!dev->setting.no_retry && ++retry < fw->setting.retry);
+
+	TP_ERR(dev->id, "[ilitek_update_start] fw update failed, err: %d\n",
+		error);
+
+	return (error < 0) ? error : -EFAULT;
+}
+
+void ilitek_update_setting(void *handle, struct ilitek_fw_settings *setting)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&fw->setting, setting, sizeof(struct ilitek_fw_settings));
+}
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.h b/drivers/input/touchscreen/ilitek/ilitek_update.h
new file mode 100644
index 000000000000..b7987b9fb121
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.h
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_UPDATE_H__
+#define __ILITEK_UPDATE_H__
+
+#include "ilitek_protocol.h"
+
+#define UPDATE_LEN			1024
+
+#define ILITEK_FW_FILE_SIZE		(512 * 1024)
+#define ILITEK_FW_BUF_SIZE		(256 * 1024)
+
+enum fw_file_type {
+	fw_hex = 0,
+	fw_bin,
+	fw_ili,
+};
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ mapping_info_lego {
+	u8 tuning_ver[4];
+	u8 fw_ver[4];
+	u8 core_test;
+	u8 core_day;
+	u8 core_month;
+	u8 core_year;
+	u32 core_ver;
+	u8 vendor_ver[6];
+	u8 _reserve_1[8];
+	u16 customer_id;
+	u16 fwid;
+	u16 i2c_addr;
+	u8 _reserve_2[2];
+	char model_name[16];
+	u8 _reserve_3[2];
+	u8 ic_num;
+	u8 total_tuning_num;
+	u16 sizeof_tuning;
+	u16 sizeof_tp_param;
+	u16 sizeof_sys_info;
+	u16 sizeof_sys_algo;
+	u16 sizeof_key_info;
+	u8 block_num;
+	u8 support_tuning_num;
+	u8 _reserve_4[2];
+
+	struct __PACKED__ {
+		u8 addr[3];
+	} blocks[10];
+
+	u8 _reserve_5[9];
+	u8 end_addr[3];
+	u8 _reserve_6[2];
+};
+
+union __PACKED__ mapping_info {
+	struct __PACKED__ {
+		u8 mapping_ver[3];
+		u8 protocol_ver[3];
+		u8 ic_name[6];
+
+		struct mapping_info_lego _lego;
+	};
+};
+
+/*
+ * for V3, "check" is checksum, block[0] for AP and block[1] for Data Flash.
+ * for V6, "check" is CRC.
+ */
+struct __PACKED__ ilitek_block {
+	bool check_match;
+	u32 start;
+	u32 end;
+	u32 check;
+	u32 offset;
+};
+
+struct __PACKED__ ilitek_fw_file_info {
+	char ic_name[8];
+	u8 fw_ver[8];
+	u16 fwid;
+
+	u8 block_num;
+	struct ilitek_block blocks[ILTIEK_MAX_BLOCK_NUM];
+
+	u32 mm_addr;
+	u32 mm_size;
+
+	u32 buf_size;
+	u8 *buf;
+
+	/* fw file's sensor-id or fwid or other id */
+	u32 id;
+	u8 type;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return file size in # of bytes, or negative error code */
+typedef int (*read_fw_t)(WCHAR *, u8 *, int, void *);
+/* update progress of fw updating */
+typedef void (*update_progress_t)(u8, void *);
+/* update fw info to callers */
+typedef void (*update_fw_file_info_t)(struct ilitek_fw_file_info *, void *);
+
+/* notify caller before/after slave upgrade (for ITS only) */
+typedef void (*slave_update_notify_t)(bool, void *);
+/* update fw version and crc/checksum before/after update (for ITS only) */
+typedef void (*update_fw_ic_info_t)(bool, u8 *, u32 *, int, void *);
+
+struct ilitek_update_callback {
+	read_fw_t read_fw;
+	update_progress_t update_progress;
+	update_fw_file_info_t update_fw_file_info;
+
+	slave_update_notify_t slave_update_notify;
+	update_fw_ic_info_t update_fw_ic_info;
+};
+
+enum fw_ver_check_policy {
+	allow_fw_ver_downgrade = 1,
+	allow_fw_ver_same = 2,
+};
+
+struct ilitek_fw_settings {
+	s8 retry;
+	bool fw_check_only;
+	bool force_update;
+
+	bool fw_ver_check;
+	u8 fw_ver_policy;
+	u8 fw_ver[8];
+};
+
+struct ilitek_fw_handle {
+	struct ilitek_ts_device *dev;
+	void *_private;
+
+	/* upgrade options */
+	struct ilitek_fw_settings setting;
+
+	/* common variable */
+	int update_len;
+
+	struct ilitek_fw_file_info file;
+
+	/* M3 + M2V */
+	bool m2v;
+	bool m2v_need_update;
+	u8 *m2v_buf;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	/* upgrade status */
+	unsigned int progress_curr;
+	unsigned int progress_max;
+	u8 progress;
+
+	/* callbacks */
+	struct ilitek_update_callback cb;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			       struct ilitek_update_callback *callback,
+			       void *_private);
+
+void __DLL ilitek_update_exit(void *handle);
+
+void __DLL ilitek_update_set_data_length(void *handle, u16 len);
+
+void __DLL ilitek_update_setting(void *handle,
+				 struct ilitek_fw_settings *setting);
+
+int __DLL ilitek_update_load_fw(void *handle, WCHAR *fw_name);
+
+int __DLL ilitek_update_start(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git "a/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" "b/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"
new file mode 100644
index 0000000000000000000000000000000000000000..54af506a4b7c4546aaa12ec99f136c9083b62a83
GIT binary patch
literal 162
zcmZQCuPRR|FHUA439vJiGgJX_3J@1FfHc>ZFflPOG_>^}{sH8{XrBE*K_md<!NeuG
f)w8dM-eF8(e4Ne9!oaZdltBfE57ScAbow{|sQnt1

literal 0
HcmV?d00001

-- 
2.25.1


^ permalink raw reply related

* [PATCH] input: touchscreen: Add ilitek touchscreen driver support
From: 2724853925 @ 2025-11-16 13:38 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg
  Cc: linux-input, linux-kernel, linux-gpio, 2724853925

From: weisicheng <2724853925@qq.com>

Add basic support for Ilitek I2C touchscreen controllers (e.g., ILitek2511, ILitek2701).
This patch includes:
1. Kconfig entry for Ilitek driver configuration
2. Makefile entry to compile the ilitek driver module
3. Core driver files under drivers/input/touchscreen/ilitek/ (I2C communication,
   touch event handling, power management)

The driver is compatible with standard input subsystem and supports:
- Multi-touch detection
- Pressure sensing
- Dynamic power saving mode

Signed-off-by: weisicheng <2724853925@qq.com>
---
 drivers/input/touchscreen/Kconfig             |   13 +
 drivers/input/touchscreen/Makefile            |    1 +
 drivers/input/touchscreen/ilitek/Makefile     |   34 +
 .../input/touchscreen/ilitek/ilitek_common.h  |  264 ++
 .../input/touchscreen/ilitek/ilitek_crypto.c  |  465 +++
 .../input/touchscreen/ilitek/ilitek_crypto.h  |   61 +
 drivers/input/touchscreen/ilitek/ilitek_def.c |  235 ++
 drivers/input/touchscreen/ilitek/ilitek_def.h |  554 +++
 .../input/touchscreen/ilitek/ilitek_main.c    | 2198 ++++++++++
 .../touchscreen/ilitek/ilitek_platform_init.c |  404 ++
 .../touchscreen/ilitek/ilitek_protocol.c      | 3644 +++++++++++++++++
 .../touchscreen/ilitek/ilitek_protocol.h      |  916 +++++
 .../input/touchscreen/ilitek/ilitek_report.c  |  455 ++
 .../input/touchscreen/ilitek/ilitek_report.h  |   78 +
 .../input/touchscreen/ilitek/ilitek_tool.c    | 1156 ++++++
 drivers/input/touchscreen/ilitek/ilitek_ts.h  |  268 ++
 .../input/touchscreen/ilitek/ilitek_update.c  | 1657 ++++++++
 .../input/touchscreen/ilitek/ilitek_update.h  |  199 +
 ...344\272\244\346\265\201\347\250\213 .docx" |  Bin 0 -> 162 bytes
 19 files changed, 12602 insertions(+)
 create mode 100644 drivers/input/touchscreen/ilitek/Makefile
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_common.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_main.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_platform_init.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_tool.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_ts.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.h
 create mode 100644 "drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7d5b72ee07fa..3552629c4d4d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -515,6 +515,19 @@ config TOUCHSCREEN_ILITEK
 	  To compile this driver as a module, choose M here: the
 	  module will be called ilitek_ts_i2c.
 
+config TOUCHSCREEN_ILI2511
+	tristate "Ilitek I2C or SPI Touch ICs"
+	depends on I2C
+	depends on SPI
+	help
+	  Say Y here if you have touchscreen with ILITEK touch IC,
+	  it supports 213X/23XX/25XX and other Lego series.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ilitek.
+
 config TOUCHSCREEN_IPROC
 	tristate "IPROC touch panel driver support"
 	depends on ARCH_BCM_IPROC || COMPILE_TEST
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ab9abd151078..3124c4dd8ee1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX)	+= hynitron_cstxxx.o
 obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X)	+= hynitron-cst816x.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_ILITEK)	+= ilitek_ts_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ILI2511)	+= ilitek/
 obj-$(CONFIG_TOUCHSCREEN_IMAGIS)	+= imagis.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/ilitek/Makefile b/drivers/input/touchscreen/ilitek/Makefile
new file mode 100644
index 000000000000..c912438a3b3e
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/Makefile
@@ -0,0 +1,34 @@
+ccflags-y += -Wall
+
+ifndef __KERNEL__
+ccflags-y += -D__KERNEL__
+endif
+
+#Un-mark below item to enable FW upgrade on boot
+#ccflags-y += -DILITEK_BOOT_UPDATE
+
+ILITEK_INTERFACE = i2c
+#ILITEK_INTERFACE = spi
+
+
+ifeq ($(ILITEK_INTERFACE), spi)
+$(info start to build ilitek SPI driver)
+ccflags-y += -DILITEK_SPI_INTERFACE
+ilitek := ilitek_spi
+else
+$(info start to build ilitek I2C driver)
+ilitek := ilitek_i2c
+endif
+
+$(ilitek)-objs += \
+	ilitek_def.o \
+	ilitek_main.o \
+	ilitek_platform_init.o \
+	ilitek_update.o \
+	ilitek_tool.o \
+	ilitek_protocol.o \
+	ilitek_crypto.o \
+	ilitek_report.o
+
+obj-$(CONFIG_TOUCHSCREEN_ILI2511) += $(ilitek).o
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_common.h b/drivers/input/touchscreen/ilitek/ilitek_common.h
new file mode 100644
index 000000000000..7f058c3e4914
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_common.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _ILITEK_COMMON_H_
+#define _ILITEK_COMMON_H_
+/* Includes of headers ------------------------------------------------------*/
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "ilitek_ts.h"
+#include "ilitek_protocol.h"
+#include "ilitek_update.h"
+
+#include "ilitek_crypto.h"
+#include "ilitek_report.h"
+
+/* Extern define ------------------------------------------------------------*/
+//driver information
+#define DRIVER_VERSION_0				5
+#define DRIVER_VERSION_1				9
+#define DRIVER_VERSION_2				3
+#define DRIVER_VERSION_3				0
+#define CUSTOMER_H_ID					0
+#define CUSTOMER_L_ID					0
+#define TEST_VERSION					0
+
+#define ILITEK_IOCTL_MAX_TRANSFER			5000UL
+
+#define set_arr(arr, idx, val)			\
+	do {					\
+		if ((idx) < ARRAY_SIZE(arr))	\
+			(arr)[(idx)] = (val);	\
+	} while (0)
+
+/* i2c clock rate for rk3288 */
+#if ILITEK_PLAT == ILITEK_PLAT_ROCKCHIP && \
+	KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE
+#define SCL_RATE(rate)	.scl_rate = (rate),
+#else
+#define SCL_RATE(rate)
+#endif
+
+/* netlink */
+#if KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)	\
+	struct netlink_kernel_cfg cfg = {	\
+		.groups = 0,			\
+		.input = func,			\
+	}
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), (cfg_ptr))
+#else
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), THIS_MODULE, (cfg_ptr))
+#endif
+#else
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), 0, (func), NULL, THIS_MODULE)
+#endif
+
+/* input_dev */
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define INPUT_MT_INIT_SLOTS(dev, num)	\
+		input_mt_init_slots((dev), (num), INPUT_MT_DIRECT)
+#else
+#define INPUT_MT_INIT_SLOTS(dev, num)	input_mt_init_slots((dev), (num))
+#endif
+
+/* file_operations ioctl */
+#if KERNEL_VERSION(2, 6, 36) <= LINUX_VERSION_CODE
+#define FOPS_IOCTL	unlocked_ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		long func(struct file *fp, cmd, arg)
+#else
+#define FOPS_IOCTL	ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		s32 func(struct inode *np, struct file *fp,	cmd, arg)
+
+#endif
+
+#if KERNEL_VERSION(6, 3, 0) > LINUX_VERSION_CODE
+#define I2C_PROBE_FUNC(func, client_arg)	\
+	int func(client_arg, const struct i2c_device_id *id)
+#else
+#define I2C_PROBE_FUNC(func, client_arg)	int func(client_arg)
+#endif
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+#define REMOVE_FUNC(func, client_arg)	int func(client_arg)
+#define REMOVE_RETURN(val)		({ __typeof__(val) _val = (val); return _val; })
+#else
+#define REMOVE_FUNC(func, client_arg)	void func(client_arg)
+#define REMOVE_RETURN(val)		(val)
+#endif
+
+#if KERNEL_VERSION(6, 4, 0) > LINUX_VERSION_CODE
+#define CLASS_CREATE(name)	class_create(THIS_MODULE, (name))
+#else
+#define CLASS_CREATE(name)	class_create((name))
+#endif
+
+/* procfs */
+#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
+#define PROC_FOPS_T	file_operations
+#define PROC_READ	read
+#define PROC_WRITE	write
+#define PROC_IOCTL		FOPS_IOCTL
+#define PROC_COMPAT_IOCTL	compat_ioctl
+#define PROC_OPEN	open
+#define PROC_RELEASE	release
+#else
+#define PROC_FOPS_T	proc_ops
+#define PROC_READ	proc_read
+#define PROC_WRITE	proc_write
+#define PROC_IOCTL		proc_ioctl
+#define PROC_COMPAT_IOCTL	proc_compat_ioctl
+#define PROC_OPEN	proc_open
+#define PROC_RELEASE	proc_release
+#endif
+
+#ifdef MTK_UNDTS
+#define ISR_FUNC(func)	void func(void)
+#define ISR_RETURN(val)
+#else
+#define ISR_FUNC(func)	irqreturn_t func(int irq, void *dev_id)
+#define ISR_RETURN(val)	({ __typeof__(val) _val = (val); return _val; })
+#endif
+
+enum ilitek_irq_handle_type {
+	irq_type_normal = 0,
+	irq_type_debug,
+	irq_type_c_model,
+};
+
+struct ilitek_ts_data {
+	void *client;
+	struct device *device;
+	struct ilitek_ts_device *dev;
+
+	/* should > 2K for C-Model */
+	u8 buf[4096];
+
+	struct input_dev *input_dev;
+	struct input_dev *pen_input_dev;
+	struct regulator *vdd;
+	struct regulator *vdd_i2c;
+	struct regulator *vcc_io;
+
+	int irq;
+	int irq_gpio;
+	int reset_gpio;
+	int test_gpio;
+
+	bool system_suspend;
+	bool power_key_triggered;
+
+	u8 irq_trigger_type;
+
+	bool is_touched;
+	bool touch_key_hold_press;
+	int touch_flag[40];
+
+#if 0
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+	struct task_struct *update_thread;
+
+	atomic_t firmware_updating;
+	bool operation_protection;
+	bool unhandle_irq;
+	unsigned int irq_handle_type;
+	unsigned int irq_read_len;
+
+	u8 gesture_status;
+	u8 low_power_status;
+
+	bool esd_check;
+	bool esd_skip;
+	struct workqueue_struct *esd_workq;
+	struct delayed_work esd_work;
+	unsigned long esd_delay;
+
+	/* Mutex for protecting concurrent access */
+	struct mutex ilitek_mutex;
+
+	atomic_t irq_enabled;
+	atomic_t get_INT;
+
+	bool wake_irq_enabled;
+
+	bool irq_registered;
+};
+
+/* Extern macro -------------------------------------------------------------*/
+
+#define CEIL(n, d) (((n) % (d)) ? ((n) / (d)) + 1 : ((n) / (d)))
+
+/* Extern variables ---------------------------------------------------------*/
+
+extern u8 driver_ver[];
+
+extern struct ilitek_ts_data *ts;
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#endif
+/* Extern function prototypes -----------------------------------------------*/
+/* Extern functions ---------------------------------------------------------*/
+void ilitek_resume(void);
+void ilitek_suspend(void);
+int ilitek_main_probe(void *client, struct device *dev);
+int ilitek_main_remove(void *client);
+void ilitek_reset(int delay);
+
+int ilitek_write(u8 *cmd, int len);
+int ilitek_read(u8 *buf, int len);
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len);
+
+void ilitek_irq_enable(void);
+void ilitek_irq_disable(void);
+
+int ilitek_upgrade_firmware(char *filename);
+
+int ilitek_create_tool_node(void);
+int ilitek_remove_tool_node(void);
+
+int ilitek_create_sysfsnode(void);
+void ilitek_remove_sys_node(void);
+
+int ilitek_netlink_init(u8 unit);
+void ilitek_netlink_exit(void);
+
+void ilitek_gpio_dbg(void);
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init);
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y);
+
+int ilitek_create_esd_check_workqueue(void);
+void ilitek_remove_esd_check_workqueue(void);
+
+int ilitek_read_data_and_report(void);
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.c b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
new file mode 100644
index 000000000000..2ba6105266d2
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_crypto.h"
+
+/*
+ * The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+ * The numbers below can be computed dynamically trading ROM for RAM -
+ * This can be useful in (embedded) bootloader applications, where ROM is often limited.
+ */
+static const u8 sbox[256] = {
+	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const u8 rsbox[256] = {
+	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+/*
+ * The round constant word array, contains the values given by
+ * x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+ */
+static const u8 rcon[11] = {
+	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+/* array holding the intermediate results during decryption. */
+typedef u8 state_t[4][4];
+
+struct crypto_aes_ctx {
+	u8 key_dec[240];
+};
+
+static u8 xtime(u8 x)
+{
+	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
+}
+
+static void inv_shift_rows(state_t *state)
+{
+	u8 tmp;
+
+	/* Rotate first row 1 columns to right */
+	tmp = (*state)[3][1];
+	(*state)[3][1] = (*state)[2][1];
+	(*state)[2][1] = (*state)[1][1];
+	(*state)[1][1] = (*state)[0][1];
+	(*state)[0][1] = tmp;
+
+	/* Rotate second row 2 columns to right */
+	tmp = (*state)[0][2];
+	(*state)[0][2] = (*state)[2][2];
+	(*state)[2][2] = tmp;
+	tmp = (*state)[1][2];
+	(*state)[1][2] = (*state)[3][2];
+	(*state)[3][2] = tmp;
+
+	/* Rotate third row 3 columns to right */
+	tmp = (*state)[0][3];
+	(*state)[0][3] = (*state)[1][3];
+	(*state)[1][3] = (*state)[2][3];
+	(*state)[2][3] = (*state)[3][3];
+	(*state)[3][3] = tmp;
+}
+
+static void inv_sub_bytes(state_t *state)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[j][i] = rsbox[(*state)[j][i]];
+}
+
+/*
+ * multiply is used to multiply numbers in the field GF(2^8)
+ * Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+ *       The compiler seems to be able to vectorize the operation better this way.
+ *       See https://github.com/kokke/tiny-AES-c/pull/34
+ */
+static u8 multiply(u8 x, u8 y)
+{
+	return (((y & 1) * x) ^
+		(((y >> 1) & 1) * xtime(x)) ^
+		(((y >> 2) & 1) * xtime(xtime(x))) ^
+		(((y >> 3) & 1) * xtime(xtime(xtime(x)))) ^
+		(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
+}
+
+/*
+ * inv_mix_cols function mixes the columns of the state matrix.
+ */
+static void inv_mix_cols(state_t *state)
+{
+	int i;
+	u8 a, b, c, d;
+
+	for (i = 0; i < 4; ++i) {
+		a = (*state)[i][0];
+		b = (*state)[i][1];
+		c = (*state)[i][2];
+		d = (*state)[i][3];
+
+		(*state)[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^
+			multiply(c, 0x0d) ^ multiply(d, 0x09);
+		(*state)[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^
+			multiply(c, 0x0b) ^ multiply(d, 0x0d);
+		(*state)[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^
+			multiply(c, 0x0e) ^ multiply(d, 0x0b);
+		(*state)[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^
+			multiply(c, 0x09) ^ multiply(d, 0x0e);
+	}
+}
+
+/*
+ * This function adds the round key to state.
+ * The round key is added to the state by an XOR function.
+ */
+static void add_round_key(u8 round, state_t *state, const u8 *key)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[i][j] ^= key[(round * Nb * 4) + (i * Nb) + j];
+}
+
+static void aes_decrypt(state_t *state, const u8 *key)
+{
+	u8 round;
+
+	/* Add the first round key to the state before starting the rounds. */
+	add_round_key(Nr, state, key);
+
+	/*
+	 * There will be Nr rounds.
+	 * The first Nr-1 rounds are identical.
+	 * These Nr rounds are executed in the loop below.
+	 * Last one without InvMixColumn()
+	 */
+	for (round = Nr - 1; ; --round) {
+		inv_shift_rows(state);
+		inv_sub_bytes(state);
+		add_round_key(round, state, key);
+
+		if (!round)
+			break;
+
+		inv_mix_cols(state);
+	}
+}
+
+/*
+ * Produces Nb(Nr+1) round keys, used in each round to decrypt the states.
+ */
+static void aes_expandkey(u8 *round_key, const u8 *key)
+{
+	unsigned int i, j, k;
+	u8 tmp_a[4];
+	u8 tmp_b;
+
+	/* The first round key is the key itself. */
+	for (i = 0; i < Nk; ++i) {
+		round_key[(i * 4) + 0] = key[(i * 4) + 0];
+		round_key[(i * 4) + 1] = key[(i * 4) + 1];
+		round_key[(i * 4) + 2] = key[(i * 4) + 2];
+		round_key[(i * 4) + 3] = key[(i * 4) + 3];
+	}
+
+	/* All other round keys are found from the previous round keys. */
+	for (i = Nk; i < Nb * (Nr + 1); ++i) {
+		k = (i - 1) * 4;
+		tmp_a[0] = round_key[k + 0];
+		tmp_a[1] = round_key[k + 1];
+		tmp_a[2] = round_key[k + 2];
+		tmp_a[3] = round_key[k + 3];
+
+		if (!(i % Nk)) {
+			/*
+			 * Shifts the 4 bytes in a word to the left once.
+			 * [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+			 */
+			tmp_b = tmp_a[0];
+			tmp_a[0] = tmp_a[1];
+			tmp_a[1] = tmp_a[2];
+			tmp_a[2] = tmp_a[3];
+			tmp_a[3] = tmp_b;
+
+			tmp_a[0] = sbox[tmp_a[0]];
+			tmp_a[1] = sbox[tmp_a[1]];
+			tmp_a[2] = sbox[tmp_a[2]];
+			tmp_a[3] = sbox[tmp_a[3]];
+
+			tmp_a[0] = tmp_a[0] ^ rcon[i / Nk];
+		}
+
+		j = i * 4;
+		k = (i - Nk) * 4;
+		round_key[j + 0] = round_key[k + 0] ^ tmp_a[0];
+		round_key[j + 1] = round_key[k + 1] ^ tmp_a[1];
+		round_key[j + 2] = round_key[k + 2] ^ tmp_a[2];
+		round_key[j + 3] = round_key[k + 3] ^ tmp_a[3];
+	}
+}
+
+u8 crypto_key[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			       0x4, 0x5, 0x6, 0x7,
+			       0x8, 0x9, 0xa, 0xb,
+			       0xc, 0xd, 0xe, 0xf };
+
+u8 crypto_iv[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			      0x4, 0x5, 0x6, 0x7,
+			      0x8, 0x9, 0xa, 0xb,
+			      0xc, 0xd, 0xe, 0xf };
+
+void ilitek_decrypt(u8 *buf, u32 len)
+{
+	u8 key[AES_KEY_LEN];
+	u8 iv[AES_KEY_LEN];
+	u8 *tmp = buf;
+	u8 iv_tmp[AES_KEY_LEN];
+	struct crypto_aes_ctx ctx;
+
+	u32 i, j;
+
+	memcpy(key, crypto_key, AES_KEY_LEN);
+	memcpy(iv, crypto_iv, AES_KEY_LEN);
+
+	aes_expandkey(ctx.key_dec, key);
+
+	for (i = 0; i < len; i += AES_KEY_LEN, tmp += AES_KEY_LEN) {
+		memcpy(iv_tmp, tmp, AES_KEY_LEN);
+
+		aes_decrypt((state_t *)tmp, ctx.key_dec);
+		for (j = 0; j < AES_KEY_LEN; j++)
+			tmp[j] ^= iv[j];
+
+		memcpy(iv, iv_tmp, AES_KEY_LEN);
+	}
+}
+
+static void sha256_init(struct sha256_ctx *ctx)
+{
+	ctx->datalen = 0;
+	ctx->bitlen[0] = 0;
+	ctx->bitlen[1] = 0;
+
+	/*
+	 * 8 hash value for sha256
+	 * square root of prime number 2,3,5,7,11,13,17,19
+	 * then take 32 bit numbers after the decimal point
+	 */
+	ctx->state[0] = 0x6a09e667;
+	ctx->state[1] = 0xbb67ae85;
+	ctx->state[2] = 0x3c6ef372;
+	ctx->state[3] = 0xa54ff53a;
+	ctx->state[4] = 0x510e527f;
+	ctx->state[5] = 0x9b05688c;
+	ctx->state[6] = 0x1f83d9ab;
+	ctx->state[7] = 0x5be0cd19;
+}
+
+static void sha256_transform(struct sha256_ctx *ctx, u8 *data)
+{
+	static u32 key[64] = {
+		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	};
+
+	u32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+	for (i = 0, j = 0; i < 16; ++i, j += 4)
+		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+
+	for (; i < 64; ++i)
+		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+
+	a = ctx->state[0];
+	b = ctx->state[1];
+	c = ctx->state[2];
+	d = ctx->state[3];
+	e = ctx->state[4];
+	f = ctx->state[5];
+	g = ctx->state[6];
+	h = ctx->state[7];
+
+	for (i = 0; i < 64; ++i) {
+		t1 = h + EP1(e) + CH(e, f, g) + key[i] + m[i];
+		t2 = EP0(a) + MAJ(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + t1;
+		d = c;
+		c = b;
+		b = a;
+		a = t1 + t2;
+	}
+
+	ctx->state[0] += a;
+	ctx->state[1] += b;
+	ctx->state[2] += c;
+	ctx->state[3] += d;
+	ctx->state[4] += e;
+	ctx->state[5] += f;
+	ctx->state[6] += g;
+	ctx->state[7] += h;
+}
+
+static void sha256_update(struct sha256_ctx *ctx, u8 byte)
+{
+	ctx->data[ctx->datalen] = byte;
+	ctx->datalen++;
+	if (ctx->datalen == 64) {
+		sha256_transform(ctx, ctx->data);
+		DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], 512);
+		ctx->datalen = 0;
+	}
+}
+
+static void sha256_final(struct sha256_ctx *ctx, u8 *hash)
+{
+	u32 i;
+
+	i = ctx->datalen;
+
+	/* Pad whatever data is left in the buffer. */
+	if (ctx->datalen < 56) {
+		ctx->data[i++] = 0x80;
+		while (i < 56)
+			ctx->data[i++] = 0x00;
+	} else {
+		ctx->data[i++] = 0x80;
+		while (i < 64)
+			ctx->data[i++] = 0x00;
+		sha256_transform(ctx, ctx->data);
+		memset(ctx->data, 0, 56);
+	}
+
+	/* Append to the padding the total message's length in bits and transform. */
+	DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8);
+	ctx->data[63] = ctx->bitlen[0];
+	ctx->data[62] = ctx->bitlen[0] >> 8;
+	ctx->data[61] = ctx->bitlen[0] >> 16;
+	ctx->data[60] = ctx->bitlen[0] >> 24;
+	ctx->data[59] = ctx->bitlen[1];
+	ctx->data[58] = ctx->bitlen[1] >> 8;
+	ctx->data[57] = ctx->bitlen[1] >> 16;
+	ctx->data[56] = ctx->bitlen[1] >> 24;
+	sha256_transform(ctx, ctx->data);
+
+	/*
+	 * Since this implementation uses little endian byte ordering and SHA uses big endian,
+	 * reverse all the bytes when copying the final state to the output hash.
+	 */
+	for (i = 0; i < 4; ++i) {
+		hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+	}
+}
+
+void get_sha256(u32 start, u32 end, u8 *buf, u32 buf_size, u8 sha256[32])
+{
+	struct sha256_ctx ctx;
+	u32 i;
+
+	sha256_init(&ctx);
+
+	if (end >= buf_size || start > buf_size || end < start) {
+		TP_ERR(NULL, "start/end addr: %#x/%#x buf size: %#x OOB\n", start, end, buf_size);
+		return;
+	}
+
+	for (i = start; i <= end && i < buf_size; i++)
+		sha256_update(&ctx, buf[i]);
+
+	sha256_final(&ctx, sha256);
+
+	TP_MSG_ARR(NULL, "sha256:", TYPE_U8, 32, sha256);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.h b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
new file mode 100644
index 000000000000..ef01ca434776
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_CRYPTO_H__
+#define __ILITEK_CRYPTO_H__
+
+#include "ilitek_def.h"
+
+#define Nb	(4)
+#define Nk	(4)       /* The number of 32 bit words in a key. */
+#define Nr	(10)      /* The number of rounds in AES Cipher. */
+
+#define AES_KEY_LEN      (16) /* Key length in bytes */
+
+extern u8 crypto_key[AES_KEY_LEN];
+extern u8 crypto_iv[AES_KEY_LEN];
+
+#define DBL_INT_ADD(a, b, c)			\
+	do {					\
+		if ((a) > 0xffffffff - (c))	\
+			++(b);			\
+		(a) += (c);			\
+	} while (0)
+
+#define ROTLEFT(a, b)	(((a) << (b)) | ((a) >> (32 - (b))))
+#define ROTRIGHT(a, b)	(((a) >> (b)) | ((a) << (32 - (b))))
+
+#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
+#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
+#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
+
+struct sha256_ctx {
+	u8 data[64];
+	u32 datalen;
+	u32 bitlen[2];
+	u32 state[8];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL ilitek_decrypt(u8 *buf, u32 len);
+
+void __DLL get_sha256(u32 start, u32 end,
+		      u8 *buf, u32 buf_size, u8 sha256[32]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.c b/drivers/input/touchscreen/ilitek/ilitek_def.c
new file mode 100644
index 000000000000..e9934cac240d
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_def.h"
+
+int tp_log_level = log_level_msg;
+bool tp_print_en = true;
+FILE *tp_fp;
+
+char g_str[4096];
+msg_t g_msg;
+
+#if defined(__KERNEL__) || defined(__UEFI_DXE__)
+
+int get_time_ms(u32 *t_ms)
+{
+	*t_ms = 0;
+
+	return -EINVAL;
+}
+
+#else
+#ifdef _WIN32
+
+static int gettimeofday(struct timeval *tp, void *tzp)
+{
+	time_t clock;
+	struct tm tm;
+	SYSTEMTIME wtm;
+
+	GetLocalTime(&wtm);
+	tm.tm_year = wtm.wYear - 1900;
+	tm.tm_mon = wtm.wMonth - 1;
+	tm.tm_mday = wtm.wDay;
+	tm.tm_hour = wtm.wHour;
+	tm.tm_min = wtm.wMinute;
+	tm.tm_sec = wtm.wSecond;
+	tm.tm_isdst = -1;
+	clock = mktime(&tm);
+	tp->tv_sec = (long)clock;
+	tp->tv_usec = wtm.wMilliseconds * 1000;
+	return 0;
+}
+
+#endif
+
+int get_time_ms(u32 *t_ms)
+{
+	static u32 time_ms_init;
+	struct timeval t;
+	u32 time_ms;
+
+	gettimeofday(&t, NULL);
+	time_ms = t.tv_sec * 1000 + t.tv_usec / 1000;
+	time_ms_init = (!time_ms_init) ? time_ms : time_ms_init;
+
+	*t_ms = time_ms - time_ms_init;
+
+	return 0;
+}
+
+#endif
+
+void tp_log_arr(char *id, int level, const char *header, const char *tag,
+		int type, int len, void *buf)
+{
+	const int num = 64;
+	int i, idx = 0;
+	u32 time_ms;
+	int error;
+
+	if (level > tp_log_level || !buf)
+		return;
+
+	error = get_time_ms(&time_ms);
+
+	do {
+		_memset(g_str, 0, sizeof(g_str));
+
+		if (!error)
+			_sprintf(g_str, 0, "[%7u.%03u]",
+				time_ms / 1000, time_ms % 1000);
+
+		if (id)
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 "[" PFMT_C8 "] " PFMT_C8 " ",
+				 header, id, tag);
+		else
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 " " PFMT_C8 " ",
+				 header, tag);
+
+		for (i = 0; i < num && idx < len; i++, idx++) {
+			switch (type) {
+			default:
+			case TYPE_U8:
+				_sprintf(g_str, _strlen(g_str), "%02x-",
+					((u8 *)buf)[idx]);
+				break;
+			case TYPE_INT:
+				_sprintf(g_str, _strlen(g_str), "%d-",
+					((int *)buf)[idx]);
+				break;
+			}
+		}
+		_sprintf(g_str, _strlen(g_str) - 1, ", len: [%d/%d]\n",
+			idx, len);
+
+		if (tp_print_en)
+			TP_PRINTF(PFMT_C8, g_str);
+		if (g_msg)
+			g_msg(level, g_str);
+		TP_LOG(tp_fp, g_str);
+	} while (idx < len);
+}
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items)
+{
+	int error = 0;
+
+	MUTEX_INIT(q->mutex);
+
+	MUTEX_LOCK(q->mutex);
+
+	do {
+		q->item_size = item_size;
+		q->curr_size = 0;
+		q->max_size = max_items;
+
+		q->buf = (u8 *)CALLOC(max_items, item_size);
+		if (!q->buf) {
+			error = -ENOMEM;
+			break;
+		}
+
+		q->push_ptr = q->buf;
+		q->pop_ptr = q->buf;
+		q->end_ptr = q->buf + (max_items - 1) * item_size;
+	} while (false);
+
+	MUTEX_UNLOCK(q->mutex);
+
+	return error;
+}
+
+void queue_exit(struct queue *q)
+{
+	if (q->buf)
+		CFREE(q->buf);
+	MUTEX_EXIT(q->mutex);
+}
+
+void queue_push(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	/* Stop push data when queue is full */
+	if (q->curr_size >= q->max_size)
+		goto release_push_lock;
+
+	q->curr_size++;
+	if (q->push_ptr == q->end_ptr)
+		q->push_ptr = q->buf;
+	else
+		q->push_ptr += q->item_size;
+
+	if (q->push_ptr == q->pop_ptr)
+		TP_ERR(NULL, "[Warn]Queue overload, queue size: %u\n", q->curr_size);
+
+release_push_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void queue_pop(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	if (!q->curr_size)
+		goto release_pop_lock;
+
+	q->curr_size--;
+	if (q->pop_ptr == q->end_ptr)
+		q->pop_ptr = q->buf;
+	else
+		q->pop_ptr += q->item_size;
+
+release_pop_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void set_print_en(bool enable)
+{
+	tp_print_en = enable;
+}
+
+void set_log_level(int level)
+{
+	tp_log_level = level;
+}
+
+int set_log_fopen(WCHAR *filename)
+{
+	int error;
+
+	if (tp_fp)
+		return -EINVAL;
+
+	error = WFOPEN(&tp_fp, filename, "w+");
+	if (error < 0) {
+		tp_fp = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+void set_log_fclose(void)
+{
+	if (!tp_fp)
+		return;
+
+	_fclose(tp_fp);
+	tp_fp = NULL;
+}
+
+void set_log_fwrite(char *str)
+{
+	TP_LOG(tp_fp, str);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.h b/drivers/input/touchscreen/ilitek/ilitek_def.h
new file mode 100644
index 000000000000..d6f2077d7760
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.h
@@ -0,0 +1,554 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_DEF_H__
+#define __ILITEK_DEF_H__
+
+#define COMMONFLOW_CODE_VERSION		0x00000301
+
+/*
+ * Windows
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+#define __MAYBE_UNUSED
+
+#ifdef _WINDLL
+#define __DLL __declspec(dllexport)
+#else
+#define __DLL __declspec(dllimport)
+#endif
+
+#define __PACKED__
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	sprintf_s((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size)	strncpy_s((dst), (dst_size), (src), (n))
+#define _strcasecmp(l, r)		_stricmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat_s((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)		sscanf_s(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy_s((dst), (dst_size), (src))
+#define _strtok(str, del, next_token)	strtok_s((str), (del), (next_token))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				wchar_t
+#define WSTRING				wstring
+#define WCSCPY(dst, src, dst_size)	wcscpy_s((dst), (dst_size), (src))
+#define WCSCASECMP(str, tag)		_wcsicmp((str), (L##tag))
+#define WCSRCHR(str, ch)		wcsrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	swprintf_s((buf), (size), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)					\
+	((!((*(pfp)) = _wfsopen((filename), (const wchar_t *)(L##mode), \
+				_SH_DENYWR))) ? -EFAULT : 0)
+#define WACCESS(filename, mode)		_waccess((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)					\
+	do {							\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_U8TEXT);		\
+		fwprintf((fp), (fmt), ##__VA_ARGS__);		\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_TEXT);		\
+	} while (false)
+
+#define PFMT_C16			"%ls"
+#define PFMT_C8				"%hs"
+
+#include <codecvt>
+#define TO_WCHAR(x)	\
+	std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t > ().from_bytes(x).c_str()
+
+#define _localtime(ptm, ptime)		localtime_s((ptm), (ptime))
+
+#define _fopen(pfp, filename, mode)	\
+	(fopen_s((pfp), (filename), (const char *)(mode)))
+#define _fclose(pfp)			fclose((pfp))
+
+#define MUTEX_T				HANDLE
+#define MUTEX_INIT(x)			((x) = CreateMutex(NULL, false, NULL))
+#define MUTEX_LOCK(x)			(WaitForSingleObject((x), INFINITE))
+#define MUTEX_UNLOCK(x)			(ReleaseMutex((x)))
+#define MUTEX_EXIT(x)			(CloseHandle((x)))
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+/*
+ * UEFI Driver
+ */
+#elif defined(__UEFI_DXE__)
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+
+#define UEFI_MAX_STR_SIZE	0x1000
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	AsciiSPrint((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) \
+	AsciiStrnCpyS(dst, dst_size, src, (UINTN)n)
+#define _strcasecmp(l, r)		AsciiStriCmp((l), (r))
+#define _strcat(dst, src, dst_size)	\
+	AsciiStrCatS((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)
+#define _strlen(str)			AsciiStrnLenS((str), UEFI_MAX_STR_SIZE)
+#define _strcpy(dst, src, dst_size)	\
+	_strncpy((dst), (src), _strlen(src), (dst_size))
+#define _strtok(str, del, next_token)
+
+static __MAYBE_UNUSED char *_strrchr(const char *s, char c)
+{
+	char *found = NULL;
+
+	do {
+		if (*s == c)
+			found = (char *)s;
+	} while (*s++ != '\0');
+
+	return found;
+}
+
+#define _memset(ptr, ch, size)		\
+	SetMem((ptr), (UINTN)(size), (UINT8)(ch))
+#define _memcpy(dst, src, size)		\
+	CopyMem((dst), (src), (UINTN)(size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		_strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	(-EINVAL)
+#define WACCESS(filename, mode)		(-EINVAL)
+#define WFPRINTF(fp, fmt, ...)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%a"
+#define PFMT_C8				"%a"
+#define PFMT_U8				"%u"
+#define PFMT_U16			"%u"
+#define PFMT_X8				"%02x"
+#define PFMT_X16			"%04x"
+#define PFMT_X64			"%016llx"
+
+#define _localtime(ptm, ptime)
+#define _fclose(pfp)
+
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define MALLOC(size)		AllocatePool(size)
+#define CALLOC(num, size)	AllocateZeroPool(num * size)
+#define FREE(ptr)			\
+	do {				\
+		FreePool((ptr));	\
+		(ptr) = NULL;		\
+	} while (0)
+
+#define CFREE(ptr)		FREE(ptr)
+#define TP_PRINTF(fmt, ...)	DebugPrint(DEBUG_INFO, fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+/*
+ * Linux-Based System
+ */
+#else
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+#define _sprintf(buf, idx, fmt, ...)	sprintf((buf) + (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) strncpy((dst), (src), (n))
+#define _strcasecmp(l, r)		strcasecmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat((dst), (src))
+#define _sscanf(str, fmt, ...)		sscanf(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy((dst), (src))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define WTEXT(str)			str
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	_fopen(pfp, filename, mode)
+#define WACCESS(filename, mode)		_access((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)		fprintf((fp), (fmt), ##__VA_ARGS__)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%s"
+#define PFMT_C8				"%s"
+
+#define _localtime(ptm, ptime)						\
+		do {							\
+			struct tm *__tm__;				\
+									\
+			__tm__ = localtime((ptime));			\
+			_memcpy((ptm), __tm__, sizeof(struct tm));	\
+		} while (false)
+
+#ifdef __KERNEL__
+#define FILE				void
+#define _fopen(pfp, filename, mode)	(-EINVAL)
+#define _fclose(pfp)
+#define _access(filename, mode)		(-EINVAL)
+
+#define _strtok(str, del, next_token)
+
+#include <linux/spinlock.h>
+#define MUTEX_T				spinlock_t
+#define MUTEX_INIT(x)			spin_lock_init(&(x))
+#define MUTEX_LOCK(x)			spin_lock(&(x))
+#define MUTEX_UNLOCK(x)			spin_unlock(&(x))
+#define MUTEX_EXIT(x)
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#define MALLOC(size)		kmalloc(size, GFP_KERNEL)
+#define CALLOC(num, size)	vmalloc(num * size)
+#define FREE(ptr)		\
+	do {			\
+		kfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		\
+	do {			\
+		vfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+
+#define TP_PRINTF(fmt, ...)	printk(fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+#else
+#include <sys/time.h>
+#include <unistd.h>
+
+#define _fopen(pfp, filename, mode)	\
+	((!((*(pfp)) = fopen((filename), (mode)))) ? -EFAULT : 0)
+#define _fclose(pfp)			fclose((pfp))
+#define _access(filename, mode)		access((filename), (mode))
+
+#define _strtok(str, del, next_token)	strtok((str), (del))
+
+#ifdef __UEFI_APP__
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+#else
+#include <pthread.h>
+#define MUTEX_T				pthread_mutex_t
+#define MUTEX_INIT(x)			(pthread_mutex_init(&(x), NULL))
+#define MUTEX_LOCK(x)			(pthread_mutex_lock(&(x)))
+#define MUTEX_UNLOCK(x)			(pthread_mutex_unlock(&(x)))
+#define MUTEX_EXIT(x)			(pthread_mutex_destroy(&(x)))
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#define TP_PRINTF(fmt, ...)							\
+	__android_log_print(ANDROID_LOG_INFO, "ILITEK COMMON", "[%s][%d]" fmt,	\
+			    __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _WIN32 */
+
+#define U82U64(byte, order)	\
+	((u64)((u64)(byte) << ((order) * 8)))
+
+#ifndef PFMT_C16
+#define PFMT_C16			"%ls"
+#endif
+#ifndef PFMT_C8
+#define PFMT_C8				"%hs"
+#endif
+#ifndef PFMT_U8
+#define PFMT_U8				"%hhu"
+#endif
+#ifndef PFMT_U16
+#define PFMT_U16			"%hu"
+#endif
+#ifndef PFMT_X8
+#define PFMT_X8				"%hhx"
+#endif
+#ifndef PFMT_X16
+#define PFMT_X16			"%hx"
+#endif
+
+#ifndef PFMT_X64
+#define PFMT_X64			"%llx"
+#endif
+
+#ifndef TP_PRINTF
+#define TP_PRINTF(fmt, ...)				\
+	do {						\
+		printf(fmt, ##__VA_ARGS__);		\
+		fflush(stdout);				\
+	} while (0)
+#endif
+
+#ifndef TP_LOG
+#define TP_LOG(fp, str)				\
+	do {					\
+		if (!fp)			\
+			break;			\
+		fprintf((fp), PFMT_C8, (str));	\
+		fflush((fp));			\
+	} while (0)
+#endif
+
+#ifndef TP_PRINT
+#define TP_PRINT(_id, level, need_tag, tag, fmt, ...)			\
+	do {								\
+		char *__id__ = (_id);					\
+		u32 __time_ms__;					\
+									\
+		if (level > tp_log_level)				\
+			break;						\
+									\
+		g_str[0] = '\0';					\
+									\
+		if (need_tag) {						\
+			if (!get_time_ms(&__time_ms__))			\
+				_sprintf(g_str, _strlen(g_str),		\
+					"[%7u.%03u]",			\
+					__time_ms__ / 1000,		\
+					__time_ms__ % 1000);		\
+			_sprintf(g_str, _strlen(g_str), PFMT_C8, tag);	\
+		}							\
+									\
+		if (__id__) {						\
+			_sprintf(g_str, _strlen(g_str),			\
+				 "[" PFMT_C8 "] " fmt,			\
+				 __id__, ##__VA_ARGS__);		\
+		} else {						\
+			_sprintf(g_str, _strlen(g_str), " " fmt,	\
+				 ##__VA_ARGS__);			\
+		}							\
+									\
+		if (tp_print_en)					\
+			TP_PRINTF(PFMT_C8, g_str);			\
+		if (g_msg)						\
+			g_msg(level, g_str);				\
+		TP_LOG(tp_fp, g_str);					\
+	} while (0)
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d)	(((n) + (d) - 1) / (d))
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x)		((void)(x))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a)		((sizeof(a) / sizeof(*(a))))
+#endif
+
+#ifndef MIN
+#define MIN(l, r)		(((l) > (r)) ? (r) : (l))
+#endif
+
+#ifndef MAX
+#define MAX(l, r)		(((l) > (r)) ? (l) : (r))
+#endif
+
+#define EILICOMM		200
+#define EILIBUSY		201
+#define EILITIME		202
+#define EILIPROTO		203
+
+enum ilitek_log_level {
+	log_level_none = -1,	/* no log displayed */
+	log_level_err = 0,	/* critical errors */
+	log_level_warn,		/* warnings */
+	log_level_tag,		/* special-required tags */
+	log_level_info,		/* important/UI messages */
+	log_level_msg,		/* non-important messages */
+	log_level_dbg,		/* debugging messages */
+	log_level_pkt,		/* tx/rx packets */
+
+	log_level_max,		/* sentinel */
+};
+
+#define _TP_ERR(fmt, ...)	\
+	TP_PRINT(NULL, log_level_err, false, "", fmt, ##__VA_ARGS__)
+#define _TP_WARN(fmt, ...)	\
+	TP_PRINT(NULL, log_level_warn, false, "", fmt, ##__VA_ARGS__)
+#define _TP_TAG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_tag, false, "", fmt, ##__VA_ARGS__)
+#define _TP_INFO(fmt, ...)	\
+	TP_PRINT(NULL, log_level_info, false, "", fmt, ##__VA_ARGS__)
+#define _TP_MSG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_msg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_DBG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_dbg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_PKT(fmt, ...)	\
+	TP_PRINT(NULL, log_level_pkt, false, "", fmt, ##__VA_ARGS__)
+
+
+#define TP_ERR(id, fmt, ...)	\
+	TP_PRINT(id, log_level_err, true, "[ILITEK][ERR]", fmt, ##__VA_ARGS__)
+#define TP_WARN(id, fmt, ...)	\
+	TP_PRINT(id, log_level_warn, true, "[ILITEK][WARN]", fmt, ##__VA_ARGS__)
+#define TP_TAG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_tag, true, "[ILITEK][TAG]", fmt, ##__VA_ARGS__)
+#define TP_INFO(id, fmt, ...)	\
+	TP_PRINT(id, log_level_info, true, "[ILITEK][INFO]", fmt, ##__VA_ARGS__)
+#define TP_MSG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_msg, true, "[ILITEK][MSG]", fmt, ##__VA_ARGS__)
+#define TP_DBG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_dbg, true, "[ILITEK][DBG]", fmt, ##__VA_ARGS__)
+#define TP_PKT(id, fmt, ...)	\
+	TP_PRINT(id, log_level_pkt, true, "[ILITEK][PKT]", fmt, ##__VA_ARGS__)
+
+enum ilitek_array_type {
+	TYPE_U8 = 0,
+	TYPE_INT,
+	TYPE_U32,
+};
+
+#define TP_ERR_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_err, "[ILITEK][ERR]", tag, type, len, buf)
+#define TP_WARN_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_warn, "[ILITEK][WARN]", tag, type, len, buf)
+#define TP_TAG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_tag, "[ILITEK][TAG]", tag, type, len, buf)
+#define TP_INFO_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_info, "[ILITEK][INFO]", tag, type, len, buf)
+#define TP_MSG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_msg, "[ILITEK][MSG]", tag, type, len, buf)
+#define TP_DBG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_dbg, "[ILITEK][DBG]", tag, type, len, buf)
+#define TP_PKT_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_pkt, "[ILITEK][PKT]", tag, type, len, buf)
+
+extern int tp_log_level;
+extern bool tp_print_en;
+extern char g_str[4096];
+extern FILE *tp_fp;
+
+typedef void (*msg_t)(int, char *);
+extern msg_t g_msg;
+
+struct queue {
+	u32 curr_size;
+	u32 max_size;
+	u8 *buf;
+	u8 *push_ptr;
+	u8 *pop_ptr;
+	u8 *end_ptr;
+	u32 item_size;
+
+	MUTEX_T mutex;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL tp_log_arr(char *id, int level, const char *header,
+		      const char *tag, int type, int len, void *buf);
+
+int __DLL get_time_ms(u32 *t_ms);
+void __DLL set_print_en(bool enable);
+void __DLL set_log_level(int level);
+int __DLL set_log_fopen(WCHAR *filename);
+void __DLL set_log_fclose(void);
+void __DLL set_log_fwrite(char *str);
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items);
+void queue_exit(struct queue *q);
+void queue_push(struct queue *q);
+void queue_pop(struct queue *q);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_main.c b/drivers/input/touchscreen/ilitek/ilitek_main.c
new file mode 100644
index 000000000000..9bb1fe6aa4b7
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_main.c
@@ -0,0 +1,2198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+struct ilitek_ts_data *ts;
+
+u8 driver_ver[] = {
+	DRIVER_VERSION_0, DRIVER_VERSION_1, DRIVER_VERSION_2, DRIVER_VERSION_3,
+	CUSTOMER_H_ID, CUSTOMER_L_ID, TEST_VERSION,
+};
+
+static bool checksum_failed_release = ILITEK_CHECKSUM_FAILED_RELEASE;
+module_param(checksum_failed_release, bool, 0664);
+MODULE_PARM_DESC(checksum_failed_release,
+		 "When packet's checksum is wrong, (default)release all touch point or ignore the packet");
+
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+extern struct tpd_device *tpd;
+#ifdef ILITEK_ENABLE_DMA
+static u8 *I2CDMABuf_va;
+static dma_addr_t I2CDMABuf_pa;
+#endif
+#endif
+
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+struct wake_lock ilitek_wake_lock;
+#endif
+
+#ifdef ILITEK_TUNING_MESSAGE
+static struct sock *ilitek_netlink_sock;
+bool ilitek_debug_flag;
+static u_int ilitek_pid = 100, ilitek_seq = 23;
+#endif
+
+static void __maybe_unused ilitek_udp_reply(void *payload, int size)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int len = NLMSG_SPACE(size);
+	int ret;
+	int pid = ilitek_pid, seq = ilitek_seq;
+
+	TP_DBG(NULL, "[%s] ilitek_debug_flag: %d\n", __func__, ilitek_debug_flag);
+	if (!ilitek_debug_flag)
+		return;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		TP_ERR(NULL, "alloc skb error\n");
+		return;
+	}
+
+	nlh = nlmsg_put(skb, pid, seq, 0, size, 0);
+	if (!nlh)
+		goto nlmsg_failure;
+
+	nlh->nlmsg_flags = 0;
+	memcpy(NLMSG_DATA(nlh), payload, size);
+
+	NETLINK_CB(skb).portid = 0;	/* from kernel */
+	NETLINK_CB(skb).dst_group = 0;	/* unicast */
+
+	ret = netlink_unicast(ilitek_netlink_sock, skb, pid, MSG_DONTWAIT);
+	if (ret < 0)
+		TP_ERR(NULL, "ilitek send failed, ret: %d\n", ret);
+	return;
+
+nlmsg_failure:
+	kfree_skb(skb);
+
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void __maybe_unused udp_receive(struct sk_buff *skb)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	int count = 0, ret = 0, i = 0;
+	u8 *data;
+	struct nlmsghdr *nlh;
+
+	nlh = (struct nlmsghdr *)skb->data;
+	ilitek_pid = NETLINK_CREDS(skb)->pid;
+	ilitek_seq = nlh->nlmsg_seq;
+
+	TP_DBG(NULL, "netlink received, pid: %d, seq: %d\n",
+		     ilitek_pid, ilitek_seq);
+
+	data = (u8 *) NLMSG_DATA(nlh);
+	count = nlmsg_len(nlh);
+	if (!strcmp(data, "Open!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = true;
+		ilitek_udp_reply(data, sizeof("Open!"));
+	} else if (!strcmp(data, "Close!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = false;
+	} else if (!strcmp(data, "Wifi_Paint_Start") ||
+		   !strcmp(data, "Daemon_Debug_Start")) {
+		ilitek_debug_flag = true;
+	} else if (!strcmp(data, "Wifi_Paint_End") ||
+		   !strcmp(data, "Daemon_Debug_End")) {
+		ilitek_debug_flag = false;
+	}
+
+
+	TP_DBG(NULL, "count = %d  data[count -3] = %d data[count -2] = %c\n",
+	       count, data[count - 3], data[count - 2]);
+	for (i = 0; i < count; i++)
+		//TP_MSG(NULL, "data[%d] = 0x%x\n", i, data[i]);
+	if (data[count - 2] == 'I' && (count == 20 || count == 52) &&
+	    data[0] == 0x77 && data[1] == 0x77) {
+		TP_DBG(NULL, "IOCTL_WRITE CMD = %d\n", data[2]);
+		switch (data[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", data[3]);
+			if (data[3] == 0)
+				ilitek_debug_flag = false;
+			else if (data[3] == 1)
+				ilitek_debug_flag = true;
+			break;
+		case 15:
+			if (data[3] == 0) {
+				ilitek_irq_disable();
+				TP_DBG(NULL, "ilitek_irq_disable.\n");
+			} else {
+				ilitek_irq_enable();
+				TP_DBG(NULL, "ilitek_irq_enable.\n");
+			}
+			break;
+		case 16:
+			ts->operation_protection = data[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 8:
+			TP_MSG(NULL, "get driver version\n");
+			ilitek_udp_reply(driver_ver, 7);
+			break;
+		case 18:
+			TP_DBG(NULL, "firmware update write 33 bytes data\n");
+			ret = ilitek_write(&data[3], 33);
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			if (ret < 0)
+				data[0] = 1;
+			else
+				data[0] = 0;
+			ilitek_udp_reply(data, 1);
+			return;
+		default:
+			return;
+		}
+	} else if (data[count - 2] == 'W') {
+		ret = ilitek_write(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+		if (ret < 0)
+			data[0] = 1;
+		else
+			data[0] = 0;
+		ilitek_udp_reply(data, 1);
+	} else if (data[count - 2] == 'R') {
+		ret = ilitek_read(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		if (ret < 0)
+			data[count - 2] = 1;
+		else
+			data[count - 2] = 0;
+		ilitek_udp_reply(data, count - 1);
+	}
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void ilitek_esd_check(struct work_struct *work)
+{
+	int retry = 3;
+	static bool is_first_run = true;
+	static u32 protocol_ver;
+
+	/*
+	 * update protocol version at the first run
+	 */
+	if (is_first_run) {
+		is_first_run = false;
+		protocol_ver = ts->dev->protocol.ver;
+		TP_MSG(NULL, "[ESD] firstly loading protocol ver: %x as ref.\n",
+			protocol_ver);
+	}
+
+	if (ts->operation_protection || ts->esd_skip) {
+		TP_MSG(NULL, "[ESD] operation_protection: %hhu, esd_skip: %hhu\n",
+			ts->operation_protection, ts->esd_skip);
+		goto skip_return;
+	}
+
+	mutex_lock(&ts->ilitek_mutex);
+
+	for (; retry-- > 0;) {
+		if (api_protocol_set_cmd(ts->dev, GET_PTL_VER, NULL) < 0) {
+			TP_ERR(NULL, "[ESD] i2c comm. failed\n");
+			continue;
+		}
+
+		if (protocol_ver != ts->dev->protocol.ver) {
+			TP_ERR(NULL, "unexpected ptl ver (referance)%x vs. %x\n",
+				protocol_ver, ts->dev->protocol.ver);
+			continue;
+		}
+
+		goto pass_return;
+	}
+
+	ilitek_reset(ts->dev->reset_time);
+
+pass_return:
+	mutex_unlock(&ts->ilitek_mutex);
+
+skip_return:
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+}
+
+void ilitek_irq_enable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	enable_irq(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 1);
+}
+
+void ilitek_irq_disable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (!atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	disable_irq_nosync(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 0);
+}
+
+#ifdef ILITEK_ENABLE_DMA
+static int ilitek_dma_i2c_read(u8 *buf, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int err;
+
+
+	if (len < 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_recv(client, buf, len);
+	}
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+	err = i2c_master_recv(client, (u8 *)I2CDMABuf_pa, len);
+	if (err < 0)
+		return err;
+
+	memcpy(buf, I2CDMABuf_va, len);
+
+	return 0;
+}
+
+static int ilitek_dma_i2c_write(u8 *cmd, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+
+	if (len <= 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_send(client, cmd, len);
+	}
+
+	memcpy(I2CDMABuf_va, cmd, len);
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+
+	return i2c_master_send(client, (u8 *)I2CDMABuf_pa, len);
+}
+#endif
+
+static int ilitek_i2c_transfer(struct i2c_msg *msgs, int cnt)
+{
+	int err = 0;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int count = ILITEK_I2C_RETRY_COUNT;
+
+#ifdef ILITEK_ENABLE_DMA
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		count = ILITEK_I2C_RETRY_COUNT;
+		while (count-- >= 0) {
+			msgs[i].ext_flag = 0;
+			if (msgs[i].flags == I2C_M_RD)
+				err = ilitek_dma_i2c_read(msgs[i].buf, msgs[i].len);
+			else if (!msgs[i].flags)
+				err = ilitek_dma_i2c_write(msgs[i].buf, msgs[i].len);
+
+			if (err < 0) {
+				TP_ERR(NULL, "i2c[0x%hx] dma tx/rx failed, err: %d\n",
+					msgs[i].addr, err);
+				mdelay(20);
+				continue;
+			}
+
+			break;
+		}
+	}
+#else
+	while (count-- >= 0) {
+		err = i2c_transfer(client->adapter, msgs, cnt);
+		if (err < 0) {
+			TP_ERR(NULL, "i2c[0x%hx] tx/rx failed, err: %d\n",
+				msgs[0].addr, err);
+			mdelay(20);
+			continue;
+		}
+		break;
+	}
+#endif
+
+	return err;
+}
+
+static int __maybe_unused ilitek_i2c_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error;
+
+	/*
+	 * Default ILITEK_BL_ADDR. is firstly used.
+	 * if communication failed, change between BL addr. and
+	 * other addr. defined by DTS, then retry.
+	 */
+	static unsigned short addr = ILITEK_BL_ADDR;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	struct i2c_msg msgs[2] = {
+		{.addr = addr, .flags = 0, .len = w_len,
+		 .buf = cmd, SCL_RATE(400000)},
+		{.addr = addr, .flags = I2C_M_RD, .len = r_len,
+		 .buf = buf, SCL_RATE(400000)}
+	};
+
+	/*
+	 * IMPORTANT: If I2C repeat start is required, please check with ILITEK.
+	 */
+	if (w_len > 0 && r_len > 0 && !delay_ms) {
+		if (ilitek_i2c_transfer(msgs, 2) < 0) {
+			/* try another i2c addr. (default: 0x41) */
+			addr = (addr == ILITEK_BL_ADDR) ?
+				client->addr : ILITEK_BL_ADDR;
+			msgs[0].addr = msgs[1].addr = addr;
+
+			return ilitek_i2c_transfer(msgs, 2);
+		}
+
+		return 0;
+	}
+
+	if (w_len > 0 && ilitek_i2c_transfer(msgs, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		error = ilitek_i2c_transfer(msgs, 1);
+		if (error < 0)
+			return error;
+	}
+
+	if (delay_ms > 0)
+		mdelay(delay_ms);
+
+	if (r_len > 0 && ilitek_i2c_transfer(msgs + 1, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		return ilitek_i2c_transfer(msgs + 1, 1);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_i2c_write(u8 *cmd, int len)
+{
+	return ilitek_i2c_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_i2c_read(u8 *buf, int len)
+{
+	return ilitek_i2c_write_and_read(NULL, 0, 0, buf, len);
+}
+
+static int __maybe_unused ilitek_spi_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error = 0;
+	u8 *wbuf, *rbuf;
+	struct spi_device *spi = (struct spi_device *)ts->client;
+	struct spi_transfer xfer = {
+		.len = r_len + 4,
+		.speed_hz = ((struct spi_device *)ts->client)->max_speed_hz,
+	};
+	struct spi_message msg;
+
+	if (w_len > 0 && r_len > 0) {
+		error = ilitek_spi_write_and_read(cmd, w_len, delay_ms,
+						  NULL, 0);
+		if (error < 0)
+			return error;
+
+		return ilitek_spi_write_and_read(NULL, 0, 0, buf, r_len);
+	}
+
+	wbuf = CALLOC(4096, sizeof(u8));
+	rbuf = CALLOC(4096, sizeof(u8));
+
+	if (!wbuf || !rbuf) {
+		error = -ENOMEM;
+		goto exit;
+	}
+
+	xfer.tx_buf = wbuf;
+	xfer.rx_buf = rbuf;
+
+	wbuf[1] = 0xAA;
+
+	/* wbuf[0] set as 0x83 for spi data read */
+	if (r_len > 0) {
+		wbuf[0] = 0x83;
+		memset(wbuf + 2, 0, xfer.len - 2);
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfer, &msg);
+		error = spi_sync(spi, &msg);
+		if (error < 0)
+			goto exit;
+
+		TP_DBG(NULL, "[rbuf]: %*phD, len: %d\n", xfer.len, rbuf, xfer.len);
+
+		memcpy(buf, rbuf + 4, r_len);
+	} else if (w_len > 0) {
+		wbuf[0] = 0x82;
+		wbuf[2] = cmd[0];
+		wbuf[3] = 0;
+		memcpy(wbuf + 4, cmd + 1, w_len - 1);
+
+		TP_DBG(NULL, "[wbuf]: %*phD, len: %d\n", 3 + w_len, wbuf, 3 + w_len);
+
+		error = spi_write(spi, wbuf, 3 + w_len);
+		if (error < 0)
+			goto exit;
+
+		if (delay_ms > 0)
+			mdelay(delay_ms);
+	}
+
+exit:
+	CFREE(wbuf);
+	CFREE(rbuf);
+
+	return (error < 0) ? error : 0;
+}
+
+static int __maybe_unused ilitek_spi_write(u8 *cmd, int len)
+{
+	return ilitek_spi_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_spi_read(u8 *buf, int len)
+{
+	return ilitek_spi_write_and_read(NULL, 0, 0, buf, len);
+}
+
+int ilitek_write(u8 *cmd, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write(cmd, len);
+#else
+	error = ilitek_i2c_write(cmd, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_read(u8 *buf, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_read(buf, len);
+#else
+	error = ilitek_i2c_read(buf, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#else
+	error = ilitek_i2c_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+void __maybe_unused ilitek_gpio_dbg(void)
+{
+#if defined(ILITEK_GPIO_DEBUG)
+	gpio_direction_output(ts->test_gpio, 0);
+	mdelay(1);
+	gpio_direction_output(ts->test_gpio, 1);
+#endif
+}
+
+void ilitek_reset(int delay)
+{
+	TP_MSG(NULL, "reset_gpio: %d, delay: %d\n", ts->reset_gpio, delay);
+
+	ilitek_irq_disable();
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK && defined(MTK_UNDTS)
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ZERO);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(delay);
+#elif ILITEK_PLAT == ILITEK_PLAT_MTK
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 0);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#else
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 0);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#endif
+
+	ilitek_irq_enable();
+}
+
+static int ilitek_free_gpio(void)
+{
+
+#ifndef MTK_UNDTS
+	if (gpio_is_valid(ts->reset_gpio)) {
+		TP_MSG(NULL, "reset_gpio is valid so free\n");
+		gpio_free(ts->reset_gpio);
+	}
+	if (gpio_is_valid(ts->irq_gpio)) {
+		TP_MSG(NULL, "irq_gpio is valid so free\n");
+		gpio_free(ts->irq_gpio);
+	}
+#endif
+
+#if defined(ILITEK_GPIO_DEBUG)
+	if (gpio_is_valid(ts->test_gpio)) {
+		TP_MSG(NULL, "test_gpio is valid so free\n");
+		gpio_free(ts->test_gpio);
+	}
+#endif
+
+	return 0;
+}
+
+static int ilitek_request_pen_input_dev(void)
+{
+	int error;
+	struct input_dev *input;
+
+	int x_min = ts->dev->screen_info.pen_x_min;
+	int y_min = ts->dev->screen_info.pen_y_min;
+	int x_max = ts->dev->screen_info.pen_x_max;
+	int y_max = ts->dev->screen_info.pen_y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	TP_DBG(NULL, "registering pen input device\n");
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOOL_PEN, input->keybit);		/* In Range */
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);	/* Invert */
+	__set_bit(BTN_STYLUS, input->keybit);		/* Barrel Switch */
+	__set_bit(BTN_TOUCH, input->keybit);		/* Tip Switch */
+
+	input->name = "ILITEK STYLUS";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
+
+	input_set_abs_params(input, ABS_PRESSURE,
+			     ts->dev->screen_info.pressure_min,
+			     ts->dev->screen_info.pressure_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_X,
+			     ts->dev->screen_info.x_tilt_min,
+			     ts->dev->screen_info.x_tilt_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_Y,
+			     ts->dev->screen_info.y_tilt_min,
+			     ts->dev->screen_info.y_tilt_max, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "register pen device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+
+	ts->pen_input_dev = input;
+
+	return 0;
+}
+
+static int ilitek_request_input_dev(void)
+{
+	int error;
+	int i;
+	struct input_dev *input;
+
+#ifdef ILITEK_USE_MTK_INPUT_DEV
+	input = tpd->dev;
+	if (!input)
+		return -ENOMEM;
+#ifdef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
+			input_set_capability(input, EV_KEY,
+					     tpd_dts_data.tpd_key_local[i]);
+	}
+#endif
+#else
+	int x_min = ts->dev->screen_info.x_min;
+	int y_min = ts->dev->screen_info.y_min;
+	int x_max = ts->dev->screen_info.x_max;
+	int y_max = ts->dev->screen_info.y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+#endif
+
+	TP_DBG(NULL, "registering touch input device\n");
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	INPUT_MT_INIT_SLOTS(input, MAX(2, ts->dev->tp_info.max_fingers));
+#else
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0,
+			     ts->dev->tp_info.max_fingers, 0, 0);
+#endif
+
+#ifdef ILITEK_REPORT_PRESSURE
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+#endif
+
+	for (i = 0; i < ts->dev->tp_info.key_num; i++)
+		set_bit(ts->dev->key.info.keys[i].id & KEY_MAX, input->keybit);
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+#ifndef ILITEK_USE_MTK_INPUT_DEV
+	input->name = ILITEK_TS_NAME;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x_min = 0; y_min = 0;
+	x_max = TOUCH_SCREEN_X_MAX; y_max = TOUCH_SCREEN_Y_MAX;
+#endif
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 32767, 0, 0);
+	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 32767, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "input_register_device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+#endif
+
+	ts->input_dev = input;
+
+	if (ts->dev->tp_info.pen_modes)
+		ilitek_request_pen_input_dev();
+
+	return 0;
+}
+
+static int ilitek_touch_down(int id, int x, int y, int p, int h, int w)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x = (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX /
+		(ts->dev->screen_info.x_max - ts->dev->screen_info.x_min);
+	y = (y - ts->dev->screen_info.y_min) * TOUCH_SCREEN_Y_MAX /
+		(ts->dev->screen_info.y_max - ts->dev->screen_info.y_min);
+#endif
+
+	input_report_key(input, BTN_TOUCH, 1);
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_mt_slot(input, id);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+#endif
+	input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+	input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+	input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, h);
+	input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, w);
+#ifdef ILITEK_REPORT_PRESSURE
+	input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+#endif
+#ifndef ILITEK_TOUCH_PROTOCOL_B
+	input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id);
+	input_mt_sync(input);
+#endif
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(x, y, 1);
+			TP_DBG(NULL, "tpd_button(x, y, 1) = tpd_button(%d, %d, 1)\n", x, y);
+		}
+	}
+#endif
+#endif
+#endif
+	return 0;
+}
+
+static int ilitek_touch_release(int id)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	if (ts->touch_flag[id] == 1) {
+		TP_DBG(NULL, "release point id = %d\n", id);
+		input_mt_slot(input, id);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+#else
+	input_report_key(input, BTN_TOUCH, 0);
+	input_mt_sync(input);
+#endif
+	set_arr(ts->touch_flag, id, 0);
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() ||
+		    RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(0, 0, 0);
+			TP_DBG(NULL, "tpd_button(x, y, 0) = tpd_button(0, 0, 0)\n");
+		}
+	}
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int ilitek_touch_release_all_point(void)
+{
+	struct input_dev *input = ts->input_dev;
+	int i = 0;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_report_key(input, BTN_TOUCH, 0);
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		ilitek_touch_release(i);
+#else
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		set_arr(ts->touch_flag, i, 0);
+	ilitek_touch_release(0);
+#endif
+	ts->is_touched = false;
+	input_sync(input);
+	return 0;
+}
+
+static int ilitek_check_key_down(int x, int y)
+{
+	int j;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if ((x >= ts->dev->key.info.keys[j].x &&
+		     x <= ts->dev->key.info.keys[j].x +
+			ts->dev->key.info.x_len) &&
+		    (y >= ts->dev->key.info.keys[j].y &&
+		     y <= ts->dev->key.info.keys[j].y +
+			ts->dev->key.info.y_len)) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 1);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				x = tpd_dts_data.tpd_key_dim_local[j].key_x;
+				y = tpd_dts_data.tpd_key_dim_local[j].key_y;
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key down\n",
+					j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_down(0, x, y, 10, 128, 1);
+			}
+#else
+			x = touch_key_point_maping_array[j].point_x;
+			y = touch_key_point_maping_array[j].point_y;
+			ilitek_touch_down(0, x, y, 10, 128, 1);
+#endif
+#endif
+			ts->dev->key.clicked[j] = true;
+			ts->touch_key_hold_press = true;
+			ts->is_touched = true;
+			TP_DBG(NULL, "Key, Keydown ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int ilitek_check_key_release(int x, int y, int check_point)
+{
+	int j = 0;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if (!ts->dev->key.clicked[j])
+			continue;
+
+		if (check_point) {
+			if (x < ts->dev->key.info.keys[j].x ||
+			    x > ts->dev->key.info.keys[j].x + ts->dev->key.info.x_len ||
+			    y < ts->dev->key.info.keys[j].y ||
+			    y > ts->dev->key.info.keys[j].y + ts->dev->key.info.y_len) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+				input_report_key(ts->input_dev,
+						 ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+				if (tpd_dts_data.use_tpd_button) {
+					TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+					ilitek_touch_release(0);
+				}
+#else
+				ilitek_touch_release(0);
+#endif
+#endif
+				ts->dev->key.clicked[j] = false;
+				ts->touch_key_hold_press = false;
+				TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+					ts->dev->key.info.keys[j].id, x, y,
+					ts->dev->key.clicked[j]);
+				break;
+			}
+		} else {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_release(0);
+			}
+#else
+			ilitek_touch_release(0);
+#endif
+#endif
+			ts->dev->key.clicked[j] = false;
+			ts->touch_key_hold_press = false;
+			TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+int event_spacing;
+static u8 finger_state;
+static int start_x;
+static int start_y;
+static int current_x;
+static int current_y;
+
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+static struct timeval start_event_time;
+#else
+unsigned long start_event_time_jiffies;
+#endif
+
+static int ilitek_get_time_diff(void)
+{
+	int diff_milliseconds = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+	struct timeval time_now;
+
+	do_gettimeofday(&time_now);
+	diff_milliseconds += (time_now.tv_sec - start_event_time.tv_sec) * 1000;
+
+	if (time_now.tv_usec < start_event_time.tv_usec) {
+		diff_milliseconds -= 1000;
+		diff_milliseconds += (1000 * 1000 + time_now.tv_usec - start_event_time.tv_usec) / 1000;
+	} else
+		diff_milliseconds += (time_now.tv_usec - start_event_time.tv_usec) / 1000;
+
+	if (diff_milliseconds < (-10000))
+		diff_milliseconds = 10000;
+	TP_MSG(NULL, "time_now.tv_sec = %d start_event_time.tv_sec = %d time_now.tv_usec = %d start_event_time.tv_usec = %d diff_milliseconds = %d\n",
+			(int)time_now.tv_sec, (int)start_event_time.tv_sec, (int)time_now.tv_usec, (int)start_event_time.tv_usec, diff_milliseconds);
+#else
+	diff_milliseconds = jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_event_time_jiffies);
+	TP_MSG(NULL, "jiffies_to_msecs(jiffies) = %u jiffies_to_msecs(start_event_time_jiffies) = %u diff_milliseconds = %d\n", jiffies_to_msecs(jiffies),
+			jiffies_to_msecs(start_event_time_jiffies), diff_milliseconds);
+#endif
+	return diff_milliseconds;
+}
+
+static u8 ilitek_double_click_touch(int finger_id, int x, int y,
+					 u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_id > 0) {
+		finger_state = 0;
+		goto out;
+	}
+	if (finger_state == 0 || finger_state == 5) {
+
+		finger_state = 1;
+		start_x = x;
+		start_y = y;
+		current_x = 0;
+		current_y = 0;
+		event_spacing = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+		do_gettimeofday(&start_event_time);
+#else
+		start_event_time_jiffies = jiffies;
+#endif
+	} else if (finger_state == 1) {
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 4;
+	} else if (finger_state == 2) {
+		finger_state = 3;
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > (DOUBLE_CLICK_ONE_CLICK_USED_TIME + DOUBLE_CLICK_NO_TOUCH_TIME))
+			finger_state = 0;
+	} else if (finger_state == 3) {
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_TOTAL_USED_TIME) {
+			start_x = current_x;
+			start_y = current_y;
+			finger_state = 4;
+		}
+	}
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+static u8 ilitek_double_click_release(u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_state == 1) {
+		finger_state = 2;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 0;
+	}
+	if (finger_state == 3) {
+		event_spacing = ilitek_get_time_diff();
+		if ((event_spacing < DOUBLE_CLICK_TOTAL_USED_TIME && event_spacing > 50) && (ABSSUB(current_x, start_x) < DOUBLE_CLICK_DISTANCE)
+				&& ((ABSSUB(current_y, start_y) < DOUBLE_CLICK_DISTANCE))) {
+			finger_state = 5;
+			goto out;
+		} else
+			finger_state = 0;
+	} else if (finger_state == 4)
+		finger_state = 0;
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y)
+{
+	struct input_dev *input = ts->input_dev;
+
+	if (ts->gesture_status == Gesture_Double_Click) {
+		if (touch) {
+			finger_state = ilitek_double_click_touch(idx, x, y, finger_state);
+			return;
+		}
+		finger_state = ilitek_double_click_release(finger_state);
+
+		if (finger_state != 5)
+			return;
+	}
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_timeout(&ilitek_wake_lock, 5 * HZ);
+#endif
+
+	/* prevent power key being triggered multiple times */
+	if (ts->power_key_triggered)
+		return;
+
+	input_report_key(input, KEY_POWER, 1);
+	input_sync(input);
+	input_report_key(input, KEY_POWER, 0);
+	input_sync(input);
+
+	ts->power_key_triggered = true;
+}
+
+static void ilitek_report_touch_event(struct touch_data *touch, void *_private)
+{
+	struct input_dev *input = ts->input_dev;
+	u8 i, released_cnt = 0;
+
+	u16 x_max = ts->dev->screen_info.x_max;
+	u16 x_min = ts->dev->screen_info.x_min;
+	u16 y_max = ts->dev->screen_info.y_max;
+	u16 y_min = ts->dev->screen_info.y_min;
+
+	/*
+	 * ISR may be activated after registering irq and
+	 * before creating input_dev
+	 */
+	if (!input) {
+		TP_ERR(NULL, "input_dev is not registerred\n");
+		return;
+	}
+
+	for (i = 0; i < touch->cnt; i++) {
+		if (!touch->finger[i].status) {
+			released_cnt++;
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+			ilitek_touch_release(touch->finger[i].id);
+#endif
+			continue;
+		}
+
+		set_arr(ts->touch_flag, touch->finger[i].id, 1);
+
+		touch->finger[i].x = (ILITEK_REVERT_X) ?
+			x_max - touch->finger[i].x + x_min : touch->finger[i].x;
+		touch->finger[i].y = (ILITEK_REVERT_Y) ?
+			y_max - touch->finger[i].y + y_min : touch->finger[i].y;
+
+#if ILITEK_ROTATE_FLAG
+		swap(touch->finger[i].x, touch->finger[i].y);
+#endif
+
+		if (ts->system_suspend) {
+			TP_MSG(NULL, "system is suspend not report point\n");
+			ilitek_gesture_handle(true, i, touch->finger[i].x,
+					      touch->finger[i].y);
+			continue;
+		}
+
+		if (!ts->is_touched)
+			ilitek_check_key_down(touch->finger[i].x,
+					      touch->finger[i].y);
+
+		if (!ts->touch_key_hold_press) {
+			ts->is_touched = true;
+			ilitek_touch_down(touch->finger[i].id,
+					  touch->finger[i].x,
+					  touch->finger[i].y,
+					  touch->finger[i].pressure,
+					  touch->finger[i].height,
+					  touch->finger[i].width);
+		} else {
+			ilitek_check_key_release(touch->finger[i].x,
+						 touch->finger[i].y, 1);
+		}
+	}
+
+	if (touch->cnt == released_cnt) {
+		if (ts->is_touched)
+			ilitek_touch_release_all_point();
+
+		ilitek_check_key_release(0, 0, 0);
+		ts->is_touched = false;
+
+		if (ts->system_suspend)
+			ilitek_gesture_handle(false, 0, 0, 0);
+	}
+
+	input_sync(input);
+}
+
+static void ilitek_report_pen_event(struct pen_data *pen, void *_private)
+{
+	static int curr_tool = BTN_TOOL_PEN;
+	struct input_dev *pen_input = ts->pen_input_dev;
+	int tool;
+
+	u16 x_max = ts->dev->screen_info.pen_x_max;
+	u16 x_min = ts->dev->screen_info.pen_x_min;
+	u16 y_max = ts->dev->screen_info.pen_y_max;
+	u16 y_min = ts->dev->screen_info.pen_y_min;
+
+	if (!pen_input)
+		return;
+
+	tool = (pen->pen.in_range && pen->pen.invert) ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	if (curr_tool != tool) {
+		input_report_key(pen_input, curr_tool, 0);
+		input_sync(pen_input);
+		curr_tool = tool;
+	}
+
+	pen->pen.x = (ILITEK_REVERT_X) ?
+		x_max - pen->pen.x + x_min : pen->pen.x;
+	pen->pen.y = (ILITEK_REVERT_Y) ?
+		y_max - pen->pen.y + y_min : pen->pen.y;
+
+#if ILITEK_ROTATE_FLAG
+	swap(pen->pen.x, pen->pen.y);
+#endif
+
+	input_report_key(pen_input, BTN_TOUCH,
+			 pen->pen.tip_sw || pen->pen.eraser);
+	input_report_key(pen_input, curr_tool, pen->pen.in_range);
+	input_report_key(pen_input, BTN_STYLUS, pen->pen.barrel_sw);
+	input_event(pen_input, EV_ABS, ABS_X, pen->pen.x);
+	input_event(pen_input, EV_ABS, ABS_Y, pen->pen.y);
+	input_event(pen_input, EV_ABS, ABS_PRESSURE, pen->pen.pressure);
+	input_event(pen_input, EV_ABS, ABS_TILT_X, pen->pen.x_tilt);
+	input_event(pen_input, EV_ABS, ABS_TILT_Y, pen->pen.y_tilt);
+
+	input_sync(pen_input);
+}
+
+
+static void ilitek_report_buf(u8 *buf, int size,
+			      bool is_last, void *_private)
+{
+	UNUSED(is_last);
+
+	ilitek_udp_reply(buf, size);
+}
+
+int ilitek_read_data_and_report(void)
+{
+	int error;
+	u8 i, count;
+	struct ilitek_report report;
+
+	memset(&report, 0, sizeof(report));
+	report.cb.report_touch_event = ilitek_report_touch_event;
+	report.cb.report_pen_event = ilitek_report_pen_event;
+	report.cb.report_buf = ilitek_report_buf;
+
+	switch (ts->irq_handle_type) {
+	case irq_type_c_model:
+		memset(ts->buf, 0, sizeof(ts->buf));
+
+		for (i = 0, count = 1; i < count; i++) {
+			ilitek_read(ts->buf, ts->irq_read_len);
+			ilitek_udp_reply(ts->buf, ts->irq_read_len);
+			count = ts->buf[ts->irq_read_len - 1];
+		}
+
+		break;
+
+	case irq_type_debug:
+	case irq_type_normal:
+	default:
+		error = ilitek_report_update(ts->dev, &report);
+		if (error < 0) {
+			if (error == -EILIPROTO && !checksum_failed_release)
+				break;
+
+			if (ts->is_touched) {
+				ilitek_touch_release_all_point();
+				ilitek_check_key_release(0, 0, 0);
+			}
+
+			return error;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+static ISR_FUNC(ilitek_i2c_isr)
+{
+	int error;
+
+	TP_DBG(NULL, "%s\n", __func__);
+
+	atomic_set(&ts->get_INT, 1);
+	ilitek_gpio_dbg();
+
+	ts->esd_skip = true;
+
+	if (atomic_read(&ts->firmware_updating)) {
+		TP_DBG(NULL, "firmware_updating return\n");
+		goto exit;
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_disable();
+#endif
+
+	if (!ts->unhandle_irq) {
+		mutex_lock(&ts->ilitek_mutex);
+		error = ilitek_read_data_and_report();
+		if (error < 0)
+			TP_ERR(NULL, "process error\n");
+		mutex_unlock(&ts->ilitek_mutex);
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_enable();
+#endif
+
+exit:
+	ts->esd_skip = false;
+
+	ISR_RETURN(IRQ_HANDLED);
+}
+
+static int ilitek_request_irq(void)
+{
+	int error;
+
+#ifdef MTK_UNDTS
+	mt_set_gpio_mode(ILITEK_IRQ_GPIO, GPIO_CTP_EINT_PIN_M_EINT);
+	mt_set_gpio_dir(ILITEK_IRQ_GPIO, GPIO_DIR_IN);
+	mt_set_gpio_pull_enable(ILITEK_IRQ_GPIO, GPIO_PULL_ENABLE);
+	mt_set_gpio_pull_select(ILITEK_IRQ_GPIO, GPIO_PULL_UP);
+
+	mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+	mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, ilitek_i2c_isr, 1);
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	struct device_node *node;
+
+	node = of_find_matching_node(NULL, touch_of_match);
+	if (node)
+		ts->irq = irq_of_parse_and_map(node, 0);
+#else
+	ts->irq = gpio_to_irq(ts->irq_gpio);
+#endif
+
+	TP_MSG(NULL, "ts->irq: %d\n", ts->irq);
+	if (ts->irq <= 0)
+		return -EINVAL;
+
+	error = request_threaded_irq(ts->irq, NULL, ilitek_i2c_isr,
+				     ts->irq_trigger_type | IRQF_ONESHOT,
+				     "ilitek_touch_irq", ts);
+	if (error) {
+		TP_ERR(NULL, "request threaded irq failed, err: %d\n", error);
+		return error;
+	}
+#endif
+
+	ts->irq_registered = true;
+	atomic_set(&ts->irq_enabled, 1);
+
+	return 0;
+}
+
+static int ilitek_read_fw(char *filename, unsigned char *buf, int size, void *data)
+{
+	int error, fw_size;
+	const struct firmware *fw;
+	struct device *device = (struct device *)data;
+
+	error = request_firmware(&fw, filename, device);
+	if (error) {
+		TP_ERR(NULL, "request fw: %s failed, err:%d\n", filename, error);
+		return error;
+	}
+
+	if (size < fw->size) {
+		fw_size = -EFBIG;
+		goto release_fw;
+	}
+
+	fw_size = fw->size;
+	memcpy(buf, fw->data, fw->size);
+
+release_fw:
+	release_firmware(fw);
+
+	return fw_size;
+}
+
+struct ilitek_update_callback update_cb = {
+	.read_fw = ilitek_read_fw,
+	.update_progress = NULL,
+	.update_fw_file_info = NULL,
+
+	.slave_update_notify = NULL,
+	.update_fw_ic_info = NULL,
+};
+
+int ilitek_upgrade_firmware(char *filename)
+{
+	int error;
+	struct ilitek_fw_handle *handle;
+	struct ilitek_fw_settings setting;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	atomic_set(&ts->firmware_updating, 1);
+	ts->operation_protection = true;
+
+	handle = ilitek_update_init(ts->dev, false, &update_cb, ts->device);
+
+	setting.force_update = false;
+	setting.fw_check_only = false;
+	setting.fw_ver_check = false;
+	setting.retry = 3;
+	ilitek_update_setting(handle, &setting);
+
+	error = ilitek_update_load_fw(handle, filename);
+	if (error < 0)
+		goto err_return;
+	error = ilitek_update_start(handle);
+	if (error < 0)
+		goto err_return;
+
+err_return:
+	ilitek_update_exit(handle);
+
+	ts->operation_protection = false;
+	atomic_set(&ts->firmware_updating, 0);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return error;
+}
+
+
+static int __maybe_unused ilitek_update_thread(void *arg)
+{
+#ifdef ILITEK_BOOT_UPDATE
+	int error;
+
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (kthread_should_stop()) {
+		TP_MSG(NULL, "ilitek_update_thread, stop\n");
+		return -1;
+	}
+
+	mdelay(100);
+
+	error = ilitek_upgrade_firmware("ilitek.ili");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.hex");
+		if (error < 0) {
+			error = ilitek_upgrade_firmware("ilitek.bin");
+			if (error < 0)
+				return error;
+		}
+	}
+
+	error = ilitek_request_input_dev();
+	if (error)
+		return (error < 0) ? error : -EFAULT;
+#endif
+
+	return 0;
+}
+
+void ilitek_suspend(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	ts->esd_skip = true;
+	if (ts->esd_check && ts->esd_workq)
+		cancel_delayed_work_sync(&ts->esd_work);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ts->wake_irq_enabled = (enable_irq_wake(ts->irq) == 0);
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_set_idle(ts->dev, true) < 0)
+				TP_ERR(NULL, "enable Idle mode failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	} else {
+		/*
+		 * Must disable irq before sleep cmd,
+		 * Avoid getting into ISR handling (and do i2c read),
+		 * after sending sleep cmd.
+		 */
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_protocol_set_cmd(ts->dev, SET_IC_SLEEP,
+						 NULL) < 0)
+				TP_ERR(NULL, "set tp sleep failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->power_key_triggered = false;
+	ts->system_suspend = true;
+}
+
+void ilitek_resume(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_set_idle(ts->dev, false);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+
+		if (ts->gesture_status == Gesture_Double_Click)
+			finger_state = 0;
+
+		if (ts->wake_irq_enabled) {
+			disable_irq_wake(ts->irq);
+			ts->wake_irq_enabled = false;
+		}
+	} else {
+		if (ts->dev->protocol.flag == PTL_V3) {
+			/*
+			 * If ILITEK_SLEEP is defined and FW support wakeup cmd,
+			 * the hw reset can be mark.
+			 */
+			ilitek_reset(ts->dev->reset_time);
+		}
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_protocol_set_cmd(ts->dev, SET_IC_WAKE, NULL);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->esd_skip = false;
+	if (ts->esd_check && ts->esd_workq)
+		queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	ilitek_touch_release_all_point();
+	ilitek_check_key_release(0, 0, 0);
+
+	ts->system_suspend = false;
+
+	ilitek_irq_enable();
+}
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+int ilitek_resume_allwin(struct i2c_client *client)
+{
+	ilitek_resume();
+	return 0;
+}
+#endif
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+static int __maybe_unused ilitek_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+#ifdef CONFIG_QCOM_DRM
+	struct msm_drm_notifier *ev_data = data;
+#else
+	struct fb_event *ev_data = data;
+#endif
+	int *blank;
+
+	TP_MSG(NULL, "FB EVENT event: %lu\n", event);
+
+#ifdef CONFIG_QCOM_DRM
+	if (!ev_data || (ev_data->id != 0))
+		return 0;
+#endif
+	if (ev_data && ev_data->data && event == ILITEK_EVENT_BLANK) {
+		blank = ev_data->data;
+		TP_MSG(NULL, "blank: %d\n", *blank);
+		if (*blank == ILITEK_BLANK_POWERDOWN) {
+			ilitek_suspend();
+		} else if (*blank == ILITEK_BLANK_UNBLANK ||
+			   *blank == ILITEK_BLANK_NORMAL) {
+			ilitek_resume();
+		}
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void __maybe_unused ilitek_early_suspend(struct early_suspend *h)
+{
+	ilitek_suspend();
+}
+
+static void __maybe_unused ilitek_late_resume(struct early_suspend *h)
+{
+	ilitek_resume();
+}
+#endif
+#endif
+
+static void ilitek_get_gpio_num(void)
+{
+#ifdef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	TP_MSG(NULL, "(config_info.wakeup_gpio.gpio) = %d (config_info.int_number) = %d\n", (config_info.wakeup_gpio.gpio), (config_info.int_number));
+	ts->reset_gpio = (config_info.wakeup_gpio.gpio);
+	ts->irq_gpio = (config_info.int_number);
+#else
+#ifdef CONFIG_OF
+	ts->reset_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,reset-gpio", 0);
+	if (ts->reset_gpio < 0)
+		TP_ERR(NULL, "reset_gpio = %d\n", ts->reset_gpio);
+	ts->irq_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,irq-gpio", 0);
+	if (ts->irq_gpio < 0)
+		TP_ERR(NULL, "irq_gpio = %d\n", ts->irq_gpio);
+#endif
+#endif
+#else
+	ts->reset_gpio = ILITEK_RESET_GPIO;
+	ts->irq_gpio = ILITEK_IRQ_GPIO;
+#endif
+
+	TP_MSG(NULL, "reset_gpio = %d irq_gpio = %d\n", ts->reset_gpio, ts->irq_gpio);
+
+
+#if defined(ILITEK_GPIO_DEBUG)
+	do {
+		ts->test_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,test-gpio", 0);
+		if (ts->test_gpio < 0) {
+			TP_ERR(NULL, "test_gpio: %d\n", ts->test_gpio);
+			break;
+		}
+
+		TP_MSG(NULL, "test_gpio: %d\n", ts->test_gpio);
+
+		if (gpio_request(ts->test_gpio, "ilitek-test-gpio")) {
+			TP_ERR(NULL, "request test_gpio failed\n");
+			break;
+		}
+
+		gpio_direction_output(ts->test_gpio, 1);
+
+	} while (0);
+#endif
+}
+
+static int ilitek_request_gpio(void)
+{
+	int ret = 0;
+
+	ts->irq_gpio = -ENODEV;
+	ts->reset_gpio = -ENODEV;
+
+	ilitek_get_gpio_num();
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+	if (ts->reset_gpio > 0) {
+		ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio so free retry\n");
+			gpio_free(ts->reset_gpio);
+			ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request reset_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio\n");
+		} else {
+			ret = gpio_direction_output(ts->reset_gpio, 1);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction output rest gpio err\n");
+		}
+	}
+	if (ts->irq_gpio > 0) {
+		ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio so free retry\n");
+			gpio_free(ts->irq_gpio);
+			ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request irq_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio\n");
+		} else {
+			ret = gpio_direction_input(ts->irq_gpio);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction input irq gpio err\n");
+		}
+	}
+#endif
+	return ret;
+}
+
+int ilitek_create_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to create esd workqueue\n");
+
+	INIT_DELAYED_WORK(&ts->esd_work, ilitek_esd_check);
+	ts->esd_workq = create_singlethread_workqueue("ilitek_esd_wq");
+	if (!ts->esd_workq)
+		return -ENOMEM;
+
+	ts->esd_skip = false;
+	ts->esd_delay = 2 * HZ;
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	return 0;
+}
+
+void ilitek_remove_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to remove esd workqueue\n");
+
+	if (ts->esd_workq) {
+		cancel_delayed_work_sync(&ts->esd_work);
+		destroy_workqueue(ts->esd_workq);
+		ts->esd_workq = NULL;
+	}
+}
+
+static int ilitek_register_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+	int error;
+
+	ts->fb_notif.notifier_call = ilitek_notifier_callback;
+
+#ifdef CONFIG_QCOM_DRM
+	error = msm_drm_register_client(&ts->fb_notif);
+#else
+	error = fb_register_client(&ts->fb_notif);
+#endif
+	if (error)
+		TP_ERR(NULL, "register fb_notifier failed, err: %d\n", error);
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	ts->early_suspend.suspend = ilitek_early_suspend;
+	ts->early_suspend.resume = ilitek_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+#endif /* ILITEK_PLAT != ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	device_enable_async_suspend(ts->device);
+	pm_runtime_set_active(ts->device);
+	pm_runtime_get(ts->device);
+	pm_runtime_enable(ts->device);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+#ifdef CONFIG_QCOM_DRM
+	msm_drm_unregister_client(&ts->fb_notif);
+#else
+	fb_unregister_client(&ts->fb_notif);
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+}
+
+int ilitek_netlink_init(u8 unit)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	NETLINK_KERNEL_CFG_DECLARE(cfg, udp_receive);
+
+	if (ilitek_netlink_sock)
+		ilitek_netlink_exit();
+
+	ilitek_netlink_sock = NETLINK_KERNEL_CREATE(unit, &cfg, udp_receive);
+
+	if (!ilitek_netlink_sock) {
+		TP_ERR(NULL, "netlink_kernel_create failed\n");
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+#endif
+	return -EPERM;
+}
+
+void ilitek_netlink_exit(void)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	if (ilitek_netlink_sock)
+		netlink_kernel_release(ilitek_netlink_sock);
+
+	ilitek_netlink_sock = NULL;
+#endif
+}
+
+static int __maybe_unused ilitek_alloc_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	I2CDMABuf_va = (u8 *) dma_alloc_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+	if (!I2CDMABuf_va) {
+		TP_ERR(NULL, "ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n");
+		I2CDMABuf_va = (u8 *) dma_alloc_coherent(NULL, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+		if (!I2CDMABuf_va) {
+			TP_ERR(NULL, "ilitek [TPD] NULL dma_alloc_coherent error\n");
+			return -ENOMEM;
+		}
+	}
+	memset(I2CDMABuf_va, 0, ILITEK_DMA_SIZE);
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_free_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	if (I2CDMABuf_va) {
+		dma_free_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE,
+				  I2CDMABuf_va, I2CDMABuf_pa);
+
+		I2CDMABuf_va = NULL;
+		I2CDMABuf_pa = 0;
+
+	}
+#endif
+	return 0;
+}
+
+static int __maybe_unused ilitek_power_on(bool status)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int error;
+
+	TP_MSG(NULL, "%s\n", status ? "POWER ON" : "POWER OFF");
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	input_set_power_enable(&(config_info.input_type), status);
+#else
+
+	if (status) {
+		if (ts->vdd) {
+			error = regulator_enable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_enable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	} else {
+		if (ts->vdd) {
+			error = regulator_disable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_disable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	}
+
+#ifdef MTK_UNDTS
+	if (status)
+		hwPowerOn(PMIC_APP_CAP_TOUCH_VDD, VOL_3300, "TP");
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_request_regulator(struct ilitek_ts_data *ts)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int ret = 0;
+	char *vdd_name = "vdd";
+	char *vcc_i2c_name = "vcc_i2c";
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	vdd_name = "vtouch";
+	ts->vdd = regulator_get(tpd->tpd_dev, vdd_name);
+	tpd->reg = ts->vdd;
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+	}
+#elif ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	ts->vdd = regulator_get(ts->device, vdd_name);
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+
+	}
+
+	ts->vdd_i2c = regulator_get(ts->device, vcc_i2c_name);
+	if (IS_ERR(ts->vdd_i2c)) {
+		TP_ERR(NULL, "regulator_get vdd_i2c fail\n");
+		ts->vdd_i2c = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd_i2c, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set i2c to 3000~3300mv.\n");
+	}
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+#endif /* ILITEK_ENABLE_REGULATOR_POWER_ON */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_regulator(void)
+{
+#if defined(ILITEK_ENABLE_REGULATOR_POWER_ON) && ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	if (ts->vdd)
+		regulator_put(ts->vdd);
+	if (ts->vdd_i2c)
+		regulator_put(ts->vdd_i2c);
+#endif
+}
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init)
+{
+	if (init) {
+		device_init_wakeup(ts->device, 1);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+		wake_lock_init(&ilitek_wake_lock, WAKE_LOCK_SUSPEND, "ilitek wakelock");
+#endif
+		return;
+	}
+
+	device_init_wakeup(ts->device, 0);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_destroy(&ilitek_wake_lock);
+#endif
+}
+
+static int _ilitek_write_then_read(unsigned char *wbuf, int wlen,
+				   unsigned char *rbuf, int rlen, void *data)
+{
+	return ilitek_write_and_read(wbuf, wlen, 1, rbuf, rlen);
+}
+
+static int ilitek_read_interrupt_in(unsigned char *rbuf, int rlen,
+				    unsigned int timeout_ms, void *data)
+{
+	return ilitek_write_and_read(NULL, 0, 0, rbuf, rlen);
+}
+
+static void _ilitek_init_ack(unsigned int tout_ms, void *data)
+{
+	UNUSED(tout_ms);
+
+	ilitek_irq_enable();
+	ts->unhandle_irq = true;
+	atomic_set(&ts->get_INT, 0);
+}
+
+static int _ilitek_wait_ack(u8 cmd, unsigned int tout_ms, void *data)
+{
+	unsigned int t_ms = 0;
+	int tmp, error = -ETIME;
+
+	UNUSED(cmd);
+
+	do {
+		tmp = atomic_read(&ts->get_INT);
+		if (tmp) {
+			error = 0;
+			break;
+		}
+
+		udelay(1000);
+		t_ms++;
+	} while (t_ms < tout_ms);
+
+	ts->unhandle_irq = false;
+	ilitek_irq_disable();
+
+	return error;
+}
+
+static void _ilitek_delay(unsigned int delay_ms)
+{
+	mdelay(delay_ms);
+}
+
+static int _ilitek_reset(unsigned int delay_ms, void *data)
+{
+	/* return error if no reset gpio found */
+	if (ts->reset_gpio < 0)
+		return -ENODEV;
+
+	ilitek_reset(delay_ms);
+	return 0;
+}
+
+struct ilitek_ts_callback dev_cb = {
+	.write_then_read = _ilitek_write_then_read,
+	.read_interrupt_in = ilitek_read_interrupt_in,
+	.init_ack = _ilitek_init_ack,
+	.wait_ack = _ilitek_wait_ack,
+	.hw_reset = _ilitek_reset,
+	.re_enum = NULL,
+	.delay_ms = _ilitek_delay,
+	.msg = NULL,
+
+	.write_then_read_direct = NULL,
+	.mode_switch_notify = NULL,
+};
+
+int ilitek_main_probe(void *client, struct device *device)
+{
+	struct ilitek_ts_settings setting;
+
+	TP_MSG(NULL, "driver version: %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu\n",
+		driver_ver[0], driver_ver[1], driver_ver[2], driver_ver[3],
+		driver_ver[4], driver_ver[5], driver_ver[6]);
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts) {
+		TP_ERR(NULL, "allocate ts failed\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	ts->device = device;
+
+	mutex_init(&ts->ilitek_mutex);
+	ts->unhandle_irq = false;
+
+	ilitek_alloc_dma();
+	ilitek_request_regulator(ts);
+	ilitek_power_on(true);
+	ilitek_request_gpio();
+
+	ilitek_reset(600);
+
+	ts->dev = ilitek_dev_init(interface_i2c, "0", false, &dev_cb, ts);
+	if (!ts->dev)
+		goto err_free_gpio;
+
+	memset(&setting, 0, sizeof(setting));
+	setting.sensor_id_mask = ILITEK_SENSOR_ID_MASK;
+	ilitek_dev_setting(ts->dev, &setting);
+
+	if (api_update_ts_info(ts->dev) < 0)
+		goto err_dev_exit;
+
+	ts->irq_trigger_type = (ts->dev->protocol.flag == PTL_V6) ?
+		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+	if (ilitek_request_irq())
+		goto err_dev_exit;
+
+#ifdef ILITEK_BOOT_UPDATE
+	ts->update_thread = kthread_run(ilitek_update_thread, NULL,
+					"ilitek_update_thread");
+	if (IS_ERR(ts->update_thread))
+		goto err_free_irq;
+#else
+	if (ilitek_request_input_dev())
+		goto err_free_irq;
+#endif
+
+	ilitek_register_resume_suspend();
+	ilitek_create_sysfsnode();
+	ilitek_create_tool_node();
+	ilitek_netlink_init(NETLINK_USERSOCK);
+
+	ts->esd_check = ILITEK_ESD_CHECK_ENABLE;
+	if (ts->esd_check)
+		ilitek_create_esd_check_workqueue();
+
+	ts->gesture_status = ILITEK_GESTURE_DEFAULT;
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, true);
+
+	ts->low_power_status = ILITEK_LOW_POWER_DEFAULT;
+
+	return 0;
+
+err_free_irq:
+	free_irq(ts->irq, ts);
+
+err_dev_exit:
+	ilitek_dev_exit(ts->dev);
+
+err_free_gpio:
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+	kfree(ts);
+
+	return -ENODEV;
+}
+
+int ilitek_main_remove(void *client)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (!ts)
+		return 0;
+
+	ilitek_netlink_exit();
+
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, false);
+
+	ilitek_remove_esd_check_workqueue();
+	ilitek_remove_tool_node();
+	ilitek_remove_sys_node();
+	ilitek_release_resume_suspend();
+
+	if (ts->pen_input_dev)
+		input_unregister_device(ts->pen_input_dev);
+
+	if (ts->input_dev)
+		input_unregister_device(ts->input_dev);
+
+#ifndef MTK_UNDTS
+	free_irq(ts->irq, ts);
+#endif
+
+	ilitek_dev_exit(ts->dev);
+
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+
+	kfree(ts);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_platform_init.c b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
new file mode 100644
index 000000000000..0b7f97ab7746
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+
+#define TPD_OK (0)
+
+#ifdef MTK_UNDTS
+static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
+static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
+struct touch_vitual_key_map_t touch_key_point_maping_array[] = { {key_1}, {key_2}, {key_3}, {key_4} };
+
+static struct i2c_board_info __initdata ilitek_i2c_tpd = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41)
+};
+#endif
+
+/* probe function is used for matching and initializing input device */
+static int /*__devinit*/ tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	TP_MSG(NULL, "TPD probe\n");
+
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -1;
+	}
+
+	ret = ilitek_main_probe(client, &client->dev);
+	if (ret == 0)	// If probe is success, then enable the below flag.
+		tpd_load_status = 1;
+
+	TP_MSG(NULL, "TPD probe done\n");
+	return TPD_OK;
+}
+
+static int tpd_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	strcpy(info->type, TPD_DEVICE);
+	return TPD_OK;
+}
+
+static int /*__devexit*/ tpd_remove(struct i2c_client *client)
+{
+	TP_MSG(NULL, "TPD removed\n");
+	return ilitek_main_remove(client);
+}
+
+/* The I2C device list is used for matching I2C device and I2C device driver. */
+static const struct i2c_device_id tpd_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+
+MODULE_DEVICE_TABLE(i2c, tpd_device_id);
+
+const struct of_device_id touch_dt_match_table[] = {
+	{ .compatible = "mediatek,cap_touch",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, touch_dt_match_table);
+
+static struct i2c_driver tpd_i2c_driver = {
+
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.of_match_table = of_match_ptr(touch_dt_match_table),
+	},
+	.probe = tpd_probe,
+	.remove = tpd_remove,
+	.id_table = tpd_device_id,
+	.detect = tpd_detect,
+};
+
+static int tpd_local_init(void)
+{
+	TP_MSG(NULL, "TPD init device driver\n");
+
+	if (i2c_add_driver(&tpd_i2c_driver) != 0) {
+		TP_ERR(NULL, "Unable to add i2c driver.\n");
+		return -1;
+	}
+	if (tpd_load_status == 0) {
+		TP_ERR(NULL, "Add error touch panel driver.\n");
+		i2c_del_driver(&tpd_i2c_driver);
+		return -1;
+	}
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button)
+		tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, tpd_dts_data.tpd_key_dim_local);
+
+#else
+	tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);	// initialize tpd button data
+#endif
+	tpd_type_cap = 1;
+	TP_MSG(NULL, "TPD init done\n");
+	return TPD_OK;
+}
+
+#ifdef MTK_UNDTS
+static void tpd_resume(struct early_suspend *h)
+{
+#else
+static void tpd_resume(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD wake up\n");
+	ilitek_resume();
+	TP_MSG(NULL, "TPD wake up done\n");
+}
+
+#ifdef MTK_UNDTS
+static void tpd_suspend(struct early_suspend *h)
+{
+#else
+static void tpd_suspend(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD enter sleep\n");
+	ilitek_suspend();
+	TP_MSG(NULL, "TPD enter sleep done\n");
+}
+
+static struct tpd_driver_t tpd_device_driver = {
+	.tpd_device_name = ILITEK_TS_NAME,
+	.tpd_local_init = tpd_local_init,
+	.suspend = tpd_suspend,
+	.resume = tpd_resume,
+	.tpd_have_button = 1,
+};
+
+static int __init ilitek_touch_driver_init(void)
+{
+	TP_MSG(NULL, "touch panel driver init\n");
+
+#ifdef MTK_UNDTS
+	i2c_register_board_info(2, &ilitek_i2c_tpd, 1);
+#else
+	tpd_get_dts_info();
+#endif
+	if (tpd_driver_add(&tpd_device_driver) < 0)
+		TP_ERR(NULL, "TPD add TP driver failed\n");
+
+	return 0;
+
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "touch panel driver exit\n");
+	tpd_driver_remove(&tpd_device_driver);
+}
+
+#else
+
+#ifdef ILITEK_SPI_INTERFACE
+static int ilitek_touch_driver_probe(struct spi_device *spi)
+{
+	int error;
+
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		TP_ERR(NULL, "SPI setup failed, err: %d\n", error);
+		return error;
+	}
+
+	TP_MSG(NULL, "SPI start probe, max_speed_hz: %d\n", spi->max_speed_hz);
+
+	return ilitek_main_probe(spi, &spi->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct spi_device *spi)
+{
+	REMOVE_RETURN(ilitek_main_remove(spi));
+}
+#else
+static I2C_PROBE_FUNC(ilitek_touch_driver_probe, struct i2c_client *client)
+{
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -ENODEV;
+	}
+
+	TP_MSG(NULL, "ILITEK client->addr: 0x%x, client->irq: %d\n",
+		client->addr, client->irq);
+
+	return ilitek_main_probe(client, &client->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct i2c_client *client)
+{
+	REMOVE_RETURN(ilitek_main_remove(client));
+}
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id ilitek_touch_match_table[] = {
+	{.compatible = "tchip,ilitek",},
+	{},
+};
+#endif
+
+static const struct i2c_device_id ilitek_touch_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+MODULE_DEVICE_TABLE(i2c, ilitek_touch_device_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ilitekts_acpi_id[] = {
+	{"ILIT2901", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
+#endif
+
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+static int __maybe_unused ilitek_ts_suspend(struct device *dev)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+static int __maybe_unused ilitek_ts_resume(struct device *dev)
+{
+	ilitek_resume();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_ts_suspend, ilitek_ts_resume);
+#endif
+
+#ifdef ILITEK_SPI_INTERFACE
+static struct spi_driver ilitek_touch_device_driver = {
+#else
+static struct i2c_driver ilitek_touch_device_driver = {
+#endif
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = ilitek_touch_match_table,
+#endif
+#ifdef CONFIG_ACPI
+		.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
+#endif
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+		.pm = &ilitek_pm_ops,
+#endif
+	},
+
+	.probe = ilitek_touch_driver_probe,
+	.remove = ilitek_touch_driver_remove,
+
+#if !defined(ILITEK_SPI_INTERFACE)
+	.id_table = ilitek_touch_device_id,
+#endif
+};
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+static const unsigned short normal_i2c[2] = { 0x41, I2C_CLIENT_END };
+struct ctp_config_info config_info = {
+	.input_type = CTP_TYPE,
+	.name = NULL,
+	.int_number = 0,
+};
+
+static int twi_id;
+static int screen_max_x;
+static int screen_max_y;
+static int revert_x_flag;
+static int revert_y_flag;
+static int exchange_x_y_flag;
+static int ctp_get_system_config(void)
+{
+	twi_id = config_info.twi_id;
+	screen_max_x = config_info.screen_max_x;
+	screen_max_y = config_info.screen_max_y;
+	TP_MSG(NULL, "Ilitek: screen_max_x = %d\n", screen_max_x);
+	revert_x_flag = config_info.revert_x_flag;
+	revert_y_flag = config_info.revert_y_flag;
+	exchange_x_y_flag = config_info.exchange_x_y_flag;
+	if ((screen_max_x == 0) || (screen_max_y == 0)) {
+		TP_ERR(NULL, "%s:read config error!\n", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+int ctp_ts_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+
+	if (twi_id == adapter->nr) {
+		strlcpy(info->type, ILITEK_TS_NAME, I2C_NAME_SIZE);
+		return 0;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static struct i2c_board_info i2c_info_dev = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41),
+	.platform_data = NULL,
+};
+
+static int add_ctp_device(void)
+{
+	struct i2c_adapter *adap;
+	//script_parser_fetch("ctp_para", "ctp_twi_id", &twi_id, 1);
+	adap = i2c_get_adapter(twi_id);
+	i2c_new_device(adap, &i2c_info_dev);
+	return 0;
+}
+
+static int ilitek_init_allwin(void)
+{
+
+	int ret = 0;
+
+	if (input_fetch_sysconfig_para(&(config_info.input_type))) {
+		TP_ERR(NULL, "Ilitek:  ctp_fetch_sysconfig_para err.\n");
+		return -1;
+	} else {
+		ret = input_init_platform_resource(&(config_info.input_type));
+		if (0 != ret)
+			TP_ERR(NULL, "Ilitek: ctp_ops.init_platform_resource err.\n");
+	}
+
+	if (config_info.ctp_used == 0) {
+		TP_ERR(NULL, "Ilitek: *** if use ctp,please put the sys_config.fex ctp_used set to 1.\n");
+		return -1;
+	}
+
+	if (ctp_get_system_config() < 0)
+		TP_ERR(NULL, "Ilitek: %s:read config fail!\n", __func__);
+
+	add_ctp_device();
+	ilitek_touch_device_driver.address_list = normal_i2c;
+	ilitek_touch_device_driver.detect = ctp_ts_detect;
+	return 0;
+
+}
+
+#endif
+static int __init ilitek_touch_driver_init(void)
+{
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	if (ilitek_init_allwin() < 0) {
+		TP_ERR(NULL, "ilitek_init_allwin failed.\n");
+		return -ENODEV;
+	}
+#endif
+
+	TP_MSG(NULL, "add ILITEK touch device driver\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	return spi_register_driver(&ilitek_touch_device_driver);
+#else
+	return i2c_add_driver(&ilitek_touch_device_driver);
+#endif
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "remove touch device driver i2c driver.\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	spi_unregister_driver(&ilitek_touch_device_driver);
+#else
+	i2c_del_driver(&ilitek_touch_device_driver);
+#endif
+}
+#endif
+
+module_init(ilitek_touch_driver_init);
+module_exit(ilitek_touch_driver_exit);
+MODULE_AUTHOR("ILITEK");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.c b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
new file mode 100644
index 000000000000..945bab688405
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
@@ -0,0 +1,3644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_protocol.h"
+
+typedef int (*protocol_func_t)(struct ilitek_ts_device *, void *);
+
+struct protocol_map {
+	u8 cmd;
+	u8 flag;
+	protocol_func_t func;
+	const char *desc;
+};
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} touch_fmts[touch_fmt_max];
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} pen_fmts[pen_fmt_max];
+
+#define X(_cmd, _protocol, _cmd_id, _api) \
+	static int _api(struct ilitek_ts_device *, void *);
+ILITEK_CMD_MAP
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api) {_cmd, _protocol, _api, #_cmd_id},
+struct protocol_map protocol_maps[] = { ILITEK_CMD_MAP };
+#undef X
+
+u16 le16(const u8 *p)
+{
+	return p[0] | p[1] << 8;
+}
+
+u16 be16(const u8 *p)
+{
+	return p[1] | p[0] << 8;
+}
+
+u32 le32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val += (p[bytes] << (8 * bytes));
+
+	return val;
+}
+
+u32 be32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val = (val << 8) | (*p++);
+
+	return val;
+}
+
+static bool is_2501x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "25011") ||
+	    !strcmp(dev->mcu_info.ic_name, "25012"))
+		return true;
+
+	return false;
+}
+
+bool is_29xx(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "2900") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901") ||
+	    !strcmp(dev->mcu_info.ic_name, "2910") ||
+	    !strcmp(dev->mcu_info.ic_name, "2911") ||
+	    !strcmp(dev->mcu_info.ic_name, "2531") ||
+	    !strcmp(dev->mcu_info.ic_name, "2532") ||
+	    !strcmp(dev->mcu_info.ic_name, "2921") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901M") ||
+	    is_2501x(handle))
+		return true;
+
+	return false;
+}
+
+bool _is_231x(char *ic_name)
+{
+	if (!strcmp(ic_name, "2312") || !strcmp(ic_name, "2315"))
+		return true;
+
+	return false;
+}
+
+bool is_231x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	return _is_231x(dev->mcu_info.ic_name);
+}
+
+bool has_hw_key(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle || !dev->tp_info.key_num)
+		return false;
+
+	if (dev->key.info.mode == key_hw ||
+	    dev->key.info.mode == key_hsw)
+		return true;
+
+	return false;
+}
+
+u8 get_protocol_ver_flag(u32 ver)
+{
+	if (((ver >> 16) & 0xFF) == 0x3 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_6 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_7)
+		return PTL_V3;
+
+	if (((ver >> 16) & 0xFF) == 0x6 ||
+	    (ver & 0xFFFF00) == BL_PROTOCOL_V1_8)
+		return PTL_V6;
+
+	return PTL_ANY;
+}
+
+void grid_reset(struct grids *grid)
+{
+	grid->mc.need_update = false;
+	grid->sc_x.need_update = false;
+	grid->sc_y.need_update = false;
+	grid->pen_x.need_update = false;
+	grid->pen_y.need_update = false;
+
+	grid->key_mc.need_update = false;
+	grid->key_x.need_update = false;
+	grid->key_y.need_update = false;
+
+	grid->self.need_update = false;
+
+	if (grid->mc.data)
+		_memset(grid->mc.data, 0,
+		       grid->mc.X * grid->mc.Y * sizeof(s32));
+	if (grid->sc_x.data)
+		_memset(grid->sc_x.data, 0,
+		grid->sc_x.X * grid->sc_x.Y * sizeof(s32));
+	if (grid->sc_y.data)
+		_memset(grid->sc_y.data, 0,
+		grid->sc_y.X * grid->sc_y.Y * sizeof(s32));
+	if (grid->pen_x.data)
+		_memset(grid->pen_x.data, 0,
+		grid->pen_x.X * grid->pen_x.Y * sizeof(s32));
+	if (grid->pen_y.data)
+		_memset(grid->pen_y.data, 0,
+		grid->pen_y.X * grid->pen_y.Y * sizeof(s32));
+
+	if (grid->key_mc.data)
+		_memset(grid->key_mc.data, 0,
+		grid->key_mc.X * grid->key_mc.Y * sizeof(s32));
+	if (grid->key_x.data)
+		_memset(grid->key_x.data, 0,
+		grid->key_x.X * grid->key_x.Y * sizeof(s32));
+	if (grid->key_y.data)
+		_memset(grid->key_y.data, 0,
+		grid->key_y.X * grid->key_y.Y * sizeof(s32));
+
+	if (grid->self.data)
+		_memset(grid->self.data, 0,
+		grid->self.X * grid->self.Y * sizeof(s32));
+
+	grid->dmsg.pen_need_update = false;
+	grid->dmsg.touch_need_update = false;
+	_memset(grid->dmsg.touch, 0, sizeof(grid->dmsg.touch));
+	_memset(grid->dmsg.pen, 0, sizeof(grid->dmsg.pen));
+}
+
+void grid_free(struct grids *grid)
+{
+	if (grid->mc.data)
+		CFREE(grid->mc.data);
+	if (grid->sc_x.data)
+		CFREE(grid->sc_x.data);
+	if (grid->sc_y.data)
+		CFREE(grid->sc_y.data);
+	if (grid->pen_x.data)
+		CFREE(grid->pen_x.data);
+	if (grid->pen_y.data)
+		CFREE(grid->pen_y.data);
+
+	if (grid->key_mc.data)
+		CFREE(grid->key_mc.data);
+	if (grid->key_x.data)
+		CFREE(grid->key_x.data);
+	if (grid->key_y.data)
+		CFREE(grid->key_y.data);
+
+	if (grid->self.data)
+		CFREE(grid->self.data);
+}
+
+int grid_alloc(void *handle, struct grids *grid)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int X, Y, key;
+
+	_memset(grid, 0, sizeof(*grid));
+
+	if (!dev)
+		return -EINVAL;
+
+	X = dev->tp_info.x_ch;
+	Y = dev->tp_info.y_ch;
+	key = dev->tp_info.key_num;
+
+	grid->mc.data = (s32 *)CALLOC(X * Y, sizeof(s32));
+	grid->sc_x.data = (s32 *)CALLOC(X, sizeof(s32));
+	grid->sc_y.data = (s32 *)CALLOC(Y, sizeof(s32));
+	grid->pen_x.data = (s32 *)CALLOC(X * 8, sizeof(s32));
+	grid->pen_y.data = (s32 *)CALLOC(Y * 8, sizeof(s32));
+	grid->key_mc.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_x.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_y.data = (s32 *)CALLOC(1, sizeof(s32));
+	grid->self.data = (s32 *)CALLOC(4, sizeof(s32));
+	if (!grid->mc.data || !grid->sc_x.data || !grid->sc_y.data ||
+	    !grid->pen_x.data || !grid->pen_y.data || !grid->key_mc.data ||
+	    !grid->key_x.data || !grid->key_y.data || !grid->self.data)
+		goto err_free;
+
+	grid->mc.X = X; grid->mc.Y = Y;
+	grid->sc_x.X = X; grid->sc_x.Y = 1;
+	grid->sc_y.X = 1; grid->sc_y.Y = Y;
+	grid->pen_x.X = X; grid->pen_x.Y = 8;
+	grid->pen_y.X = 8; grid->pen_y.Y = Y;
+
+	grid->key_mc.X = key; grid->key_mc.Y = 1;
+	grid->key_x.X = key; grid->key_x.Y = 1;
+	grid->key_y.X = 1; grid->key_y.Y = 1;
+
+	grid->self.X = 4, grid->self.Y = 1;
+
+	grid_reset(grid);
+
+	return 0;
+
+err_free:
+	grid_free(grid);
+
+	return -ENOMEM;
+}
+
+static u16 update_crc(u16 crc, u8 newbyte)
+{
+	char i;
+		const u16 crc_poly = 0x8408;
+
+	crc ^= newbyte;
+
+	for (i = 0; i < 8; i++) {
+		if (crc & 0x01)
+			crc = (crc >> 1) ^ crc_poly;
+		else
+			crc = crc >> 1;
+	}
+
+	return crc;
+}
+
+u16 get_crc(u32 start, u32 end,
+	    u8 *buf, u32 buf_size)
+{
+	u16 crc = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		crc = update_crc(crc, buf[i]);
+
+	return crc;
+}
+
+u32 get_checksum(u32 start, u32 end,
+		 u8 *buf, u32 buf_size)
+{
+	u32 sum = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		sum += buf[i];
+
+	return sum;
+}
+
+bool is_checksum_matched(u8 checksum, int start, int end,
+			 u8 *buf, int buf_size)
+{
+	u8 check;
+
+	check = ~(get_checksum(start, end, buf, buf_size)) + 1;
+	if (check != checksum) {
+		TP_ERR_ARR(NULL, "[data]", TYPE_U8, end - start, buf + start);
+		TP_ERR(NULL, "checksum : 0x%02x/0x%02x not matched\n",
+			check, checksum);
+		return false;
+	}
+
+	return true;
+}
+
+bool support_mcu_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060009))
+		return false;
+
+	return true;
+}
+
+bool support_sensor_id(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060004))
+		return false;
+
+	return true;
+}
+
+bool support_production_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_fwid(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010802) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_power_status(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x06000a))
+		return false;
+
+	return true;
+}
+
+int bridge_set_int_monitor(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf3;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int bridge_set_test_mode(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf2;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int reset_helper(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool need_re_enum = true;
+
+	if (dev->_interface == interface_i2c) {
+		/* sw reset if no reset-gpio found */
+		if (!dev->cb.hw_reset ||
+		    dev->cb.hw_reset(dev->reset_time, dev->_private) < 0)
+			return api_protocol_set_cmd(dev, SET_SW_RST,
+						    &need_re_enum);
+
+		return 0;
+	}
+
+	return api_protocol_set_cmd(dev, SET_SW_RST, &need_re_enum);
+}
+
+static int re_enum_helper(struct ilitek_ts_device *dev, u8 enum_type)
+{
+	int error;
+	int retry = 5;
+
+	if (!dev->cb.re_enum)
+		return -EINVAL;
+
+	do {
+		error = dev->cb.re_enum(enum_type, dev->_private);
+		if (!error)
+			return 0;
+
+		TP_WARN(dev->id, "re-enum failed, error: %d, retry: %d\n", error, retry);
+		dev->cb.delay_ms(500);
+	} while (!dev->setting.no_retry && retry--);
+
+	TP_ERR(dev->id, "re-enum retry failed\n");
+
+	return -ENODEV;
+}
+
+int read_interrupt_in(void *handle, u8 *buf, int rlen,
+		      unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_interrupt_in)
+		return -EINVAL;
+
+	error = dev->cb.read_interrupt_in(buf, rlen, timeout_ms,
+					   dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[int-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		 unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_ctrl_in)
+		return -EINVAL;
+
+	if (dev->quirks & QUIRK_BRIDGE) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA4;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = 0;
+		dev->wbuf[4] = (rlen + 6) & 0xFF;
+		dev->wbuf[5] = ((rlen + 6) >> 8) & 0xFF;
+		dev->wbuf[6] = cmd;
+
+		error = dev->cb.write_then_read_direct(dev->wbuf, 64,
+							dev->rbuf, 64, dev->_private);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.read_ctrl_in(buf, rlen, timeout_ms, dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[ctrl-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int write_then_read(void *handle, u8 *cmd, int wlen,
+		    u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[wbuf]:", TYPE_U8, wlen, cmd);
+
+	if (!wlen && (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+	    dev->quirks & QUIRK_BRIDGE)) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA3;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = rlen;
+
+		error = write_then_read_direct(dev, dev->wbuf, 64, NULL, 0);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.write_then_read(cmd, wlen, buf, rlen, dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[rbuf]:", TYPE_U8, rlen, buf);
+
+	return (error < 0) ? error : 0;
+}
+
+int write_then_read_direct(void *handle, u8 *cmd, int wlen,
+			   u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read_direct)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-wbuf]:", TYPE_U8, wlen, cmd);
+
+	error = dev->cb.write_then_read_direct(cmd, wlen, buf, rlen,
+					       dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-rbuf]:", TYPE_U8, rlen, buf);
+
+	return error;
+}
+
+int write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct ilitek_ts_callback *cb = &dev->cb;
+
+	u8 ack_cmd;
+
+	TP_DBG(dev->id, "cmd: 0x" PFMT_X8 ", tout_ms: %d\n",
+		cmd[0], timeout_ms);
+
+	if (dev->quirks & QUIRK_WAIT_ACK_DELAY) {
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		cb->delay_ms(dev->setting.wait_ack_delay);
+		return 0;
+	}
+
+	if (dev->setting.no_INT_ack) {
+		/* prevent bridge int handling flow affecting the following read */
+		if (dev->quirks & QUIRK_BRIDGE)
+			bridge_set_int_monitor(dev, false);
+
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		/*
+		* for no-INT-ack flow, add delay to prevent
+		* interrupting FW flow too soon, while FW should
+		* be handling previous write command. ex. 0xcd/ 0xc3
+		*/
+		cb->delay_ms(5);
+
+		goto check_busy;
+	}
+
+	if (!cb->init_ack || !cb->wait_ack)
+		return -EINVAL;
+
+	cb->init_ack(timeout_ms, dev->_private);
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, true);
+
+	error = write_then_read(dev, cmd, wlen, NULL, 0);
+	if (error < 0)
+		return error;
+
+	ack_cmd = (cmd[0] == CMD_ACCESS_SLAVE) ? cmd[2] : cmd[0];
+	error = cb->wait_ack(ack_cmd, timeout_ms, dev->_private);
+
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, false);
+
+	/* cmd[0] should be ILITEK cmd code */
+	if (error < 0) {
+		TP_WARN(dev->id, "wait 0x" PFMT_X8 " ack %d ms timeout, err: %d\n",
+			cmd[0], timeout_ms, error);
+
+		if (dev->setting.no_retry)
+			return -EILITIME;
+
+		goto check_busy;
+	}
+
+	return 0;
+
+check_busy:
+	return api_check_busy(dev, timeout_ms, 10);
+}
+
+/* Common APIs */
+static int api_protocol_get_scrn_res(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_screen_info *screen_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 28);
+	if (error < 0)
+		return error;
+
+	screen_info = (struct ilitek_screen_info *)dev->rbuf;
+
+	dev->screen_info.x_min = screen_info->x_min;
+	dev->screen_info.y_min = screen_info->y_min;
+	dev->screen_info.x_max = screen_info->x_max;
+	dev->screen_info.y_max = screen_info->y_max;
+
+	TP_DBG(dev->id, "screen x: " PFMT_U16 "~" PFMT_U16 ", screen y: " PFMT_U16 "~" PFMT_U16 "\n",
+		dev->screen_info.x_min, dev->screen_info.x_max,
+		dev->screen_info.y_min, dev->screen_info.y_max);
+
+	dev->screen_info.pressure_min = 0;
+	dev->screen_info.pressure_max = 0;
+	dev->screen_info.x_tilt_min = 0;
+	dev->screen_info.x_tilt_max = 0;
+	dev->screen_info.y_tilt_min = 0;
+	dev->screen_info.y_tilt_max = 0;
+	if (dev->protocol.ver > 0x60006) {
+		dev->screen_info.pressure_min = screen_info->pressure_min;
+		dev->screen_info.pressure_max = screen_info->pressure_max;
+		dev->screen_info.x_tilt_min = screen_info->x_tilt_min;
+		dev->screen_info.x_tilt_max = screen_info->x_tilt_max;
+		dev->screen_info.y_tilt_min = screen_info->y_tilt_min;
+		dev->screen_info.y_tilt_max = screen_info->y_tilt_max;
+
+		dev->screen_info.pen_x_min = screen_info->pen_x_min;
+		dev->screen_info.pen_y_min = screen_info->pen_y_min;
+		dev->screen_info.pen_x_max = screen_info->pen_x_max;
+		dev->screen_info.pen_y_max = screen_info->pen_y_max;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v3 *tp_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 15);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v3 *)dev->rbuf;
+
+	dev->tp_info.block_num = 2;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+
+	dev->tp_info.support_modes = tp_info->support_modes;
+	if (dev->tp_info.support_modes > 3 || !dev->tp_info.support_modes)
+		dev->tp_info.support_modes = 1;
+
+	TP_DBG(dev->id, "touch ch.(start/end) x: " PFMT_U8 "/" PFMT_U8 ", y: " PFMT_U8 "/" PFMT_U8 "\n",
+		tp_info->touch_start_y, tp_info->touch_end_y,
+		tp_info->touch_start_x, tp_info->touch_end_x);
+
+	if (dev->tp_info.key_num) {
+		/* check v3 key is virtual or hw keys */
+		dev->key.info.mode =
+			(tp_info->touch_start_y == 0xff &&
+			 tp_info->touch_end_y == 0xff &&
+			 tp_info->touch_start_x == 0xff &&
+			 tp_info->touch_end_x == 0xff) ?
+			key_hw : key_vitual;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v6 *tp_info;
+	u8 i;
+
+#define X(_enum, _code, _name) {_code, _name},
+	const struct {
+		const int code;
+		const char *str;
+	} pen_modes[] = { STYLUS_MODES };
+#undef X
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 21);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v6 *)dev->rbuf;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+	dev->tp_info.ic_num = tp_info->ic_num;
+	dev->tp_info.format = tp_info->format;
+	dev->tp_info.support_modes = tp_info->support_modes;
+	dev->tp_info.die_num = tp_info->die_num;
+
+	if (dev->tp_info.format == 5)
+		api_protocol_set_cmd(dev, GET_CRYPTO_INFO, NULL);
+
+	if (dev->tp_info.ic_num > ARRAY_SIZE(dev->ic)) {
+		TP_ERR(dev->id, "invalid ic_num: " PFMT_U8 "\n", dev->tp_info.ic_num);
+		return -EINVAL;
+	}
+	TP_MSG(dev->id, "[Panel Information] Chip count: %u\n",
+		dev->tp_info.ic_num);
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max tp: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	if (dev->protocol.ver < 0x60003)
+		return 0;
+
+	dev->tp_info.block_num = tp_info->block_num;
+	TP_MSG(dev->id, "[Panel Information] Block Number: " PFMT_U8 "\n",
+		dev->tp_info.block_num);
+
+	if (dev->protocol.ver < 0x60007)
+		return 0;
+
+	dev->tp_info.pen_modes = tp_info->pen_modes;
+
+	_memset(dev->pen_mode, 0, sizeof(dev->pen_mode));
+	if (!dev->tp_info.pen_modes)
+		_strcpy(dev->pen_mode, "Disable",
+			sizeof(dev->pen_mode));
+	for (i = 0; i < ARRAY_SIZE(pen_modes); i++) {
+		if (!(tp_info->pen_modes & pen_modes[i].code))
+			continue;
+
+		if (_strlen(dev->pen_mode))
+			_strcat(dev->pen_mode, ",", sizeof(dev->pen_mode));
+
+		_strcat(dev->pen_mode, pen_modes[i].str,
+			sizeof(dev->pen_mode));
+	}
+
+	TP_DBG(dev->id, "pen_modes: " PFMT_U8 "\n", dev->tp_info.pen_modes);
+	TP_MSG(dev->id, "[Panel Information] Pen Mode: " PFMT_C8 "\n",
+		dev->pen_mode);
+
+	dev->tp_info.pen_format = tp_info->pen_format;
+	dev->tp_info.pen_x_resolution = tp_info->pen_x_resolution;
+	dev->tp_info.pen_y_resolution = tp_info->pen_y_resolution;
+	TP_MSG(dev->id, "[Panel Information] Pen Format: 0x" PFMT_X8 "\n",
+		dev->tp_info.pen_format);
+	TP_MSG(dev->id, "[Panel Information] Pen X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.pen_x_resolution,
+		dev->tp_info.pen_y_resolution);
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+#define X(_enum, _id, _size, _cnt)	\
+	pen_fmts[_id].size = _size;	\
+	pen_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_PEN_REPORT_FORMAT;
+#undef X
+
+	if (dev->protocol.flag == PTL_V3)
+		error = api_protocol_get_tp_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		error = api_protocol_get_tp_info_v6(dev, data);
+	else
+		return -EINVAL;
+
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max fingers: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	switch (dev->tp_info.format) {
+	case touch_fmt_0x1:
+	case touch_fmt_0x2:
+	case touch_fmt_0x3:
+	case touch_fmt_0x4:
+	case touch_fmt_0x10:
+		if (dev->setting.default_format_enabled)
+			goto default_fmt_enabled;
+
+		dev->fmt.touch_size = touch_fmts[dev->tp_info.format].size;
+		dev->fmt.touch_max_cnt = touch_fmts[dev->tp_info.format].max_cnt;
+		break;
+
+default_fmt_enabled:
+	default:
+	case touch_fmt_0x11:
+	case touch_fmt_0x0:
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+		break;
+	}
+
+	switch (dev->tp_info.pen_format) {
+	case pen_fmt_0x1:
+	case pen_fmt_0x2:
+		dev->fmt.pen_size = pen_fmts[dev->tp_info.pen_format].size;
+		dev->fmt.pen_max_cnt = pen_fmts[dev->tp_info.pen_format].max_cnt;
+		break;
+	default:
+	case pen_fmt_0x0:
+		dev->fmt.pen_size = pen_fmts[pen_fmt_0x0].size;
+		dev->fmt.pen_max_cnt = pen_fmts[pen_fmt_0x0].max_cnt;
+		break;
+	}
+
+	TP_MSG(dev->id, "[Panel Information] X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_resolution, dev->tp_info.y_resolution);
+	TP_MSG(dev->id, "[Panel Information] X/Y channel: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_ch, dev->tp_info.y_ch);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Fingers\n",
+		dev->tp_info.max_fingers);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Keys\n",
+		dev->tp_info.key_num);
+
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " modes\n",
+		dev->tp_info.support_modes);
+
+	TP_DBG(dev->id, "touch format: 0x" PFMT_X8 ", size: %u bytes, max cnt: %u per packet\n",
+		dev->tp_info.format, dev->fmt.touch_size,
+		dev->fmt.touch_max_cnt);
+
+	if (dev->tp_info.key_num > 0) {
+		error = api_protocol_set_cmd(dev, GET_KEY_INFO, NULL);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v3 *key_info;
+	unsigned int i;
+
+	UNUSED(data);
+
+	/* Only i2c interface has key for V3 */
+	if (dev->_interface != interface_i2c)
+		return 0;
+
+	if (dev->tp_info.key_num > 20) {
+		TP_ERR(dev->id, "key count: " PFMT_U8 " invalid\n", dev->tp_info.key_num);
+		return -EINVAL;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 29);
+	if (error < 0)
+		return error;
+
+	for (i = 0; dev->tp_info.key_num > 5U &&
+	     i < DIV_ROUND_UP(dev->tp_info.key_num, 5U) - 1U; i++) {
+		TP_MSG(dev->id, "read keyinfo again, i: %u\n", i);
+		error = write_then_read(dev, NULL, 0,
+					dev->rbuf + 29 + 5 * i, 25);
+		if (error < 0)
+			return error;
+	}
+
+	key_info = (struct ilitek_key_info_v3 *)dev->rbuf;
+	dev->key.info.x_len = be16(key_info->x_len);
+	dev->key.info.y_len = be16(key_info->y_len);
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = be16(key_info->keys[i].x);
+		dev->key.info.keys[i].y = be16(key_info->keys[i].y);
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v6 *key_info;
+	unsigned int i, offset;
+
+	UNUSED(data);
+
+	if (dev->tp_info.key_num > ARRAY_SIZE(dev->key.info.keys)) {
+		TP_ERR(dev->id, "exception keycount " PFMT_U8 " > %d\n", dev->tp_info.key_num,
+			(int)ARRAY_SIZE(dev->key.info.keys));
+		return -EINVAL;
+	}
+
+	switch (dev->_interface) {
+	case interface_i2c:
+		if (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+		    dev->quirks & QUIRK_BRIDGE) {
+			error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+			if (error < 0)
+				return error;
+			error = read_ctrl_in(dev, CMD_GET_KEY_INFO,
+					     dev->rbuf,
+					     5 + dev->tp_info.key_num * 5, 2000);
+			if (error < 0)
+				return error;
+			offset = (dev->quirks & QUIRK_BRIDGE) ? 1 : 0;
+		} else {
+			error = write_then_read(dev, dev->wbuf, 1,
+						dev->rbuf, 5 + dev->tp_info.key_num * 5);
+			if (error < 0)
+				return error;
+			offset = 0;
+		}
+		break;
+
+	case interface_usb:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 6;
+		break;
+	case interface_hid_over_i2c:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 4;
+		break;
+	default: return -EINVAL;
+	};
+
+	key_info = (struct ilitek_key_info_v6 *)(dev->rbuf + offset);
+	dev->key.info.mode = key_info->mode;
+	TP_MSG(dev->id, "[Panel Information] key mode: " PFMT_U8 "\n", dev->key.info.mode);
+
+	dev->key.info.x_len = key_info->x_len;
+	dev->key.info.y_len = key_info->y_len;
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = key_info->keys[i].x;
+		dev->key.info.keys[i].y = key_info->keys[i].y;
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_key_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_key_info_v6(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_get_ptl_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->protocol.flag = PTL_V6;
+	dev->reset_time = 1000;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->protocol.ver = (dev->rbuf[0] << 16) + (dev->rbuf[1] << 8) +
+			     dev->rbuf[2];
+	TP_MSG(dev->id, "[Protocol Version]: %x.%x.%x\n",
+		(dev->protocol.ver >> 16) & 0xFF,
+		(dev->protocol.ver >> 8) & 0xFF,
+		dev->protocol.ver & 0xFF);
+
+	dev->protocol.flag = get_protocol_ver_flag(dev->protocol.ver);
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->reset_time = 200;
+		break;
+	case PTL_V6:
+		dev->reset_time = 600;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol ver.: 0x%x\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_fw_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->fw_ver, dev->rbuf, 8);
+
+	if (dev->ic[0].mode == BL_MODE) {
+		TP_MSG_ARR(dev->id, "[BL Firmware Version]", TYPE_U8,
+			   8, dev->fw_ver);
+	} else {
+		TP_MSG_ARR(dev->id, "[FW Version]", TYPE_U8, 4, dev->fw_ver);
+		TP_MSG_ARR(dev->id, "[Customer Version]", TYPE_U8,
+			   4, dev->fw_ver + 4);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < ic_num; i++) {
+		dev->ic[i].mode = dev->rbuf[i * 2];
+
+		if (dev->ic[i].mode == AP_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "AP");
+		else if (dev->ic[i].mode == BL_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "BL");
+		else
+			_sprintf(dev->ic[i].mode_str, 0, "UNKNOWN");
+	}
+
+	TP_MSG(dev->id, "[Current Mode] Master: 0x" PFMT_X8 " " PFMT_C8 "\n",
+		dev->ic[0].mode, dev->ic[0].mode_str);
+	for (i = 1; i < ic_num; i++)
+		TP_MSG(dev->id, "[Current Mode] Slave[" PFMT_U8 "]: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, dev->ic[i].mode, dev->ic[i].mode_str);
+
+	return 0;
+}
+
+static int api_protocol_power_status(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 func, lvd_level_sel;
+
+	if (!data)
+		return -EFAULT;
+
+	if (!support_power_status(dev)) {
+		_memset(&dev->pwr, 0, sizeof(dev->pwr));
+		return 0;
+	}
+
+	func = ((*(u16 *)data) >> 8) & 0xff;
+	lvd_level_sel = (*(u16 *)data) & 0xff;
+
+	dev->wbuf[1] = func;
+	switch (func) {
+	/* get level select */
+	case 0x02:
+		dev->wbuf[2] = lvd_level_sel;
+		return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+
+	/* clear flag */
+	case 0x00:
+		return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+
+	/* get flag */
+	case 0x01:
+		error = write_then_read(dev, dev->wbuf, 2,
+					dev->rbuf, 4);
+		if (error < 0)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev->pwr.header = be16(dev->rbuf);
+	dev->pwr.vdd33_lvd_flag = dev->rbuf[2];
+	dev->pwr.vdd33_lvd_level_sel = dev->rbuf[3];
+
+	TP_DBG(dev->id, "[Power-Status] header: 0x" PFMT_X16 ", flag: 0x" PFMT_X8 ", level_sel: 0x" PFMT_X8 "\n",
+		dev->pwr.header, dev->pwr.vdd33_lvd_flag,
+		dev->pwr.vdd33_lvd_level_sel);
+
+	return 0;
+}
+
+static int api_protocol_get_sensor_id(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_sensor_id(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->sensor.header = be16(dev->rbuf);
+	dev->sensor.id = dev->rbuf[2];
+
+	TP_MSG(dev->id, "[Sensor ID] header: 0x" PFMT_X16 ", id: 0x" PFMT_X8 "\n",
+		dev->sensor.header,
+		(u8)(dev->sensor.id & dev->setting.sensor_id_mask));
+
+	return 0;
+}
+
+static int api_protocol_get_product_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_production_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->product_info, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[Production Info]", TYPE_U8, 8, dev->product_info);
+
+	return 0;
+}
+
+static int api_protocol_get_fwid(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_fwid(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->customer_id = le16(dev->rbuf);
+	dev->fwid = le16(dev->rbuf + 2);
+
+	TP_MSG(dev->id, "[Customer ID] 0x%04x\n", dev->customer_id);
+	TP_MSG(dev->id, "[FWID] 0x%04x\n", dev->fwid);
+
+	return 0;
+}
+
+static int api_protocol_get_crypto_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	u16 __MAYBE_UNUSED crypto_ver;
+	u32 crypto_opt;
+
+	UNUSED(data);
+
+	/*
+	 * encrypted report format should be supported after AP v6.0.8
+	 * set report format to 0 if protocol version not matched or
+	 * crypto info say it's not supported.
+	 */
+	if (dev->protocol.ver < 0x060008 ||
+	    write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6) < 0) {
+		dev->tp_info.format = 0;
+		return 0;
+	}
+
+	crypto_ver = le16(dev->rbuf);
+	crypto_opt = le32(dev->rbuf + 2, 4);
+
+	TP_MSG(dev->id, "[Encrypt Ver.] 0x%x\n", crypto_ver);
+	TP_MSG(dev->id, "[Encrypt Options] 0x%x\n", crypto_opt);
+
+	if (!(crypto_opt & 1))
+		dev->tp_info.format = 0;
+
+	return 0;
+}
+
+static int api_protocol_get_hid_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060009)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6);
+	if (error < 0)
+		return error;
+
+	_memcpy(&dev->hid_info, dev->rbuf, sizeof(dev->hid_info));
+
+	TP_MSG(dev->id, "vid/pid/rev: 0x%x/0x%x/0x%x\n",
+		dev->hid_info.vid, dev->hid_info.pid, dev->hid_info.rev);
+
+	return 0;
+}
+
+static bool is_special_char(char c)
+{
+	return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+		(c >= '0' && c <= '9')) ? false : true;
+}
+
+static int api_protocol_get_mcu_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_ver {
+		u16 ic_name;
+		u8 df_start_addr[3];
+		u8 df_size;
+
+		char module_name[26];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, use GET_MCU_VER (0x61) cmd
+	 */
+	if (dev->protocol.flag == PTL_V6 && support_mcu_info(dev)) {
+		error = api_protocol_set_cmd(dev, GET_MCU_INFO, NULL);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1,
+					dev->rbuf, 32);
+		if (error < 0)
+			return error;
+
+		parser = (struct mcu_ver *)dev->rbuf;
+
+		_memset(dev->mcu_info.ic_name, 0,
+			sizeof(dev->mcu_info.ic_name));
+		_sprintf(dev->mcu_info.ic_name, 0, "%04x", parser->ic_name);
+
+		_memset(dev->mcu_info.module_name, 0,
+			sizeof(dev->mcu_info.module_name));
+		_memcpy(dev->mcu_info.module_name, parser->module_name,
+			sizeof(parser->module_name));
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		if (is_29xx(dev)) {
+			/* modify reset time to 100ms for 29xx ICs */
+			dev->reset_time = 100;
+
+			/* set mm_addr for bin file update */
+			dev->mcu_info.mm_addr =
+				is_2501x(dev) ? MM_ADDR_2501X : MM_ADDR_29XX;
+			dev->mcu_info.min_addr = START_ADDR_29XX;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		} else {
+			dev->mcu_info.mm_addr = MM_ADDR_LEGO;
+			dev->mcu_info.min_addr = START_ADDR_LEGO;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		}
+	}
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+	if (!strcmp(dev->mcu_info.ic_name, "2133"))
+		_sprintf(dev->mcu_info.ic_name, 0, "2132S");
+
+	_memset(dev->mcu_info.ic_full_name, 0,
+		sizeof(dev->mcu_info.ic_full_name));
+	_sprintf(dev->mcu_info.ic_full_name, 0,
+		"ILI" PFMT_C8, dev->mcu_info.ic_name);
+
+	TP_MSG(dev->id, "[MCU Kernel Version] " PFMT_C8 "\n",
+		dev->mcu_info.ic_full_name);
+	TP_MSG(dev->id, "[Module Name]: [" PFMT_C8 "]\n",
+		dev->mcu_info.module_name);
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_info {
+		char ic_name[5];
+		char mask_ver[2];
+		u8 mm_addr[3];
+		char module_name[18];
+		u8 reserve[4];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd only support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, return 0 to skip this command.
+	 */
+	if (dev->protocol.flag != PTL_V6 || !support_mcu_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	if (error < 0)
+		return error;
+
+	parser = (struct mcu_info *)dev->rbuf;
+
+	_memset(dev->mcu_info.ic_name, 0, sizeof(dev->mcu_info.ic_name));
+	_memcpy(dev->mcu_info.ic_name, parser->ic_name,
+		sizeof(parser->ic_name));
+
+	_memcpy(dev->mcu_info.module_name, parser->module_name,
+		sizeof(parser->module_name));
+	dev->mcu_info.mm_addr = le32(parser->mm_addr, 3);
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_fs_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->sine.start & 0xFF;
+	dev->wbuf[2] = freq->sine.start >> 8;
+	dev->wbuf[3] = freq->sine.end & 0xFF;
+	dev->wbuf[4] = freq->sine.end >> 8;
+	dev->wbuf[5] = freq->sine.step;
+	dev->wbuf[6] = freq->mc_swcap.start & 0xFF;
+	dev->wbuf[7] = freq->mc_swcap.start >> 8;
+	dev->wbuf[8] = freq->mc_swcap.end & 0xFF;
+	dev->wbuf[9] = freq->mc_swcap.end >> 8;
+	dev->wbuf[10] = freq->mc_swcap.step;
+	dev->wbuf[11] = freq->sc_swcap.start & 0xFF;
+	dev->wbuf[12] = freq->sc_swcap.start >> 8;
+	dev->wbuf[13] = freq->sc_swcap.end & 0xFF;
+	dev->wbuf[14] = freq->sc_swcap.end >> 8;
+	dev->wbuf[15] = freq->sc_swcap.step;
+	dev->wbuf[16] = freq->frame_cnt & 0xFF;
+	dev->wbuf[17] = freq->frame_cnt >> 8;
+	dev->wbuf[18] = freq->scan_type;
+
+	if (dev->protocol.ver < 0x60005)
+		return write_then_read(dev, dev->wbuf, 19, NULL, 0);
+
+	do {
+		if (dev->protocol.ver < 0x60009) {
+			error = write_then_read(dev, dev->wbuf, 19,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[16] = freq->dump1.start & 0xFF;
+		dev->wbuf[17] = freq->dump1.start >> 8;
+		dev->wbuf[18] = freq->dump1.end & 0xFF;
+		dev->wbuf[19] = freq->dump1.end >> 8;
+		dev->wbuf[20] = freq->dump1.step;
+		dev->wbuf[21] = freq->dump1_val;
+		dev->wbuf[22] = freq->dump2.start & 0xFF;
+		dev->wbuf[23] = freq->dump2.start >> 8;
+		dev->wbuf[24] = freq->dump2.end & 0xFF;
+		dev->wbuf[25] = freq->dump2.end >> 8;
+		dev->wbuf[26] = freq->dump2.step;
+		dev->wbuf[27] = freq->dump2_val;
+		dev->wbuf[28] = freq->frame_cnt & 0xFF;
+		dev->wbuf[29] = freq->frame_cnt >> 8;
+		dev->wbuf[30] = freq->scan_type;
+
+		if (dev->protocol.ver < 0x6000a) {
+			error = write_then_read(dev, dev->wbuf, 31,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[31] = freq->mc_frame_cnt & 0xFF;
+		dev->wbuf[32] = freq->mc_frame_cnt >> 8;
+		dev->wbuf[33] = freq->dump_frame_cnt & 0xFF;
+		dev->wbuf[34] = freq->dump_frame_cnt >> 8;
+
+		error = write_then_read(dev, dev->wbuf, 35, dev->rbuf, 5);
+
+	} while (0);
+
+	if (error < 0)
+		return error;
+
+	freq->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != 0x5a || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			freq->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_short_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct short_settings *_short = (struct short_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[short info] dump1: 0x" PFMT_X8 ", dump2: 0x" PFMT_X8 ", vref: 0x" PFMT_X8 ", postidle: 0x" PFMT_X16 "\n",
+		_short->dump_1, _short->dump_2,
+		_short->v_ref_L, _short->post_idle);
+
+	dev->wbuf[1] = _short->dump_1;
+	dev->wbuf[2] = _short->dump_2;
+	dev->wbuf[3] = _short->v_ref_L;
+	dev->wbuf[4] = _short->post_idle & 0xFF;
+	dev->wbuf[5] = (_short->post_idle >> 8) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 6, NULL, 0);
+}
+
+static int api_protocol_set_open_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct open_settings *open = (struct open_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id,
+		"[open info] freq.: " PFMT_U16 ", gain: 0x" PFMT_X8 ", gain_rfb: 0x" PFMT_X8
+		", afe_res_sel: 0x" PFMT_X8 ", mc_fsel: 0x" PFMT_X8 "\n",
+		open->freq, open->gain, open->gain_rfb,
+		open->afe_res_sel, open->mc_fsel);
+
+	dev->wbuf[wlen++] = open->freq & 0xFF;
+	dev->wbuf[wlen++] = (open->freq >> 8) & 0xFF;
+	dev->wbuf[wlen++] = open->gain;
+	dev->wbuf[wlen++] = open->gain_rfb;
+	dev->wbuf[wlen++] = open->afe_res_sel;
+	dev->wbuf[wlen++] = open->mc_fsel;
+
+	if (dev->protocol.ver > 0x060009) {
+		TP_DBG(dev->id, "[open info] frame: " PFMT_U16 "\n",
+			open->frame);
+
+		dev->wbuf[wlen++] = open->frame & 0xFF;
+		dev->wbuf[wlen++] = (open->frame >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_charge_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error, i;
+	struct charge_curve_settings *curve =
+		(struct charge_curve_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "charge-curve info. scan mode: 0x" PFMT_U8 "\n",
+		curve->scan_mode);
+
+	dev->wbuf[1] = curve->scan_mode;
+	dev->wbuf[2] = curve->dump.start & 0xFF;
+	dev->wbuf[3] = (curve->dump.start >> 8) & 0xFF;
+	dev->wbuf[4] = curve->dump.end & 0xFF;
+	dev->wbuf[5] = (curve->dump.end >> 8) & 0xFF;
+	dev->wbuf[6] = curve->dump.step;
+	dev->wbuf[7] = curve->dump.post_idle & 0xFF;
+	dev->wbuf[8] = (curve->dump.post_idle >> 8) & 0xFF;
+	dev->wbuf[9] = curve->dump.fix_val & 0xFF;
+	dev->wbuf[10] = (curve->dump.fix_val >> 8) & 0xFF;
+	dev->wbuf[11] = curve->charge.start & 0xFF;
+	dev->wbuf[12] = (curve->charge.start >> 8) & 0xFF;
+	dev->wbuf[13] = curve->charge.end & 0xFF;
+	dev->wbuf[14] = (curve->charge.end >> 8) & 0xFF;
+	dev->wbuf[15] = curve->charge.step;
+	dev->wbuf[16] = curve->charge.post_idle & 0xFF;
+	dev->wbuf[17] = (curve->charge.post_idle >> 8) & 0xFF;
+	dev->wbuf[18] = curve->charge.fix_val & 0xFF;
+	dev->wbuf[19] = (curve->charge.fix_val >> 8) & 0xFF;
+	dev->wbuf[20] = curve->c_sub & 0xFF;
+	dev->wbuf[21] = (curve->c_sub >> 8) & 0xFF;
+	dev->wbuf[22] = curve->frame_cnt & 0xFF;
+	dev->wbuf[23] = (curve->frame_cnt >> 8) & 0xFF;
+
+	for (i = 0; i < (int)ARRAY_SIZE(curve->pt); i++) {
+		dev->wbuf[24 + i * 4] = curve->pt[i].x & 0xFF;
+		dev->wbuf[24 + i * 4 + 1] = (curve->pt[i].x >> 8) & 0xFF;
+		dev->wbuf[24 + i * 4 + 2] = curve->pt[i].y & 0xFF;
+		dev->wbuf[24 + i * 4 + 3] = (curve->pt[i].y >> 8) & 0xFF;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 60, dev->rbuf, 5);
+	if (error < 0)
+		return error;
+
+	curve->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != AP_MODE || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			curve->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_p2p_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct p2p_settings *p2p = (struct p2p_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[p2p info] frame_cnt.: " PFMT_U16 ", type: 0x" PFMT_X8 "\n",
+		p2p->frame_cnt, p2p->type);
+
+	dev->wbuf[wlen++] = p2p->frame_cnt & 0xFF;
+	dev->wbuf[wlen++] = (p2p->frame_cnt >> 8) & 0xFF;
+
+	if (dev->protocol.ver > 0x060009) {
+		dev->wbuf[wlen++] = p2p->type & 0xFF;
+		dev->wbuf[wlen++] = p2p->freq & 0xFF;
+		dev->wbuf[wlen++] = (p2p->freq >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_pen_fs_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->pen.mode;
+	dev->wbuf[2] = freq->pen.start & 0xFF;
+	dev->wbuf[3] = freq->pen.start >> 8;
+	dev->wbuf[4] = freq->pen.end & 0xFF;
+	dev->wbuf[5] = freq->pen.end >> 8;
+	dev->wbuf[6] = freq->frame_cnt & 0xFF;
+	dev->wbuf[7] = freq->frame_cnt >> 8;
+
+	error = write_then_read(dev, dev->wbuf, 8, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int api_protocol_get_core_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->core_ver, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[CoreVersion]", TYPE_U8, 4, dev->core_ver);
+
+	return 0;
+}
+
+static int api_protocol_get_tuning_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->tuning_ver, dev->rbuf, 4);
+
+	TP_MSG_ARR(dev->id, "[TurningVersion]", TYPE_U8, 4, dev->tuning_ver);
+
+	return 0;
+}
+
+static int api_protocol_set_sw_reset(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	int wlen = 1;
+	bool need_re_enum = (data) ? *(bool *)data : false;
+	bool force_reset = (!data) ? true : false;
+
+	/* make sure touch report in default I2C-HID mode after force reset */
+	if (dev->_interface == interface_hid_over_i2c && !force_reset)
+		return 0;
+
+	dev->wbuf[1] = 0;
+	error = write_then_read(dev, dev->wbuf, wlen, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(dev->reset_time);
+
+	if (dev->_interface == interface_usb && need_re_enum)
+		return re_enum_helper(dev, enum_sw_reset);
+
+	return 0;
+}
+
+static int api_protocol_get_sys_busy(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	if (data)
+		*(u8 *)data = 0;
+
+	_memset(dev->rbuf, 0, 64);
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 1);
+	if (error < 0)
+		return error;
+
+	if (data)
+		*(u8 *)data = dev->rbuf[0];
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	/*
+	 * No need to get/print AP CRC by 0xC7 in BL mode,
+	 * and 2501x ICs would get wrong crc in BL.
+	 */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1,
+				dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	TP_MSG(dev->id, "[FW CRC] Master: 0x%x\n", dev->ic[0].crc[0]);
+
+	for (i = 1; i < ic_num; i++) {
+		dev->ic[i].crc[0] = le16(dev->rbuf + 2 * i);
+		TP_MSG(dev->id, "[FW CRC] Slave[" PFMT_U8 "]: 0x%x\n",
+			i, dev->ic[i].crc[0]);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error, rlen;
+
+	UNUSED(data);
+
+	rlen = (is_231x(dev)) ? 4 : 2;
+
+	if (dev->_interface == interface_i2c) {
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		dev->cb.delay_ms(600);
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	}
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	if (is_231x(dev))
+		dev->ic[0].crc[0] |= (le16(dev->rbuf + 2) << 16);
+
+	TP_MSG(dev->id, "[Check Code] AP: 0x%x\n", dev->ic[0].crc[0]);
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_ap_crc_v6(dev, data);
+	else if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_ap_crc_v3(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_set_mode_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 mode = dev->wbuf[1];
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * Bridge with V3 IC need to set bridge into/out test mode additionally.
+	 */
+	if (dev->quirks & QUIRK_BRIDGE)
+		return bridge_set_test_mode(dev, (mode) ? true : false);
+
+	return 0;
+}
+
+static int api_protocol_write_enable(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool in_ap = (data) ? *(bool *)data : true;
+
+	error = write_then_read(dev, dev->wbuf,
+				(in_ap) ? 3 : 10, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * V3 need AP/BL mode switch delay
+	 */
+	if (in_ap)
+		dev->cb.delay_ms(is_231x(dev) ? 1000 : 100);
+	else
+		dev->cb.delay_ms(10);
+
+	return 0;
+}
+
+static int api_protocol_write_data_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 33, NULL, 0);
+}
+
+static int api_protocol_get_df_crc(struct ilitek_ts_device *dev,  void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->ic[0].crc[1] = 0;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[1] = le16(dev->rbuf + 2) << 16 | le16(dev->rbuf);
+	TP_MSG(dev->id, "[Check Code] Data: 0x%x\n", dev->ic[0].crc[1]);
+
+	return 0;
+}
+
+static int api_protocol_set_mode_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_get_crc_by_addr(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 start, end, t_ms;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		start = le32(dev->wbuf + 2, 3);
+		end = le32(dev->wbuf + 5, 3);
+		t_ms = ((end - start) / 4096 + 1) * TOUT_CD * TOUT_CD_RATIO;
+
+		error = write_then_wait_ack(dev, dev->wbuf, 8, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_get_crc_by_num(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 t_ms = (dev->wbuf[2] == 0) ? TOUT_CF_BLOCK_0 : TOUT_CF_BLOCK_N;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		error = write_then_wait_ack(dev, dev->wbuf, 3, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_read_flash(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 code = *(u32 *)data;
+	bool prepare;
+	int rlen;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		if ((dev->protocol.ver & 0xFFFF00) == BL_PROTOCOL_V1_7 &&
+		     dev->fw_ver[3] < 3) {
+			TP_ERR(dev->id, "BL: 0x%x, FW: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 " not support cmd: 0x" PFMT_X8 "\n",
+				dev->protocol.ver, dev->fw_ver[0],
+				dev->fw_ver[1], dev->fw_ver[2], dev->fw_ver[3],
+				dev->wbuf[0]);
+			return -EINVAL;
+		}
+
+		return write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	}
+
+	if (!data)
+		return -EINVAL;
+
+	prepare = (code >> 16) ? true : false;
+	rlen = code & 0xFFFF;
+
+	if (prepare) {
+		error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+		dev->cb.delay_ms(100);
+
+		return error;
+	}
+
+	if (dev->_interface == interface_i2c)
+		error = write_then_read(dev, dev->wbuf, 2, dev->rbuf, rlen);
+	else
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+
+	return error;
+}
+
+static int api_protocol_set_flash_addr(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 addr = *(u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		dev->wbuf[3] = addr & 0xFF;
+		dev->wbuf[2] = (addr >> 8) & 0xFF;
+		dev->wbuf[1] = (addr >> 16) & 0xFF;
+
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		return 0;
+	}
+
+	dev->wbuf[1] = addr & 0xFF;
+	dev->wbuf[2] = (addr >> 8) & 0xFF;
+	dev->wbuf[3] = (addr >> 16) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 4, NULL, 0);
+}
+
+static int api_protocol_set_data_len(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_set_flash_enable(struct ilitek_ts_device *dev,
+					 void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	int wlen, rlen;
+	bool in_ap = ((type & 0x1) != 0) ? true : false;
+	bool is_slave = ((type & 0x2) != 0) ? true : false;
+
+	u32 set_start, set_end, get_start, get_end;
+
+	if (!is_slave) {
+		wlen = (in_ap) ? 3 : 9;
+		rlen = (in_ap || dev->protocol.ver < 0x010803) ? 0 : 6;
+
+		set_start = le32(dev->wbuf + 3, 3);
+		set_end = le32(dev->wbuf + 6, 3);
+
+		error = write_then_read(dev, dev->wbuf, wlen,
+					dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+
+		if (in_ap || dev->protocol.ver < 0x010803)
+			return 0;
+
+		get_start = le32(dev->rbuf, 3);
+		get_end = le32(dev->rbuf + 3, 3);
+
+		if (set_start != get_start || set_end != get_end) {
+			TP_ERR(dev->id, "start/end addr.: 0x%x/0x%x vs. 0x%x/0x%x not match\n",
+				set_start, set_end, get_start, get_end);
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	error = write_then_wait_ack(dev, dev->wbuf, 9,
+				    TOUT_CC_SLAVE * TOUT_CC_SLAVE_RATIO);
+	if (error < 0)
+		return error;
+	dev->cb.delay_ms(2000);
+
+	return (dev->_interface == interface_usb) ?
+		re_enum_helper(dev, enum_sw_reset) : 0;
+}
+
+static int api_protocol_write_data_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, TOUT_C3 * TOUT_C3_RATIO);
+}
+
+static int api_protocol_write_data_m2v(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, 30000);
+}
+
+static int api_protocol_access_slave(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_slave_access *access;
+
+	if (!data)
+		return -EINVAL;
+
+	access = (struct ilitek_slave_access *)data;
+
+	dev->wbuf[1] = access->slave_id;
+	dev->wbuf[2] = access->func;
+	_memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (access->func) {
+	case CMD_GET_AP_CRC:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 4);
+		*((u32 *)access->data) = le32(dev->rbuf, 4);
+		break;
+
+	case CMD_GET_MCU_MOD:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 1);
+		*((u8 *)access->data) = dev->rbuf[0];
+		break;
+
+	case CMD_GET_FW_VER:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 8);
+		_memcpy((u8 *)access->data, dev->rbuf, 8);
+
+		break;
+
+	case CMD_WRITE_ENABLE:
+		dev->wbuf[3] = ((u8 *)access->data)[0];
+		dev->wbuf[4] = ((u8 *)access->data)[1];
+		dev->wbuf[5] = ((u8 *)access->data)[2];
+		dev->wbuf[6] = ((u8 *)access->data)[3];
+		dev->wbuf[7] = ((u8 *)access->data)[4];
+		dev->wbuf[8] = ((u8 *)access->data)[5];
+
+		error = write_then_wait_ack(dev, dev->wbuf, 9, 5000);
+		break;
+
+	default:
+		error = write_then_wait_ack(dev, dev->wbuf, 3, 5000);
+		break;
+	};
+
+	return error;
+}
+
+static int api_protocol_set_ap_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, false, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_bl_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, true, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_idle(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_set_sleep(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_wakeup(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_func_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool get = (data) ? *(bool *)data : true;
+
+	if (!data)
+		return -EINVAL;
+
+	if (get) {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+		if (error < 0)
+			return error;
+
+		dev->func.header = be16(dev->rbuf);
+		dev->func.mode = dev->rbuf[2];
+		TP_MSG(dev->id, "[FW Mode] 0x" PFMT_X8 "\n", dev->func.mode);
+
+		return 0;
+	}
+
+	if (dev->protocol.flag == PTL_V3) {
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+		return 0;
+	} else if (dev->protocol.flag == PTL_V6) {
+		error = write_then_wait_ack(dev, dev->wbuf, 4,
+					    TOUT_68 * TOUT_68_RATIO);
+		if (error < 0)
+			return error;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int api_protocol_c_model_info(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060008)
+		return write_then_read(dev, dev->wbuf, 12, NULL, 0);
+
+	return write_then_read(dev, dev->wbuf, 18, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct tuning_para_settings tuning =
+		*(struct tuning_para_settings *)data;
+	u32 wlen;
+
+	int header;
+	int tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = tuning.func;
+	dev->wbuf[2] = tuning.ctrl;
+	dev->wbuf[3] = tuning.type;
+
+	if (tuning.func == 0x0) {
+		wlen = 4;
+		tout_ms = TOUT_65_READ * TOUT_65_READ_RATIO;
+
+		switch (tuning.ctrl) {
+		case 0x3: case 0x5: case 0x10:
+			wlen += tuning.len;
+			tout_ms = TOUT_65_WRITE * TOUT_65_WRITE_RATIO;
+
+			//TODO: add memory range check
+			_memcpy(dev->wbuf + 4, tuning.buf, tuning.len);
+			break;
+		}
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	switch (tuning.ctrl) {
+	case 0x2: case 0x4:
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = read_ctrl_in(dev, CMD_TUNING_PARA_V6,
+				     dev->rbuf, 1024, 5000);
+		if (error < 0)
+			return error;
+
+		header = (dev->_interface == interface_i2c) ? 5 : 6;
+		header = (dev->quirks & QUIRK_BRIDGE) ? 6 : header;
+
+		_memcpy(tuning.buf, dev->rbuf + header, tuning.len);
+
+		break;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_cdc_init_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	int wlen;
+	struct cdc_settings *set = (struct cdc_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (set->is_freq) {
+		dev->wbuf[1] = 0x0F;
+		dev->wbuf[2] = set->freq.sine.start;
+		dev->wbuf[3] = set->freq.sine.end;
+		dev->wbuf[4] = set->freq.sine.step;
+
+		error = write_then_read(dev, dev->wbuf, 5, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(200);
+	} else {
+		dev->wbuf[1] = set->cmd;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = set->config & 0xFF;
+		wlen = 4;
+
+		if (set->config & 0xFF00) {
+			dev->wbuf[3] = (set->config >> 8) & 0xFF;
+			dev->wbuf[4] = set->config & 0xFF;
+			wlen = 5;
+		}
+
+		error = write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(10);
+	}
+
+	return api_check_busy(dev, 15000, 10);
+}
+
+static int api_protocol_get_cdc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_wait_ack(dev, dev->wbuf, 1, TOUT_F2 * TOUT_F2_RATIO);
+}
+
+static int api_protocol_set_cdc_init_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	struct cdc_settings *set = (struct cdc_settings *)data;
+	int wlen = 1, tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[wlen++] = set->cmd;
+
+	if (set->is_freq) {
+		tout_ms = set->freq.sine.steps * TOUT_F1_FREQ_MC;
+		tout_ms += (set->freq.mc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.sc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump1.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump2.steps * TOUT_F1_FREQ_SC);
+
+		tout_ms += (set->freq.pen.steps * TOUT_F1_FREQ_MC);
+
+		tout_ms *= MAX(set->freq.frame_cnt, 1);
+		tout_ms *= TOUT_F1_FREQ_RATIO;
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (set->is_p2p) {
+		tout_ms = MAX(set->p2p.frame_cnt, 1) *
+			TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (dev->protocol.ver > 0x60008)
+		dev->wbuf[wlen++] = set->config & 0xFF;
+
+	tout_ms = TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+	if (set->is_curve) {
+		tout_ms = set->curve.dump.steps * TOUT_F1_CURVE;
+		tout_ms += (set->curve.charge.steps * TOUT_F1_CURVE);
+		tout_ms *= MAX(set->curve.frame_cnt, 1);
+		tout_ms *= TOUT_F1_CURVE_RATIO;
+	} else if (set->is_short) {
+		tout_ms = TOUT_F1_SHORT * TOUT_F1_SHORT_RATIO;
+	} else if (set->is_open) {
+		tout_ms = MAX(set->open.frame, 1) *
+			TOUT_F1_OPEN * TOUT_F1_OPEN_RATIO;
+	} else if (set->is_key && dev->protocol.ver < 0x6000a) {
+		tout_ms = TOUT_F1_KEY;
+	}
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+}
+
+static int api_protocol_get_cdc_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u32 *cdc_info = (u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	*cdc_info = le32(dev->rbuf, 4);
+
+	return 0;
+}
+
+int api_protocol_set_cmd(void *handle, u8 idx, void *data)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev || idx >= ARRAY_SIZE(protocol_maps))
+		return -EINVAL;
+
+	if (!(dev->protocol.flag & protocol_maps[idx].flag) &&
+	    protocol_maps[idx].flag != PTL_ANY) {
+		TP_ERR(dev->id, "Unexpected cmd: " PFMT_C8 " for 0x" PFMT_X8 " only, now is 0x" PFMT_X8 "\n",
+			protocol_maps[idx].desc, protocol_maps[idx].flag,
+			dev->protocol.flag);
+		return -EINVAL;
+	}
+
+	dev->wbuf[0] = protocol_maps[idx].cmd;
+	error = protocol_maps[idx].func(dev, data);
+	if (error < 0) {
+		TP_ERR(dev->id, "failed to execute cmd: 0x" PFMT_X8 " " PFMT_C8 ", err: %d\n",
+			protocol_maps[idx].cmd, protocol_maps[idx].desc, error);
+		return error;
+	}
+
+	return 0;
+}
+
+int api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	u8 cmd = 0;
+
+	if (!force && dev->fw_mode == mode)
+		return 0;
+
+	_memset(dev->wbuf, 0, sizeof(dev->wbuf));
+
+	if (dev->protocol.flag == PTL_V3) {
+		/* V3 only support suspend and normal mode */
+		if (mode != mode_normal &&
+		    mode != mode_suspend &&
+		    mode != mode_test)
+			return -EPROTONOSUPPORT;
+		dev->wbuf[1] = (mode == mode_normal) ? 0x00 : 0x01;
+		cmd = SET_TEST_MOD;
+	} else if (dev->protocol.flag == PTL_V6) {
+		dev->wbuf[1] = mode;
+		dev->wbuf[2] = (eng) ? 0x01 : 0x00;
+		cmd = SET_MOD_CTRL;
+	}
+
+	error = api_protocol_set_cmd(dev, cmd, NULL);
+	if (error < 0)
+		return error;
+
+	/* switch from test to normal mode should wait 1 sec. delay */
+	if (dev->protocol.flag == PTL_V6 &&
+	    dev->fw_mode == mode_test && mode == mode_normal)
+		dev->cb.delay_ms(1000);
+	else
+		dev->cb.delay_ms(100);
+
+	dev->fw_mode = mode;
+
+	return 0;
+}
+
+u16 api_get_block_crc_by_addr(void *handle, u8 type,
+			      u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = start;
+	dev->wbuf[3] = (start >> 8) & 0xFF;
+	dev->wbuf[4] = (start >> 16) & 0xFF;
+	dev->wbuf[5] = end & 0xFF;
+	dev->wbuf[6] = (end >> 8) & 0xFF;
+	dev->wbuf[7] = (end >> 16) & 0xFF;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+u16 api_get_block_crc_by_num(void *handle, u8 type,
+			     u8 block_num)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = block_num;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+int api_set_data_len(void *handle, u16 data_len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[1] = data_len & 0xFF;
+	dev->wbuf[2] = (data_len >> 8) & 0xFF;
+
+	return api_protocol_set_cmd(dev, SET_DATA_LEN, NULL);
+}
+
+int api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 type;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = start & 0xFF;
+	dev->wbuf[4] = (start >> 8) & 0xFF;
+	dev->wbuf[5] = start >> 16;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (end >> 8) & 0xFF;
+	dev->wbuf[8] = end >> 16;
+
+	type = (in_ap) ? 0x1 : 0x0;
+	type |= (is_slave) ? 0x2 : 0x0;
+
+	return api_protocol_set_cmd(dev, SET_FLASH_EN, &type);
+}
+
+int api_write_data_v6(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V6, &wlen);
+}
+
+int api_access_slave(void *handle, u8 id, u8 func, void *data)
+{
+	struct ilitek_slave_access access;
+
+	access.slave_id = id;
+	access.func = func;
+	access.data = data;
+
+	return api_protocol_set_cmd(handle, ACCESS_SLAVE, &access);
+}
+
+int api_write_enable_v3(void *handle, bool in_ap, bool write_ap,
+			u32 end, u32 checksum)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = (write_ap) ? 0x0 : 0x1;
+	dev->wbuf[4] = (end >> 16) & 0xFF;
+	dev->wbuf[5] = (end >> 8) & 0xFF;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (checksum >> 16) & 0xFF;
+	dev->wbuf[8] = (checksum >> 8) & 0xFF;
+	dev->wbuf[9] = checksum & 0xFF;
+
+	return api_protocol_set_cmd(dev, WRITE_ENABLE, &in_ap);
+}
+
+int api_write_data_v3(void *handle)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V3, NULL);
+}
+
+int api_check_busy(void *handle, int timeout_ms, int delay_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 busy;
+
+	/* retry 2 times at least */
+	int i = MAX(DIV_ROUND_UP(timeout_ms, delay_ms), 2);
+
+	_memset(dev->wbuf, 0, 64);
+
+	while (i--) {
+		api_protocol_set_cmd(dev, GET_SYS_BUSY, &busy);
+		if (busy == ILITEK_TP_SYSTEM_READY)
+			return 0;
+
+		/* delay ms for each check busy */
+		dev->cb.delay_ms(delay_ms);
+
+		/* if caller set no_retry then skip check busy retry */
+		if (dev->setting.no_retry)
+			break;
+	}
+
+	TP_WARN(dev->id, "check busy timeout: %d ms, state: 0x" PFMT_X8 "\n",
+		timeout_ms, busy);
+
+	return -EILIBUSY;
+}
+
+int api_to_bl_mode(void *handle, bool to_bl,
+		   u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+
+	do {
+		if (api_protocol_set_cmd(dev, GET_MCU_MOD, NULL) < 0)
+			continue;
+
+		if (dev->ic[0].mode == target_mode)
+			goto success_change_mode;
+
+		if (to_bl) {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, true, false,
+						     0, 0) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_BL_MODE, NULL);
+		} else {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, false, false,
+						     start, end) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_AP_MODE, NULL);
+		}
+
+		switch (dev->_interface) {
+		case interface_hid_over_i2c:
+		case interface_i2c:
+			dev->cb.delay_ms(1000 + 100 * cnt);
+			break;
+		case interface_usb:
+			re_enum_helper(dev, enum_ap_bl);
+			break;
+		}
+	} while (!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+
+	/* update fw ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+
+	/* update protocol ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	return 0;
+}
+
+int api_set_idle(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = (enable) ? 1 : 0;
+	return api_protocol_set_cmd(dev, SET_MCU_IDLE, NULL);
+}
+
+int api_set_func_mode(void *handle, u8 mode)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	bool get = false;
+
+	_memset(dev->wbuf, 0, 64);
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->wbuf[1] = 0x55;
+		dev->wbuf[2] = 0xAA;
+		break;
+	case PTL_V6:
+		dev->wbuf[1] = 0x5A;
+		dev->wbuf[2] = 0xA5;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol: %x, flag: " PFMT_U8 "",
+			dev->protocol.ver, dev->protocol.flag);
+		return -EINVAL;
+	}
+	dev->wbuf[3] = mode;
+
+	if (dev->protocol.ver < 0x30400) {
+		TP_ERR(dev->id, "protocol: 0x%x not support\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	error = api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+	if (error < 0)
+		return error;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		return error;
+
+	return (dev->func.mode == mode) ? 0 : -EFAULT;
+}
+
+int api_get_func_mode(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool get = true;
+
+	return api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+}
+
+int api_erase_data_v3(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+
+	TP_INFO(dev->id, "erase data flash for " PFMT_C8 ", mode: " PFMT_X8 "\n",
+		dev->mcu_info.ic_name, dev->ic[0].mode);
+
+	if (is_231x(dev)) {
+		/* V3 231x only support erase data flash in AP mode */
+		if (dev->ic[0].mode != AP_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, true, false, 0, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(100);
+
+		dev->wbuf[1] = 0x02;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V3, NULL);
+		if (error < 0)
+			return error;
+
+		switch (dev->_interface) {
+		case interface_usb:
+			return re_enum_helper(dev, enum_ap_bl);
+		default:
+			dev->cb.delay_ms(1500);
+			break;
+		}
+	} else {
+		/* V3 251x only support erase data flash in BL mode */
+		if (dev->ic[0].mode != BL_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, false, false, 0xf01f, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		_memset(dev->wbuf + 1, 0xFF, 32);
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(500);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v3(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 addr, end = start + len, copied;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += 32, copied += 32) {
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, READ_FLASH, NULL);
+		if (error < 0)
+			return error;
+
+		_memcpy(buf + copied, dev->rbuf, 32);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v6(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 code, addr, end = start + len, copied;
+	u16 data_len;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += data_len, copied += data_len) {
+		if (end - addr > 1024)
+			data_len = 2048;
+		else if (end - addr > 256)
+			data_len = 1024;
+		else if (end - addr > 64)
+			data_len = 256;
+		else
+			data_len = 64;
+
+		error = api_set_data_len(dev, data_len);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x1; code = 1 << 16;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x0; code = data_len & 0xFFFF;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		if (dev->_interface == interface_hid_over_i2c)
+			_memcpy(buf + copied, dev->rbuf + 5, data_len);
+		else
+			_memcpy(buf + copied, dev->rbuf, data_len);
+	}
+
+	return 0;
+}
+
+int api_read_flash(void *handle, u8 *buf,
+		   u32 start, u32 len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (dev->protocol.flag == PTL_V3)
+		return api_read_flash_v3(dev, buf, start, len);
+
+	return api_read_flash_v6(dev, buf, start, len);
+}
+
+int _api_read_mp_result(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)(&dev->mp);
+	u32 addr = (is_29xx(dev)) ? 0x2e000 : 0x3e000;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return api_read_flash_v6(dev, buf, addr, 1000);
+
+	/* 1000 bytes data/ 2 bytes crc/ 1 bytes checksum */
+	tuning.len = 1003;
+	tuning.buf = buf;
+
+	tuning.func = 0x0; tuning.ctrl = 0x4; tuning.type = 0x10;
+	error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+	if (error < 0)
+		return error;
+
+	tuning.func = 0x1; tuning.ctrl = 0x4; tuning.type = 0x10;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_read_mp_result(void *handle)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_read_mp_result(handle);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+int _api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)mp;
+	u16 crc;
+
+	crc = get_crc(0, 1000, buf, sizeof(struct mp_station));
+	mp->crc = crc;
+
+	tuning.func = 0x0;
+	tuning.ctrl = 0x5;
+	tuning.type = 0x10;
+	tuning.buf = buf;
+	tuning.len = 1002;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_write_mp_result(handle, mp);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+static void mp_result(const char *item, u8 data)
+{
+	if (data == 1)
+		TP_INFO(NULL, PFMT_C8 " Result: PASS\n", item);
+	else if (data == 2)
+		TP_INFO(NULL, PFMT_C8 " Result: NG\n", item);
+	else
+		TP_INFO(NULL, PFMT_C8 " Result: N/A\n", item);
+}
+
+void api_decode_mp_result(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct mp_station *mp = &dev->mp;
+	int i, j;
+	struct mp_station_old *_mp;
+	char module_name[32];
+	char bar_code[256];
+
+	TP_DBG_ARR(NULL, "[MpResult Raw]:", TYPE_U8, 1002, (u8 *)mp);
+
+	TP_MSG(NULL, "mp result ver: 0x%08x\n", mp->info.mp_result_ver);
+
+	/* For Old Mp Result Format */
+	if (mp->info.mp_result_ver == 0xFFFFFFFF ||
+	    mp->info.mp_result_ver < 0x01000000) {
+		_mp = (struct mp_station_old *)mp;
+
+		for (i = 0; i < 9; i++) {
+			TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+			if (!_mp->station[i].week ||
+			    _mp->station[i].week == 0xFF) {
+				TP_INFO(NULL, "No Test\n");
+				continue;
+			}
+
+			TP_INFO(NULL, "Week of year : " PFMT_U8 "\n",
+				_mp->station[i].week);
+			TP_INFO(NULL, "Year : 20%02u\n",
+				_mp->station[i].year);
+			TP_INFO_ARR(NULL, "Firmware Version : ",
+				   TYPE_U8, 8, _mp->station[i].fw_ver);
+
+			_memset(module_name, 0, sizeof(module_name));
+			_memcpy(module_name, _mp->station[i].module,
+				sizeof(_mp->station[i].module));
+			module_name[sizeof(_mp->station[i].module) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].module);
+			     j++) {
+				if ((u8)module_name[j] == 0xFF) {
+					module_name[j] = '\0';
+					break;
+				}
+			}
+			TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n",
+				module_name);
+
+			mp_result("Short Test", _mp->station[i].short_test);
+			mp_result("Open Test", _mp->station[i].open_test);
+			mp_result("Self Cap Test", _mp->station[i].self_test);
+			mp_result("Uniformity Test",
+				_mp->station[i].uniform_test);
+			mp_result("DAC Test", _mp->station[i].dac_test);
+			mp_result("Key Raw Test", _mp->station[i].key_test);;
+			mp_result("Painting Test", _mp->station[i].paint_test);
+			mp_result("MicroOpen Test", _mp->station[i].mopen_test);
+			mp_result("GPIO Test", _mp->station[i].gpio_test);
+			mp_result("Final", _mp->station[i].final_result);
+
+			_memset(bar_code, 0, sizeof(bar_code));
+			_memcpy(bar_code, _mp->station[i].bar_code,
+				sizeof(_mp->station[i].bar_code));
+			bar_code[sizeof(_mp->station[i].bar_code) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].bar_code);
+			     j++) {
+				if ((u8)bar_code[j] == 0xFF) {
+					bar_code[j] = '\0';
+					break;
+				}
+			}
+
+			TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+			TP_INFO(NULL, "Customer ID : 0x%04x\n",
+				_mp->station[i].custom_id);
+			TP_INFO(NULL, "FWID : 0x%04x\n",
+				_mp->station[i].fwid);
+		}
+
+		return;
+	}
+
+	TP_INFO(NULL, "[MP Result]\n");
+	TP_INFO(NULL, "***Customer Info***\n");
+
+	TP_INFO(NULL, "MPResult Version:%02X.%02X.%02X.%02X\n",
+		(mp->info.mp_result_ver >> 24) & 0xFF,
+		(mp->info.mp_result_ver >> 16) & 0xFF,
+		(mp->info.mp_result_ver >> 8) & 0xFF,
+		mp->info.mp_result_ver & 0xFF);
+	TP_INFO(NULL, "Customer ID : 0x%04X\n", mp->info.customer_id);
+	TP_INFO(NULL, "FW ID : 0x%04X\n", mp->info.fwid);
+
+	for (i = 0; i < (int)ARRAY_SIZE(mp->station); i++) {
+		TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+		if (!mp->station[i].week || mp->station[i].week == 0xFF) {
+			TP_INFO(NULL, "No Test\n");
+			continue;
+		}
+
+		TP_INFO(NULL, "Week of year : %d\n", mp->station[i].week);
+		TP_INFO(NULL, "Year : 20%02d\n", mp->station[i].year);
+		TP_INFO(NULL, "Firmware Version : 0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+			mp->station[i].fw_ver[7], mp->station[i].fw_ver[6],
+			mp->station[i].fw_ver[5], mp->station[i].fw_ver[4],
+			mp->station[i].fw_ver[3], mp->station[i].fw_ver[2],
+			mp->station[i].fw_ver[1], mp->station[i].fw_ver[0]);
+
+		_memset(module_name, 0, sizeof(module_name));
+		_memcpy(module_name, mp->station[i].module,
+			sizeof(mp->station[i].module));
+		module_name[sizeof(mp->station[i].module) + 1] = '\0';
+		for (j = 0; j < (int)sizeof(mp->station[i].module); j++) {
+			if ((u8)module_name[j] == 0xFF) {
+				module_name[j] = '\0';
+				break;
+			}
+		}
+		TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n", module_name);
+
+		mp_result("Short Test", mp->station[i].short_test);
+		mp_result("Open Test", mp->station[i].open_test);
+		mp_result("Self Cap Test", mp->station[i].self_test);
+		mp_result("Uniformity Test", mp->station[i].uniform_test);
+		mp_result("DAC Test", mp->station[i].dac_test);
+		mp_result("Key Raw Test", mp->station[i].key_test);;
+		mp_result("Painting Test", mp->station[i].paint_test);
+		mp_result("MicroOpen Test", mp->station[i].mopen_test);
+		mp_result("GPIO Test", mp->station[i].gpio_test);
+		mp_result("Final", mp->station[i].final_result);
+
+		TP_INFO(NULL, "Tool Version : " PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "\n",
+			mp->station[i].tool_ver[7], mp->station[i].tool_ver[6],
+			mp->station[i].tool_ver[5], mp->station[i].tool_ver[4],
+			mp->station[i].tool_ver[3], mp->station[i].tool_ver[2],
+			mp->station[i].tool_ver[1], mp->station[i].tool_ver[0]);
+
+		_memset(bar_code, 0, sizeof(bar_code));
+		_memcpy(bar_code, mp->station[i].bar_code,
+			sizeof(mp->station[i].bar_code));
+		bar_code[sizeof(mp->station[i].bar_code) + 1] = '\0';
+		TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+		TP_INFO(NULL, "Customer ID : 0x%04x\n",
+			mp->station[i].custom_id);
+		TP_INFO(NULL, "FWID : 0x%04x\n", mp->station[i].fwid);
+	}
+}
+
+int api_read_tuning(void *handle, u8 *buf, int rlen)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	int got, need;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return -EINVAL;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	for (got = 0, need = rlen; need > 0; got += 1024, need -= 1024) {
+		tuning.len = MIN(need, 1024);
+		tuning.buf = buf + got;
+
+		tuning.func = 0x0; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+
+		tuning.func = 0x1; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+	}
+
+	TP_DBG_ARR(dev->id, "[tuning]:", TYPE_U8, rlen, buf);
+
+	return api_set_ctrl_mode(dev, mode_normal, false, true);
+}
+
+int api_write_data_m2v(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_M2V, &wlen);
+}
+
+int api_to_bl_mode_m2v(void *handle, bool to_bl)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+	u8 mode;
+
+	if (dev->_interface != interface_usb)
+		return -EINVAL;
+
+	do {
+		dev->cb.delay_ms(100);//Reed Add : 20230927
+
+		if (api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) < 0)
+			continue;
+
+		if (mode == target_mode)
+			goto success_change_mode;
+
+		dev->cb.delay_ms(300);//Reed Add : 20230927
+
+		if (to_bl && api_access_slave(dev, 0x80, CMD_SET_BL_MODE,
+					      NULL) < 0)
+			continue;
+		else if (!to_bl && api_access_slave(dev, 0x80, CMD_SET_AP_MODE,
+						    NULL) < 0)
+			continue;
+
+		do {
+			dev->cb.delay_ms(100);//Reed Add : 20230927
+			if (!api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) &&
+			    mode == target_mode)
+				goto success_change_mode;
+			dev->cb.delay_ms(5000);
+		} while (!dev->setting.no_retry && cnt++ < retry);
+		break;
+	} while (!!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "M2V current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "M2V current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		mode, (to_bl) ? "BL" : "AP");
+
+	return 0;
+}
+
+int api_get_ic_crc(void *handle, u8 final_fw_mode)
+{
+	int error;
+	u8 i;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		break;
+	case PTL_V6:
+		for (i = 1; i < dev->tp_info.block_num; i++) {
+			dev->ic[0].crc[i] = api_get_block_crc_by_num(dev,
+				CRC_CALCULATE, i);
+		}
+		break;
+	default:
+		error = -EINVAL; break;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, final_fw_mode, false, true);
+
+	return error;
+}
+
+void api_print_ts_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 i;
+
+	TP_INFO(dev->id, "[Protocol Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X\n",
+		(dev->protocol.ver >> 16) & 0xff,
+		(dev->protocol.ver >> 8) & 0xff,
+		dev->protocol.ver & 0xff);
+
+	TP_INFO(dev->id, "[Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[0], dev->fw_ver[1],
+		dev->fw_ver[2], dev->fw_ver[3]);
+	TP_INFO(dev->id, "[Customer Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[4], dev->fw_ver[5],
+		dev->fw_ver[6], dev->fw_ver[7]);
+
+	TP_INFO(dev->id, "[Kernel Version]\n");
+	if (dev->protocol.flag == PTL_V6 &&
+	    support_mcu_info(dev)) {
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X (" PFMT_C8 ")\n",
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1],
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[4], dev->mcu_info.ic_name);
+	} else {
+		TP_INFO(dev->id, "0x%c%c.0x%c%c\n",
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1]);
+	}
+
+	TP_INFO(dev->id, "[Current Mode]\n");
+	TP_INFO(dev->id, "Master : " PFMT_C8 "\n", dev->ic[0].mode_str);
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	for (i = 1; i < dev->tp_info.ic_num; i++)
+		TP_INFO(dev->id, "Slave : " PFMT_C8 "\n", dev->ic[i].mode_str);
+
+	TP_INFO(dev->id, "[Module Name]\n");
+	TP_INFO(dev->id, PFMT_C8 "\n", dev->mcu_info.module_name);
+
+	TP_INFO(dev->id, "[Core Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->core_ver[0], dev->core_ver[1],
+		dev->core_ver[2], dev->core_ver[3]);
+
+	if (is_231x(dev)) {
+		switch (dev->core_ver[1]) {
+		case 0x00:
+			TP_INFO(dev->id, "(Dual Interface)\n"); break;
+		case 0x01:
+			TP_INFO(dev->id, "(USB Interface)\n"); break;
+		case 0x02:
+			TP_INFO(dev->id, "(I2C Interface)\n"); break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[Tuning Version]\n");
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+			dev->tuning_ver[0], dev->tuning_ver[1],
+			dev->tuning_ver[2], dev->tuning_ver[3]);
+	}
+
+	TP_INFO(dev->id, "[Panel Information]\n");
+	TP_INFO(dev->id, "X resolution : " PFMT_U16 "\n", dev->tp_info.x_resolution);
+	TP_INFO(dev->id, "Y resolution : " PFMT_U16 "\n", dev->tp_info.y_resolution);
+	TP_INFO(dev->id, "AA X Channel : " PFMT_U16 "\n", dev->tp_info.x_ch);
+	TP_INFO(dev->id, "AA Y Channel : " PFMT_U16 "\n", dev->tp_info.y_ch);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Fingers\n", dev->tp_info.max_fingers);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Touch Keys\n", dev->tp_info.key_num);
+
+	if (dev->tp_info.key_num) {
+		switch (dev->key.info.mode) {
+		case key_disable:
+			TP_INFO(dev->id, "Key Mode : NO_Key\n");
+			break;
+		case key_hw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_1\n");
+			break;
+		case key_hsw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_2\n");
+			break;
+		case key_vitual:
+			TP_INFO(dev->id, "Key Mode : Virtual_Key\n");
+			break;
+		case key_fw_disable:
+			TP_INFO(dev->id, "Key Mode : FW_disable\n");
+			break;
+		default:
+			TP_INFO(dev->id, "Key Mode : Unknown(0x" PFMT_X8 ")\n",
+				dev->key.info.mode);
+			break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "Support Pen Type : " PFMT_C8 "\n",
+			dev->pen_mode);
+		TP_INFO(dev->id, "Chip Counts : " PFMT_U8 "\n", dev->tp_info.ic_num);
+		TP_INFO(dev->id, "Report Format : " PFMT_U8 "\n", dev->tp_info.format);
+		TP_INFO(dev->id, "Block Number : " PFMT_U8 "\n", dev->tp_info.block_num);
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[FW CRC]\n");
+		TP_INFO(dev->id, "Master : 0x%04X\n", dev->ic[0].crc[0]);
+		for (i = 1; i < dev->tp_info.ic_num; i++)
+			TP_INFO(dev->id, "Slave : 0x%04X\n", dev->ic[i].crc[0]);
+	} else {
+		TP_INFO(dev->id, "[Check Code]\n");
+		TP_INFO(dev->id, "AP : 0x%08X\n", dev->ic[0].crc[0]);
+		TP_INFO(dev->id, "DATA : 0x%08X\n", dev->ic[0].crc[1]);
+	}
+}
+
+void api_read_then_print_m2v_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 m2v_mode;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	api_set_ctrl_mode(dev, mode_suspend, false, true);
+	api_access_slave(dev, 0x80, CMD_GET_FW_VER, m2v_fw_ver);
+	api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &m2v_mode);
+	api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &m2v_checksum);
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	TP_INFO(dev->id, "[M2V Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+		m2v_fw_ver[0], m2v_fw_ver[1], m2v_fw_ver[2], m2v_fw_ver[3],
+		m2v_fw_ver[4], m2v_fw_ver[5], m2v_fw_ver[6], m2v_fw_ver[7]);
+
+	TP_INFO(dev->id, "[M2V Current Mode]\n");
+	TP_INFO(dev->id, "Mode : " PFMT_C8 " Mode\n",
+		(m2v_mode == AP_MODE) ? "AP" : "BL");
+
+	TP_INFO(dev->id, "[M2V FW CheckSum]\n");
+	TP_INFO(dev->id, "FW CheckSum : 0x%08X\n", m2v_checksum);
+}
+
+int api_update_ts_info(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	/*
+	 * previous set suspend mode is in V6 format.
+	 * set suspend mode again if device protocol is checked as V3.
+	 */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_PRODUCT_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_FWID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_SENSOR_ID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_HID_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	/* BL mode should perform FW upgrade afterward */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	/* V3 need to get DF CRC */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_TUNING_VER, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_CORE_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_SCRN_RES, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->tp_info.ic_num > 1) {
+		error = api_protocol_set_cmd(dev, GET_AP_CRC,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_MCU_MOD,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	return error;
+}
+
+void __ilitek_get_info(void *handle, struct ilitek_common_info *info)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!info || !dev)
+		return;
+
+	_memcpy(info, &dev->quirks, sizeof(struct ilitek_common_info));
+}
+
+void ilitek_dev_set_quirks(void *handle, u32 quirks)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	dev->quirks = quirks;
+}
+
+void ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->sys, sys, sizeof(struct ilitek_sys_info));
+}
+
+void ilitek_dev_setting(void *handle, struct ilitek_ts_settings *setting)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->setting, setting, sizeof(struct ilitek_ts_settings));
+
+	if (dev->setting.default_format_enabled) {
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+	}
+
+	TP_MSG(dev->id, "no-retry: %d, no-INT-ack: %d\n",
+		dev->setting.no_retry, dev->setting.no_INT_ack);
+}
+
+void ilitek_dev_bind_callback(void *handle, struct ilitek_ts_callback *callback)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (callback) {
+		_memcpy(&dev->cb, callback, sizeof(struct ilitek_ts_callback));
+		if (dev->cb.msg)
+			g_msg = dev->cb.msg;
+	}
+}
+
+void *ilitek_dev_init(u8 _interface, const char *id,
+		      bool need_update_ts_info,
+		      struct ilitek_ts_callback *callback, void *_private)
+{
+	struct ilitek_ts_device *dev;
+
+	dev = (struct ilitek_ts_device *)MALLOC(sizeof(*dev));
+	if (!dev)
+		return NULL;
+
+	TP_MSG(NULL, "commonflow code version: 0x%x\n",
+		COMMONFLOW_CODE_VERSION);
+
+	TP_DBG(NULL, "sizeof(ilitek_ts_device): %u\n",
+		(unsigned int)sizeof(struct ilitek_ts_device));
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(dev, 0, sizeof(*dev));
+
+	_strcpy(dev->id, id, sizeof(dev->id));
+	ilitek_dev_bind_callback(dev, callback);
+
+	dev->_interface = _interface;
+	dev->_private = _private;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	dev->fw_mode = mode_unknown;
+
+	if (need_update_ts_info && api_update_ts_info(dev) < 0) {
+		ilitek_dev_exit(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+void ilitek_dev_exit(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/*
+	 * LIBUSB would kill /dev/hidraw* and make system stop handling
+	 * device's usb. sw reset is required to re-enum usb then /dev/hidraw*
+	 * would be created and system would start to handle touch event.
+	 */
+	if (dev->quirks & QUIRK_LIBUSB || dev->setting.sw_reset_at_last)
+		api_protocol_set_cmd(dev, SET_SW_RST, NULL);
+
+	if (dev)
+		FREE(dev);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.h b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
new file mode 100644
index 000000000000..ad9c5c18cd36
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
@@ -0,0 +1,916 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_PROTOCOL_H__
+#define __ILITEK_PROTOCOL_H__
+
+#include "ilitek_def.h"
+
+/* quirks definition */
+#define QUIRK_WAIT_ACK_DELAY		0x1
+#define QUIRK_BRIDGE			0x2
+#define QUIRK_DAEMON_I2C		0x4
+#define QUIRK_WIFI_ITS_I2C		0x8
+#define QUIRK_LIBUSB			0x10
+
+#define START_ADDR_LEGO			0x3000
+#define START_ADDR_29XX			0x4000
+#define END_ADDR_LEGO			0x40000
+
+#define MM_ADDR_LEGO			0x3020
+#define MM_ADDR_29XX			0x4020
+#define MM_ADDR_2501X			0x4038
+
+#define DF_START_ADDR_LEGO		0x3C000
+#define DF_START_ADDR_29XX		0x2C000
+
+#define ILITEK_TP_SYSTEM_READY		0x50
+
+#define CRC_CALCULATE			0
+#define CRC_GET				1
+
+#define ILTIEK_MAX_BLOCK_NUM		20
+
+#define PTL_ANY				0x00
+#define PTL_V3				0x03
+#define PTL_V6				0x06
+
+#define BL_PROTOCOL_V1_8		0x10800
+#define BL_PROTOCOL_V1_7		0x10700
+#define BL_PROTOCOL_V1_6		0x10600
+
+#define TOUT_CF_BLOCK_0			2500
+#define TOUT_CF_BLOCK_N			500
+#define TOUT_F1_SHORT			1600
+#define TOUT_F1_OPEN			12
+#define TOUT_F1_FREQ_MC			2
+#define TOUT_F1_FREQ_SC			1
+#define TOUT_F1_CURVE			13
+#define TOUT_F1_KEY			400
+#define TOUT_F1_OTHER			27
+#define TOUT_F2				7
+#define TOUT_CD				27
+#define TOUT_C3				100
+#define TOUT_65_WRITE			135
+#define TOUT_65_READ			3
+#define TOUT_68				24
+#define TOUT_CC_SLAVE			16000
+
+#define TOUT_F1_SHORT_RATIO		2
+#define TOUT_F1_OPEN_RATIO		3
+#define TOUT_F1_FREQ_RATIO		3
+#define TOUT_F1_CURVE_RATIO		3
+#define TOUT_F1_OTHER_RATIO		3
+#define TOUT_F2_RATIO			3
+#define TOUT_CD_RATIO			3
+#define TOUT_C3_RATIO			3
+#define TOUT_65_WRITE_RATIO		3
+#define TOUT_65_READ_RATIO		3
+#define TOUT_68_RATIO			3
+#define TOUT_CC_SLAVE_RATIO		2
+
+#define AP_MODE		0x5A
+#define BL_MODE		0x55
+
+#define STYLUS_MODES			\
+	X(STYLUS_WGP,	0x1,	"WGP")	\
+	X(STYLUS_USI,	0x2,	"USI")	\
+	X(STYLUS_MPP,	0x4,	"MPP")
+
+#define ILITEK_CMD_MAP							\
+	X(0x20, PTL_ANY, GET_TP_INFO, api_protocol_get_tp_info)		\
+	X(0x21, PTL_ANY, GET_SCRN_RES, api_protocol_get_scrn_res)	\
+	X(0x22, PTL_ANY, GET_KEY_INFO, api_protocol_get_key_info)	\
+	X(0x30, PTL_ANY, SET_IC_SLEEP, api_protocol_set_sleep)		\
+	X(0x31, PTL_ANY, SET_IC_WAKE, api_protocol_set_wakeup)		\
+	X(0x34, PTL_ANY, SET_MCU_IDLE, api_protocol_set_idle)		\
+	X(0x40, PTL_ANY, GET_FW_VER, api_protocol_get_fw_ver)		\
+	X(0x42, PTL_ANY, GET_PTL_VER, api_protocol_get_ptl_ver)		\
+	X(0x43, PTL_ANY, GET_CORE_VER, api_protocol_get_core_ver)	\
+	X(0x60, PTL_ANY, SET_SW_RST, api_protocol_set_sw_reset)		\
+	X(0x61, PTL_ANY, GET_MCU_VER, api_protocol_get_mcu_ver)		\
+	X(0x68, PTL_ANY, SET_FUNC_MOD, api_protocol_set_func_mode)	\
+	X(0x80, PTL_ANY, GET_SYS_BUSY, api_protocol_get_sys_busy)	\
+	X(0xC0, PTL_ANY, GET_MCU_MOD, api_protocol_get_mcu_mode)	\
+	X(0xC1, PTL_ANY, SET_AP_MODE, api_protocol_set_ap_mode)		\
+	X(0xC2, PTL_ANY, SET_BL_MODE, api_protocol_set_bl_mode)		\
+	X(0xC5, PTL_ANY, READ_FLASH, api_protocol_read_flash)		\
+	X(0xC7, PTL_ANY, GET_AP_CRC, api_protocol_get_ap_crc)		\
+	X(0xC8, PTL_ANY, SET_ADDR, api_protocol_set_flash_addr)		\
+									\
+	/* v3 only cmds */						\
+	X(0x25, PTL_V3, GET_CDC_INFO_V3, api_protocol_get_cdc_info_v3)	\
+	X(0x63, PTL_V3, TUNING_PARA_V3, api_protocol_tuning_para_v3)	\
+	X(0xC3, PTL_V3, WRITE_DATA_V3, api_protocol_write_data_v3)	\
+	X(0xC4, PTL_V3, WRITE_ENABLE, api_protocol_write_enable)	\
+	X(0xCA, PTL_V3, GET_DF_CRC, api_protocol_get_df_crc)		\
+	X(0xF2, PTL_V3, SET_TEST_MOD, api_protocol_set_mode_v3)		\
+	X(0xF3, PTL_V3, INIT_CDC_V3, api_protocol_set_cdc_init_v3)	\
+									\
+	/* v6 only cmds */						\
+	X(0x24, PTL_V6, POWER_STATUS, api_protocol_power_status)	\
+	X(0x27, PTL_V6, GET_SENSOR_ID, api_protocol_get_sensor_id)	\
+	X(0x44, PTL_V6, GET_TUNING_VER, api_protocol_get_tuning_ver)	\
+	X(0x45, PTL_V6, GET_PRODUCT_INFO, api_protocol_get_product_info)\
+	X(0x46, PTL_V6, GET_FWID, api_protocol_get_fwid)		\
+	X(0x47, PTL_V6, GET_CRYPTO_INFO, api_protocol_get_crypto_info)	\
+	X(0x48, PTL_V6, GET_HID_INFO, api_protocol_get_hid_info)	\
+	X(0x62, PTL_V6, GET_MCU_INFO, api_protocol_get_mcu_info)	\
+	X(0x65, PTL_V6, TUNING_PARA_V6, api_protocol_tuning_para_v6)	\
+	X(0x69, PTL_V6, SET_FS_INFO, api_protocol_set_fs_info)		\
+	X(0x6A, PTL_V6, SET_SHORT_INFO, api_protocol_set_short_info)	\
+	X(0x6B, PTL_V6, C_MODEL_INFO, api_protocol_c_model_info)	\
+	X(0x6C, PTL_V6, SET_P2P_INFO, api_protocol_set_p2p_info)	\
+	X(0x6D, PTL_V6, SET_OPEN_INFO, api_protocol_set_open_info)	\
+	X(0x6E, PTL_V6, SET_CHARGE_INFO, api_protocol_set_charge_info)	\
+	X(0x6F, PTL_V6, SET_PEN_FS_INFO, api_protocol_set_pen_fs_info)	\
+	X(0xB0, PTL_V6, WRITE_DATA_M2V, api_protocol_write_data_m2v)	\
+	X(0xC3, PTL_V6, WRITE_DATA_V6, api_protocol_write_data_v6)	\
+	X(0xC9, PTL_V6, SET_DATA_LEN, api_protocol_set_data_len)	\
+	X(0xCB, PTL_V6, ACCESS_SLAVE, api_protocol_access_slave)	\
+	X(0xCC, PTL_V6, SET_FLASH_EN, api_protocol_set_flash_enable)	\
+	X(0xCD, PTL_V6, GET_BLK_CRC_ADDR, api_protocol_get_crc_by_addr)	\
+	X(0xCF, PTL_V6, GET_BLK_CRC_NUM, api_protocol_get_crc_by_num)	\
+	X(0xF0, PTL_V6, SET_MOD_CTRL, api_protocol_set_mode_v6)		\
+	X(0xF1, PTL_V6, INIT_CDC_V6, api_protocol_set_cdc_init_v6)	\
+	X(0xF2, PTL_V6, GET_CDC_V6, api_protocol_get_cdc_v6)
+
+
+#define X(_cmd, _protocol, _cmd_id, _api)	_cmd_id,
+enum ilitek_cmd_ids {
+	ILITEK_CMD_MAP
+	/* ALWAYS keep at the end */
+	MAX_CMD_CNT
+};
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api)	CMD_##_cmd_id = _cmd,
+enum ilitek_cmds { ILITEK_CMD_MAP };
+#undef X
+
+enum ilitek_hw_interfaces {
+	interface_i2c = 0,
+	interface_hid_over_i2c,
+	interface_usb,
+};
+
+enum ilitek_fw_modes {
+	mode_unknown = -1,
+	mode_normal = 0,
+	mode_test,
+	mode_debug,
+	mode_suspend,
+};
+
+enum ilitek_key_modes {
+	key_disable = 0,
+	key_hw = 1,
+	key_hsw = 2,
+	key_vitual = 3,
+	key_fw_disable = 0xff,
+};
+
+#define ILITEK_TOUCH_REPORT_FORMAT			\
+	X(touch_fmt_0x0,	0x0, 5, 10)	\
+	X(touch_fmt_0x1,	0x1, 6, 10)	\
+	X(touch_fmt_0x2,	0x2, 10, 5)	\
+	X(touch_fmt_0x3,	0x3, 10, 5)	\
+	X(touch_fmt_0x4,	0x4, 10, 5)	\
+	X(touch_fmt_0x10,	0x10, 10, 6)	\
+	X(touch_fmt_0x11,	0x11, 5, 10)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_touch_fmts {
+	ILITEK_TOUCH_REPORT_FORMAT
+	touch_fmt_max = 0x100,
+};
+#undef X
+
+#define ILITEK_PEN_REPORT_FORMAT		\
+	X(pen_fmt_0x0, 0x0, 12, 1)	\
+	X(pen_fmt_0x1, 0x1, 18, 1)	\
+	X(pen_fmt_0x2, 0x2, 22, 1)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_pen_fmts {
+	ILITEK_PEN_REPORT_FORMAT
+	pen_fmt_max = 0x100,
+};
+#undef X
+
+struct ilitek_slave_access {
+	u8 slave_id;
+	u8 func;
+	void *data;
+};
+
+struct tuning_para_settings {
+	u8 func;
+	u8 ctrl;
+	u8 type;
+
+	u8 *buf;
+	u32 len;
+};
+
+struct reports {
+	bool touch_need_update;
+	bool pen_need_update;
+
+	u8 touch[64];
+	u8 pen[64];
+};
+
+struct grid_data {
+	bool need_update;
+	unsigned int X, Y;
+
+	s32 *data;
+};
+
+struct grids {
+	struct grid_data mc;
+	struct grid_data sc_x;
+	struct grid_data sc_y;
+	struct grid_data pen_x;
+	struct grid_data pen_y;
+
+	struct grid_data key_mc;
+	struct grid_data key_x;
+	struct grid_data key_y;
+
+	struct grid_data self;
+
+	/* touch/pen debug message along with frame update */
+	struct reports dmsg;
+};
+
+enum ilitek_enum_type {
+	enum_ap_bl = 0,
+	enum_sw_reset,
+};
+
+typedef void (*update_grid_t)(u32, u32, struct grids *, void *);
+typedef void (*update_report_rate_t)(unsigned int);
+
+typedef int (*write_then_read_t)(u8 *, int, u8 *, int, void *);
+typedef int (*read_ctrl_in_t)(u8 *, int, unsigned int, void *);
+typedef int (*read_interrupt_in_t)(u8 *, int, unsigned int, void *);
+typedef void (*init_ack_t)(unsigned int, void *);
+typedef int (*wait_ack_t)(u8, unsigned int, void *);
+typedef int (*hw_reset_t)(unsigned int, void *);
+typedef int (*re_enum_t)(u8, void *);
+typedef void (*delay_ms_t)(unsigned int);
+
+
+typedef int (*write_then_read_direct_t)(u8 *, int, u8 *, int, void *);
+typedef void (*mode_switch_notify_t)(bool, bool, void *);
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_fmt {
+	u8 id : 6;
+	u8 status : 1;
+	u8 reserve : 1;
+	u16 x;
+	u16 y;
+	u8 pressure;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ touch_iwb_fmt {
+	u8 status : 3;
+	u8 reserve : 5;
+	u8 id : 6;
+	u8 reserve_1 : 2;
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ pen_fmt {
+	union __PACKED__ {
+		u8 modes;
+		struct __PACKED__ {
+			u8 tip_sw : 1;
+			u8 barrel_sw : 1;
+			u8 eraser : 1;
+			u8 invert : 1;
+			u8 in_range : 1;
+			u8 reserve : 3;
+		};
+	};
+	u16 x;
+	u16 y;
+	u16 pressure;
+	s16 x_tilt;
+	s16 y_tilt;
+
+	u8 battery;
+
+	union __PACKED__ {
+		/* usi v1.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 width;
+			u8 style;
+		} usi_1;
+
+		/* usi v2.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 color_24[3];
+			u8 no_color;
+			u8 width;
+			u8 style;
+		} usi_2;
+	};
+};
+
+struct __PACKED__ ilitek_report_fmt_info {
+	u32 touch_size;
+	u32 touch_max_cnt;
+
+	u32 pen_size;
+	u32 pen_max_cnt;
+};
+
+struct __PACKED__ ilitek_screen_info {
+	u16 x_min;
+	u16 y_min;
+	u16 x_max;
+	u16 y_max;
+	u16 pressure_min;
+	u16 pressure_max;
+	s16 x_tilt_min;
+	s16 x_tilt_max;
+	s16 y_tilt_min;
+	s16 y_tilt_max;
+	u16 pen_x_min;
+	u16 pen_y_min;
+	u16 pen_x_max;
+	u16 pen_y_max;
+};
+
+struct __PACKED__ ilitek_tp_info_v6 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u16 x_ch;
+	u16 y_ch;
+	u8 max_fingers;
+	u8 key_num;
+	u8 ic_num;
+	u8 support_modes;
+	u8 format;
+	u8 die_num;
+	u8 block_num;
+	u8 pen_modes;
+	u8 pen_format;
+	u16 pen_x_resolution;
+	u16 pen_y_resolution;
+};
+
+struct __PACKED__ ilitek_tp_info_v3 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u8 x_ch;
+	u8 y_ch;
+	u8 max_fingers;
+	u8 reserve;
+	u8 key_num;
+	u8 reserve_1;
+	u8 touch_start_y;
+	u8 touch_end_y;
+	u8 touch_start_x;
+	u8 touch_end_x;
+	u8 support_modes;
+};
+
+struct __PACKED__ ilitek_key_info_v6 {
+	u8 mode;
+	u16 x_len;
+	u16 y_len;
+
+	struct __PACKED__ _ilitek_key_info_v6 {
+		u8 id;
+		u16 x;
+		u16 y;
+	} keys[50];
+};
+
+struct __PACKED__ ilitek_key_info_v3 {
+	u8 x_len[2];
+	u8 y_len[2];
+
+	struct __PACKED__ _ilitek_key_info_v3 {
+		u8 id;
+		u8 x[2];
+		u8 y[2];
+	} keys[20];
+};
+
+struct __PACKED__ ilitek_ts_kernel_info {
+	char ic_name[6];
+	char mask_ver[2];
+	u32 mm_addr;
+	u32 min_addr;
+	u32 max_addr;
+	char module_name[32];
+
+	char ic_full_name[16];
+};
+
+struct __PACKED__ ilitek_key_info {
+	struct ilitek_key_info_v6 info;
+	bool clicked[50];
+};
+
+struct __PACKED__ ilitek_power_status {
+	u16 header;
+	u8 vdd33_lvd_flag;
+	u8 vdd33_lvd_level_sel;
+};
+
+struct __PACKED__ ilitek_sensor_id {
+	u16 header;
+	u8 id;
+};
+
+struct __PACKED__ ilitek_func_mode {
+	u16 header;
+	u8 mode;
+};
+
+struct __PACKED__ ilitek_ts_protocol {
+	u32 ver;
+	u8 flag;
+};
+
+struct __PACKED__ ilitek_ts_ic {
+	u8 mode;
+	u32 crc[ILTIEK_MAX_BLOCK_NUM];
+
+	char mode_str[32];
+};
+
+struct __PACKED__ ilitek_hid_info {
+	u16 pid;
+	u16 vid;
+	u16 rev;
+};
+
+struct __PACKED__ freq_category {
+	u32 start;
+	u32 end;
+	u32 step;
+	u32 steps;
+
+		u32 size;
+	char limit[1024];
+
+	u8 mode;
+
+	s32 *data;
+};
+
+struct __PACKED__ freq_settings {
+	bool prepared;
+
+	unsigned int frame_cnt;
+
+	/* add from v6.0.A */
+	unsigned int mc_frame_cnt;
+	unsigned int dump_frame_cnt;
+
+	unsigned int scan_type;
+
+	struct freq_category sine;
+	struct freq_category mc_swcap;
+	struct freq_category sc_swcap;
+	struct freq_category pen;
+
+	struct freq_category dump1;
+	struct freq_category dump2;
+	u8 dump1_val;
+	u8 dump2_val;
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ short_settings {
+	bool prepared;
+
+	u8 dump_1;
+	u8 dump_2;
+	u8 v_ref_L;
+	u16 post_idle;
+};
+
+struct __PACKED__ open_settings {
+	bool prepared;
+
+	u16 freq;
+	u8 gain;
+	u8 gain_rfb;
+	u8 afe_res_sel;
+	u8 mc_fsel;
+	u16 frame;
+};
+
+struct __PACKED__ p2p_settings {
+	bool prepared;
+
+	u16 frame_cnt;
+	u8 type;
+
+	/* add from v6.0.A */
+	u16 freq;
+};
+
+struct __PACKED__ charge_curve_sweep {
+	u16 start;
+	u16 end;
+	u8 step;
+	u16 post_idle;
+	u16 fix_val;
+
+	u16 steps;
+};
+
+struct __PACKED__ charge_curve_settings {
+	bool prepared;
+
+	u8 scan_mode;
+
+	struct charge_curve_sweep dump;
+	struct charge_curve_sweep charge;
+
+	u16 c_sub;
+	u16 frame_cnt;
+
+	struct __PACKED__ charge_curve_point {
+		u16 x;
+		u16 y;
+		u16 *dump_max;
+		u16 *dump_avg;
+		u16 *charge_max;
+		u16 *charge_avg;
+	} pt[9];
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ cdc_settings {
+	u8 cmd;
+	u16 config;
+
+	bool skip_checksum;
+
+	/* freq. */
+	struct freq_settings freq;
+	/* short */
+	struct short_settings _short;
+	/* open */
+	struct open_settings open;
+	/* p2p */
+	struct p2p_settings p2p;
+	/* charge curve */
+	struct charge_curve_settings curve;
+
+	/* status only writable by CDC commonflow */
+	bool is_key;
+	bool is_p2p;
+	bool is_freq;
+	bool is_curve;
+	bool is_short;
+	bool is_open;
+	bool is_16bit;
+	bool is_sign;
+	bool is_fast_mode;
+	unsigned int total_bytes;
+
+	/* error code during cdc data collection */
+	s32 error;
+};
+
+struct __PACKED__ mp_station_old {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test:2;
+		u8 open_test:2;
+		u8 self_test:2;
+		u8 uniform_test:2;
+
+		u8 dac_test:2;
+		u8 key_test:2;
+		u8 final_result:2;
+		u8 paint_test:2;
+
+		u8 mopen_test:2;
+		u8 gpio_test:2;
+		u8 reserve_1:4;
+
+		char bar_code[28];
+		u8 reserve_2[35];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[10];
+};
+
+struct __PACKED__ mp_station {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test : 2;
+		u8 open_test : 2;
+		u8 self_test : 2;
+		u8 uniform_test : 2;
+
+		u8 dac_test : 2;
+		u8 key_test : 2;
+		u8 final_result : 2;
+		u8 paint_test : 2;
+
+		u8 mopen_test : 2;
+		u8 gpio_test : 2;
+		u8 reserve : 4;
+
+		u8 tool_ver[8];
+		char bar_code[135];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[5];
+
+	struct __PACKED__ {
+		u8 reserve_1[91];
+		u32 mp_result_ver;
+		u16 customer_id;
+		u16 fwid;
+		u8 reserve_2;
+	} info;
+
+	u16 crc;
+};
+
+struct __PACKED__ ilitek_ts_settings {
+	bool no_retry;
+	bool no_INT_ack;
+
+	bool sw_reset_at_last;
+
+	u8 sensor_id_mask;
+
+	/* only used for QUIRK_WAIT_ACK_DELAY */
+	u32 wait_ack_delay;
+
+	/*
+	 * engineer mode would likely report default format
+	 * ex. IWB-format
+	 */
+	bool default_format_enabled;
+};
+
+struct __PACKED__ ilitek_sys_info {
+	u16 pid;
+};
+
+struct __PACKED__ ilitek_ts_callback {
+	/* Please don't use "repeated start" for I2C interface */
+	write_then_read_t write_then_read;
+	read_ctrl_in_t read_ctrl_in;
+	read_interrupt_in_t read_interrupt_in;
+	init_ack_t init_ack;
+	wait_ack_t wait_ack;
+	hw_reset_t hw_reset;
+	re_enum_t re_enum;
+	delay_ms_t delay_ms;
+	msg_t msg;
+
+	/* write cmd without adding any hid header */
+	write_then_read_direct_t write_then_read_direct;
+	/* notify caller after AP/BL mode switch command */
+	mode_switch_notify_t mode_switch_notify;
+};
+
+struct __PACKED__ ilitek_common_info {
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen;
+	struct ilitek_tp_info_v6 tp;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu;
+	struct ilitek_hid_info hid;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+};
+
+struct __PACKED__ ilitek_ts_device {
+	void *_private;
+	char id[64];
+	u32 reset_time;
+
+	struct ilitek_ts_settings setting;
+
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen_info;
+	struct ilitek_tp_info_v6 tp_info;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu_info;
+	struct ilitek_hid_info hid_info;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+
+	u8 fw_mode;
+	struct mp_station mp;
+
+	u8 wbuf[4096];
+	u8 rbuf[4096];
+	struct ilitek_ts_callback cb;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16 __DLL le16(const u8 *p);
+u16 __DLL be16(const u8 *p);
+u32 __DLL le32(const u8 *p, int bytes);
+u32 __DLL be32(const u8 *p, int bytes);
+
+bool __DLL is_29xx(void *handle);
+
+bool __DLL _is_231x(char *ic_name);
+bool __DLL is_231x(void *handle);
+
+bool __DLL has_hw_key(void *handle);
+
+u8 __DLL get_protocol_ver_flag(u32 ver);
+
+int __DLL grid_alloc(void *handle, struct grids *grid);
+void __DLL grid_free(struct grids *grid);
+void __DLL grid_reset(struct grids *grid);
+
+u16 __DLL get_crc(u32 start, u32 end,
+		  u8 *buf, u32 buf_size);
+
+u32 __DLL get_checksum(u32 start, u32 end,
+		       u8 *buf, u32 buf_size);
+
+bool __DLL is_checksum_matched(u8 checksum, int start, int end,
+			       u8 *buf, int buf_size);
+
+bool __DLL support_sensor_id(void *handle);
+bool __DLL support_production_info(void *handle);
+bool __DLL support_fwid(void *handle);
+bool __DLL support_mcu_info(void *handle);
+bool __DLL support_power_status(void *handle);
+int __DLL write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms);
+
+int __DLL bridge_set_int_monitor(void *handle, bool enable);
+int __DLL bridge_set_test_mode(void *handle, bool enable);
+
+int __DLL reset_helper(void *handle);
+
+int __DLL write_then_read(void *handle, u8 *cmd, int wlen,
+			  u8 *buf, int rlen);
+int __DLL write_then_read_direct(void *handle, u8 *cmd, int wlen,
+				 u8 *buf, int rlen);
+int __DLL read_interrupt_in(void *handle, u8 *buf, int rlen,
+			    unsigned int timeout_ms);
+int __DLL read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		       unsigned int timeout_ms);
+
+void __DLL __ilitek_get_info(void *handle,
+			     struct ilitek_common_info *info);
+
+void __DLL ilitek_dev_set_quirks(void *handle, u32 quirks);
+void __DLL ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys);
+void __DLL ilitek_dev_setting(void *handle,
+			      struct ilitek_ts_settings *setting);
+
+void __DLL ilitek_dev_bind_callback(void *handle,
+				    struct ilitek_ts_callback *callback);
+
+void __DLL *ilitek_dev_init(u8 _interface, const char *id,
+			    bool need_update_ts_info,
+			    struct ilitek_ts_callback *callback,
+			    void *_private);
+void __DLL ilitek_dev_exit(void *handle);
+
+void __DLL api_print_ts_info(void *handle);
+void __DLL api_read_then_print_m2v_info(void *handle);
+
+int __DLL api_update_ts_info(void *handle);
+
+int __DLL api_protocol_set_cmd(void *handle, u8 idx, void *data);
+int __DLL api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force);
+
+u16 __DLL api_get_block_crc_by_addr(void *handle, u8 type,
+				    u32 start, u32 end);
+u16 __DLL api_get_block_crc_by_num(void *handle, u8 type,
+				   u8 block_num);
+
+int __DLL api_set_data_len(void *handle, u16 data_len);
+int __DLL api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			      u32 start, u32 end);
+int __DLL api_write_data_v6(void *handle, int wlen);
+int __DLL api_access_slave(void *handle, u8 id, u8 func, void *data);
+int __DLL api_check_busy(void *handle, int timeout_ms, int delay_ms);
+int __DLL api_write_enable_v3(void *handle, bool in_ap,
+			      bool write_ap, u32 end, u32 checksum);
+int __DLL api_write_data_v3(void *handle);
+
+int __DLL api_to_bl_mode(void *handle, bool bl, u32 start, u32 end);
+
+int __DLL api_write_data_m2v(void *handle, int wlen);
+int __DLL api_to_bl_mode_m2v(void *handle, bool to_bl);
+
+int __DLL api_set_idle(void *handle, bool enable);
+int __DLL api_set_func_mode(void *handle, u8 mode);
+int __DLL api_get_func_mode(void *handle);
+
+int __DLL api_erase_data_v3(void *handle);
+
+int __DLL api_read_flash(void *handle, u8 *buf,
+			 u32 start_addr, u32 len);
+
+int __DLL _api_read_mp_result(void *handle);
+int __DLL api_read_mp_result(void *handle);
+int __DLL _api_write_mp_result(void *handle, struct mp_station *mp);
+int __DLL api_write_mp_result(void *handle, struct mp_station *mp);
+void __DLL api_decode_mp_result(void *handle);
+
+int __DLL api_read_tuning(void *handle, u8 *buf, int rlen);
+
+int __DLL api_get_ic_crc(void *handle, u8 final_fw_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.c b/drivers/input/touchscreen/ilitek/ilitek_report.c
new file mode 100644
index 000000000000..dbe8abfd64ef
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_report.h"
+#include "ilitek_crypto.h"
+
+static bool is_debug_packet_id(u8 id, u8 _interface)
+{
+	return ((_interface == interface_i2c && id == 0xdb) ||
+		(_interface == interface_usb && id == 0xaa));
+}
+
+static bool is_pen_packet_id(u8 id)
+{
+	return (id == 0x0c || id == 0x0d);
+}
+
+static void touch_decode(struct ilitek_ts_device *dev,
+			 struct ilitek_report *report,
+			 u8 *buf, u8 cnt)
+{
+	struct touch_fmt *parser, *finger;
+	struct touch_iwb_fmt *parser_iwb;
+	u8 i, j, offset;
+	u32 size = dev->fmt.touch_size;
+
+	offset = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	for (i = 0; i < cnt && i < dev->tp_info.max_fingers; i++) {
+		finger = &report->touch.finger[i];
+
+		do {
+			if (dev->protocol.flag == PTL_V3 &&
+			    dev->_interface == interface_i2c) {
+				finger->id = i;
+				finger->status = buf[1 + offset + i * 5] >> 7;
+				finger->x = ((buf[1 + offset + i * 5] & 0x3F) << 8) +
+					buf[2 + offset + i * 5];
+				finger->y = ((buf[3 + offset + i * 5] & 0x3F) << 8) +
+					buf[4 + offset + i * 5];
+				finger->pressure = buf[5 + offset + i * 5];
+				finger->height = 128;
+				finger->width = 1;
+
+				break;
+			}
+
+			parser = (struct touch_fmt *)
+				(buf + 1 + i * size);
+			parser_iwb = (struct touch_iwb_fmt *)
+				(buf + 1 + i * size);
+
+			finger->id = parser->id;
+			finger->status = parser->status;
+			finger->x = parser->x;
+			finger->y = parser->y;
+
+			if (dev->setting.default_format_enabled)
+				break;
+
+			switch (dev->tp_info.format) {
+			case touch_fmt_0x1:
+				finger->pressure = parser->pressure;
+				break;
+			case touch_fmt_0x2:
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x3:
+				finger->pressure = parser->pressure;
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x4:
+				finger->id = parser_iwb->id;
+				finger->status =
+					(parser_iwb->status == 0x7) ? 1 : 0;
+				finger->x = parser_iwb->x;
+				finger->y = parser_iwb->y;
+				finger->width = parser_iwb->width;
+				finger->height = parser_iwb->height;
+				break;
+			case touch_fmt_0x10:
+				finger->width = le16(buf + i * size + 6);
+				finger->height = le16(buf + i * size + 8);
+				finger->algo = buf[i * size + 10];
+				report->touch.algo = 0;
+				break;
+			case touch_fmt_0x11:
+				finger->id =
+					(!finger->id) ? 0 : finger->id - 1;
+				break;
+			}
+		} while (false);
+
+		TP_DBG(dev->id,
+		       "[touch-report] id:%hhu, status:%hhu, x:%hu, y:%hu, p:%hhu, w:%hu, h:%hu, algo: 0x%hhx\n",
+			finger->id, finger->status, finger->x, finger->y,
+			finger->pressure, finger->width, finger->height,
+			finger->algo);
+
+		if (finger->id >= dev->tp_info.max_fingers) {
+			TP_ERR(dev->id, "invalid touch id: %hhu >= %hhu\n",
+				finger->id, dev->tp_info.max_fingers);
+			return;
+		}
+
+		/*
+		 * if x/y within key's range, skip touch range check.
+		 */
+		for (j = 0; j < dev->tp_info.key_num; j++) {
+			if ((finger->x < dev->key.info.keys[j].x &&
+			     finger->x > dev->key.info.keys[j].x +
+				     dev->key.info.x_len) &&
+			    (finger->y < dev->key.info.keys[j].y &&
+			     finger->y > dev->key.info.keys[j].y +
+				     dev->key.info.y_len))
+				continue;
+
+			goto skip_touch_range_check;
+		}
+
+		if (finger->status &&
+		    (finger->x - 1 > dev->screen_info.x_max ||
+		     finger->y - 1 > dev->screen_info.y_max ||
+		     finger->x < dev->screen_info.x_min ||
+		     finger->y < dev->screen_info.y_min)) {
+			TP_ERR(dev->id, "Point[%d]: (%d, %d), Limit: (%d:%d, %d:%d) OOB\n",
+				finger->id, finger->x, finger->y,
+				dev->screen_info.x_min, dev->screen_info.x_max,
+				dev->screen_info.y_min, dev->screen_info.y_max);
+			return;
+		}
+
+skip_touch_range_check:
+		continue;
+	}
+
+	report->touch.cnt = i;
+
+	/*
+	 * report touch event callback,
+	 * which includes actual count of finger report just parsed above.
+	 */
+	if (report->cb.report_touch_event)
+		report->cb.report_touch_event(&report->touch, report->_private);
+}
+
+static void pen_decode(struct ilitek_ts_device *dev,
+		       struct ilitek_report *report,
+		       u8 *buf)
+{
+	struct pen_fmt *parser = (struct pen_fmt *)(buf + 1);
+	struct pen_fmt *pen = &report->pen.pen;
+
+	report->pen.cnt = buf[61];
+	report->pen.algo = buf[62];
+
+	memcpy(pen, parser, sizeof(struct pen_fmt));
+
+	switch (dev->tp_info.pen_format) {
+	default:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery);
+		break;
+	case 1:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_1.barrel_pressure,
+			pen->usi_1.idx, pen->usi_1.color, pen->usi_1.width,
+			pen->usi_1.style);
+		break;
+	case 2:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color24: %u, no_color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_2.barrel_pressure,
+			pen->usi_2.idx, le32(pen->usi_2.color_24, 3),
+			pen->usi_2.no_color, pen->usi_2.width,
+			pen->usi_2.style);
+		break;
+	}
+
+	/* report pen event callback */
+	if (report->cb.report_pen_event)
+		report->cb.report_pen_event(&report->pen, report->_private);
+}
+
+static void dmsg_decode(struct ilitek_ts_device *dev,
+			struct ilitek_report *report,
+			u8 *buf, int buf_size)
+{
+	if ((int)buf[1] >= buf_size)
+		return;
+
+	buf[buf[1]] = '\0';
+	TP_DBG(dev->id, "%s\n", (char *)(buf + 2));
+
+	if (report->cb.report_dmsg)
+		report->cb.report_dmsg((char *)(buf + 2),
+					buf_size - 2, report->_private);
+}
+
+static void report_buf(struct ilitek_report *report, u8 *buf,
+		       bool is_last)
+{
+	if (!report->cb.report_buf)
+		return;
+
+	report->cb.report_buf(buf, 64, is_last, report->_private);
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v3(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+
+	u8 cnt;
+	int idx = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	UNUSED(buf_size);
+
+	if (dev->_interface == interface_i2c) {
+		if (dev->quirks & QUIRK_BRIDGE) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+
+			if (buf[0] != 0x03 || buf[1] != 0xa3 || buf[2] != 0x10)
+				return -EAGAIN;
+
+			/* move algo byte to index 62 */
+			buf[62] = buf[35];
+		} else if (dev->quirks & QUIRK_DAEMON_I2C ||
+			   dev->quirks & QUIRK_BRIDGE ||
+			   dev->quirks & QUIRK_WIFI_ITS_I2C) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+		} else {
+			dev->wbuf[0] = 0x10;
+			error = write_then_read(dev, dev->wbuf, 1, buf, 32);
+			if (error < 0)
+				return error;
+
+			report->touch.algo = buf[31];
+			buf[31] = 0;
+
+			if (buf[0] == 2) {
+				error = write_then_read(dev, NULL, 0,
+							 buf + 31, 20);
+				if (error < 0)
+					return error;
+			}
+
+			/* move algo byte to index 62 */
+			buf[62] = report->touch.algo;
+		}
+
+		switch (dev->rbuf[idx]) {
+		default:
+		case 1:
+			cnt = 6;
+			break;
+		case 0:
+			cnt = 0;
+			break;
+		case 2:
+			cnt = 10;
+			break;
+		}
+	} else {
+		error = read_interrupt_in(dev, buf, 64, 1000);
+		if (error < 0)
+			return error;
+
+		cnt = buf[55];
+
+		/* move algo byte to index 62 */
+		buf[62] = buf[56];
+	}
+
+	report->touch.algo = buf[62];
+
+	report->touch.dbg_size = 64;
+	memcpy(report->touch.dbg, buf, 64);
+
+	report_buf(report, buf, true);
+
+	return cnt;
+}
+
+static bool is_iwb_fmt(struct ilitek_ts_device *dev)
+{
+	return dev->tp_info.format == touch_fmt_0x4 &&
+		!dev->setting.default_format_enabled;
+}
+
+static bool need_skip_checksum(struct ilitek_ts_device *dev,
+			       struct ilitek_report *report,
+			       u8 packet_id, bool is_first)
+{
+	/*
+	 * don't check checksum for below situation:
+	 *   1. debug packet
+	 *   2. normal mode pen packet w/ HID.
+	 *   3. IWB format packet
+	 */
+
+	if (report->skip_checksum)
+		return true;
+
+	if (is_first) {
+		if (is_debug_packet_id(packet_id, dev->_interface))
+			return true;
+
+		if ((dev->_interface == interface_usb ||
+		     dev->_interface == interface_hid_over_i2c) &&
+		    packet_id == 0x0d)
+			return true;
+	}
+
+	if (is_iwb_fmt(dev))
+		return true;
+
+	return false;
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v6(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+	u8 i, cnt;
+	u8 size = dev->fmt.touch_size, max_cnt = dev->fmt.touch_max_cnt;
+	u8 tmp[64];
+
+	UNUSED(buf_size);
+
+	error = read_interrupt_in(dev, tmp, 64, 1000);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.format == 5)
+		ilitek_decrypt(tmp + 1, 48);
+
+	/* set packet id to 0x48 forcely for using BRIDGE finger report */
+	if (dev->quirks & QUIRK_BRIDGE && !is_pen_packet_id(tmp[0]))
+		tmp[0] = 0x48;
+
+	if (!need_skip_checksum(dev, report, tmp[0], true) &&
+	    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+		return -EILIPROTO;
+
+	report->pen.algo = tmp[62];
+	report->touch.algo = tmp[62];
+	report->pen.dbg_size = 64;
+	report->touch.dbg_size = 64;
+	memcpy(report->pen.dbg, tmp, 64);
+	memcpy(report->touch.dbg, tmp, 64);
+	memcpy(buf, tmp, 64);
+
+	/*
+	 * no need to check contact count byte for debug packet and pen packet.
+	 */
+	if (is_pen_packet_id(tmp[0]) ||
+	    is_debug_packet_id(tmp[0], dev->_interface)) {
+		report_buf(report, tmp, true);
+		return 0;
+	}
+
+	cnt = is_iwb_fmt(dev) ? tmp[55] : tmp[61];
+	for (i = 1; i < DIV_ROUND_UP(cnt, max_cnt); i++) {
+		report_buf(report, tmp, false);
+
+		error = read_interrupt_in(dev, tmp, 64, 1000);
+		if (error < 0)
+			return error;
+
+		if (dev->tp_info.format == 5)
+			ilitek_decrypt(tmp + 1, 48);
+
+		if (!need_skip_checksum(dev, report, tmp[0], false) &&
+		    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+			return -EILIPROTO;
+
+		/* copy and skip the first rid byte */
+		memcpy(buf + i * size * max_cnt + 1, tmp + 1, 63);
+	}
+	report_buf(report, tmp, true);
+
+	return cnt;
+}
+
+int ilitek_report_update(void *handle, struct ilitek_report *report)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt;
+
+	if (!dev)
+		return -EINVAL;
+
+	/* initial report event */
+	memset(&report->touch, 0, sizeof(report->touch));
+	memset(&report->pen, 0, sizeof(report->pen));
+
+	memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (dev->protocol.flag) {
+	default: return -EPERM;
+	case PTL_V3:
+		cnt = report_get_raw_v3(dev, report, dev->rbuf,
+					 sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		break;
+
+	case PTL_V6:
+		cnt = report_get_raw_v6(dev, report, dev->rbuf,
+					sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		/* pen packet (V6 only) */
+		if (is_pen_packet_id(dev->rbuf[0])) {
+			pen_decode(dev, report, dev->rbuf);
+			return 0;
+		}
+
+		break;
+	}
+
+	/* debug message packet */
+	if (is_debug_packet_id(dev->rbuf[0], dev->_interface)) {
+		dmsg_decode(dev, report, dev->rbuf, sizeof(dev->rbuf));
+		return 0;
+	}
+
+	touch_decode(dev, report, dev->rbuf, cnt);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.h b/drivers/input/touchscreen/ilitek/ilitek_report.h
new file mode 100644
index 000000000000..e49333f9618b
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_REPORT_H__
+#define __ILITEK_REPORT_H__
+
+#include "ilitek_def.h"
+#include "ilitek_protocol.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_data {
+	struct touch_fmt finger[40];
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+struct __PACKED__ pen_data {
+	struct pen_fmt pen;
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return touch event */
+typedef void(*report_touch_event_t)(struct touch_data *, void *);
+/* return pen event */
+typedef void(*report_pen_event_t)(struct pen_data *, void *);
+/* return debug msg */
+typedef void(*report_dmsg_t)(char *, int, void *);
+/* return raw data buf */
+typedef void(*report_buf_t)(u8 *, int, bool, void *);
+
+struct ilitek_report_callback {
+	report_touch_event_t report_touch_event;
+	report_pen_event_t report_pen_event;
+	report_dmsg_t report_dmsg;
+	report_buf_t report_buf;
+};
+
+struct ilitek_report {
+	struct touch_data touch;
+	struct pen_data pen;
+	struct ilitek_report_callback cb;
+
+	bool skip_checksum;
+
+	void *_private;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	int __DLL ilitek_report_update(void *handle, struct ilitek_report *report);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_tool.c b/drivers/input/touchscreen/ilitek/ilitek_tool.c
new file mode 100644
index 000000000000..689c1c09d397
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_tool.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+#include <linux/firmware.h>
+
+#include <linux/miscdevice.h>
+
+static struct proc_dir_entry *ilitek_proc;
+static struct proc_dir_entry *ilitek_proc_entry;
+
+#define ILITEK_IOCTL_BASE                       100
+#define ILITEK_IOCTL_I2C_WRITE_DATA             _IOWR(ILITEK_IOCTL_BASE, 0, uint64_t)
+#define ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT      _IOWR(ILITEK_IOCTL_BASE, 0, uint32_t)
+#define ILITEK_IOCTL_I2C_WRITE_LENGTH           _IOWR(ILITEK_IOCTL_BASE, 1, int32_t)
+#define ILITEK_IOCTL_I2C_READ_DATA              _IOWR(ILITEK_IOCTL_BASE, 2, uint64_t)
+#define ILITEK_IOCTL_I2C_READ_DATA_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 2, uint32_t)
+#define ILITEK_IOCTL_I2C_READ_LENGTH            _IOWR(ILITEK_IOCTL_BASE, 3, int32_t)
+#define ILITEK_IOCTL_DRIVER_INFORMATION		_IOWR(ILITEK_IOCTL_BASE, 8, int32_t)
+#define ILITEK_IOCTL_USB_UPDATE_RESOLUTION      _IOWR(ILITEK_IOCTL_BASE, 9, int32_t)
+#define ILITEK_IOCTL_I2C_INT_FLAG	        _IOWR(ILITEK_IOCTL_BASE, 10, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE                 _IOWR(ILITEK_IOCTL_BASE, 11, int32_t)
+#define ILITEK_IOCTL_STOP_READ_DATA             _IOWR(ILITEK_IOCTL_BASE, 12, int32_t)
+#define ILITEK_IOCTL_START_READ_DATA            _IOWR(ILITEK_IOCTL_BASE, 13, int32_t)
+#define ILITEK_IOCTL_I2C_SWITCH_IRQ		_IOWR(ILITEK_IOCTL_BASE, 15, int32_t)
+#define ILITEK_IOCTL_UPDATE_FLAG		_IOWR(ILITEK_IOCTL_BASE, 16, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE_FW		_IOWR(ILITEK_IOCTL_BASE, 18, int32_t)
+#define ILITEK_IOCTL_RESET			_IOWR(ILITEK_IOCTL_BASE, 19, int32_t)
+#define ILITEK_IOCTL_INT_STATUS			_IOWR(ILITEK_IOCTL_BASE, 20, int32_t)
+
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#define ILITEK_IOCTL_DEBUG_SWITCH		_IOWR(ILITEK_IOCTL_BASE, 21, int32_t)
+#endif
+
+#define ILITEK_IOCTL_I2C_INT_CLR		_IOWR(ILITEK_IOCTL_BASE, 22, int32_t)
+#define ILITEK_IOCTL_I2C_INT_POLL		_IOWR(ILITEK_IOCTL_BASE, 23, uint64_t)
+#define ILITEK_IOCTL_I2C_INT_POLL_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 23, uint32_t)
+#define ILITEK_IOCTL_I2C_ISR_TYPE		_IOWR(ILITEK_IOCTL_BASE, 24, uint32_t)
+#define ILITEK_IOCTL_I2C_NETLINK		_IOWR(ILITEK_IOCTL_BASE, 25, uint16_t)
+
+
+#define ILITEK_DEVICE_NODE_PERMISSON			0666
+
+static s32 ilitek_file_open(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = true;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static s32 ilitek_file_close(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = false;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static ssize_t ilitek_file_write(struct file *filp,
+				 const char *buf, size_t size, loff_t *f_pos)
+{
+	s32 ret = 0, count = 0;
+	u8 buffer[512];
+	u32 *data;
+	char *token = NULL, *cur = NULL;
+
+	if (size > sizeof(buffer)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(buffer));
+		return -EINVAL;
+	}
+
+	ret = copy_from_user(buffer, buf, size);
+	if (ret < 0) {
+		TP_ERR(NULL, "copy data from user space, failed");
+		return -1;
+	}
+
+	token = cur = buffer;
+
+	data = kcalloc(size, sizeof(u32), GFP_KERNEL);
+
+	while ((token = strsep(&cur, ",")) != NULL) {
+		//data[count] = str2hex(token);
+		sscanf(token, "%x", &data[count]);
+		TP_MSG(NULL, "data[%d] = %x\n", count, data[count]);
+		count++;
+	}
+
+	if (buffer[size - 2] == 'I' && (size == 20 || size == 52) && buffer[0] == 0x77 && buffer[1] == 0x77) {
+
+		TP_MSG(NULL, "IOCTL_WRITE CMD = %d\n", buffer[2]);
+		switch (buffer[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+#ifdef ILITEK_TUNING_MESSAGE
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", buffer[3]);
+			if (buffer[3] == 0) {
+				ilitek_debug_flag = false;
+			} else if (buffer[3] == 1) {
+				ilitek_debug_flag = true;
+			}
+			break;
+#endif
+		case 15:
+			if (buffer[3] == 0)
+				ilitek_irq_disable();
+			else
+				ilitek_irq_enable();
+
+			break;
+		case 16:
+			ts->operation_protection = buffer[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 18:
+			ilitek_irq_disable();
+			mutex_lock(&ts->ilitek_mutex);
+			ret = ilitek_write(&buffer[3], 33);
+			mutex_unlock(&ts->ilitek_mutex);
+			ilitek_irq_enable();
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+
+			return ret;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	if (buffer[size - 2] == 'W') {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write(buffer, size - 2);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			return ret;
+		}
+	} else if (!strncmp(buffer, "unhandle_irq", strlen("unhandle_irq"))) {
+		ts->unhandle_irq = !ts->unhandle_irq;
+		TP_MSG(NULL, "ts->unhandle_irq = %d.\n", ts->unhandle_irq);
+	} else if (!strncmp(buffer, "dbg_pkt", strlen("dbg_pkt"))) {
+		set_log_level(log_level_pkt);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_pkt);
+	} else if (!strncmp(buffer, "dbg_debug", strlen("dbg_debug"))) {
+		set_log_level(log_level_dbg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_dbg);
+	} else if (!strncmp(buffer, "dbg_info", strlen("dbg_info"))) {
+		set_log_level(log_level_msg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_msg);
+	} else if (!strncmp(buffer, "dbg_err", strlen("dbg_err"))) {
+		set_log_level(log_level_err);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_err);
+	} else if (!strncmp(buffer, "dbg_num", strlen("dbg_num"))) {
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", tp_log_level);
+	}
+#ifdef ILITEK_TUNING_MESSAGE
+	else if (!strncmp(buffer, "truning_dbg_flag", strlen("truning_dbg_flag"))) {
+		ilitek_debug_flag = !ilitek_debug_flag;
+		TP_MSG(NULL, " %s debug_flag message(%X).\n", ilitek_debug_flag ? "Enabled" : "Disabled", ilitek_debug_flag);
+	}
+#endif
+	else if (!strncmp(buffer, "irq_status", strlen("irq_status"))) {
+		TP_MSG(NULL, "gpio_get_value(i2c.irq_gpio) = %d.\n", gpio_get_value(ts->irq_gpio));
+	} else if (!strncmp(buffer, "enable", strlen("enable"))) {
+		ilitek_irq_enable();
+		TP_MSG(NULL, "irq enable\n");
+	} else if (!strncmp(buffer, "disable", strlen("disable"))) {
+		ilitek_irq_disable();
+		TP_MSG(NULL, "irq disable\n");
+	} else if (!strncmp(buffer, "info", strlen("info"))) {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		api_update_ts_info(ts->dev);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+	} else if (!strncmp(buffer, "reset", strlen("reset"))) {
+		ilitek_reset(ts->dev->reset_time);
+	}
+
+	TP_DBG(NULL, "ilitek return count = %zu\n", size);
+	kfree(data);
+	return size;
+}
+
+static FOPS_IOCTL_FUNC(ilitek_file_ioctl, uint32_t cmd, unsigned long arg)
+{
+	static u8 *buffer;
+	static unsigned long len;
+	s32 ret = 0;
+	int tmp;
+
+	buffer = kmalloc(ILITEK_IOCTL_MAX_TRANSFER, GFP_KERNEL);
+	memset(buffer, 0, ILITEK_IOCTL_MAX_TRANSFER);
+
+	switch (cmd) {
+	case ILITEK_IOCTL_I2C_WRITE_DATA:
+	case ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid write len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(buffer, (u8 *)arg, len)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, len, 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write failed, cmd: %x\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_I2C_READ_DATA:
+	case ILITEK_IOCTL_I2C_READ_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid read len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(NULL, 0, 0, buffer, len);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c read failed, buf: %x\n", buffer[0]);
+			break;
+		}
+
+		if (copy_to_user((u8 *)arg, buffer, len)) {
+			ret = -EFAULT;
+			TP_ERR(NULL, "copy data to user space, failed\n");
+		}
+		break;
+	case ILITEK_IOCTL_I2C_WRITE_LENGTH:
+	case ILITEK_IOCTL_I2C_READ_LENGTH:
+		len = arg;
+		break;
+	case ILITEK_IOCTL_DRIVER_INFORMATION:
+		memcpy(buffer, driver_ver, 7);
+		if (copy_to_user((u8 *)arg, buffer, 7))
+			ret = -EFAULT;
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE:
+		break;
+	case ILITEK_IOCTL_I2C_INT_FLAG:
+		buffer[0] = !(gpio_get_value(ts->irq_gpio));
+		if (copy_to_user((u8 *)arg, buffer, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_FLAG = %d.\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_START_READ_DATA:
+		ilitek_irq_enable();
+		ts->unhandle_irq = false;
+		TP_MSG(NULL, "enable_irq and ts->unhandle_irq = false.\n");
+		break;
+	case ILITEK_IOCTL_STOP_READ_DATA:
+		ilitek_irq_disable();
+		ts->unhandle_irq = true;
+		TP_MSG(NULL, "disable_irq and ts->unhandle_irq = true.\n");
+		break;
+	case ILITEK_IOCTL_RESET:
+		ilitek_reset(ts->dev->reset_time);
+		break;
+	case ILITEK_IOCTL_INT_STATUS:
+		if (put_user(gpio_get_value(ts->irq_gpio), (s32 *)arg))
+			ret = -EFAULT;
+		break;
+#ifdef ILITEK_TUNING_MESSAGE
+	case ILITEK_IOCTL_DEBUG_SWITCH:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+		TP_MSG(NULL, "ilitek The debug_flag = %d.\n", buffer[0]);
+		if (buffer[0] == 0)
+			ilitek_debug_flag = false;
+		else if (buffer[0] == 1)
+			ilitek_debug_flag = true;
+		break;
+#endif
+	case ILITEK_IOCTL_I2C_SWITCH_IRQ:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		if (buffer[0] == 0)
+			ilitek_irq_disable();
+		else
+			ilitek_irq_enable();
+
+		break;
+	case ILITEK_IOCTL_UPDATE_FLAG:
+		ts->operation_protection = arg;
+		TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE_FW:
+		if (copy_from_user(buffer, (u8 *)arg, 35)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, buffer[34], 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write, failed\n");
+
+		break;
+	case ILITEK_IOCTL_I2C_INT_CLR:
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_CLR, set get_INT false\n");
+		atomic_set(&ts->get_INT, 0);
+		break;
+	case ILITEK_IOCTL_I2C_INT_POLL:
+	case ILITEK_IOCTL_I2C_INT_POLL_COMPAT:
+		tmp = atomic_read(&ts->get_INT);
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_POLL, get_INT: %d\n", tmp);
+
+		if (copy_to_user((u8 *)arg, &tmp, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+		}
+		break;
+	case ILITEK_IOCTL_I2C_ISR_TYPE:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_ISR_TYPE, set ISR type: %lu\n", arg);
+		ts->irq_handle_type = (arg >> 16);
+		ts->irq_read_len = arg & 0xFFFF;
+		break;
+	case ILITEK_IOCTL_I2C_NETLINK:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_NETLINK, set netlink: %s with ETH: %hhu\n",
+			(arg >> 8) ? "ON" : "OFF", (u8)(arg & 0xFF));
+
+		if (arg >> 8)
+			ret = ilitek_netlink_init(arg & 0xFF);
+		else
+			ilitek_netlink_exit();
+
+		break;
+	default:
+		TP_ERR(NULL, "unrecognized ioctl cmd: 0x%04x\n", cmd);
+		ret = -EINVAL;
+		break;
+	}
+
+	kfree(buffer);
+	return (ret < 0) ? ret : 0;
+}
+
+static ssize_t ilitek_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	u8 *tmp;
+	s32 ret;
+
+	if (count > 8192)
+		count = 8192;
+
+	tmp = kmalloc(count, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = ilitek_read(tmp, count);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+	if (ret < 0) {
+		TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		goto err_free;
+	}
+
+	if (copy_to_user(buf, tmp, count)) {
+		ret = -EFAULT;
+		goto err_free;
+	}
+
+err_free:
+	kfree(tmp);
+
+	return ret > 0 ? count : ret;
+}
+
+/* compat ioctl for 32/64 bit user program compatibility */
+#ifdef CONFIG_COMPAT
+static long ilitek_file_compat_ioctl(struct file *fp, uint32_t cmd,
+				     unsigned long arg)
+{
+	return ilitek_file_ioctl(fp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+
+static struct file_operations ilitek_fops = {
+	.FOPS_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = ilitek_file_compat_ioctl,
+#endif
+
+	.read = ilitek_file_read,
+	.write = ilitek_file_write,
+	.open = ilitek_file_open,
+	.release = ilitek_file_close,
+};
+
+static struct PROC_FOPS_T ilitek_proc_fops = {
+	.PROC_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.PROC_COMPAT_IOCTL = ilitek_file_compat_ioctl,
+#endif
+
+	.PROC_READ = ilitek_file_read,
+	.PROC_WRITE = ilitek_file_write,
+	.PROC_OPEN = ilitek_file_open,
+	.PROC_RELEASE = ilitek_file_close,
+};
+
+static ssize_t ilitek_driver_version_read(struct file *fp, char __user *buf,
+					  size_t size, loff_t *off)
+{
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	cnt = scnprintf(str, sizeof(str), "driver-version-tag: [%*phD]\n",
+			7, driver_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_drv_version = {
+	.PROC_READ = ilitek_driver_version_read,
+};
+
+static ssize_t ilitek_update_fw_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int error;
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.bin");
+		if (error < 0)
+			cnt += scnprintf(str, sizeof(str),
+					 "upgrade failed, err: %d\n", error);
+	} else {
+		cnt += scnprintf(str, sizeof(str),
+				 "upgrade success, fw version: %*phD\n",
+				 8, ts->dev->fw_ver);
+	}
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwupdate = {
+	.PROC_READ = ilitek_update_fw_read,
+};
+
+static ssize_t ilitek_firmware_version_read(struct file *fp, char __user *buf,
+					    size_t size, loff_t *off)
+{
+	int error;
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (error < 0)
+		cnt = scnprintf(str, sizeof(str), "read failed, err: %d\n",
+				error);
+	else
+		cnt = scnprintf(str, sizeof(str), "fw-version-tag: [%*phD]\n",
+				8, ts->dev->fw_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwversion = {
+	.PROC_READ = ilitek_firmware_version_read,
+};
+
+static ssize_t ilitek_console_write(struct file *filp, const char *buf,
+				    size_t size, loff_t *f_pos)
+{
+	int error;
+	char tmp[256], str[256];
+	char *ptr, *cur = str;
+	size_t wlen, rlen, _wlen = 0;
+	u8 cmd[64], data[64];
+
+	if (size > sizeof(tmp)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(tmp));
+		return -EINVAL;
+	}
+
+	error = copy_from_user(tmp, buf, size);
+	if (error < 0) {
+		TP_ERR(NULL, "copy_from_user failed, err: %d", error);
+		return error;
+	}
+
+	memset(str, 0, sizeof(str));
+	sscanf(tmp, "%zu:%zu:%s", &wlen, &rlen, str);
+	TP_MSG(NULL, "wlen: %zu, rlen: %zu, command: %s\n", wlen, rlen, str);
+
+	while ((ptr = strsep(&cur, "-")) && _wlen < sizeof(cmd))
+		sscanf(ptr, "%hhx", &cmd[_wlen++]);
+
+	if (wlen > 0 && wlen != _wlen) {
+		TP_ERR(NULL, "write cmd length: %zu not match with %s\n",
+			wlen, str);
+		return -EINVAL;
+	} else if (rlen > sizeof(data)) {
+		TP_ERR(NULL, "invalid read cmd length: %zu > %zu too large\n",
+			rlen, sizeof(data));
+		return -EINVAL;
+	}
+
+	if (wlen > 0)
+		TP_MSG(NULL, "[write]: %*phD, len: %zu\n",
+			(int)wlen, cmd, wlen);
+
+	error = ilitek_write_and_read(cmd, wlen, 1, data, rlen);
+	if (error < 0)
+		return error;
+
+	if (rlen > 0)
+		TP_MSG(NULL, "[read]: %*phD, len: %zu\n",
+			(int)rlen, data, rlen);
+
+	return size;
+}
+
+static struct PROC_FOPS_T ilitek_fops_console = {
+	.PROC_WRITE = ilitek_console_write,
+};
+
+static ssize_t ilitek_func_mode_write(struct file *fp, const char *buf,
+				      size_t size, loff_t *off)
+{
+	int error, cnt;
+	char str[64];
+	u8 func_mode;
+
+	cnt = MIN(size, sizeof(str));
+	memset(str, 0, sizeof(str));
+	if (copy_from_user(str, buf, cnt))
+		return -EFAULT;
+
+	sscanf(str, "%hhu", &func_mode);
+	TP_MSG(NULL, "set func mode: %hhu, support max: %hhu modes\n",
+		func_mode, ts->dev->tp_info.support_modes);
+
+	if (func_mode >= ts->dev->tp_info.support_modes)
+		return -EINVAL;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	error |= api_set_func_mode(ts->dev, func_mode);
+	error |= api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return (error < 0) ? error : cnt;
+}
+
+static ssize_t ilitek_func_mode_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 i;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_protocol_set_cmd(ts->dev, GET_TP_INFO, NULL);
+	api_get_func_mode(ts->dev);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "function mode: ");
+	for (i = 0; i < ts->dev->tp_info.support_modes; i++) {
+		if (i == ts->dev->func.mode)
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "[%hhu] ", i);
+		else
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "%hhu ", i);
+	}
+	cnt += scnprintf(str + strlen(str), sizeof(str) - cnt, "\n");
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_func_mode = {
+	.PROC_READ = ilitek_func_mode_read,
+	.PROC_WRITE = ilitek_func_mode_write,
+};
+
+static ssize_t ilitek_crypto_key_write(struct file *fp, const char *buf,
+				       size_t size, loff_t *off)
+{
+	u8 crypto[AES_KEY_LEN * 2];
+
+	if (size != sizeof(crypto) ||
+	    copy_from_user(crypto, buf, sizeof(crypto)))
+		return -EFAULT;
+
+	TP_MSG(NULL, "set crypto_key: %*phD\n", AES_KEY_LEN, crypto);
+	TP_MSG(NULL, "set crypto_iv: %*phD\n", AES_KEY_LEN,
+		crypto + AES_KEY_LEN);
+
+	memcpy(crypto_key, crypto, AES_KEY_LEN);
+	memcpy(crypto_iv, crypto + AES_KEY_LEN, AES_KEY_LEN);
+
+	return sizeof(crypto);
+}
+
+static ssize_t ilitek_crypto_key_read(struct file *fp, char __user *buf,
+				      size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "key: %*phD\n",
+			 AES_KEY_LEN, crypto_key);
+	cnt += scnprintf(str + strlen(str), sizeof(str), "iv: %*phD\n",
+			 AES_KEY_LEN, crypto_iv);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_crypto_key = {
+	.PROC_READ = ilitek_crypto_key_read,
+	.PROC_WRITE = ilitek_crypto_key_write,
+};
+
+static ssize_t ilitek_setmode_0_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 0);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_0 = {
+	.PROC_READ = ilitek_setmode_0_read,
+};
+
+static ssize_t ilitek_setmode_1_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 1);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_1 = {
+	.PROC_READ = ilitek_setmode_1_read,
+};
+
+static ssize_t ilitek_setmode_2_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 2);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_2 = {
+	.PROC_READ = ilitek_setmode_2_read,
+};
+
+static ssize_t ilitek_driver_version_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "driver-version-tag: [%*phD]\n",
+			 7, driver_ver);
+}
+
+static DEVICE_ATTR(driver_version, 0664, ilitek_driver_version_show, NULL);
+
+static ssize_t ilitek_eds_check_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	if (ts->esd_check)
+		return scnprintf(buf, PAGE_SIZE, "[enable] disable\n");
+
+	return scnprintf(buf, PAGE_SIZE, "enable [disable]\n");
+}
+
+static ssize_t ilitek_esd_check_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	TP_MSG(NULL, "set esd check: %hhx to %s\n", ts->esd_check, buf);
+
+	if (!strncmp(buf, "enable", 6) && !ts->esd_check) {
+		ilitek_create_esd_check_workqueue();
+		ts->esd_check = true;
+	} else if (!strncmp(buf, "disable", 7) && ts->esd_check) {
+		ilitek_remove_esd_check_workqueue();
+		ts->esd_check = false;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(esd_check, 0664,
+		   ilitek_eds_check_show,
+		   ilitek_esd_check_store);
+
+
+static ssize_t ilitek_low_power_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->low_power_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_low_power_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	int i;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set low power: %hhx to %s\n", ts->low_power_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		ts->low_power_status = modes[i].id;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(low_power, 0664,
+		   ilitek_low_power_show,
+		   ilitek_low_power_store);
+
+static ssize_t ilitek_gesture_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->gesture_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_gesture_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int i;
+	u8 type = ts->gesture_status;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set gesture: %hhx to %s\n", ts->gesture_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		type = modes[i].id;
+	}
+
+	if (!ts->gesture_status && type)
+		ilitek_register_gesture(ts, true);
+	else if (ts->gesture_status && !type)
+		ilitek_register_gesture(ts, false);
+
+	ts->gesture_status = type;
+
+	return size;
+}
+static DEVICE_ATTR(gesture, 0664, ilitek_gesture_show, ilitek_gesture_store);
+
+static ssize_t ilitek_firmware_version_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	int ret;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (ret < 0) {
+		TP_ERR(NULL, "read tp info failed, err: %d\n", ret);
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n", ret);
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "fw-version-tag: [%*phD]\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(firmware_version, 0664, ilitek_firmware_version_show, NULL);
+
+static ssize_t ilitek_module_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_protocol_set_cmd(ts->dev, GET_MCU_VER, NULL);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	if (error < 0)
+		TP_ERR(NULL, "read mcu ver. failed, err: %d\n", error);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+out:
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE, "module-name-tag: [%s]\n",
+			 ts->dev->mcu_info.module_name);
+}
+
+static DEVICE_ATTR(product_id, 0664, ilitek_module_name_show, NULL);
+
+static ssize_t ilitek_update_fw_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0)
+		error = ilitek_upgrade_firmware("ilitek.bin");
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "upgrade failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE,
+			 "upgrade success, current fw version: %*phD\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(update_fw, 0664, ilitek_update_fw_show, NULL);
+
+static struct attribute *ilitek_sysfs_attrs_ctrl[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_gesture.attr,
+	&dev_attr_low_power.attr,
+	&dev_attr_esd_check.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static struct attribute_group ilitek_attribute_group[] = {
+	{ .attrs = ilitek_sysfs_attrs_ctrl },
+};
+
+int ilitek_create_sysfsnode(void)
+{
+	int error;
+
+	error = sysfs_create_group(&ts->device->kobj, ilitek_attribute_group);
+	if (error < 0) {
+		TP_ERR(NULL, "sysfs_create_group failed, err: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+void ilitek_remove_sys_node(void)
+{
+	sysfs_remove_group(&ts->device->kobj, ilitek_attribute_group);
+}
+
+static struct miscdevice ilitek_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ilitek_ctrl",
+	.fops = &ilitek_fops,
+	.mode = 0666,
+};
+
+int ilitek_create_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+	misc_register(&ilitek_misc);
+
+	ilitek_proc = proc_create("ilitek_ctrl", ILITEK_DEVICE_NODE_PERMISSON,
+				  NULL, &ilitek_proc_fops);
+
+	if (!ilitek_proc)
+		TP_ERR(NULL, "proc_create(ilitek_ctrl, ILITEK_DEVICE_NODE_PERMISSON, NULL, &ilitek_fops) fail\n");
+
+	ilitek_proc_entry = proc_mkdir("ilitek", NULL);
+	if (!ilitek_proc_entry) {
+		TP_ERR(NULL, "Error, failed to creat procfs.\n");
+		return -EINVAL;
+	}
+
+	if (!proc_create("driver_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_drv_version))
+		TP_ERR(NULL, "failed to create procfs driver_version.\n");
+
+	if (!proc_create("firmware_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwversion))
+		TP_ERR(NULL, "failed to create procfs firmware_version.\n");
+
+	if (!proc_create("console", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_console))
+		TP_ERR(NULL, "failed to create procfs console.\n");
+
+	if (!proc_create("update_fw", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwupdate))
+		TP_ERR(NULL, "failed to create procfs update_fw.\n");
+
+	if (!proc_create("func_mode", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_func_mode))
+		TP_ERR(NULL, "failed to create procfs func_mode.\n");
+
+	if (!proc_create("crypto_key", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_crypto_key))
+		TP_ERR(NULL, "failed to create procfs crypto_key.\n");
+
+	/*
+	 * below setmode_X is historical setting for some customer need
+	 * to set function mode by cat procfs node.
+	 * please make sure it's not risky to modifiy below.
+	 */
+	if (!proc_create("setmode_0", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_0))
+		TP_ERR(NULL, "failed to create procfs setmode_0.\n");
+	if (!proc_create("setmode_1", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_1))
+		TP_ERR(NULL, "failed to create procfs setmode_1.\n");
+	if (!proc_create("setmode_2", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_2))
+		TP_ERR(NULL, "failed to create procfs setmode_2.\n");
+#endif
+
+	return 0;
+}
+
+int ilitek_remove_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+
+	misc_deregister(&ilitek_misc);
+
+	if (ilitek_proc) {
+		TP_MSG(NULL, "remove procfs ilitek_ctrl.\n");
+		remove_proc_entry("ilitek_ctrl", NULL);
+		ilitek_proc = NULL;
+	}
+
+	if (ilitek_proc_entry) {
+		TP_MSG(NULL, "remove procfs inode\n");
+		remove_proc_entry("driver_version", ilitek_proc_entry);
+		remove_proc_entry("firmware_version", ilitek_proc_entry);
+		remove_proc_entry("console", ilitek_proc_entry);
+		remove_proc_entry("update_fw", ilitek_proc_entry);
+		remove_proc_entry("func_mode", ilitek_proc_entry);
+		remove_proc_entry("crypto_key", ilitek_proc_entry);
+
+		remove_proc_entry("setmode_0", ilitek_proc_entry);
+		remove_proc_entry("setmode_1", ilitek_proc_entry);
+		remove_proc_entry("setmode_2", ilitek_proc_entry);
+
+		remove_proc_entry("ilitek", NULL);
+		ilitek_proc_entry = NULL;
+	}
+#endif
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_ts.h b/drivers/input/touchscreen/ilitek/ilitek_ts.h
new file mode 100644
index 000000000000..9ee0b13e08cd
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_ts.h
@@ -0,0 +1,268 @@
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ *
+ */
+
+#ifndef __ILITEK_TS_H__
+#define __ILITEK_TS_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include <linux/init.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+//#if defined(CONFIG_FB)
+#if 0
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#define ILITEK_BLANK_POWERDOWN			FB_BLANK_POWERDOWN
+#define	ILITEK_BLANK_UNBLANK			FB_BLANK_UNBLANK
+#define	ILITEK_EVENT_BLANK			FB_EVENT_BLANK
+#define	ILITEK_BLANK_NORMAL			FB_BLANK_NORMAL
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#define ILITEK_BL_ADDR				0x41
+#define ILITEK_SENSOR_ID_MASK			0xff
+
+#define ILITEK_PLAT_QCOM			1
+#define ILITEK_PLAT_MTK				2
+#define ILITEK_PLAT_ROCKCHIP			3
+#define ILITEK_PLAT_ALLWIN			4
+#define ILITEK_PLAT_AMLOGIC			5
+#define ILITEK_PLAT				ILITEK_PLAT_ROCKCHIP
+//#define CONFIG_QCOM_DRM
+#if ILITEK_PLAT == ILITEK_PLAT_QCOM
+#ifdef CONFIG_QCOM_DRM
+#include <linux/msm_drm_notify.h>
+#define ILITEK_BLANK_POWERDOWN			MSM_DRM_BLANK_POWERDOWN
+#define ILITEK_BLANK_UNBLANK			MSM_DRM_BLANK_UNBLANK
+#define ILITEK_EVENT_BLANK			MSM_DRM_EVENT_BLANK
+#define ILITEK_BLANK_NORMAL			MSM_DRM_BLANK_UNBLANK
+#endif
+#endif
+
+#define ILITEK_GESTURE_TYPES			\
+	X(Disable, 0, "disable")		\
+	X(Single_Click, 1, "single-click")	\
+	X(Double_Click, 2, "double-click")
+#define ILITEK_GESTURE_DEFAULT			Gesture_Disable
+
+#define X(_type, _id, _str)	Gesture_##_type = _id,
+enum Gesture_Type {
+	ILITEK_GESTURE_TYPES
+};
+#undef X
+
+#define ILITEK_LOW_POWER_TYPES		\
+	X(Sleep,	0, "sleep")	\
+	X(Idle,		1, "idle")	\
+	X(PowerOff,	2, "poweroff")
+#define ILITEK_LOW_POWER_DEFAULT		Low_Power_Sleep
+
+#define X(_type, _id, _str)	Low_Power_##_type = _id,
+enum Low_Power_Type {
+	ILITEK_LOW_POWER_TYPES
+};
+#undef X
+
+#define ILITEK_CHECKSUM_FAILED_RELEASE		true
+
+#define ILITEK_TOUCH_PROTOCOL_B
+//#define ILITEK_REPORT_PRESSURE
+//#define ILITEK_USE_LCM_RESOLUTION
+//#define ILITEK_ISR_PROTECT
+
+#define ILITEK_TUNING_MESSAGE
+#define ILITEK_REGISTER_SUSPEND_RESUME
+#define ILITEK_ESD_CHECK_ENABLE			0
+
+#define ILITEK_TOOL
+
+#define ILITEK_ROTATE_FLAG			0
+#define ILITEK_REVERT_X				0
+#define ILITEK_REVERT_Y				0
+#define TOUCH_SCREEN_X_MAX			1080	//LCD_WIDTH
+#define TOUCH_SCREEN_Y_MAX			1920	//LCD_HEIGHT
+#define ILITEK_RESOLUTION_MAX			16384
+//#define ILITEK_ENABLE_REGULATOR_POWER_ON
+#define ILITEK_GET_GPIO_NUM
+
+#define ILITEK_GET_TIME_FUNC_WITH_TIME		0
+#define ILITEK_GET_TIME_FUNC_WITH_JIFFIES	1
+#define ILITEK_GET_TIME_FUNC			ILITEK_GET_TIME_FUNC_WITH_JIFFIES
+
+#define DOUBLE_CLICK_DISTANCE			1000
+#define DOUBLE_CLICK_ONE_CLICK_USED_TIME	800
+#define DOUBLE_CLICK_NO_TOUCH_TIME		1000
+#define DOUBLE_CLICK_TOTAL_USED_TIME		(DOUBLE_CLICK_NO_TOUCH_TIME + (DOUBLE_CLICK_ONE_CLICK_USED_TIME * 2))
+
+//#define ILITEK_WAKELOCK_SUPPORT
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+#include <linux/wakelock.h>
+#endif
+
+#define ILITEK_TS_NAME				"ilitek_ts"
+
+#define ABSSUB(a, b) ((a > b) ? (a - b) : (b - a))
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+//#define MTK_UNDTS //no use dts and for mtk old version
+
+#define ILITEK_ENABLE_DMA
+#define ILITEK_DMA_SIZE				4096
+#define ILITEK_USE_MTK_INPUT_DEV
+
+#if define(ILITEK_USE_MTK_INPUT_DEV) && !defined(ILITEK_USE_LCM_RESOLUTION)
+#define ILITEK_USE_LCM_RESOLUTION
+#endif
+
+#ifdef ILITEK_GET_GPIO_NUM
+#undef ILITEK_GET_GPIO_NUM
+#endif
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include <linux/namei.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#ifdef MTK_UNDTS
+
+#define TPD_KEY_COUNT   4
+#define key_1           {60, 17000}	//auto define
+#define key_2           {180, 17000}
+#define key_3           {300, 17000}
+#define key_4           {420, 17000}
+
+#define TPD_KEYS        {KEY_MENU, KEY_HOMEPAGE, KEY_BACK, KEY_SEARCH}	//change for you panel key info
+#define TPD_KEYS_DIM    {{key_1, 50, 30 }, {key_2, 50, 30 }, {key_3, 50, 30 }, {key_4, 50, 30 } }
+
+struct touch_vitual_key_map_t {
+	int point_x;
+	int point_y;
+};
+
+extern struct touch_vitual_key_map_t touch_key_point_maping_array[];
+
+#include <mach/mt_pm_ldo.h>
+#include <cust_eint.h>
+#include "cust_gpio_usage.h"
+#include <mach/mt_gpio.h>
+#include <mach/mt_typedefs.h>
+#include <pmic_drv.h>
+#include <mach/mt_boot.h>
+#include <linux/dma-mapping.h>
+
+#else
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_MTK_BOOT
+#include "mt_boot_common.h"
+#endif
+
+#endif /* MTK_UNDTS */
+
+#include "tpd.h"
+extern struct tpd_device *tpd;
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#include <linux/irq.h>
+#include <linux/init-input.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+extern struct ctp_config_info config_info;
+#endif
+
+
+#ifndef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef MTK_UNDTS
+#define ILITEK_IRQ_GPIO				GPIO_CTP_EINT_PIN
+#define ILITEK_RESET_GPIO			GPIO_CTP_RST_PIN
+#else
+#define ILITEK_IRQ_GPIO				GTP_INT_PORT
+#define ILITEK_RESET_GPIO			GTP_RST_PORT
+#endif
+#elif ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#define ILITEK_IRQ_GPIO				config_info.int_number
+#define ILITEK_RESET_GPIO			config_info.wakeup_gpio.gpio
+#else
+#define ILITEK_IRQ_GPIO				9
+#define ILITEK_RESET_GPIO			10
+#endif
+#endif
+
+#define ILITEK_I2C_RETRY_COUNT			3
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+extern int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg);
+extern int ilitek_resume_allwin(struct i2c_client *client);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.c b/drivers/input/touchscreen/ilitek/ilitek_update.c
new file mode 100644
index 000000000000..d7e873a3c204
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_update.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifndef __KERNEL__
+static int hex_to_bin(u8 ch)
+{
+	u8 cu = ch & 0xdf;
+	return -1 +
+		((ch - '0' +  1) & (unsigned)((ch - '9' - 1) &
+		('0' - 1 - ch)) >> 8) +
+		((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) &
+		('A' - 1 - cu)) >> 8);
+}
+
+static int hex2bin(u8 *dst, const u8 *src, size_t count)
+{
+	int hi = 0, lo = 0;
+
+	while (count--) {
+		hi = hex_to_bin(*src++);
+		lo = hex_to_bin(*src++);
+		if (hi < 0 || lo < 0) {
+			TP_ERR(NULL, "hex_to_bin failed, hi: %d, lo: %d\n",
+				hi, lo);
+			return -EINVAL;
+		}
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
+#endif
+
+static u32 get_tag_addr(u32 start, u32 end,
+			const u8 *buf, unsigned int buf_size,
+			const u8 *tag, unsigned int tag_size)
+{
+	unsigned int i;
+
+	for (i = start; i <= end - tag_size && i < buf_size - tag_size; i++) {
+		if (!memcmp(buf + i, tag, tag_size))
+			return i + tag_size + 1;
+	}
+
+	return end;
+}
+
+static u32 get_endaddr(u32 start, u32 end, const u8 *buf,
+		       unsigned int buf_size, bool is_AP)
+{
+	u32 addr;
+	u8 tag[32];
+	const u8 ap_tag[] = "ILITek AP CRC   ";
+	const u8 blk_tag[] = "ILITek END TAG  ";
+
+	_memset(tag, 0xFF, sizeof(tag));
+	_memcpy(tag + 16, (is_AP) ? ap_tag : blk_tag, 16);
+
+	addr = get_tag_addr(start, end, buf, buf_size, tag, sizeof(tag));
+	TP_DBG(NULL, "find tag in start/end: 0x%x/0x%x, tag addr: 0x%x\n",
+		start, end, addr);
+
+	return addr;
+}
+
+static int decode_mm(struct ilitek_fw_handle *fw, u32 addr,
+		     u8 *buf, u32 buf_size)
+{
+	u8 i;
+	union mapping_info *mapping;
+
+	TP_INFO(NULL, "------------Memory Mapping information------------\n");
+	TP_INFO(NULL, "memory-mapping-info addr: 0x%x\n", addr);
+
+	mapping = (union mapping_info *)(buf + addr);
+	_memset(fw->file.ic_name, 0, sizeof(fw->file.ic_name));
+
+	switch (mapping->mapping_ver[2]) {
+	case 0x2:
+		_memcpy(fw->file.ic_name, mapping->ic_name,
+			sizeof(mapping->ic_name));
+		break;
+	default:
+	case 0x1:
+		_sprintf(fw->file.ic_name, 0, "%02x%02x",
+			mapping->ic_name[1], mapping->ic_name[0]);
+		break;
+	}
+
+	if (!strcmp(fw->file.ic_name, "2133"))
+		_sprintf(fw->file.ic_name, 0, "2132S");
+
+	if (fw->dev && strcmp(fw->dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " not matched\n",
+			fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EINVAL;
+	}
+
+	TP_MSG(NULL, "Hex Mapping Ver.: 0x%x\n",
+		le32(mapping->mapping_ver, 3));
+	TP_MSG(NULL, "Hex Protocol: 0x%x\n",
+		le32(mapping->protocol_ver, 3));
+	TP_MSG(NULL, "Hex MCU Ver.: " PFMT_C8 "\n", fw->file.ic_name);
+
+	_memset(fw->file.fw_ver, 0, sizeof(fw->file.fw_ver));
+	fw->file.fwid = 0xffff;
+
+	fw->file.mm_addr = addr;
+	switch (addr) {
+	case 0x4038:
+	case 0x4020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x2C007];
+		fw->file.fw_ver[5] = buf[0x2C006];
+		fw->file.fw_ver[6] = buf[0x2C005];
+		fw->file.fw_ver[7] = buf[0x2C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x3020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x3C007];
+		fw->file.fw_ver[5] = buf[0x3C006];
+		fw->file.fw_ver[6] = buf[0x3C005];
+		fw->file.fw_ver[7] = buf[0x3C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x2020:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x2033];
+		fw->file.fw_ver[1] = buf[0x2032];
+		fw->file.fw_ver[2] = buf[0x2031];
+		fw->file.fw_ver[3] = buf[0x2030];
+		fw->file.fw_ver[4] = buf[0xF004];
+		fw->file.fw_ver[5] = buf[0xF005];
+		fw->file.fw_ver[6] = buf[0xF006];
+		fw->file.fw_ver[7] = buf[0xF007];
+
+		/* for V3 251x IC, get AP crc and DF checksum */
+		fw->file.blocks[0].check = get_crc(fw->file.blocks[0].start,
+			fw->file.blocks[0].end - 1, buf, buf_size);
+
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+
+	case 0x500:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x52D];
+		fw->file.fw_ver[1] = buf[0x52C];
+		fw->file.fw_ver[2] = buf[0x52B];
+		fw->file.fw_ver[3] = buf[0x52A];
+		fw->file.fw_ver[4] = buf[0x1F404];
+		fw->file.fw_ver[5] = buf[0x1F405];
+		fw->file.fw_ver[6] = buf[0x1F406];
+		fw->file.fw_ver[7] = buf[0x1F407];
+
+		/* for V3 231x IC, get AP checksum and DF checksum */
+		fw->file.blocks[0].check = get_checksum(
+			fw->file.blocks[0].start,
+			fw->file.blocks[0].end + 1, buf, buf_size);
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+	default:
+		fw->file.mm_size = 0;
+		break;
+	}
+
+	TP_MSG(NULL, "file fwid: 0x%04x\n", fw->file.fwid);
+
+	TP_INFO(NULL, "File FW Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[0], fw->file.fw_ver[1],
+		fw->file.fw_ver[2], fw->file.fw_ver[3]);
+	TP_INFO(NULL, "File Customer Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[4], fw->file.fw_ver[5],
+		fw->file.fw_ver[6], fw->file.fw_ver[7]);
+
+	if (le32(mapping->mapping_ver, 3) < 0x10000)
+		goto memory_mapping_end;
+
+	TP_INFO(NULL, "File Tuning Version: %02x-%02x-%02x-%02x\n",
+		mapping->_lego.tuning_ver[3], mapping->_lego.tuning_ver[2],
+		mapping->_lego.tuning_ver[1], mapping->_lego.tuning_ver[0]);
+
+	if (mapping->_lego.block_num > ARRAY_SIZE(fw->file.blocks)) {
+		TP_ERR(NULL, "Unexpected block num: " PFMT_U8 " > %u\n",
+			mapping->_lego.block_num,
+			(unsigned int)ARRAY_SIZE(fw->file.blocks));
+		goto memory_mapping_end;
+	}
+
+	fw->file.block_num = mapping->_lego.block_num;
+
+	TP_MSG(NULL, "Total " PFMT_U8 " blocks\n", fw->file.block_num);
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].start =
+			le32(mapping->_lego.blocks[i].addr, 3);
+		fw->file.blocks[i].end = (i == fw->file.block_num - 1) ?
+			le32(mapping->_lego.end_addr, 3) :
+			le32(mapping->_lego.blocks[i + 1].addr, 3);
+
+		/*
+		 * get end addr. of block,
+		 * i.e. address of block's final byte of crc.
+		 */
+		fw->file.blocks[i].end = get_endaddr(
+			fw->file.blocks[i].start, fw->file.blocks[i].end,
+			buf, buf_size, i == 0);
+
+		fw->file.blocks[i].check = get_crc(fw->file.blocks[i].start,
+			fw->file.blocks[i].end - 1,
+			buf, buf_size);
+
+		TP_MSG(NULL, "Block[%u], start:0x%x end:0x%x, crc:0x%x\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			fw->file.blocks[i].check);
+	}
+
+memory_mapping_end:
+	TP_INFO(NULL, "--------------------------------------------------\n");
+
+	return 0;
+}
+
+static int decode_hex(struct ilitek_fw_handle *fw, u8 *hex,
+		      u32 start, u32 end,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	u8 info[4], data[16];
+	unsigned int i, len, addr, type, exaddr = 0;
+	u32 mapping_info_addr = 0;
+
+	/* m2v hex has another block at the end of hex file */
+	u8 j = (fw->m2v) ? fw->file.block_num : 0;
+
+	fw->file.blocks[j].start = (~0U);
+	fw->file.blocks[j].end = 0x0;
+	fw->file.blocks[j].check = 0x0;
+	fw->file.blocks[j + 1].start = (~0U);
+	fw->file.blocks[j + 1].end = 0x0;
+	fw->file.blocks[j + 1].check = 0x0;
+
+	for (i = start; i < end; i++) {
+		/* filter out non-hexadecimal characters */
+		if (hex_to_bin(hex[i]) < 0)
+			continue;
+
+		error = hex2bin(info, hex + i, sizeof(info));
+		if (error < 0)
+			return error;
+
+		len = info[0];
+		addr = be32(info + 1, 2);
+		type = info[3];
+
+		error = hex2bin(data, hex + i + 8, len);
+		if (error < 0)
+			return error;
+
+		switch (type) {
+		case 0xAC:
+			mapping_info_addr = be32(data, len);
+			break;
+
+		case 0xAD:
+			fw->file.blocks[1].start = be32(data, len);
+			_memset(buf + fw->file.blocks[1].start, 0, 0x1000);
+			break;
+
+		case 0xBA:
+			if (be32(data, len) != 2U)
+				break;
+
+			TP_MSG(NULL, "start to decode M2V part of hex file\n");
+			fw->m2v = true;
+			////Reed Add : 20230721��������2326��������Decode_mm,�ټ�������M2V���֡���
+			if (mapping_info_addr)
+				decode_mm(fw, mapping_info_addr, buf, buf_size);
+			return decode_hex(fw, hex, i + 10 + len * 2 + 1, end,
+					  fw->m2v_buf, ILITEK_FW_BUF_SIZE);
+
+		case 0x01:
+			goto success_return;
+
+		case 0x02:
+			exaddr = be32(data, len) << 4;
+			break;
+
+		case 0x04:
+			exaddr = be32(data, len) << 16;
+			break;
+
+		case 0x05:
+			TP_MSG(NULL, "hex data type: 0x%x, start linear address: 0x%x\n",
+				type, be32(data, len));
+			break;
+
+		case 0x00:
+			addr += exaddr;
+
+			if (addr + len > buf_size) {
+				TP_ERR(NULL, "hex addr: 0x%x, buf size: 0x%x OOB\n",
+					addr + len, buf_size);
+				return -ENOBUFS;
+			}
+			_memcpy(buf + addr, data, len);
+
+			fw->file.blocks[j].start =
+				MIN(fw->file.blocks[j].start, addr);
+
+			if (addr + len < fw->file.blocks[j + 1].start) {
+				fw->file.blocks[j].end =
+					MAX(fw->file.blocks[j].end,
+					    addr + len - 1);
+				fw->file.blocks[j].check += get_checksum(
+					0, len, data, sizeof(data));
+			} else {
+				fw->file.blocks[j + 1].end =
+					MAX(fw->file.blocks[j + 1].end,
+					    addr + len - 1);
+				fw->file.blocks[j + 1].check += get_checksum(
+					0, len, data, sizeof(data));
+			}
+
+			break;
+		default:
+			TP_ERR(NULL, "unexpected type:0x%x in hex, len:%u, addr:0x%x\n",
+				type, len, addr);
+			return -EINVAL;
+		}
+
+		i = i + 10 + len * 2;
+	}
+
+success_return:
+	if (fw->m2v)
+		fw->m2v_checksum = fw->file.blocks[fw->file.block_num].check;
+	if (mapping_info_addr)
+		return decode_mm(fw, mapping_info_addr, fw->file.buf, buf_size);
+
+	return 0;
+}
+
+static int decode_bin(struct ilitek_fw_handle *fw,
+		      u8 *bin, u32 bin_size,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 mapping_info_addr;
+
+	if (!dev) {
+		TP_ERR(NULL, "offline decode bin file is not supported\n");
+		return -EINVAL;
+	}
+
+	if (bin_size > buf_size) {
+		TP_ERR(dev->id, "bin file size: 0x%x, buf size: 0x%x OOB\n",
+			bin_size, buf_size);
+		return -ENOBUFS;
+	}
+	_memcpy(buf, bin, bin_size);
+
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		return error;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		return error;
+
+	switch (dev->protocol.flag) {
+	case PTL_V6:
+		mapping_info_addr = dev->mcu_info.mm_addr;
+		break;
+
+	case PTL_V3:
+		/*
+		 * For 231x: AP checksum and DF checksum, DF start addr: 0x1f000
+		 * For 251x: AP crc and DF checksum, DF start addr: 0xf000
+		 */
+		if (is_231x(dev)) {
+			mapping_info_addr = 0x500;
+
+			fw->file.blocks[1].start = 0x1f000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x0;
+
+			/* +2 to get end addr. of last byte of 4 bytes checksum */
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true) + 2;
+
+		} else {
+			mapping_info_addr = 0x2020;
+			fw->file.blocks[1].start = 0xf000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x2000;
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true);
+		}
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * take the whole "buf" into decode_mm, "buf" should be
+	 * properly initialized, and the size should be
+	 * larger than "bin", which reduce OOB issue.
+	 */
+	return decode_mm(fw, mapping_info_addr, buf, buf_size);
+}
+
+#ifdef ILITEK_BOOT_UPDATE
+#include "ilitek_fw.h"
+
+static int decode_ili(struct ilitek_fw_handle *fw,
+		      u8 *buf, u32 buf_size)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 *ili_buf = CTPM_FW;
+	int size = sizeof(CTPM_FW);
+	u8 id;
+
+#if defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		if (!support_sensor_id(dev)) {
+			TP_WARN(dev->id,
+				"protocol: 0x%x, mode: 0x" PFMT_X8 ", sensor-id not supported, take default fw(id: 0x%x)\n",
+				dev->protocol.ver, dev->ic[0].mode,
+				ILITEK_BOOT_UPDATE_DEF_ID);
+			fw->file.id = ILITEK_BOOT_UPDATE_DEF_ID;
+			break;
+		}
+
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id >= ARRAY_SIZE(ili_arr)) {
+			TP_ERR(dev->id, "invalid sensor id: " PFMT_U8 " >= %d\n",
+				dev->sensor.id, (int)ARRAY_SIZE(ili_arr));
+			return -EINVAL;
+		}
+
+		fw->file.id = id;
+		ili_buf = ili_arr[id].buf;
+		size = ili_arr[id].size;
+		break;
+	case ili_default:
+		break;
+	default:
+		TP_ERR(dev->id, "unexpected ili-select-type: %u\n",
+			__ili_select_type__);
+		return -EINVAL;
+	}
+#endif
+
+	if (!ili_buf || size < 32)
+		return -EINVAL;
+
+	fw->setting.fw_ver_check = true;
+	fw->setting.fw_ver_policy = 0;
+	_memcpy(fw->setting.fw_ver, ili_buf + 18, 8);
+
+	TP_MSG_ARR(dev->id, "IC  fw ver:", TYPE_U8, 8, dev->fw_ver);
+	TP_MSG_ARR(dev->id, "Hex fw ver:", TYPE_U8, 8, fw->setting.fw_ver);
+
+	return decode_bin(fw, ili_buf + 32, size - 32, buf, buf_size);
+}
+#endif
+
+static bool need_retry(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 id;
+
+#if defined(ILITEK_BOOT_UPDATE) && defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	if (fw->file.type != fw_ili)
+		return false;
+
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id == fw->file.id)
+			break;
+
+		/* reload correct fw if sensor-id not matched with default fw */
+		TP_MSG(dev->id, "sensor id: 0x%x, file: 0x%x not matched, "
+			"reload fw again\n",
+			id, fw->file.id);
+
+		if (ilitek_update_load_fw(fw, "ilitek.ili") < 0) {
+			TP_ERR(dev->id, "reload ilitek.ili failed\n");
+			return false;
+		}
+
+		/* MUST DISABLE fw ver check for the second try */
+		fw->setting.fw_ver_check = false;
+
+		return true;
+	default:
+		break;
+	}
+#endif
+
+	UNUSED(dev);
+	UNUSED(id);
+
+	return false;
+}
+
+static int decode_firmware(struct ilitek_fw_handle *fw, WCHAR *filename)
+{
+	int error;
+	int size = 0;
+	u8 *buf;
+	WCHAR *file_ext;
+
+	/* initialization */
+	_memset(fw->file.buf, 0xFF, fw->file.buf_size);
+	_memset(fw->m2v_buf, 0xFF, ILITEK_FW_BUF_SIZE);
+	fw->m2v = false;
+	/*
+	 * set block num 2 for V3 AP and Data Flash as default,
+	 * for V6, block num would be updated after decoding memory mapping.
+	 */
+	fw->file.block_num = 2;
+	fw->file.blocks[0].start = (~0U);
+	fw->file.blocks[0].end = 0x0;
+	fw->file.blocks[0].check = 0x0;
+	fw->file.blocks[1].start = (~0U);
+	fw->file.blocks[1].end = 0x0;
+	fw->file.blocks[1].check = 0x0;
+
+	TP_MSG(NULL, "start to read fw file: " PFMT_C16 "\n", filename);
+
+	buf = (u8 *)CALLOC(ILITEK_FW_FILE_SIZE, 1);
+	if (!buf)
+		return -ENOMEM;
+
+	file_ext = WCSRCHR(filename, '.');
+	if (!file_ext)
+		return -ENOENT;
+
+	/* no need to read .ili file */
+	if (WCSCASECMP(file_ext, ".ili")) {
+		if (!fw->cb.read_fw) {
+			error = -EFAULT;
+			TP_ERR(NULL, "read fw callback not registered\n");
+			goto err_free;
+		}
+
+		size = fw->cb.read_fw(filename, buf, ILITEK_FW_FILE_SIZE,
+				      fw->_private);
+
+		error = size;
+		if (error < 0) {
+			TP_ERR(NULL, "read fw file: " PFMT_C16 " failed, err: %d\n",
+				filename, error);
+			goto err_free;
+		}
+	}
+
+	if (!WCSCASECMP(file_ext, ".hex")) {
+		fw->file.type = fw_hex;
+		error = decode_hex(fw, buf, 0, size, fw->file.buf,
+				   fw->file.buf_size);
+	} else if (!WCSCASECMP(file_ext, ".bin")) {
+		fw->file.type = fw_bin;
+		error = decode_bin(fw, buf, size, fw->file.buf,
+				   fw->file.buf_size);
+	}
+#ifdef ILITEK_BOOT_UPDATE
+	else if (!WCSCASECMP(file_ext, ".ili")) {
+		fw->file.type = fw_ili;
+		error = decode_ili(fw, fw->file.buf, fw->file.buf_size);
+	}
+#endif
+	else {
+		error = -EINVAL;
+	}
+
+err_free:
+	CFREE(buf);
+
+	return error;
+}
+
+static bool need_fw_update_v3(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool fw_file_has_data_flash =
+		(fw->file.blocks[1].start < fw->file.blocks[1].end);
+	bool need = false;
+
+	TP_INFO(dev->id, "------------V3 AP/DF Info.------------\n");
+
+	fw->file.blocks[0].check_match = (fw->setting.force_update) ?
+		false : dev->ic[0].crc[0] == fw->file.blocks[0].check;
+
+	TP_INFO(dev->id, "AP block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+		fw->file.blocks[0].start, fw->file.blocks[0].end,
+		dev->ic[0].crc[0], fw->file.blocks[0].check,
+		(fw->file.blocks[0].check_match) ? "matched" : "not matched");
+
+	fw->file.blocks[1].check_match = true;
+	if (fw_file_has_data_flash) {
+		fw->file.blocks[1].check_match = (fw->setting.force_update) ?
+			false : dev->ic[0].crc[1] == fw->file.blocks[1].check;
+
+		TP_INFO(dev->id, "DF block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(fw->file.blocks[1].check_match) ?
+			"matched" : "not matched");
+	} else if (!is_231x(dev)) {
+		/*
+		 * for 251x ICs, if no data flash in fw file,
+		 * need to switch to BL mode then erase data flash forcely.
+		 */
+		need = true;
+	}
+
+	TP_INFO(dev->id, "--------------------------------------\n");
+
+	need |= (!fw->file.blocks[0].check_match ||
+		 !fw->file.blocks[1].check_match);
+
+	if (dev->ic[0].mode == BL_MODE)
+		return true;
+
+	return need;
+}
+
+static bool need_fw_update_v6(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	bool need = false;
+
+	TP_INFO(dev->id, "------------Lego Block Info.------------\n");
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].check_match = (fw->setting.force_update) ?
+			false : (dev->ic[0].crc[i] == fw->file.blocks[i].check);
+
+		need = (!fw->file.blocks[i].check_match) ? true : need;
+
+		TP_INFO(dev->id, "Block[" PFMT_U8 "]: Start/End Addr.: 0x%x/0x%x, IC/File CRC: 0x%x/0x%x " PFMT_C8 "\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			dev->ic[0].crc[i], fw->file.blocks[i].check,
+			(fw->file.blocks[i].check_match) ?
+			"matched" : "not matched");
+	}
+
+	/* check BL mode firstly before AP-cmd related varaible, ex: ic_num */
+	if (dev->ic[0].mode == BL_MODE) {
+		need = true;
+		goto force_return;
+	}
+
+	for (i = 1; i < dev->tp_info.ic_num; i++) {
+		TP_INFO(dev->id, "Master/Slave[" PFMT_U8 "] CRC: 0x%x/0x%x " PFMT_C8 ", Slave Mode: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, fw->file.blocks[0].check, dev->ic[i].crc[0],
+			(fw->file.blocks[0].check == dev->ic[i].crc[0]) ?
+			"matched" : "not matched",
+			dev->ic[i].mode, dev->ic[i].mode_str);
+
+		if (dev->ic[i].crc[0] == fw->file.blocks[0].check &&
+			dev->ic[i].mode == AP_MODE)
+			continue;
+		need = true;
+	}
+
+	if (fw->m2v) {
+		api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &fw->m2v_checksum);
+
+		fw->file.blocks[fw->file.block_num].check_match =
+			fw->file.blocks[fw->file.block_num].check ==
+			fw->m2v_checksum;
+		TP_INFO(dev->id, "M2V IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->m2v_checksum,
+			fw->file.blocks[fw->file.block_num].check,
+			(fw->file.blocks[fw->file.block_num].check_match) ?
+			"matched" : "not matched");
+
+		fw->m2v_need_update =
+			!fw->file.blocks[fw->file.block_num].check_match ||
+			fw->setting.force_update;
+		need |= fw->m2v_need_update;
+	}
+
+force_return:
+	TP_INFO(dev->id, "----------------------------------------\n");
+
+	return need;
+}
+
+static bool need_fw_update(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool need = false;
+
+	struct ilitek_fw_settings *set = &fw->setting;
+	u64 dev_fw_ver, file_fw_ver;
+
+	if (dev->protocol.flag == PTL_V3)
+		need = need_fw_update_v3(fw);
+	else if (dev->protocol.flag == PTL_V6)
+		need = need_fw_update_v6(fw);
+
+	if (fw->cb.update_fw_ic_info)
+		fw->cb.update_fw_ic_info(false,
+					 dev->fw_ver, dev->ic[0].crc,
+					 fw->file.block_num, fw->_private);
+
+	if (set->force_update)
+		return true;
+	else if (set->fw_check_only)
+		return false;
+
+	if (set->fw_ver_check && dev->ic[0].mode == AP_MODE) {
+		TP_INFO(dev->id, "IC FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			dev->fw_ver[0], dev->fw_ver[1], dev->fw_ver[2],
+			dev->fw_ver[3], dev->fw_ver[4], dev->fw_ver[5],
+			dev->fw_ver[6], dev->fw_ver[7]);
+		TP_INFO(dev->id, "File FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			set->fw_ver[0], set->fw_ver[1],
+			set->fw_ver[2], set->fw_ver[3],
+			set->fw_ver[4], set->fw_ver[5],
+			set->fw_ver[6], set->fw_ver[7]);
+
+		dev_fw_ver =
+			U82U64(dev->fw_ver[0], 7) + U82U64(dev->fw_ver[1], 6) +
+			U82U64(dev->fw_ver[2], 5) + U82U64(dev->fw_ver[3], 4) +
+			U82U64(dev->fw_ver[4], 3) + U82U64(dev->fw_ver[5], 2) +
+			U82U64(dev->fw_ver[6], 1) + U82U64(dev->fw_ver[7], 0);
+		file_fw_ver =
+			U82U64(set->fw_ver[0], 7) + U82U64(set->fw_ver[1], 6) +
+			U82U64(set->fw_ver[2], 5) + U82U64(set->fw_ver[3], 4) +
+			U82U64(set->fw_ver[4], 3) + U82U64(set->fw_ver[5], 2) +
+			U82U64(set->fw_ver[6], 1) + U82U64(set->fw_ver[7], 0);
+
+		TP_MSG(dev->id, "IC fw ver: 0x" PFMT_X64 ", File fw ver: 0x" PFMT_X64 "\n",
+			(long long unsigned int)dev_fw_ver,
+			(long long unsigned int)file_fw_ver);
+
+		if (file_fw_ver > dev_fw_ver) {
+			TP_INFO(dev->id, "IC FW version is older than File FW version\n");
+			return true;
+		} else if (file_fw_ver == dev_fw_ver) {
+			TP_INFO(dev->id, "File FW version is the same, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_same) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_same) ?
+				need : false;
+		} else {
+			TP_INFO(dev->id, "File FW version is older, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				need : false;
+		}
+	}
+
+	return need;
+}
+
+static int update_master(struct ilitek_fw_handle *fw, int idx, u32 len)
+{
+	int error = 0;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+	u16 file_crc;
+	int retry = 3;
+
+	TP_MSG(dev->id, "updating block[%d], data len: %u, start/end addr: 0x%x/0x%x\n",
+		idx, len, fw->file.blocks[idx].start, fw->file.blocks[idx].end);
+
+err_retry:
+	if ((dev->setting.no_retry && error < 0) || retry-- < 0)
+		return (error < 0) ? error : -EINVAL;
+
+	error = api_write_enable_v6(dev, false, false,
+				     fw->file.blocks[idx].start,
+				     fw->file.blocks[idx].end);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[idx].start;
+	     i < fw->file.blocks[idx].end; i += len) {
+		/*
+		 * check end addr. of data write buffer is within valid range.
+		 */
+		if (i + len > END_ADDR_LEGO) {
+			TP_ERR(dev->id, "block[%d] write addr. 0x%x + 0x%x > 0x%x OOB\n",
+				idx, i, len, END_ADDR_LEGO);
+			return -EINVAL;
+		}
+
+		_memcpy(dev->wbuf + 1, fw->file.buf + i, len);
+		error = api_write_data_v6(dev, len + 1);
+
+		if (error < 0)
+			goto err_retry;
+
+		fw->progress_curr = MIN(i + len - fw->file.blocks[idx].offset,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "block[%d] update progress: " PFMT_U8 "%%\n",
+			idx, fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	file_crc = get_crc(fw->file.blocks[idx].start,
+			   fw->file.blocks[idx].end - 1,
+			   fw->file.buf, fw->file.buf_size);
+	dev->ic[0].crc[idx] =
+		api_get_block_crc_by_addr(dev, CRC_GET,
+					  fw->file.blocks[idx].start,
+					  fw->file.blocks[idx].end);
+
+	TP_INFO(dev->id, "block[%d]: start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+		idx, fw->file.blocks[idx].start, fw->file.blocks[idx].end,
+		dev->ic[0].crc[idx], file_crc,
+		(file_crc == dev->ic[0].crc[idx]) ?
+		"matched" : "not matched");
+
+	if (file_crc != dev->ic[0].crc[idx]) {
+		error = -EINVAL;
+		goto err_retry;
+	}
+
+	return 0;
+}
+
+static int update_slave(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[0].crc[0] == dev->ic[i].crc[0] &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "updating slave, master/slave[" PFMT_U8 "] crc: 0x%x/0x%x\n",
+			i, dev->ic[0].crc[0], dev->ic[i].crc[0]);
+		error = api_access_slave(dev, 0x3, CMD_WRITE_DATA_V6, NULL);
+		if (error < 0)
+			return error;
+		error = api_write_enable_v6(dev, false, true,
+					    fw->file.blocks[0].start,
+					    fw->file.blocks[0].end);
+		if (error < 0)
+			return error;
+
+		goto success_return;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[i].mode == AP_MODE &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "changing slave[" PFMT_U8 "]: 0x" PFMT_X8 " to AP mode\n",
+			i, dev->ic[i].mode);
+		error = api_access_slave(dev, 0x3, CMD_SET_AP_MODE, NULL);
+		if (error < 0)
+			return error;
+		break;
+	}
+
+success_return:
+	return 0;
+}
+
+static int update_M3_M2V(struct ilitek_fw_handle *fw, u32 len)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 i;
+	u8 buf[6];
+	u32 AdressRange, M2V_Buf_Len, CheckSumRange;
+	u16 AddDataCount;
+
+	u8 j = fw->file.block_num;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	//Reed Add : 20230721
+	AdressRange = fw->file.blocks[j].end - fw->file.blocks[j].start;
+	M2V_Buf_Len = fw->file.blocks[j].end - fw->file.blocks[j].start + 1;
+	CheckSumRange = fw->file.blocks[j].check;
+	AddDataCount = len - M2V_Buf_Len % len;
+	AddDataCount = (AddDataCount >= 8) ? AddDataCount : AddDataCount + len;
+	AdressRange += AddDataCount;
+	CheckSumRange += AddDataCount * 0xFF;
+
+	error = api_to_bl_mode_m2v(dev, true);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_set_data_len(dev, len);
+	if (error < 0)
+		return error;
+
+	TP_INFO(dev->id, "updating M2V, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+		fw->file.blocks[j].start, fw->file.blocks[j].end,
+		fw->file.blocks[j].check);
+
+	//Reed Add : 20230721
+	buf[0] = (AdressRange >> 16) & 0xFF;
+	buf[1] = (AdressRange >> 8) & 0xFF;
+	buf[2] = AdressRange & 0xFF;
+	buf[3] = (CheckSumRange >> 16) & 0xFF;
+	buf[4] = (CheckSumRange >> 8) & 0xFF;
+	buf[5] = CheckSumRange & 0xFF;
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+	error = api_access_slave(dev, 0x80, CMD_WRITE_ENABLE, buf);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[j].start;
+	     i < fw->file.blocks[j].end; i += len) {
+		_memcpy(dev->wbuf + 1, fw->m2v_buf + i, len);
+
+		error = api_write_data_m2v(dev, len + 1);
+		if (error < 0)
+			return error;
+
+		fw->progress_curr = MIN(fw->progress_curr + len,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "m2v update progress: " PFMT_U8 "%%\n", fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	error = api_to_bl_mode_m2v(dev, false);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_access_slave(dev, 0x80, CMD_GET_FW_VER, fw->m2v_fw_ver);
+	if (error < 0)
+		return error;
+
+	TP_MSG_ARR(dev->id, "update M2V success, fw version:", TYPE_U8,
+		   8, fw->m2v_fw_ver);
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_8(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	error = api_set_data_len(dev, fw->update_len);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		if (fw->file.blocks[i].check_match)
+			continue;
+
+		error = update_master(fw, i, fw->update_len);
+		if (error < 0) {
+			TP_ERR(dev->id, "Upgrade Block:" PFMT_U8 " failed, err: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	error = api_to_bl_mode(dev, false, fw->file.blocks[0].start,
+			       fw->file.blocks[0].end);
+	if (error < 0)
+		return error;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	/* get tp info. for updating ic_num */
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.ic_num > 1) {
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(true, fw->_private);
+		error = update_slave(fw);
+
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(false, fw->_private);
+
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade slave failed, err: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	if (fw->m2v && fw->m2v_need_update) {
+		error = update_M3_M2V(fw, 1024);
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade m2v slave failed, err: %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_7(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+
+	/*
+	 * Erase data initially for the case that hex w/o data flash section.
+	 * Due to historical factor, please double confirm before modify here.
+	 */
+	error = api_erase_data_v3(dev);
+	if (error < 0)
+		return error;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false,
+					     fw->file.blocks[1].end + 1,
+					     fw->file.blocks[1].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start;
+		     i <= fw->file.blocks[1].end; i += 32) {
+			_memset(dev->wbuf + 1, 0, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->cb.delay_ms(50);
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[1] = (le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+
+		TP_INFO(dev->id, "DF block, start/end addr.: 0x%x/0x%x, ic/file checkksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(dev->ic[0].crc[1] == fw->file.blocks[1].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[1] != fw->file.blocks[1].check)
+			return -EFAULT;
+	}
+
+	/*
+	 * Update AP code forcely if AP crc/checksum not match or
+	 * Data Flash has been updated.
+	 */
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true,
+					     fw->file.blocks[0].end + 1,
+					     fw->file.blocks[0].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start;
+		     i <= fw->file.blocks[0].end; i += 32) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[0] =
+			(le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+		TP_INFO(dev->id, "AP block, start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			dev->ic[0].crc[0], fw->file.blocks[0].check,
+			(dev->ic[0].crc[0] == fw->file.blocks[0].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[0] != fw->file.blocks[0].check)
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_6(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	struct ilitek_ts_callback *cb = &dev->cb;
+	unsigned int i, j;
+
+	u32 bytes, end, check;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[1].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[1].end + 1) % 32) : 31;
+		end = fw->file.blocks[1].end + bytes;
+		check = fw->file.blocks[1].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) DF start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[1].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start, j = 0;
+		     i <= fw->file.blocks[1].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+	}
+
+	/*
+	* Update AP code forcely if AP crc/checksum not match or
+	* Data Flash has been updated.
+	*/
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[0].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[0].end + 1) % 32) : 31;
+		end = fw->file.blocks[0].end + bytes;
+		check = fw->file.blocks[0].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) AP start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[0].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start, j = 0;
+			i <= fw->file.blocks[0].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[0].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+
+#ifdef ILITEK_BOOT_UPDATE
+		/*
+		 * .ili file fill DF section with default 0xFF, but not 0
+		 * which make flow stuck in BL mode.
+		 * so HW-reset is need before switching to AP mode.
+		 * remove HW-reset after fixing .ili converter bug.
+		 */
+		reset_helper(dev);
+#endif
+	}
+
+	return 0;
+}
+
+static void update_progress(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	unsigned int last_end = 0, last_offset = 0;
+
+	fw->progress = 0;
+	fw->progress_max = 0;
+	fw->progress_curr = 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		fw->progress_max +=
+			(fw->file.blocks[1].end > fw->file.blocks[1].start) ?
+			fw->file.blocks[1].end - fw->file.blocks[1].start : 0;
+		fw->progress_max +=
+			(fw->file.blocks[0].end > fw->file.blocks[0].start) ?
+			fw->file.blocks[0].end - fw->file.blocks[0].start : 0;
+		break;
+
+	case PTL_V6:
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->file.blocks[i].check_match)
+				continue;
+
+			fw->progress_max +=
+				fw->file.blocks[i].end -
+				fw->file.blocks[i].start;
+			last_offset += fw->file.blocks[i].start - last_end;
+			fw->file.blocks[i].offset = last_offset;
+
+			last_end = fw->file.blocks[i].end;
+		}
+
+		if (fw->m2v_need_update) {
+			fw->progress_max +=
+				fw->file.blocks[fw->file.block_num].end -
+				fw->file.blocks[fw->file.block_num].start;
+		}
+
+		break;
+	}
+}
+
+void *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			 struct ilitek_update_callback *cb, void *_private)
+{
+	struct ilitek_fw_handle *fw;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)_dev;
+
+	if (need_update_ts_info && dev && api_update_ts_info(dev) < 0)
+		return NULL;
+
+	fw = (struct ilitek_fw_handle *)MALLOC(sizeof(*fw));
+	if (!fw)
+		return NULL;
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(fw, 0, sizeof(*fw));
+	fw->dev = (dev) ? dev : NULL;
+
+	/* initial update-len to default UPDATE_LEN */
+	fw->update_len = UPDATE_LEN;
+
+	fw->dev = dev;
+	fw->_private = _private;
+	fw->file.buf_size = ILITEK_FW_BUF_SIZE;
+	fw->file.buf = (u8 *)CALLOC(fw->file.buf_size, 1);
+	if (!fw->file.buf)
+		goto err_free_fw;
+
+	fw->m2v_buf = (u8 *)CALLOC(ILITEK_FW_BUF_SIZE, 1);
+	if (!fw->m2v_buf)
+		goto err_free_fw_buf;
+
+	if (cb)
+		_memcpy(&fw->cb, cb, sizeof(*cb));
+
+	return fw;
+
+err_free_fw_buf:
+	CFREE(fw->file.buf);
+err_free_fw:
+	FREE(fw);
+
+	return NULL;
+}
+
+void ilitek_update_exit(void *handle)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	if (fw->file.buf)
+		CFREE(fw->file.buf);
+
+	if (fw->m2v_buf)
+		CFREE(fw->m2v_buf);
+
+	if (fw)
+		FREE(fw);
+}
+
+void ilitek_update_set_data_length(void *handle, u16 len)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!fw)
+		return;
+
+	fw->update_len = len;
+}
+
+int ilitek_update_load_fw(void *handle, WCHAR *fw_name)
+{
+	int error;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	u32 i;
+
+	if (!handle)
+		return -EINVAL;
+
+	error = decode_firmware(fw, fw_name);
+	if (error < 0)
+		return error;
+
+	if (fw->cb.update_fw_file_info)
+		fw->cb.update_fw_file_info(&fw->file, fw->_private);
+
+	if (!fw->dev)
+		return 0;
+
+	/* for Lego and V6 IC, check block's start/end address validity */
+	if (fw->dev->protocol.flag == PTL_V6) {
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->dev->mcu_info.min_addr <=
+				fw->file.blocks[i].start &&
+			    fw->dev->mcu_info.max_addr >
+				fw->file.blocks[i].end)
+				continue;
+
+			if (!(fw->file.blocks[i].start % 0x1000))
+				continue;
+
+			TP_ERR(fw->dev->id, "Block[%u] addr. OOB (0x%x <= 0x%x/0x%x < 0x%x) or invalid start addr\n",
+				i, fw->dev->mcu_info.min_addr,
+				fw->file.blocks[i].start,
+				fw->file.blocks[i].end,
+				fw->dev->mcu_info.max_addr);
+			return -EINVAL;
+		}
+	}
+
+	TP_MSG(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " matched\n",
+		fw->dev->mcu_info.ic_name, fw->file.ic_name);
+
+	return 0;
+}
+
+int ilitek_update_start(void *handle)
+{
+	int error;
+	s8 retry = 0;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+	struct ilitek_ts_device *dev;
+
+	if (!handle)
+		return -EINVAL;
+	dev = fw->dev;
+
+	/*
+	 * Some platform (ITS-Bridge) might change touch controller
+	 * after loading fw file, get panel info. forcely and
+	 * re-check the ic/file are matched.
+	 */
+	error = api_update_ts_info(dev);
+	if (error < 0 || strcmp(dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "get ic info failed, err: %d or ic/file (" PFMT_C8 "/" PFMT_C8 ") not matched\n",
+			error, fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EPERM;
+	}
+
+	TP_INFO(dev->id, "[ilitek_update_start] start\n");
+
+	do {
+		TP_DBG(dev->id, "retry: %hhd, retry_limit: %hhd\n",
+			retry, fw->setting.retry);
+		if (retry)
+			reset_helper(dev);
+
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			continue;
+
+		if (!need_fw_update(fw)) {
+			if (is_231x(dev))
+				goto erase_data_flash_231x;
+			goto success_return;
+		}
+
+		update_progress(fw);
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(0, fw->_private);
+
+		error = api_to_bl_mode(dev, true, 0, 0);
+		if (error < 0)
+			continue;
+
+		TP_INFO_ARR(dev->id, "[BL Firmware Version]",
+			    TYPE_U8, 8, dev->fw_ver);
+		TP_INFO(dev->id, "[ilitek_update_start] start to program\n");
+
+		switch (dev->protocol.ver & 0xFFFF00) {
+		case BL_PROTOCOL_V1_8:
+			error = ilitek_update_BL_v1_8(fw);
+			break;
+		case BL_PROTOCOL_V1_7:
+			error = ilitek_update_BL_v1_7(fw);
+			break;
+		case BL_PROTOCOL_V1_6:
+			error = ilitek_update_BL_v1_6(fw);
+			break;
+		default:
+			TP_ERR(dev->id, "BL protocol ver: 0x%x not supported\n",
+				dev->protocol.ver);
+			continue;
+		}
+		if (error < 0)
+			continue;
+
+		error = api_to_bl_mode(dev, false,
+				       fw->file.blocks[0].start,
+				       fw->file.blocks[0].end);
+		if (error < 0)
+			continue;
+
+erase_data_flash_231x:
+		/*
+		 * If no data flash section in firmware file,
+		 * 231x need to erase data flash after change to AP mode.
+		 */
+		if (fw->file.blocks[1].end < fw->file.blocks[1].start &&
+		    is_231x(dev)) {
+			error = api_erase_data_v3(dev);
+			if (error < 0)
+				continue;
+		}
+
+success_return:
+		error = api_update_ts_info(dev);
+		if (error < 0)
+			continue;
+
+		error = api_set_ctrl_mode(dev, mode_normal, false, true);
+		if (error < 0)
+			continue;
+
+		if (fw->cb.update_fw_ic_info)
+			fw->cb.update_fw_ic_info(true,
+						 dev->fw_ver, dev->ic[0].crc,
+						 fw->file.block_num,
+						 fw->_private);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(100, fw->_private);
+
+		if (need_retry(fw))
+			continue;
+
+		TP_INFO(dev->id, "[ilitek_update_start] success\n");
+
+		return 0;
+	} while (!dev->setting.no_retry && ++retry < fw->setting.retry);
+
+	TP_ERR(dev->id, "[ilitek_update_start] fw update failed, err: %d\n",
+		error);
+
+	return (error < 0) ? error : -EFAULT;
+}
+
+void ilitek_update_setting(void *handle, struct ilitek_fw_settings *setting)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&fw->setting, setting, sizeof(struct ilitek_fw_settings));
+}
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.h b/drivers/input/touchscreen/ilitek/ilitek_update.h
new file mode 100644
index 000000000000..b7987b9fb121
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.h
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_UPDATE_H__
+#define __ILITEK_UPDATE_H__
+
+#include "ilitek_protocol.h"
+
+#define UPDATE_LEN			1024
+
+#define ILITEK_FW_FILE_SIZE		(512 * 1024)
+#define ILITEK_FW_BUF_SIZE		(256 * 1024)
+
+enum fw_file_type {
+	fw_hex = 0,
+	fw_bin,
+	fw_ili,
+};
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ mapping_info_lego {
+	u8 tuning_ver[4];
+	u8 fw_ver[4];
+	u8 core_test;
+	u8 core_day;
+	u8 core_month;
+	u8 core_year;
+	u32 core_ver;
+	u8 vendor_ver[6];
+	u8 _reserve_1[8];
+	u16 customer_id;
+	u16 fwid;
+	u16 i2c_addr;
+	u8 _reserve_2[2];
+	char model_name[16];
+	u8 _reserve_3[2];
+	u8 ic_num;
+	u8 total_tuning_num;
+	u16 sizeof_tuning;
+	u16 sizeof_tp_param;
+	u16 sizeof_sys_info;
+	u16 sizeof_sys_algo;
+	u16 sizeof_key_info;
+	u8 block_num;
+	u8 support_tuning_num;
+	u8 _reserve_4[2];
+
+	struct __PACKED__ {
+		u8 addr[3];
+	} blocks[10];
+
+	u8 _reserve_5[9];
+	u8 end_addr[3];
+	u8 _reserve_6[2];
+};
+
+union __PACKED__ mapping_info {
+	struct __PACKED__ {
+		u8 mapping_ver[3];
+		u8 protocol_ver[3];
+		u8 ic_name[6];
+
+		struct mapping_info_lego _lego;
+	};
+};
+
+/*
+ * for V3, "check" is checksum, block[0] for AP and block[1] for Data Flash.
+ * for V6, "check" is CRC.
+ */
+struct __PACKED__ ilitek_block {
+	bool check_match;
+	u32 start;
+	u32 end;
+	u32 check;
+	u32 offset;
+};
+
+struct __PACKED__ ilitek_fw_file_info {
+	char ic_name[8];
+	u8 fw_ver[8];
+	u16 fwid;
+
+	u8 block_num;
+	struct ilitek_block blocks[ILTIEK_MAX_BLOCK_NUM];
+
+	u32 mm_addr;
+	u32 mm_size;
+
+	u32 buf_size;
+	u8 *buf;
+
+	/* fw file's sensor-id or fwid or other id */
+	u32 id;
+	u8 type;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return file size in # of bytes, or negative error code */
+typedef int (*read_fw_t)(WCHAR *, u8 *, int, void *);
+/* update progress of fw updating */
+typedef void (*update_progress_t)(u8, void *);
+/* update fw info to callers */
+typedef void (*update_fw_file_info_t)(struct ilitek_fw_file_info *, void *);
+
+/* notify caller before/after slave upgrade (for ITS only) */
+typedef void (*slave_update_notify_t)(bool, void *);
+/* update fw version and crc/checksum before/after update (for ITS only) */
+typedef void (*update_fw_ic_info_t)(bool, u8 *, u32 *, int, void *);
+
+struct ilitek_update_callback {
+	read_fw_t read_fw;
+	update_progress_t update_progress;
+	update_fw_file_info_t update_fw_file_info;
+
+	slave_update_notify_t slave_update_notify;
+	update_fw_ic_info_t update_fw_ic_info;
+};
+
+enum fw_ver_check_policy {
+	allow_fw_ver_downgrade = 1,
+	allow_fw_ver_same = 2,
+};
+
+struct ilitek_fw_settings {
+	s8 retry;
+	bool fw_check_only;
+	bool force_update;
+
+	bool fw_ver_check;
+	u8 fw_ver_policy;
+	u8 fw_ver[8];
+};
+
+struct ilitek_fw_handle {
+	struct ilitek_ts_device *dev;
+	void *_private;
+
+	/* upgrade options */
+	struct ilitek_fw_settings setting;
+
+	/* common variable */
+	int update_len;
+
+	struct ilitek_fw_file_info file;
+
+	/* M3 + M2V */
+	bool m2v;
+	bool m2v_need_update;
+	u8 *m2v_buf;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	/* upgrade status */
+	unsigned int progress_curr;
+	unsigned int progress_max;
+	u8 progress;
+
+	/* callbacks */
+	struct ilitek_update_callback cb;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			       struct ilitek_update_callback *callback,
+			       void *_private);
+
+void __DLL ilitek_update_exit(void *handle);
+
+void __DLL ilitek_update_set_data_length(void *handle, u16 len);
+
+void __DLL ilitek_update_setting(void *handle,
+				 struct ilitek_fw_settings *setting);
+
+int __DLL ilitek_update_load_fw(void *handle, WCHAR *fw_name);
+
+int __DLL ilitek_update_start(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git "a/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" "b/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"
new file mode 100644
index 0000000000000000000000000000000000000000..54af506a4b7c4546aaa12ec99f136c9083b62a83
GIT binary patch
literal 162
zcmZQCuPRR|FHUA439vJiGgJX_3J@1FfHc>ZFflPOG_>^}{sH8{XrBE*K_md<!NeuG
f)w8dM-eF8(e4Ne9!oaZdltBfE57ScAbow{|sQnt1

literal 0
HcmV?d00001

-- 
2.25.1


^ permalink raw reply related

* [PATCH] input: touchscreen: Add ilitek touchscreen driver support
From: 2724853925 @ 2025-11-16 13:33 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg
  Cc: linux-input, linux-kernel, linux-gpio, 2724853925, weisicheng

From: weisicheng <7855009+weisicheng@user.noreply.gitee.com>

Add basic support for Ilitek I2C touchscreen controllers (e.g., ILitek2511, ILitek2701).
This patch includes:
1. Kconfig entry for Ilitek driver configuration
2. Makefile entry to compile the ilitek driver module
3. Core driver files under drivers/input/touchscreen/ilitek/ (I2C communication,
   touch event handling, power management)

The driver is compatible with standard input subsystem and supports:
- Multi-touch detection
- Pressure sensing
- Dynamic power saving mode

Signed-off-by: weisicheng <2724853925@qq.com>
---
 drivers/input/touchscreen/Kconfig             |   13 +
 drivers/input/touchscreen/Makefile            |    1 +
 drivers/input/touchscreen/ilitek/Makefile     |   34 +
 .../input/touchscreen/ilitek/ilitek_common.h  |  264 ++
 .../input/touchscreen/ilitek/ilitek_crypto.c  |  465 +++
 .../input/touchscreen/ilitek/ilitek_crypto.h  |   61 +
 drivers/input/touchscreen/ilitek/ilitek_def.c |  235 ++
 drivers/input/touchscreen/ilitek/ilitek_def.h |  554 +++
 .../input/touchscreen/ilitek/ilitek_main.c    | 2198 ++++++++++
 .../touchscreen/ilitek/ilitek_platform_init.c |  404 ++
 .../touchscreen/ilitek/ilitek_protocol.c      | 3644 +++++++++++++++++
 .../touchscreen/ilitek/ilitek_protocol.h      |  916 +++++
 .../input/touchscreen/ilitek/ilitek_report.c  |  455 ++
 .../input/touchscreen/ilitek/ilitek_report.h  |   78 +
 .../input/touchscreen/ilitek/ilitek_tool.c    | 1156 ++++++
 drivers/input/touchscreen/ilitek/ilitek_ts.h  |  268 ++
 .../input/touchscreen/ilitek/ilitek_update.c  | 1657 ++++++++
 .../input/touchscreen/ilitek/ilitek_update.h  |  199 +
 ...344\272\244\346\265\201\347\250\213 .docx" |  Bin 0 -> 162 bytes
 19 files changed, 12602 insertions(+)
 create mode 100644 drivers/input/touchscreen/ilitek/Makefile
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_common.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_main.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_platform_init.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_tool.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_ts.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.h
 create mode 100644 "drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7d5b72ee07fa..3552629c4d4d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -515,6 +515,19 @@ config TOUCHSCREEN_ILITEK
 	  To compile this driver as a module, choose M here: the
 	  module will be called ilitek_ts_i2c.
 
+config TOUCHSCREEN_ILI2511
+	tristate "Ilitek I2C or SPI Touch ICs"
+	depends on I2C
+	depends on SPI
+	help
+	  Say Y here if you have touchscreen with ILITEK touch IC,
+	  it supports 213X/23XX/25XX and other Lego series.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ilitek.
+
 config TOUCHSCREEN_IPROC
 	tristate "IPROC touch panel driver support"
 	depends on ARCH_BCM_IPROC || COMPILE_TEST
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ab9abd151078..3124c4dd8ee1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX)	+= hynitron_cstxxx.o
 obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X)	+= hynitron-cst816x.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_ILITEK)	+= ilitek_ts_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ILI2511)	+= ilitek/
 obj-$(CONFIG_TOUCHSCREEN_IMAGIS)	+= imagis.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/ilitek/Makefile b/drivers/input/touchscreen/ilitek/Makefile
new file mode 100644
index 000000000000..c912438a3b3e
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/Makefile
@@ -0,0 +1,34 @@
+ccflags-y += -Wall
+
+ifndef __KERNEL__
+ccflags-y += -D__KERNEL__
+endif
+
+#Un-mark below item to enable FW upgrade on boot
+#ccflags-y += -DILITEK_BOOT_UPDATE
+
+ILITEK_INTERFACE = i2c
+#ILITEK_INTERFACE = spi
+
+
+ifeq ($(ILITEK_INTERFACE), spi)
+$(info start to build ilitek SPI driver)
+ccflags-y += -DILITEK_SPI_INTERFACE
+ilitek := ilitek_spi
+else
+$(info start to build ilitek I2C driver)
+ilitek := ilitek_i2c
+endif
+
+$(ilitek)-objs += \
+	ilitek_def.o \
+	ilitek_main.o \
+	ilitek_platform_init.o \
+	ilitek_update.o \
+	ilitek_tool.o \
+	ilitek_protocol.o \
+	ilitek_crypto.o \
+	ilitek_report.o
+
+obj-$(CONFIG_TOUCHSCREEN_ILI2511) += $(ilitek).o
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_common.h b/drivers/input/touchscreen/ilitek/ilitek_common.h
new file mode 100644
index 000000000000..7f058c3e4914
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_common.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _ILITEK_COMMON_H_
+#define _ILITEK_COMMON_H_
+/* Includes of headers ------------------------------------------------------*/
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "ilitek_ts.h"
+#include "ilitek_protocol.h"
+#include "ilitek_update.h"
+
+#include "ilitek_crypto.h"
+#include "ilitek_report.h"
+
+/* Extern define ------------------------------------------------------------*/
+//driver information
+#define DRIVER_VERSION_0				5
+#define DRIVER_VERSION_1				9
+#define DRIVER_VERSION_2				3
+#define DRIVER_VERSION_3				0
+#define CUSTOMER_H_ID					0
+#define CUSTOMER_L_ID					0
+#define TEST_VERSION					0
+
+#define ILITEK_IOCTL_MAX_TRANSFER			5000UL
+
+#define set_arr(arr, idx, val)			\
+	do {					\
+		if ((idx) < ARRAY_SIZE(arr))	\
+			(arr)[(idx)] = (val);	\
+	} while (0)
+
+/* i2c clock rate for rk3288 */
+#if ILITEK_PLAT == ILITEK_PLAT_ROCKCHIP && \
+	KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE
+#define SCL_RATE(rate)	.scl_rate = (rate),
+#else
+#define SCL_RATE(rate)
+#endif
+
+/* netlink */
+#if KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)	\
+	struct netlink_kernel_cfg cfg = {	\
+		.groups = 0,			\
+		.input = func,			\
+	}
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), (cfg_ptr))
+#else
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), THIS_MODULE, (cfg_ptr))
+#endif
+#else
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), 0, (func), NULL, THIS_MODULE)
+#endif
+
+/* input_dev */
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define INPUT_MT_INIT_SLOTS(dev, num)	\
+		input_mt_init_slots((dev), (num), INPUT_MT_DIRECT)
+#else
+#define INPUT_MT_INIT_SLOTS(dev, num)	input_mt_init_slots((dev), (num))
+#endif
+
+/* file_operations ioctl */
+#if KERNEL_VERSION(2, 6, 36) <= LINUX_VERSION_CODE
+#define FOPS_IOCTL	unlocked_ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		long func(struct file *fp, cmd, arg)
+#else
+#define FOPS_IOCTL	ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		s32 func(struct inode *np, struct file *fp,	cmd, arg)
+
+#endif
+
+#if KERNEL_VERSION(6, 3, 0) > LINUX_VERSION_CODE
+#define I2C_PROBE_FUNC(func, client_arg)	\
+	int func(client_arg, const struct i2c_device_id *id)
+#else
+#define I2C_PROBE_FUNC(func, client_arg)	int func(client_arg)
+#endif
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+#define REMOVE_FUNC(func, client_arg)	int func(client_arg)
+#define REMOVE_RETURN(val)		({ __typeof__(val) _val = (val); return _val; })
+#else
+#define REMOVE_FUNC(func, client_arg)	void func(client_arg)
+#define REMOVE_RETURN(val)		(val)
+#endif
+
+#if KERNEL_VERSION(6, 4, 0) > LINUX_VERSION_CODE
+#define CLASS_CREATE(name)	class_create(THIS_MODULE, (name))
+#else
+#define CLASS_CREATE(name)	class_create((name))
+#endif
+
+/* procfs */
+#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
+#define PROC_FOPS_T	file_operations
+#define PROC_READ	read
+#define PROC_WRITE	write
+#define PROC_IOCTL		FOPS_IOCTL
+#define PROC_COMPAT_IOCTL	compat_ioctl
+#define PROC_OPEN	open
+#define PROC_RELEASE	release
+#else
+#define PROC_FOPS_T	proc_ops
+#define PROC_READ	proc_read
+#define PROC_WRITE	proc_write
+#define PROC_IOCTL		proc_ioctl
+#define PROC_COMPAT_IOCTL	proc_compat_ioctl
+#define PROC_OPEN	proc_open
+#define PROC_RELEASE	proc_release
+#endif
+
+#ifdef MTK_UNDTS
+#define ISR_FUNC(func)	void func(void)
+#define ISR_RETURN(val)
+#else
+#define ISR_FUNC(func)	irqreturn_t func(int irq, void *dev_id)
+#define ISR_RETURN(val)	({ __typeof__(val) _val = (val); return _val; })
+#endif
+
+enum ilitek_irq_handle_type {
+	irq_type_normal = 0,
+	irq_type_debug,
+	irq_type_c_model,
+};
+
+struct ilitek_ts_data {
+	void *client;
+	struct device *device;
+	struct ilitek_ts_device *dev;
+
+	/* should > 2K for C-Model */
+	u8 buf[4096];
+
+	struct input_dev *input_dev;
+	struct input_dev *pen_input_dev;
+	struct regulator *vdd;
+	struct regulator *vdd_i2c;
+	struct regulator *vcc_io;
+
+	int irq;
+	int irq_gpio;
+	int reset_gpio;
+	int test_gpio;
+
+	bool system_suspend;
+	bool power_key_triggered;
+
+	u8 irq_trigger_type;
+
+	bool is_touched;
+	bool touch_key_hold_press;
+	int touch_flag[40];
+
+#if 0
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+	struct task_struct *update_thread;
+
+	atomic_t firmware_updating;
+	bool operation_protection;
+	bool unhandle_irq;
+	unsigned int irq_handle_type;
+	unsigned int irq_read_len;
+
+	u8 gesture_status;
+	u8 low_power_status;
+
+	bool esd_check;
+	bool esd_skip;
+	struct workqueue_struct *esd_workq;
+	struct delayed_work esd_work;
+	unsigned long esd_delay;
+
+	/* Mutex for protecting concurrent access */
+	struct mutex ilitek_mutex;
+
+	atomic_t irq_enabled;
+	atomic_t get_INT;
+
+	bool wake_irq_enabled;
+
+	bool irq_registered;
+};
+
+/* Extern macro -------------------------------------------------------------*/
+
+#define CEIL(n, d) (((n) % (d)) ? ((n) / (d)) + 1 : ((n) / (d)))
+
+/* Extern variables ---------------------------------------------------------*/
+
+extern u8 driver_ver[];
+
+extern struct ilitek_ts_data *ts;
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#endif
+/* Extern function prototypes -----------------------------------------------*/
+/* Extern functions ---------------------------------------------------------*/
+void ilitek_resume(void);
+void ilitek_suspend(void);
+int ilitek_main_probe(void *client, struct device *dev);
+int ilitek_main_remove(void *client);
+void ilitek_reset(int delay);
+
+int ilitek_write(u8 *cmd, int len);
+int ilitek_read(u8 *buf, int len);
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len);
+
+void ilitek_irq_enable(void);
+void ilitek_irq_disable(void);
+
+int ilitek_upgrade_firmware(char *filename);
+
+int ilitek_create_tool_node(void);
+int ilitek_remove_tool_node(void);
+
+int ilitek_create_sysfsnode(void);
+void ilitek_remove_sys_node(void);
+
+int ilitek_netlink_init(u8 unit);
+void ilitek_netlink_exit(void);
+
+void ilitek_gpio_dbg(void);
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init);
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y);
+
+int ilitek_create_esd_check_workqueue(void);
+void ilitek_remove_esd_check_workqueue(void);
+
+int ilitek_read_data_and_report(void);
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.c b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
new file mode 100644
index 000000000000..2ba6105266d2
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_crypto.h"
+
+/*
+ * The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+ * The numbers below can be computed dynamically trading ROM for RAM -
+ * This can be useful in (embedded) bootloader applications, where ROM is often limited.
+ */
+static const u8 sbox[256] = {
+	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const u8 rsbox[256] = {
+	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+/*
+ * The round constant word array, contains the values given by
+ * x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+ */
+static const u8 rcon[11] = {
+	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+/* array holding the intermediate results during decryption. */
+typedef u8 state_t[4][4];
+
+struct crypto_aes_ctx {
+	u8 key_dec[240];
+};
+
+static u8 xtime(u8 x)
+{
+	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
+}
+
+static void inv_shift_rows(state_t *state)
+{
+	u8 tmp;
+
+	/* Rotate first row 1 columns to right */
+	tmp = (*state)[3][1];
+	(*state)[3][1] = (*state)[2][1];
+	(*state)[2][1] = (*state)[1][1];
+	(*state)[1][1] = (*state)[0][1];
+	(*state)[0][1] = tmp;
+
+	/* Rotate second row 2 columns to right */
+	tmp = (*state)[0][2];
+	(*state)[0][2] = (*state)[2][2];
+	(*state)[2][2] = tmp;
+	tmp = (*state)[1][2];
+	(*state)[1][2] = (*state)[3][2];
+	(*state)[3][2] = tmp;
+
+	/* Rotate third row 3 columns to right */
+	tmp = (*state)[0][3];
+	(*state)[0][3] = (*state)[1][3];
+	(*state)[1][3] = (*state)[2][3];
+	(*state)[2][3] = (*state)[3][3];
+	(*state)[3][3] = tmp;
+}
+
+static void inv_sub_bytes(state_t *state)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[j][i] = rsbox[(*state)[j][i]];
+}
+
+/*
+ * multiply is used to multiply numbers in the field GF(2^8)
+ * Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+ *       The compiler seems to be able to vectorize the operation better this way.
+ *       See https://github.com/kokke/tiny-AES-c/pull/34
+ */
+static u8 multiply(u8 x, u8 y)
+{
+	return (((y & 1) * x) ^
+		(((y >> 1) & 1) * xtime(x)) ^
+		(((y >> 2) & 1) * xtime(xtime(x))) ^
+		(((y >> 3) & 1) * xtime(xtime(xtime(x)))) ^
+		(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
+}
+
+/*
+ * inv_mix_cols function mixes the columns of the state matrix.
+ */
+static void inv_mix_cols(state_t *state)
+{
+	int i;
+	u8 a, b, c, d;
+
+	for (i = 0; i < 4; ++i) {
+		a = (*state)[i][0];
+		b = (*state)[i][1];
+		c = (*state)[i][2];
+		d = (*state)[i][3];
+
+		(*state)[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^
+			multiply(c, 0x0d) ^ multiply(d, 0x09);
+		(*state)[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^
+			multiply(c, 0x0b) ^ multiply(d, 0x0d);
+		(*state)[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^
+			multiply(c, 0x0e) ^ multiply(d, 0x0b);
+		(*state)[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^
+			multiply(c, 0x09) ^ multiply(d, 0x0e);
+	}
+}
+
+/*
+ * This function adds the round key to state.
+ * The round key is added to the state by an XOR function.
+ */
+static void add_round_key(u8 round, state_t *state, const u8 *key)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[i][j] ^= key[(round * Nb * 4) + (i * Nb) + j];
+}
+
+static void aes_decrypt(state_t *state, const u8 *key)
+{
+	u8 round;
+
+	/* Add the first round key to the state before starting the rounds. */
+	add_round_key(Nr, state, key);
+
+	/*
+	 * There will be Nr rounds.
+	 * The first Nr-1 rounds are identical.
+	 * These Nr rounds are executed in the loop below.
+	 * Last one without InvMixColumn()
+	 */
+	for (round = Nr - 1; ; --round) {
+		inv_shift_rows(state);
+		inv_sub_bytes(state);
+		add_round_key(round, state, key);
+
+		if (!round)
+			break;
+
+		inv_mix_cols(state);
+	}
+}
+
+/*
+ * Produces Nb(Nr+1) round keys, used in each round to decrypt the states.
+ */
+static void aes_expandkey(u8 *round_key, const u8 *key)
+{
+	unsigned int i, j, k;
+	u8 tmp_a[4];
+	u8 tmp_b;
+
+	/* The first round key is the key itself. */
+	for (i = 0; i < Nk; ++i) {
+		round_key[(i * 4) + 0] = key[(i * 4) + 0];
+		round_key[(i * 4) + 1] = key[(i * 4) + 1];
+		round_key[(i * 4) + 2] = key[(i * 4) + 2];
+		round_key[(i * 4) + 3] = key[(i * 4) + 3];
+	}
+
+	/* All other round keys are found from the previous round keys. */
+	for (i = Nk; i < Nb * (Nr + 1); ++i) {
+		k = (i - 1) * 4;
+		tmp_a[0] = round_key[k + 0];
+		tmp_a[1] = round_key[k + 1];
+		tmp_a[2] = round_key[k + 2];
+		tmp_a[3] = round_key[k + 3];
+
+		if (!(i % Nk)) {
+			/*
+			 * Shifts the 4 bytes in a word to the left once.
+			 * [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+			 */
+			tmp_b = tmp_a[0];
+			tmp_a[0] = tmp_a[1];
+			tmp_a[1] = tmp_a[2];
+			tmp_a[2] = tmp_a[3];
+			tmp_a[3] = tmp_b;
+
+			tmp_a[0] = sbox[tmp_a[0]];
+			tmp_a[1] = sbox[tmp_a[1]];
+			tmp_a[2] = sbox[tmp_a[2]];
+			tmp_a[3] = sbox[tmp_a[3]];
+
+			tmp_a[0] = tmp_a[0] ^ rcon[i / Nk];
+		}
+
+		j = i * 4;
+		k = (i - Nk) * 4;
+		round_key[j + 0] = round_key[k + 0] ^ tmp_a[0];
+		round_key[j + 1] = round_key[k + 1] ^ tmp_a[1];
+		round_key[j + 2] = round_key[k + 2] ^ tmp_a[2];
+		round_key[j + 3] = round_key[k + 3] ^ tmp_a[3];
+	}
+}
+
+u8 crypto_key[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			       0x4, 0x5, 0x6, 0x7,
+			       0x8, 0x9, 0xa, 0xb,
+			       0xc, 0xd, 0xe, 0xf };
+
+u8 crypto_iv[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			      0x4, 0x5, 0x6, 0x7,
+			      0x8, 0x9, 0xa, 0xb,
+			      0xc, 0xd, 0xe, 0xf };
+
+void ilitek_decrypt(u8 *buf, u32 len)
+{
+	u8 key[AES_KEY_LEN];
+	u8 iv[AES_KEY_LEN];
+	u8 *tmp = buf;
+	u8 iv_tmp[AES_KEY_LEN];
+	struct crypto_aes_ctx ctx;
+
+	u32 i, j;
+
+	memcpy(key, crypto_key, AES_KEY_LEN);
+	memcpy(iv, crypto_iv, AES_KEY_LEN);
+
+	aes_expandkey(ctx.key_dec, key);
+
+	for (i = 0; i < len; i += AES_KEY_LEN, tmp += AES_KEY_LEN) {
+		memcpy(iv_tmp, tmp, AES_KEY_LEN);
+
+		aes_decrypt((state_t *)tmp, ctx.key_dec);
+		for (j = 0; j < AES_KEY_LEN; j++)
+			tmp[j] ^= iv[j];
+
+		memcpy(iv, iv_tmp, AES_KEY_LEN);
+	}
+}
+
+static void sha256_init(struct sha256_ctx *ctx)
+{
+	ctx->datalen = 0;
+	ctx->bitlen[0] = 0;
+	ctx->bitlen[1] = 0;
+
+	/*
+	 * 8 hash value for sha256
+	 * square root of prime number 2,3,5,7,11,13,17,19
+	 * then take 32 bit numbers after the decimal point
+	 */
+	ctx->state[0] = 0x6a09e667;
+	ctx->state[1] = 0xbb67ae85;
+	ctx->state[2] = 0x3c6ef372;
+	ctx->state[3] = 0xa54ff53a;
+	ctx->state[4] = 0x510e527f;
+	ctx->state[5] = 0x9b05688c;
+	ctx->state[6] = 0x1f83d9ab;
+	ctx->state[7] = 0x5be0cd19;
+}
+
+static void sha256_transform(struct sha256_ctx *ctx, u8 *data)
+{
+	static u32 key[64] = {
+		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	};
+
+	u32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+	for (i = 0, j = 0; i < 16; ++i, j += 4)
+		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+
+	for (; i < 64; ++i)
+		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+
+	a = ctx->state[0];
+	b = ctx->state[1];
+	c = ctx->state[2];
+	d = ctx->state[3];
+	e = ctx->state[4];
+	f = ctx->state[5];
+	g = ctx->state[6];
+	h = ctx->state[7];
+
+	for (i = 0; i < 64; ++i) {
+		t1 = h + EP1(e) + CH(e, f, g) + key[i] + m[i];
+		t2 = EP0(a) + MAJ(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + t1;
+		d = c;
+		c = b;
+		b = a;
+		a = t1 + t2;
+	}
+
+	ctx->state[0] += a;
+	ctx->state[1] += b;
+	ctx->state[2] += c;
+	ctx->state[3] += d;
+	ctx->state[4] += e;
+	ctx->state[5] += f;
+	ctx->state[6] += g;
+	ctx->state[7] += h;
+}
+
+static void sha256_update(struct sha256_ctx *ctx, u8 byte)
+{
+	ctx->data[ctx->datalen] = byte;
+	ctx->datalen++;
+	if (ctx->datalen == 64) {
+		sha256_transform(ctx, ctx->data);
+		DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], 512);
+		ctx->datalen = 0;
+	}
+}
+
+static void sha256_final(struct sha256_ctx *ctx, u8 *hash)
+{
+	u32 i;
+
+	i = ctx->datalen;
+
+	/* Pad whatever data is left in the buffer. */
+	if (ctx->datalen < 56) {
+		ctx->data[i++] = 0x80;
+		while (i < 56)
+			ctx->data[i++] = 0x00;
+	} else {
+		ctx->data[i++] = 0x80;
+		while (i < 64)
+			ctx->data[i++] = 0x00;
+		sha256_transform(ctx, ctx->data);
+		memset(ctx->data, 0, 56);
+	}
+
+	/* Append to the padding the total message's length in bits and transform. */
+	DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8);
+	ctx->data[63] = ctx->bitlen[0];
+	ctx->data[62] = ctx->bitlen[0] >> 8;
+	ctx->data[61] = ctx->bitlen[0] >> 16;
+	ctx->data[60] = ctx->bitlen[0] >> 24;
+	ctx->data[59] = ctx->bitlen[1];
+	ctx->data[58] = ctx->bitlen[1] >> 8;
+	ctx->data[57] = ctx->bitlen[1] >> 16;
+	ctx->data[56] = ctx->bitlen[1] >> 24;
+	sha256_transform(ctx, ctx->data);
+
+	/*
+	 * Since this implementation uses little endian byte ordering and SHA uses big endian,
+	 * reverse all the bytes when copying the final state to the output hash.
+	 */
+	for (i = 0; i < 4; ++i) {
+		hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+	}
+}
+
+void get_sha256(u32 start, u32 end, u8 *buf, u32 buf_size, u8 sha256[32])
+{
+	struct sha256_ctx ctx;
+	u32 i;
+
+	sha256_init(&ctx);
+
+	if (end >= buf_size || start > buf_size || end < start) {
+		TP_ERR(NULL, "start/end addr: %#x/%#x buf size: %#x OOB\n", start, end, buf_size);
+		return;
+	}
+
+	for (i = start; i <= end && i < buf_size; i++)
+		sha256_update(&ctx, buf[i]);
+
+	sha256_final(&ctx, sha256);
+
+	TP_MSG_ARR(NULL, "sha256:", TYPE_U8, 32, sha256);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.h b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
new file mode 100644
index 000000000000..ef01ca434776
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_CRYPTO_H__
+#define __ILITEK_CRYPTO_H__
+
+#include "ilitek_def.h"
+
+#define Nb	(4)
+#define Nk	(4)       /* The number of 32 bit words in a key. */
+#define Nr	(10)      /* The number of rounds in AES Cipher. */
+
+#define AES_KEY_LEN      (16) /* Key length in bytes */
+
+extern u8 crypto_key[AES_KEY_LEN];
+extern u8 crypto_iv[AES_KEY_LEN];
+
+#define DBL_INT_ADD(a, b, c)			\
+	do {					\
+		if ((a) > 0xffffffff - (c))	\
+			++(b);			\
+		(a) += (c);			\
+	} while (0)
+
+#define ROTLEFT(a, b)	(((a) << (b)) | ((a) >> (32 - (b))))
+#define ROTRIGHT(a, b)	(((a) >> (b)) | ((a) << (32 - (b))))
+
+#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
+#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
+#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
+
+struct sha256_ctx {
+	u8 data[64];
+	u32 datalen;
+	u32 bitlen[2];
+	u32 state[8];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL ilitek_decrypt(u8 *buf, u32 len);
+
+void __DLL get_sha256(u32 start, u32 end,
+		      u8 *buf, u32 buf_size, u8 sha256[32]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.c b/drivers/input/touchscreen/ilitek/ilitek_def.c
new file mode 100644
index 000000000000..e9934cac240d
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_def.h"
+
+int tp_log_level = log_level_msg;
+bool tp_print_en = true;
+FILE *tp_fp;
+
+char g_str[4096];
+msg_t g_msg;
+
+#if defined(__KERNEL__) || defined(__UEFI_DXE__)
+
+int get_time_ms(u32 *t_ms)
+{
+	*t_ms = 0;
+
+	return -EINVAL;
+}
+
+#else
+#ifdef _WIN32
+
+static int gettimeofday(struct timeval *tp, void *tzp)
+{
+	time_t clock;
+	struct tm tm;
+	SYSTEMTIME wtm;
+
+	GetLocalTime(&wtm);
+	tm.tm_year = wtm.wYear - 1900;
+	tm.tm_mon = wtm.wMonth - 1;
+	tm.tm_mday = wtm.wDay;
+	tm.tm_hour = wtm.wHour;
+	tm.tm_min = wtm.wMinute;
+	tm.tm_sec = wtm.wSecond;
+	tm.tm_isdst = -1;
+	clock = mktime(&tm);
+	tp->tv_sec = (long)clock;
+	tp->tv_usec = wtm.wMilliseconds * 1000;
+	return 0;
+}
+
+#endif
+
+int get_time_ms(u32 *t_ms)
+{
+	static u32 time_ms_init;
+	struct timeval t;
+	u32 time_ms;
+
+	gettimeofday(&t, NULL);
+	time_ms = t.tv_sec * 1000 + t.tv_usec / 1000;
+	time_ms_init = (!time_ms_init) ? time_ms : time_ms_init;
+
+	*t_ms = time_ms - time_ms_init;
+
+	return 0;
+}
+
+#endif
+
+void tp_log_arr(char *id, int level, const char *header, const char *tag,
+		int type, int len, void *buf)
+{
+	const int num = 64;
+	int i, idx = 0;
+	u32 time_ms;
+	int error;
+
+	if (level > tp_log_level || !buf)
+		return;
+
+	error = get_time_ms(&time_ms);
+
+	do {
+		_memset(g_str, 0, sizeof(g_str));
+
+		if (!error)
+			_sprintf(g_str, 0, "[%7u.%03u]",
+				time_ms / 1000, time_ms % 1000);
+
+		if (id)
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 "[" PFMT_C8 "] " PFMT_C8 " ",
+				 header, id, tag);
+		else
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 " " PFMT_C8 " ",
+				 header, tag);
+
+		for (i = 0; i < num && idx < len; i++, idx++) {
+			switch (type) {
+			default:
+			case TYPE_U8:
+				_sprintf(g_str, _strlen(g_str), "%02x-",
+					((u8 *)buf)[idx]);
+				break;
+			case TYPE_INT:
+				_sprintf(g_str, _strlen(g_str), "%d-",
+					((int *)buf)[idx]);
+				break;
+			}
+		}
+		_sprintf(g_str, _strlen(g_str) - 1, ", len: [%d/%d]\n",
+			idx, len);
+
+		if (tp_print_en)
+			TP_PRINTF(PFMT_C8, g_str);
+		if (g_msg)
+			g_msg(level, g_str);
+		TP_LOG(tp_fp, g_str);
+	} while (idx < len);
+}
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items)
+{
+	int error = 0;
+
+	MUTEX_INIT(q->mutex);
+
+	MUTEX_LOCK(q->mutex);
+
+	do {
+		q->item_size = item_size;
+		q->curr_size = 0;
+		q->max_size = max_items;
+
+		q->buf = (u8 *)CALLOC(max_items, item_size);
+		if (!q->buf) {
+			error = -ENOMEM;
+			break;
+		}
+
+		q->push_ptr = q->buf;
+		q->pop_ptr = q->buf;
+		q->end_ptr = q->buf + (max_items - 1) * item_size;
+	} while (false);
+
+	MUTEX_UNLOCK(q->mutex);
+
+	return error;
+}
+
+void queue_exit(struct queue *q)
+{
+	if (q->buf)
+		CFREE(q->buf);
+	MUTEX_EXIT(q->mutex);
+}
+
+void queue_push(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	/* Stop push data when queue is full */
+	if (q->curr_size >= q->max_size)
+		goto release_push_lock;
+
+	q->curr_size++;
+	if (q->push_ptr == q->end_ptr)
+		q->push_ptr = q->buf;
+	else
+		q->push_ptr += q->item_size;
+
+	if (q->push_ptr == q->pop_ptr)
+		TP_ERR(NULL, "[Warn]Queue overload, queue size: %u\n", q->curr_size);
+
+release_push_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void queue_pop(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	if (!q->curr_size)
+		goto release_pop_lock;
+
+	q->curr_size--;
+	if (q->pop_ptr == q->end_ptr)
+		q->pop_ptr = q->buf;
+	else
+		q->pop_ptr += q->item_size;
+
+release_pop_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void set_print_en(bool enable)
+{
+	tp_print_en = enable;
+}
+
+void set_log_level(int level)
+{
+	tp_log_level = level;
+}
+
+int set_log_fopen(WCHAR *filename)
+{
+	int error;
+
+	if (tp_fp)
+		return -EINVAL;
+
+	error = WFOPEN(&tp_fp, filename, "w+");
+	if (error < 0) {
+		tp_fp = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+void set_log_fclose(void)
+{
+	if (!tp_fp)
+		return;
+
+	_fclose(tp_fp);
+	tp_fp = NULL;
+}
+
+void set_log_fwrite(char *str)
+{
+	TP_LOG(tp_fp, str);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.h b/drivers/input/touchscreen/ilitek/ilitek_def.h
new file mode 100644
index 000000000000..d6f2077d7760
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.h
@@ -0,0 +1,554 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_DEF_H__
+#define __ILITEK_DEF_H__
+
+#define COMMONFLOW_CODE_VERSION		0x00000301
+
+/*
+ * Windows
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+#define __MAYBE_UNUSED
+
+#ifdef _WINDLL
+#define __DLL __declspec(dllexport)
+#else
+#define __DLL __declspec(dllimport)
+#endif
+
+#define __PACKED__
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	sprintf_s((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size)	strncpy_s((dst), (dst_size), (src), (n))
+#define _strcasecmp(l, r)		_stricmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat_s((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)		sscanf_s(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy_s((dst), (dst_size), (src))
+#define _strtok(str, del, next_token)	strtok_s((str), (del), (next_token))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				wchar_t
+#define WSTRING				wstring
+#define WCSCPY(dst, src, dst_size)	wcscpy_s((dst), (dst_size), (src))
+#define WCSCASECMP(str, tag)		_wcsicmp((str), (L##tag))
+#define WCSRCHR(str, ch)		wcsrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	swprintf_s((buf), (size), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)					\
+	((!((*(pfp)) = _wfsopen((filename), (const wchar_t *)(L##mode), \
+				_SH_DENYWR))) ? -EFAULT : 0)
+#define WACCESS(filename, mode)		_waccess((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)					\
+	do {							\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_U8TEXT);		\
+		fwprintf((fp), (fmt), ##__VA_ARGS__);		\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_TEXT);		\
+	} while (false)
+
+#define PFMT_C16			"%ls"
+#define PFMT_C8				"%hs"
+
+#include <codecvt>
+#define TO_WCHAR(x)	\
+	std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t > ().from_bytes(x).c_str()
+
+#define _localtime(ptm, ptime)		localtime_s((ptm), (ptime))
+
+#define _fopen(pfp, filename, mode)	\
+	(fopen_s((pfp), (filename), (const char *)(mode)))
+#define _fclose(pfp)			fclose((pfp))
+
+#define MUTEX_T				HANDLE
+#define MUTEX_INIT(x)			((x) = CreateMutex(NULL, false, NULL))
+#define MUTEX_LOCK(x)			(WaitForSingleObject((x), INFINITE))
+#define MUTEX_UNLOCK(x)			(ReleaseMutex((x)))
+#define MUTEX_EXIT(x)			(CloseHandle((x)))
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+/*
+ * UEFI Driver
+ */
+#elif defined(__UEFI_DXE__)
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+
+#define UEFI_MAX_STR_SIZE	0x1000
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	AsciiSPrint((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) \
+	AsciiStrnCpyS(dst, dst_size, src, (UINTN)n)
+#define _strcasecmp(l, r)		AsciiStriCmp((l), (r))
+#define _strcat(dst, src, dst_size)	\
+	AsciiStrCatS((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)
+#define _strlen(str)			AsciiStrnLenS((str), UEFI_MAX_STR_SIZE)
+#define _strcpy(dst, src, dst_size)	\
+	_strncpy((dst), (src), _strlen(src), (dst_size))
+#define _strtok(str, del, next_token)
+
+static __MAYBE_UNUSED char *_strrchr(const char *s, char c)
+{
+	char *found = NULL;
+
+	do {
+		if (*s == c)
+			found = (char *)s;
+	} while (*s++ != '\0');
+
+	return found;
+}
+
+#define _memset(ptr, ch, size)		\
+	SetMem((ptr), (UINTN)(size), (UINT8)(ch))
+#define _memcpy(dst, src, size)		\
+	CopyMem((dst), (src), (UINTN)(size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		_strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	(-EINVAL)
+#define WACCESS(filename, mode)		(-EINVAL)
+#define WFPRINTF(fp, fmt, ...)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%a"
+#define PFMT_C8				"%a"
+#define PFMT_U8				"%u"
+#define PFMT_U16			"%u"
+#define PFMT_X8				"%02x"
+#define PFMT_X16			"%04x"
+#define PFMT_X64			"%016llx"
+
+#define _localtime(ptm, ptime)
+#define _fclose(pfp)
+
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define MALLOC(size)		AllocatePool(size)
+#define CALLOC(num, size)	AllocateZeroPool(num * size)
+#define FREE(ptr)			\
+	do {				\
+		FreePool((ptr));	\
+		(ptr) = NULL;		\
+	} while (0)
+
+#define CFREE(ptr)		FREE(ptr)
+#define TP_PRINTF(fmt, ...)	DebugPrint(DEBUG_INFO, fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+/*
+ * Linux-Based System
+ */
+#else
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+#define _sprintf(buf, idx, fmt, ...)	sprintf((buf) + (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) strncpy((dst), (src), (n))
+#define _strcasecmp(l, r)		strcasecmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat((dst), (src))
+#define _sscanf(str, fmt, ...)		sscanf(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy((dst), (src))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define WTEXT(str)			str
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	_fopen(pfp, filename, mode)
+#define WACCESS(filename, mode)		_access((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)		fprintf((fp), (fmt), ##__VA_ARGS__)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%s"
+#define PFMT_C8				"%s"
+
+#define _localtime(ptm, ptime)						\
+		do {							\
+			struct tm *__tm__;				\
+									\
+			__tm__ = localtime((ptime));			\
+			_memcpy((ptm), __tm__, sizeof(struct tm));	\
+		} while (false)
+
+#ifdef __KERNEL__
+#define FILE				void
+#define _fopen(pfp, filename, mode)	(-EINVAL)
+#define _fclose(pfp)
+#define _access(filename, mode)		(-EINVAL)
+
+#define _strtok(str, del, next_token)
+
+#include <linux/spinlock.h>
+#define MUTEX_T				spinlock_t
+#define MUTEX_INIT(x)			spin_lock_init(&(x))
+#define MUTEX_LOCK(x)			spin_lock(&(x))
+#define MUTEX_UNLOCK(x)			spin_unlock(&(x))
+#define MUTEX_EXIT(x)
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#define MALLOC(size)		kmalloc(size, GFP_KERNEL)
+#define CALLOC(num, size)	vmalloc(num * size)
+#define FREE(ptr)		\
+	do {			\
+		kfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		\
+	do {			\
+		vfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+
+#define TP_PRINTF(fmt, ...)	printk(fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+#else
+#include <sys/time.h>
+#include <unistd.h>
+
+#define _fopen(pfp, filename, mode)	\
+	((!((*(pfp)) = fopen((filename), (mode)))) ? -EFAULT : 0)
+#define _fclose(pfp)			fclose((pfp))
+#define _access(filename, mode)		access((filename), (mode))
+
+#define _strtok(str, del, next_token)	strtok((str), (del))
+
+#ifdef __UEFI_APP__
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+#else
+#include <pthread.h>
+#define MUTEX_T				pthread_mutex_t
+#define MUTEX_INIT(x)			(pthread_mutex_init(&(x), NULL))
+#define MUTEX_LOCK(x)			(pthread_mutex_lock(&(x)))
+#define MUTEX_UNLOCK(x)			(pthread_mutex_unlock(&(x)))
+#define MUTEX_EXIT(x)			(pthread_mutex_destroy(&(x)))
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#define TP_PRINTF(fmt, ...)							\
+	__android_log_print(ANDROID_LOG_INFO, "ILITEK COMMON", "[%s][%d]" fmt,	\
+			    __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _WIN32 */
+
+#define U82U64(byte, order)	\
+	((u64)((u64)(byte) << ((order) * 8)))
+
+#ifndef PFMT_C16
+#define PFMT_C16			"%ls"
+#endif
+#ifndef PFMT_C8
+#define PFMT_C8				"%hs"
+#endif
+#ifndef PFMT_U8
+#define PFMT_U8				"%hhu"
+#endif
+#ifndef PFMT_U16
+#define PFMT_U16			"%hu"
+#endif
+#ifndef PFMT_X8
+#define PFMT_X8				"%hhx"
+#endif
+#ifndef PFMT_X16
+#define PFMT_X16			"%hx"
+#endif
+
+#ifndef PFMT_X64
+#define PFMT_X64			"%llx"
+#endif
+
+#ifndef TP_PRINTF
+#define TP_PRINTF(fmt, ...)				\
+	do {						\
+		printf(fmt, ##__VA_ARGS__);		\
+		fflush(stdout);				\
+	} while (0)
+#endif
+
+#ifndef TP_LOG
+#define TP_LOG(fp, str)				\
+	do {					\
+		if (!fp)			\
+			break;			\
+		fprintf((fp), PFMT_C8, (str));	\
+		fflush((fp));			\
+	} while (0)
+#endif
+
+#ifndef TP_PRINT
+#define TP_PRINT(_id, level, need_tag, tag, fmt, ...)			\
+	do {								\
+		char *__id__ = (_id);					\
+		u32 __time_ms__;					\
+									\
+		if (level > tp_log_level)				\
+			break;						\
+									\
+		g_str[0] = '\0';					\
+									\
+		if (need_tag) {						\
+			if (!get_time_ms(&__time_ms__))			\
+				_sprintf(g_str, _strlen(g_str),		\
+					"[%7u.%03u]",			\
+					__time_ms__ / 1000,		\
+					__time_ms__ % 1000);		\
+			_sprintf(g_str, _strlen(g_str), PFMT_C8, tag);	\
+		}							\
+									\
+		if (__id__) {						\
+			_sprintf(g_str, _strlen(g_str),			\
+				 "[" PFMT_C8 "] " fmt,			\
+				 __id__, ##__VA_ARGS__);		\
+		} else {						\
+			_sprintf(g_str, _strlen(g_str), " " fmt,	\
+				 ##__VA_ARGS__);			\
+		}							\
+									\
+		if (tp_print_en)					\
+			TP_PRINTF(PFMT_C8, g_str);			\
+		if (g_msg)						\
+			g_msg(level, g_str);				\
+		TP_LOG(tp_fp, g_str);					\
+	} while (0)
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d)	(((n) + (d) - 1) / (d))
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x)		((void)(x))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a)		((sizeof(a) / sizeof(*(a))))
+#endif
+
+#ifndef MIN
+#define MIN(l, r)		(((l) > (r)) ? (r) : (l))
+#endif
+
+#ifndef MAX
+#define MAX(l, r)		(((l) > (r)) ? (l) : (r))
+#endif
+
+#define EILICOMM		200
+#define EILIBUSY		201
+#define EILITIME		202
+#define EILIPROTO		203
+
+enum ilitek_log_level {
+	log_level_none = -1,	/* no log displayed */
+	log_level_err = 0,	/* critical errors */
+	log_level_warn,		/* warnings */
+	log_level_tag,		/* special-required tags */
+	log_level_info,		/* important/UI messages */
+	log_level_msg,		/* non-important messages */
+	log_level_dbg,		/* debugging messages */
+	log_level_pkt,		/* tx/rx packets */
+
+	log_level_max,		/* sentinel */
+};
+
+#define _TP_ERR(fmt, ...)	\
+	TP_PRINT(NULL, log_level_err, false, "", fmt, ##__VA_ARGS__)
+#define _TP_WARN(fmt, ...)	\
+	TP_PRINT(NULL, log_level_warn, false, "", fmt, ##__VA_ARGS__)
+#define _TP_TAG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_tag, false, "", fmt, ##__VA_ARGS__)
+#define _TP_INFO(fmt, ...)	\
+	TP_PRINT(NULL, log_level_info, false, "", fmt, ##__VA_ARGS__)
+#define _TP_MSG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_msg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_DBG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_dbg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_PKT(fmt, ...)	\
+	TP_PRINT(NULL, log_level_pkt, false, "", fmt, ##__VA_ARGS__)
+
+
+#define TP_ERR(id, fmt, ...)	\
+	TP_PRINT(id, log_level_err, true, "[ILITEK][ERR]", fmt, ##__VA_ARGS__)
+#define TP_WARN(id, fmt, ...)	\
+	TP_PRINT(id, log_level_warn, true, "[ILITEK][WARN]", fmt, ##__VA_ARGS__)
+#define TP_TAG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_tag, true, "[ILITEK][TAG]", fmt, ##__VA_ARGS__)
+#define TP_INFO(id, fmt, ...)	\
+	TP_PRINT(id, log_level_info, true, "[ILITEK][INFO]", fmt, ##__VA_ARGS__)
+#define TP_MSG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_msg, true, "[ILITEK][MSG]", fmt, ##__VA_ARGS__)
+#define TP_DBG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_dbg, true, "[ILITEK][DBG]", fmt, ##__VA_ARGS__)
+#define TP_PKT(id, fmt, ...)	\
+	TP_PRINT(id, log_level_pkt, true, "[ILITEK][PKT]", fmt, ##__VA_ARGS__)
+
+enum ilitek_array_type {
+	TYPE_U8 = 0,
+	TYPE_INT,
+	TYPE_U32,
+};
+
+#define TP_ERR_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_err, "[ILITEK][ERR]", tag, type, len, buf)
+#define TP_WARN_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_warn, "[ILITEK][WARN]", tag, type, len, buf)
+#define TP_TAG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_tag, "[ILITEK][TAG]", tag, type, len, buf)
+#define TP_INFO_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_info, "[ILITEK][INFO]", tag, type, len, buf)
+#define TP_MSG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_msg, "[ILITEK][MSG]", tag, type, len, buf)
+#define TP_DBG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_dbg, "[ILITEK][DBG]", tag, type, len, buf)
+#define TP_PKT_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_pkt, "[ILITEK][PKT]", tag, type, len, buf)
+
+extern int tp_log_level;
+extern bool tp_print_en;
+extern char g_str[4096];
+extern FILE *tp_fp;
+
+typedef void (*msg_t)(int, char *);
+extern msg_t g_msg;
+
+struct queue {
+	u32 curr_size;
+	u32 max_size;
+	u8 *buf;
+	u8 *push_ptr;
+	u8 *pop_ptr;
+	u8 *end_ptr;
+	u32 item_size;
+
+	MUTEX_T mutex;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL tp_log_arr(char *id, int level, const char *header,
+		      const char *tag, int type, int len, void *buf);
+
+int __DLL get_time_ms(u32 *t_ms);
+void __DLL set_print_en(bool enable);
+void __DLL set_log_level(int level);
+int __DLL set_log_fopen(WCHAR *filename);
+void __DLL set_log_fclose(void);
+void __DLL set_log_fwrite(char *str);
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items);
+void queue_exit(struct queue *q);
+void queue_push(struct queue *q);
+void queue_pop(struct queue *q);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_main.c b/drivers/input/touchscreen/ilitek/ilitek_main.c
new file mode 100644
index 000000000000..9bb1fe6aa4b7
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_main.c
@@ -0,0 +1,2198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+struct ilitek_ts_data *ts;
+
+u8 driver_ver[] = {
+	DRIVER_VERSION_0, DRIVER_VERSION_1, DRIVER_VERSION_2, DRIVER_VERSION_3,
+	CUSTOMER_H_ID, CUSTOMER_L_ID, TEST_VERSION,
+};
+
+static bool checksum_failed_release = ILITEK_CHECKSUM_FAILED_RELEASE;
+module_param(checksum_failed_release, bool, 0664);
+MODULE_PARM_DESC(checksum_failed_release,
+		 "When packet's checksum is wrong, (default)release all touch point or ignore the packet");
+
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+extern struct tpd_device *tpd;
+#ifdef ILITEK_ENABLE_DMA
+static u8 *I2CDMABuf_va;
+static dma_addr_t I2CDMABuf_pa;
+#endif
+#endif
+
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+struct wake_lock ilitek_wake_lock;
+#endif
+
+#ifdef ILITEK_TUNING_MESSAGE
+static struct sock *ilitek_netlink_sock;
+bool ilitek_debug_flag;
+static u_int ilitek_pid = 100, ilitek_seq = 23;
+#endif
+
+static void __maybe_unused ilitek_udp_reply(void *payload, int size)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int len = NLMSG_SPACE(size);
+	int ret;
+	int pid = ilitek_pid, seq = ilitek_seq;
+
+	TP_DBG(NULL, "[%s] ilitek_debug_flag: %d\n", __func__, ilitek_debug_flag);
+	if (!ilitek_debug_flag)
+		return;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		TP_ERR(NULL, "alloc skb error\n");
+		return;
+	}
+
+	nlh = nlmsg_put(skb, pid, seq, 0, size, 0);
+	if (!nlh)
+		goto nlmsg_failure;
+
+	nlh->nlmsg_flags = 0;
+	memcpy(NLMSG_DATA(nlh), payload, size);
+
+	NETLINK_CB(skb).portid = 0;	/* from kernel */
+	NETLINK_CB(skb).dst_group = 0;	/* unicast */
+
+	ret = netlink_unicast(ilitek_netlink_sock, skb, pid, MSG_DONTWAIT);
+	if (ret < 0)
+		TP_ERR(NULL, "ilitek send failed, ret: %d\n", ret);
+	return;
+
+nlmsg_failure:
+	kfree_skb(skb);
+
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void __maybe_unused udp_receive(struct sk_buff *skb)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	int count = 0, ret = 0, i = 0;
+	u8 *data;
+	struct nlmsghdr *nlh;
+
+	nlh = (struct nlmsghdr *)skb->data;
+	ilitek_pid = NETLINK_CREDS(skb)->pid;
+	ilitek_seq = nlh->nlmsg_seq;
+
+	TP_DBG(NULL, "netlink received, pid: %d, seq: %d\n",
+		     ilitek_pid, ilitek_seq);
+
+	data = (u8 *) NLMSG_DATA(nlh);
+	count = nlmsg_len(nlh);
+	if (!strcmp(data, "Open!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = true;
+		ilitek_udp_reply(data, sizeof("Open!"));
+	} else if (!strcmp(data, "Close!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = false;
+	} else if (!strcmp(data, "Wifi_Paint_Start") ||
+		   !strcmp(data, "Daemon_Debug_Start")) {
+		ilitek_debug_flag = true;
+	} else if (!strcmp(data, "Wifi_Paint_End") ||
+		   !strcmp(data, "Daemon_Debug_End")) {
+		ilitek_debug_flag = false;
+	}
+
+
+	TP_DBG(NULL, "count = %d  data[count -3] = %d data[count -2] = %c\n",
+	       count, data[count - 3], data[count - 2]);
+	for (i = 0; i < count; i++)
+		//TP_MSG(NULL, "data[%d] = 0x%x\n", i, data[i]);
+	if (data[count - 2] == 'I' && (count == 20 || count == 52) &&
+	    data[0] == 0x77 && data[1] == 0x77) {
+		TP_DBG(NULL, "IOCTL_WRITE CMD = %d\n", data[2]);
+		switch (data[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", data[3]);
+			if (data[3] == 0)
+				ilitek_debug_flag = false;
+			else if (data[3] == 1)
+				ilitek_debug_flag = true;
+			break;
+		case 15:
+			if (data[3] == 0) {
+				ilitek_irq_disable();
+				TP_DBG(NULL, "ilitek_irq_disable.\n");
+			} else {
+				ilitek_irq_enable();
+				TP_DBG(NULL, "ilitek_irq_enable.\n");
+			}
+			break;
+		case 16:
+			ts->operation_protection = data[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 8:
+			TP_MSG(NULL, "get driver version\n");
+			ilitek_udp_reply(driver_ver, 7);
+			break;
+		case 18:
+			TP_DBG(NULL, "firmware update write 33 bytes data\n");
+			ret = ilitek_write(&data[3], 33);
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			if (ret < 0)
+				data[0] = 1;
+			else
+				data[0] = 0;
+			ilitek_udp_reply(data, 1);
+			return;
+		default:
+			return;
+		}
+	} else if (data[count - 2] == 'W') {
+		ret = ilitek_write(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+		if (ret < 0)
+			data[0] = 1;
+		else
+			data[0] = 0;
+		ilitek_udp_reply(data, 1);
+	} else if (data[count - 2] == 'R') {
+		ret = ilitek_read(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		if (ret < 0)
+			data[count - 2] = 1;
+		else
+			data[count - 2] = 0;
+		ilitek_udp_reply(data, count - 1);
+	}
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void ilitek_esd_check(struct work_struct *work)
+{
+	int retry = 3;
+	static bool is_first_run = true;
+	static u32 protocol_ver;
+
+	/*
+	 * update protocol version at the first run
+	 */
+	if (is_first_run) {
+		is_first_run = false;
+		protocol_ver = ts->dev->protocol.ver;
+		TP_MSG(NULL, "[ESD] firstly loading protocol ver: %x as ref.\n",
+			protocol_ver);
+	}
+
+	if (ts->operation_protection || ts->esd_skip) {
+		TP_MSG(NULL, "[ESD] operation_protection: %hhu, esd_skip: %hhu\n",
+			ts->operation_protection, ts->esd_skip);
+		goto skip_return;
+	}
+
+	mutex_lock(&ts->ilitek_mutex);
+
+	for (; retry-- > 0;) {
+		if (api_protocol_set_cmd(ts->dev, GET_PTL_VER, NULL) < 0) {
+			TP_ERR(NULL, "[ESD] i2c comm. failed\n");
+			continue;
+		}
+
+		if (protocol_ver != ts->dev->protocol.ver) {
+			TP_ERR(NULL, "unexpected ptl ver (referance)%x vs. %x\n",
+				protocol_ver, ts->dev->protocol.ver);
+			continue;
+		}
+
+		goto pass_return;
+	}
+
+	ilitek_reset(ts->dev->reset_time);
+
+pass_return:
+	mutex_unlock(&ts->ilitek_mutex);
+
+skip_return:
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+}
+
+void ilitek_irq_enable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	enable_irq(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 1);
+}
+
+void ilitek_irq_disable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (!atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	disable_irq_nosync(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 0);
+}
+
+#ifdef ILITEK_ENABLE_DMA
+static int ilitek_dma_i2c_read(u8 *buf, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int err;
+
+
+	if (len < 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_recv(client, buf, len);
+	}
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+	err = i2c_master_recv(client, (u8 *)I2CDMABuf_pa, len);
+	if (err < 0)
+		return err;
+
+	memcpy(buf, I2CDMABuf_va, len);
+
+	return 0;
+}
+
+static int ilitek_dma_i2c_write(u8 *cmd, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+
+	if (len <= 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_send(client, cmd, len);
+	}
+
+	memcpy(I2CDMABuf_va, cmd, len);
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+
+	return i2c_master_send(client, (u8 *)I2CDMABuf_pa, len);
+}
+#endif
+
+static int ilitek_i2c_transfer(struct i2c_msg *msgs, int cnt)
+{
+	int err = 0;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int count = ILITEK_I2C_RETRY_COUNT;
+
+#ifdef ILITEK_ENABLE_DMA
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		count = ILITEK_I2C_RETRY_COUNT;
+		while (count-- >= 0) {
+			msgs[i].ext_flag = 0;
+			if (msgs[i].flags == I2C_M_RD)
+				err = ilitek_dma_i2c_read(msgs[i].buf, msgs[i].len);
+			else if (!msgs[i].flags)
+				err = ilitek_dma_i2c_write(msgs[i].buf, msgs[i].len);
+
+			if (err < 0) {
+				TP_ERR(NULL, "i2c[0x%hx] dma tx/rx failed, err: %d\n",
+					msgs[i].addr, err);
+				mdelay(20);
+				continue;
+			}
+
+			break;
+		}
+	}
+#else
+	while (count-- >= 0) {
+		err = i2c_transfer(client->adapter, msgs, cnt);
+		if (err < 0) {
+			TP_ERR(NULL, "i2c[0x%hx] tx/rx failed, err: %d\n",
+				msgs[0].addr, err);
+			mdelay(20);
+			continue;
+		}
+		break;
+	}
+#endif
+
+	return err;
+}
+
+static int __maybe_unused ilitek_i2c_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error;
+
+	/*
+	 * Default ILITEK_BL_ADDR. is firstly used.
+	 * if communication failed, change between BL addr. and
+	 * other addr. defined by DTS, then retry.
+	 */
+	static unsigned short addr = ILITEK_BL_ADDR;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	struct i2c_msg msgs[2] = {
+		{.addr = addr, .flags = 0, .len = w_len,
+		 .buf = cmd, SCL_RATE(400000)},
+		{.addr = addr, .flags = I2C_M_RD, .len = r_len,
+		 .buf = buf, SCL_RATE(400000)}
+	};
+
+	/*
+	 * IMPORTANT: If I2C repeat start is required, please check with ILITEK.
+	 */
+	if (w_len > 0 && r_len > 0 && !delay_ms) {
+		if (ilitek_i2c_transfer(msgs, 2) < 0) {
+			/* try another i2c addr. (default: 0x41) */
+			addr = (addr == ILITEK_BL_ADDR) ?
+				client->addr : ILITEK_BL_ADDR;
+			msgs[0].addr = msgs[1].addr = addr;
+
+			return ilitek_i2c_transfer(msgs, 2);
+		}
+
+		return 0;
+	}
+
+	if (w_len > 0 && ilitek_i2c_transfer(msgs, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		error = ilitek_i2c_transfer(msgs, 1);
+		if (error < 0)
+			return error;
+	}
+
+	if (delay_ms > 0)
+		mdelay(delay_ms);
+
+	if (r_len > 0 && ilitek_i2c_transfer(msgs + 1, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		return ilitek_i2c_transfer(msgs + 1, 1);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_i2c_write(u8 *cmd, int len)
+{
+	return ilitek_i2c_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_i2c_read(u8 *buf, int len)
+{
+	return ilitek_i2c_write_and_read(NULL, 0, 0, buf, len);
+}
+
+static int __maybe_unused ilitek_spi_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error = 0;
+	u8 *wbuf, *rbuf;
+	struct spi_device *spi = (struct spi_device *)ts->client;
+	struct spi_transfer xfer = {
+		.len = r_len + 4,
+		.speed_hz = ((struct spi_device *)ts->client)->max_speed_hz,
+	};
+	struct spi_message msg;
+
+	if (w_len > 0 && r_len > 0) {
+		error = ilitek_spi_write_and_read(cmd, w_len, delay_ms,
+						  NULL, 0);
+		if (error < 0)
+			return error;
+
+		return ilitek_spi_write_and_read(NULL, 0, 0, buf, r_len);
+	}
+
+	wbuf = CALLOC(4096, sizeof(u8));
+	rbuf = CALLOC(4096, sizeof(u8));
+
+	if (!wbuf || !rbuf) {
+		error = -ENOMEM;
+		goto exit;
+	}
+
+	xfer.tx_buf = wbuf;
+	xfer.rx_buf = rbuf;
+
+	wbuf[1] = 0xAA;
+
+	/* wbuf[0] set as 0x83 for spi data read */
+	if (r_len > 0) {
+		wbuf[0] = 0x83;
+		memset(wbuf + 2, 0, xfer.len - 2);
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfer, &msg);
+		error = spi_sync(spi, &msg);
+		if (error < 0)
+			goto exit;
+
+		TP_DBG(NULL, "[rbuf]: %*phD, len: %d\n", xfer.len, rbuf, xfer.len);
+
+		memcpy(buf, rbuf + 4, r_len);
+	} else if (w_len > 0) {
+		wbuf[0] = 0x82;
+		wbuf[2] = cmd[0];
+		wbuf[3] = 0;
+		memcpy(wbuf + 4, cmd + 1, w_len - 1);
+
+		TP_DBG(NULL, "[wbuf]: %*phD, len: %d\n", 3 + w_len, wbuf, 3 + w_len);
+
+		error = spi_write(spi, wbuf, 3 + w_len);
+		if (error < 0)
+			goto exit;
+
+		if (delay_ms > 0)
+			mdelay(delay_ms);
+	}
+
+exit:
+	CFREE(wbuf);
+	CFREE(rbuf);
+
+	return (error < 0) ? error : 0;
+}
+
+static int __maybe_unused ilitek_spi_write(u8 *cmd, int len)
+{
+	return ilitek_spi_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_spi_read(u8 *buf, int len)
+{
+	return ilitek_spi_write_and_read(NULL, 0, 0, buf, len);
+}
+
+int ilitek_write(u8 *cmd, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write(cmd, len);
+#else
+	error = ilitek_i2c_write(cmd, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_read(u8 *buf, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_read(buf, len);
+#else
+	error = ilitek_i2c_read(buf, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#else
+	error = ilitek_i2c_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+void __maybe_unused ilitek_gpio_dbg(void)
+{
+#if defined(ILITEK_GPIO_DEBUG)
+	gpio_direction_output(ts->test_gpio, 0);
+	mdelay(1);
+	gpio_direction_output(ts->test_gpio, 1);
+#endif
+}
+
+void ilitek_reset(int delay)
+{
+	TP_MSG(NULL, "reset_gpio: %d, delay: %d\n", ts->reset_gpio, delay);
+
+	ilitek_irq_disable();
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK && defined(MTK_UNDTS)
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ZERO);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(delay);
+#elif ILITEK_PLAT == ILITEK_PLAT_MTK
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 0);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#else
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 0);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#endif
+
+	ilitek_irq_enable();
+}
+
+static int ilitek_free_gpio(void)
+{
+
+#ifndef MTK_UNDTS
+	if (gpio_is_valid(ts->reset_gpio)) {
+		TP_MSG(NULL, "reset_gpio is valid so free\n");
+		gpio_free(ts->reset_gpio);
+	}
+	if (gpio_is_valid(ts->irq_gpio)) {
+		TP_MSG(NULL, "irq_gpio is valid so free\n");
+		gpio_free(ts->irq_gpio);
+	}
+#endif
+
+#if defined(ILITEK_GPIO_DEBUG)
+	if (gpio_is_valid(ts->test_gpio)) {
+		TP_MSG(NULL, "test_gpio is valid so free\n");
+		gpio_free(ts->test_gpio);
+	}
+#endif
+
+	return 0;
+}
+
+static int ilitek_request_pen_input_dev(void)
+{
+	int error;
+	struct input_dev *input;
+
+	int x_min = ts->dev->screen_info.pen_x_min;
+	int y_min = ts->dev->screen_info.pen_y_min;
+	int x_max = ts->dev->screen_info.pen_x_max;
+	int y_max = ts->dev->screen_info.pen_y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	TP_DBG(NULL, "registering pen input device\n");
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOOL_PEN, input->keybit);		/* In Range */
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);	/* Invert */
+	__set_bit(BTN_STYLUS, input->keybit);		/* Barrel Switch */
+	__set_bit(BTN_TOUCH, input->keybit);		/* Tip Switch */
+
+	input->name = "ILITEK STYLUS";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
+
+	input_set_abs_params(input, ABS_PRESSURE,
+			     ts->dev->screen_info.pressure_min,
+			     ts->dev->screen_info.pressure_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_X,
+			     ts->dev->screen_info.x_tilt_min,
+			     ts->dev->screen_info.x_tilt_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_Y,
+			     ts->dev->screen_info.y_tilt_min,
+			     ts->dev->screen_info.y_tilt_max, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "register pen device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+
+	ts->pen_input_dev = input;
+
+	return 0;
+}
+
+static int ilitek_request_input_dev(void)
+{
+	int error;
+	int i;
+	struct input_dev *input;
+
+#ifdef ILITEK_USE_MTK_INPUT_DEV
+	input = tpd->dev;
+	if (!input)
+		return -ENOMEM;
+#ifdef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
+			input_set_capability(input, EV_KEY,
+					     tpd_dts_data.tpd_key_local[i]);
+	}
+#endif
+#else
+	int x_min = ts->dev->screen_info.x_min;
+	int y_min = ts->dev->screen_info.y_min;
+	int x_max = ts->dev->screen_info.x_max;
+	int y_max = ts->dev->screen_info.y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+#endif
+
+	TP_DBG(NULL, "registering touch input device\n");
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	INPUT_MT_INIT_SLOTS(input, MAX(2, ts->dev->tp_info.max_fingers));
+#else
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0,
+			     ts->dev->tp_info.max_fingers, 0, 0);
+#endif
+
+#ifdef ILITEK_REPORT_PRESSURE
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+#endif
+
+	for (i = 0; i < ts->dev->tp_info.key_num; i++)
+		set_bit(ts->dev->key.info.keys[i].id & KEY_MAX, input->keybit);
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+#ifndef ILITEK_USE_MTK_INPUT_DEV
+	input->name = ILITEK_TS_NAME;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x_min = 0; y_min = 0;
+	x_max = TOUCH_SCREEN_X_MAX; y_max = TOUCH_SCREEN_Y_MAX;
+#endif
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 32767, 0, 0);
+	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 32767, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "input_register_device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+#endif
+
+	ts->input_dev = input;
+
+	if (ts->dev->tp_info.pen_modes)
+		ilitek_request_pen_input_dev();
+
+	return 0;
+}
+
+static int ilitek_touch_down(int id, int x, int y, int p, int h, int w)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x = (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX /
+		(ts->dev->screen_info.x_max - ts->dev->screen_info.x_min);
+	y = (y - ts->dev->screen_info.y_min) * TOUCH_SCREEN_Y_MAX /
+		(ts->dev->screen_info.y_max - ts->dev->screen_info.y_min);
+#endif
+
+	input_report_key(input, BTN_TOUCH, 1);
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_mt_slot(input, id);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+#endif
+	input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+	input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+	input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, h);
+	input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, w);
+#ifdef ILITEK_REPORT_PRESSURE
+	input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+#endif
+#ifndef ILITEK_TOUCH_PROTOCOL_B
+	input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id);
+	input_mt_sync(input);
+#endif
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(x, y, 1);
+			TP_DBG(NULL, "tpd_button(x, y, 1) = tpd_button(%d, %d, 1)\n", x, y);
+		}
+	}
+#endif
+#endif
+#endif
+	return 0;
+}
+
+static int ilitek_touch_release(int id)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	if (ts->touch_flag[id] == 1) {
+		TP_DBG(NULL, "release point id = %d\n", id);
+		input_mt_slot(input, id);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+#else
+	input_report_key(input, BTN_TOUCH, 0);
+	input_mt_sync(input);
+#endif
+	set_arr(ts->touch_flag, id, 0);
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() ||
+		    RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(0, 0, 0);
+			TP_DBG(NULL, "tpd_button(x, y, 0) = tpd_button(0, 0, 0)\n");
+		}
+	}
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int ilitek_touch_release_all_point(void)
+{
+	struct input_dev *input = ts->input_dev;
+	int i = 0;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_report_key(input, BTN_TOUCH, 0);
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		ilitek_touch_release(i);
+#else
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		set_arr(ts->touch_flag, i, 0);
+	ilitek_touch_release(0);
+#endif
+	ts->is_touched = false;
+	input_sync(input);
+	return 0;
+}
+
+static int ilitek_check_key_down(int x, int y)
+{
+	int j;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if ((x >= ts->dev->key.info.keys[j].x &&
+		     x <= ts->dev->key.info.keys[j].x +
+			ts->dev->key.info.x_len) &&
+		    (y >= ts->dev->key.info.keys[j].y &&
+		     y <= ts->dev->key.info.keys[j].y +
+			ts->dev->key.info.y_len)) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 1);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				x = tpd_dts_data.tpd_key_dim_local[j].key_x;
+				y = tpd_dts_data.tpd_key_dim_local[j].key_y;
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key down\n",
+					j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_down(0, x, y, 10, 128, 1);
+			}
+#else
+			x = touch_key_point_maping_array[j].point_x;
+			y = touch_key_point_maping_array[j].point_y;
+			ilitek_touch_down(0, x, y, 10, 128, 1);
+#endif
+#endif
+			ts->dev->key.clicked[j] = true;
+			ts->touch_key_hold_press = true;
+			ts->is_touched = true;
+			TP_DBG(NULL, "Key, Keydown ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int ilitek_check_key_release(int x, int y, int check_point)
+{
+	int j = 0;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if (!ts->dev->key.clicked[j])
+			continue;
+
+		if (check_point) {
+			if (x < ts->dev->key.info.keys[j].x ||
+			    x > ts->dev->key.info.keys[j].x + ts->dev->key.info.x_len ||
+			    y < ts->dev->key.info.keys[j].y ||
+			    y > ts->dev->key.info.keys[j].y + ts->dev->key.info.y_len) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+				input_report_key(ts->input_dev,
+						 ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+				if (tpd_dts_data.use_tpd_button) {
+					TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+					ilitek_touch_release(0);
+				}
+#else
+				ilitek_touch_release(0);
+#endif
+#endif
+				ts->dev->key.clicked[j] = false;
+				ts->touch_key_hold_press = false;
+				TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+					ts->dev->key.info.keys[j].id, x, y,
+					ts->dev->key.clicked[j]);
+				break;
+			}
+		} else {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_release(0);
+			}
+#else
+			ilitek_touch_release(0);
+#endif
+#endif
+			ts->dev->key.clicked[j] = false;
+			ts->touch_key_hold_press = false;
+			TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+int event_spacing;
+static u8 finger_state;
+static int start_x;
+static int start_y;
+static int current_x;
+static int current_y;
+
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+static struct timeval start_event_time;
+#else
+unsigned long start_event_time_jiffies;
+#endif
+
+static int ilitek_get_time_diff(void)
+{
+	int diff_milliseconds = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+	struct timeval time_now;
+
+	do_gettimeofday(&time_now);
+	diff_milliseconds += (time_now.tv_sec - start_event_time.tv_sec) * 1000;
+
+	if (time_now.tv_usec < start_event_time.tv_usec) {
+		diff_milliseconds -= 1000;
+		diff_milliseconds += (1000 * 1000 + time_now.tv_usec - start_event_time.tv_usec) / 1000;
+	} else
+		diff_milliseconds += (time_now.tv_usec - start_event_time.tv_usec) / 1000;
+
+	if (diff_milliseconds < (-10000))
+		diff_milliseconds = 10000;
+	TP_MSG(NULL, "time_now.tv_sec = %d start_event_time.tv_sec = %d time_now.tv_usec = %d start_event_time.tv_usec = %d diff_milliseconds = %d\n",
+			(int)time_now.tv_sec, (int)start_event_time.tv_sec, (int)time_now.tv_usec, (int)start_event_time.tv_usec, diff_milliseconds);
+#else
+	diff_milliseconds = jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_event_time_jiffies);
+	TP_MSG(NULL, "jiffies_to_msecs(jiffies) = %u jiffies_to_msecs(start_event_time_jiffies) = %u diff_milliseconds = %d\n", jiffies_to_msecs(jiffies),
+			jiffies_to_msecs(start_event_time_jiffies), diff_milliseconds);
+#endif
+	return diff_milliseconds;
+}
+
+static u8 ilitek_double_click_touch(int finger_id, int x, int y,
+					 u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_id > 0) {
+		finger_state = 0;
+		goto out;
+	}
+	if (finger_state == 0 || finger_state == 5) {
+
+		finger_state = 1;
+		start_x = x;
+		start_y = y;
+		current_x = 0;
+		current_y = 0;
+		event_spacing = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+		do_gettimeofday(&start_event_time);
+#else
+		start_event_time_jiffies = jiffies;
+#endif
+	} else if (finger_state == 1) {
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 4;
+	} else if (finger_state == 2) {
+		finger_state = 3;
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > (DOUBLE_CLICK_ONE_CLICK_USED_TIME + DOUBLE_CLICK_NO_TOUCH_TIME))
+			finger_state = 0;
+	} else if (finger_state == 3) {
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_TOTAL_USED_TIME) {
+			start_x = current_x;
+			start_y = current_y;
+			finger_state = 4;
+		}
+	}
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+static u8 ilitek_double_click_release(u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_state == 1) {
+		finger_state = 2;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 0;
+	}
+	if (finger_state == 3) {
+		event_spacing = ilitek_get_time_diff();
+		if ((event_spacing < DOUBLE_CLICK_TOTAL_USED_TIME && event_spacing > 50) && (ABSSUB(current_x, start_x) < DOUBLE_CLICK_DISTANCE)
+				&& ((ABSSUB(current_y, start_y) < DOUBLE_CLICK_DISTANCE))) {
+			finger_state = 5;
+			goto out;
+		} else
+			finger_state = 0;
+	} else if (finger_state == 4)
+		finger_state = 0;
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y)
+{
+	struct input_dev *input = ts->input_dev;
+
+	if (ts->gesture_status == Gesture_Double_Click) {
+		if (touch) {
+			finger_state = ilitek_double_click_touch(idx, x, y, finger_state);
+			return;
+		}
+		finger_state = ilitek_double_click_release(finger_state);
+
+		if (finger_state != 5)
+			return;
+	}
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_timeout(&ilitek_wake_lock, 5 * HZ);
+#endif
+
+	/* prevent power key being triggered multiple times */
+	if (ts->power_key_triggered)
+		return;
+
+	input_report_key(input, KEY_POWER, 1);
+	input_sync(input);
+	input_report_key(input, KEY_POWER, 0);
+	input_sync(input);
+
+	ts->power_key_triggered = true;
+}
+
+static void ilitek_report_touch_event(struct touch_data *touch, void *_private)
+{
+	struct input_dev *input = ts->input_dev;
+	u8 i, released_cnt = 0;
+
+	u16 x_max = ts->dev->screen_info.x_max;
+	u16 x_min = ts->dev->screen_info.x_min;
+	u16 y_max = ts->dev->screen_info.y_max;
+	u16 y_min = ts->dev->screen_info.y_min;
+
+	/*
+	 * ISR may be activated after registering irq and
+	 * before creating input_dev
+	 */
+	if (!input) {
+		TP_ERR(NULL, "input_dev is not registerred\n");
+		return;
+	}
+
+	for (i = 0; i < touch->cnt; i++) {
+		if (!touch->finger[i].status) {
+			released_cnt++;
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+			ilitek_touch_release(touch->finger[i].id);
+#endif
+			continue;
+		}
+
+		set_arr(ts->touch_flag, touch->finger[i].id, 1);
+
+		touch->finger[i].x = (ILITEK_REVERT_X) ?
+			x_max - touch->finger[i].x + x_min : touch->finger[i].x;
+		touch->finger[i].y = (ILITEK_REVERT_Y) ?
+			y_max - touch->finger[i].y + y_min : touch->finger[i].y;
+
+#if ILITEK_ROTATE_FLAG
+		swap(touch->finger[i].x, touch->finger[i].y);
+#endif
+
+		if (ts->system_suspend) {
+			TP_MSG(NULL, "system is suspend not report point\n");
+			ilitek_gesture_handle(true, i, touch->finger[i].x,
+					      touch->finger[i].y);
+			continue;
+		}
+
+		if (!ts->is_touched)
+			ilitek_check_key_down(touch->finger[i].x,
+					      touch->finger[i].y);
+
+		if (!ts->touch_key_hold_press) {
+			ts->is_touched = true;
+			ilitek_touch_down(touch->finger[i].id,
+					  touch->finger[i].x,
+					  touch->finger[i].y,
+					  touch->finger[i].pressure,
+					  touch->finger[i].height,
+					  touch->finger[i].width);
+		} else {
+			ilitek_check_key_release(touch->finger[i].x,
+						 touch->finger[i].y, 1);
+		}
+	}
+
+	if (touch->cnt == released_cnt) {
+		if (ts->is_touched)
+			ilitek_touch_release_all_point();
+
+		ilitek_check_key_release(0, 0, 0);
+		ts->is_touched = false;
+
+		if (ts->system_suspend)
+			ilitek_gesture_handle(false, 0, 0, 0);
+	}
+
+	input_sync(input);
+}
+
+static void ilitek_report_pen_event(struct pen_data *pen, void *_private)
+{
+	static int curr_tool = BTN_TOOL_PEN;
+	struct input_dev *pen_input = ts->pen_input_dev;
+	int tool;
+
+	u16 x_max = ts->dev->screen_info.pen_x_max;
+	u16 x_min = ts->dev->screen_info.pen_x_min;
+	u16 y_max = ts->dev->screen_info.pen_y_max;
+	u16 y_min = ts->dev->screen_info.pen_y_min;
+
+	if (!pen_input)
+		return;
+
+	tool = (pen->pen.in_range && pen->pen.invert) ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	if (curr_tool != tool) {
+		input_report_key(pen_input, curr_tool, 0);
+		input_sync(pen_input);
+		curr_tool = tool;
+	}
+
+	pen->pen.x = (ILITEK_REVERT_X) ?
+		x_max - pen->pen.x + x_min : pen->pen.x;
+	pen->pen.y = (ILITEK_REVERT_Y) ?
+		y_max - pen->pen.y + y_min : pen->pen.y;
+
+#if ILITEK_ROTATE_FLAG
+	swap(pen->pen.x, pen->pen.y);
+#endif
+
+	input_report_key(pen_input, BTN_TOUCH,
+			 pen->pen.tip_sw || pen->pen.eraser);
+	input_report_key(pen_input, curr_tool, pen->pen.in_range);
+	input_report_key(pen_input, BTN_STYLUS, pen->pen.barrel_sw);
+	input_event(pen_input, EV_ABS, ABS_X, pen->pen.x);
+	input_event(pen_input, EV_ABS, ABS_Y, pen->pen.y);
+	input_event(pen_input, EV_ABS, ABS_PRESSURE, pen->pen.pressure);
+	input_event(pen_input, EV_ABS, ABS_TILT_X, pen->pen.x_tilt);
+	input_event(pen_input, EV_ABS, ABS_TILT_Y, pen->pen.y_tilt);
+
+	input_sync(pen_input);
+}
+
+
+static void ilitek_report_buf(u8 *buf, int size,
+			      bool is_last, void *_private)
+{
+	UNUSED(is_last);
+
+	ilitek_udp_reply(buf, size);
+}
+
+int ilitek_read_data_and_report(void)
+{
+	int error;
+	u8 i, count;
+	struct ilitek_report report;
+
+	memset(&report, 0, sizeof(report));
+	report.cb.report_touch_event = ilitek_report_touch_event;
+	report.cb.report_pen_event = ilitek_report_pen_event;
+	report.cb.report_buf = ilitek_report_buf;
+
+	switch (ts->irq_handle_type) {
+	case irq_type_c_model:
+		memset(ts->buf, 0, sizeof(ts->buf));
+
+		for (i = 0, count = 1; i < count; i++) {
+			ilitek_read(ts->buf, ts->irq_read_len);
+			ilitek_udp_reply(ts->buf, ts->irq_read_len);
+			count = ts->buf[ts->irq_read_len - 1];
+		}
+
+		break;
+
+	case irq_type_debug:
+	case irq_type_normal:
+	default:
+		error = ilitek_report_update(ts->dev, &report);
+		if (error < 0) {
+			if (error == -EILIPROTO && !checksum_failed_release)
+				break;
+
+			if (ts->is_touched) {
+				ilitek_touch_release_all_point();
+				ilitek_check_key_release(0, 0, 0);
+			}
+
+			return error;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+static ISR_FUNC(ilitek_i2c_isr)
+{
+	int error;
+
+	TP_DBG(NULL, "%s\n", __func__);
+
+	atomic_set(&ts->get_INT, 1);
+	ilitek_gpio_dbg();
+
+	ts->esd_skip = true;
+
+	if (atomic_read(&ts->firmware_updating)) {
+		TP_DBG(NULL, "firmware_updating return\n");
+		goto exit;
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_disable();
+#endif
+
+	if (!ts->unhandle_irq) {
+		mutex_lock(&ts->ilitek_mutex);
+		error = ilitek_read_data_and_report();
+		if (error < 0)
+			TP_ERR(NULL, "process error\n");
+		mutex_unlock(&ts->ilitek_mutex);
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_enable();
+#endif
+
+exit:
+	ts->esd_skip = false;
+
+	ISR_RETURN(IRQ_HANDLED);
+}
+
+static int ilitek_request_irq(void)
+{
+	int error;
+
+#ifdef MTK_UNDTS
+	mt_set_gpio_mode(ILITEK_IRQ_GPIO, GPIO_CTP_EINT_PIN_M_EINT);
+	mt_set_gpio_dir(ILITEK_IRQ_GPIO, GPIO_DIR_IN);
+	mt_set_gpio_pull_enable(ILITEK_IRQ_GPIO, GPIO_PULL_ENABLE);
+	mt_set_gpio_pull_select(ILITEK_IRQ_GPIO, GPIO_PULL_UP);
+
+	mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+	mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, ilitek_i2c_isr, 1);
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	struct device_node *node;
+
+	node = of_find_matching_node(NULL, touch_of_match);
+	if (node)
+		ts->irq = irq_of_parse_and_map(node, 0);
+#else
+	ts->irq = gpio_to_irq(ts->irq_gpio);
+#endif
+
+	TP_MSG(NULL, "ts->irq: %d\n", ts->irq);
+	if (ts->irq <= 0)
+		return -EINVAL;
+
+	error = request_threaded_irq(ts->irq, NULL, ilitek_i2c_isr,
+				     ts->irq_trigger_type | IRQF_ONESHOT,
+				     "ilitek_touch_irq", ts);
+	if (error) {
+		TP_ERR(NULL, "request threaded irq failed, err: %d\n", error);
+		return error;
+	}
+#endif
+
+	ts->irq_registered = true;
+	atomic_set(&ts->irq_enabled, 1);
+
+	return 0;
+}
+
+static int ilitek_read_fw(char *filename, unsigned char *buf, int size, void *data)
+{
+	int error, fw_size;
+	const struct firmware *fw;
+	struct device *device = (struct device *)data;
+
+	error = request_firmware(&fw, filename, device);
+	if (error) {
+		TP_ERR(NULL, "request fw: %s failed, err:%d\n", filename, error);
+		return error;
+	}
+
+	if (size < fw->size) {
+		fw_size = -EFBIG;
+		goto release_fw;
+	}
+
+	fw_size = fw->size;
+	memcpy(buf, fw->data, fw->size);
+
+release_fw:
+	release_firmware(fw);
+
+	return fw_size;
+}
+
+struct ilitek_update_callback update_cb = {
+	.read_fw = ilitek_read_fw,
+	.update_progress = NULL,
+	.update_fw_file_info = NULL,
+
+	.slave_update_notify = NULL,
+	.update_fw_ic_info = NULL,
+};
+
+int ilitek_upgrade_firmware(char *filename)
+{
+	int error;
+	struct ilitek_fw_handle *handle;
+	struct ilitek_fw_settings setting;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	atomic_set(&ts->firmware_updating, 1);
+	ts->operation_protection = true;
+
+	handle = ilitek_update_init(ts->dev, false, &update_cb, ts->device);
+
+	setting.force_update = false;
+	setting.fw_check_only = false;
+	setting.fw_ver_check = false;
+	setting.retry = 3;
+	ilitek_update_setting(handle, &setting);
+
+	error = ilitek_update_load_fw(handle, filename);
+	if (error < 0)
+		goto err_return;
+	error = ilitek_update_start(handle);
+	if (error < 0)
+		goto err_return;
+
+err_return:
+	ilitek_update_exit(handle);
+
+	ts->operation_protection = false;
+	atomic_set(&ts->firmware_updating, 0);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return error;
+}
+
+
+static int __maybe_unused ilitek_update_thread(void *arg)
+{
+#ifdef ILITEK_BOOT_UPDATE
+	int error;
+
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (kthread_should_stop()) {
+		TP_MSG(NULL, "ilitek_update_thread, stop\n");
+		return -1;
+	}
+
+	mdelay(100);
+
+	error = ilitek_upgrade_firmware("ilitek.ili");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.hex");
+		if (error < 0) {
+			error = ilitek_upgrade_firmware("ilitek.bin");
+			if (error < 0)
+				return error;
+		}
+	}
+
+	error = ilitek_request_input_dev();
+	if (error)
+		return (error < 0) ? error : -EFAULT;
+#endif
+
+	return 0;
+}
+
+void ilitek_suspend(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	ts->esd_skip = true;
+	if (ts->esd_check && ts->esd_workq)
+		cancel_delayed_work_sync(&ts->esd_work);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ts->wake_irq_enabled = (enable_irq_wake(ts->irq) == 0);
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_set_idle(ts->dev, true) < 0)
+				TP_ERR(NULL, "enable Idle mode failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	} else {
+		/*
+		 * Must disable irq before sleep cmd,
+		 * Avoid getting into ISR handling (and do i2c read),
+		 * after sending sleep cmd.
+		 */
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_protocol_set_cmd(ts->dev, SET_IC_SLEEP,
+						 NULL) < 0)
+				TP_ERR(NULL, "set tp sleep failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->power_key_triggered = false;
+	ts->system_suspend = true;
+}
+
+void ilitek_resume(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_set_idle(ts->dev, false);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+
+		if (ts->gesture_status == Gesture_Double_Click)
+			finger_state = 0;
+
+		if (ts->wake_irq_enabled) {
+			disable_irq_wake(ts->irq);
+			ts->wake_irq_enabled = false;
+		}
+	} else {
+		if (ts->dev->protocol.flag == PTL_V3) {
+			/*
+			 * If ILITEK_SLEEP is defined and FW support wakeup cmd,
+			 * the hw reset can be mark.
+			 */
+			ilitek_reset(ts->dev->reset_time);
+		}
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_protocol_set_cmd(ts->dev, SET_IC_WAKE, NULL);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->esd_skip = false;
+	if (ts->esd_check && ts->esd_workq)
+		queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	ilitek_touch_release_all_point();
+	ilitek_check_key_release(0, 0, 0);
+
+	ts->system_suspend = false;
+
+	ilitek_irq_enable();
+}
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+int ilitek_resume_allwin(struct i2c_client *client)
+{
+	ilitek_resume();
+	return 0;
+}
+#endif
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+static int __maybe_unused ilitek_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+#ifdef CONFIG_QCOM_DRM
+	struct msm_drm_notifier *ev_data = data;
+#else
+	struct fb_event *ev_data = data;
+#endif
+	int *blank;
+
+	TP_MSG(NULL, "FB EVENT event: %lu\n", event);
+
+#ifdef CONFIG_QCOM_DRM
+	if (!ev_data || (ev_data->id != 0))
+		return 0;
+#endif
+	if (ev_data && ev_data->data && event == ILITEK_EVENT_BLANK) {
+		blank = ev_data->data;
+		TP_MSG(NULL, "blank: %d\n", *blank);
+		if (*blank == ILITEK_BLANK_POWERDOWN) {
+			ilitek_suspend();
+		} else if (*blank == ILITEK_BLANK_UNBLANK ||
+			   *blank == ILITEK_BLANK_NORMAL) {
+			ilitek_resume();
+		}
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void __maybe_unused ilitek_early_suspend(struct early_suspend *h)
+{
+	ilitek_suspend();
+}
+
+static void __maybe_unused ilitek_late_resume(struct early_suspend *h)
+{
+	ilitek_resume();
+}
+#endif
+#endif
+
+static void ilitek_get_gpio_num(void)
+{
+#ifdef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	TP_MSG(NULL, "(config_info.wakeup_gpio.gpio) = %d (config_info.int_number) = %d\n", (config_info.wakeup_gpio.gpio), (config_info.int_number));
+	ts->reset_gpio = (config_info.wakeup_gpio.gpio);
+	ts->irq_gpio = (config_info.int_number);
+#else
+#ifdef CONFIG_OF
+	ts->reset_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,reset-gpio", 0);
+	if (ts->reset_gpio < 0)
+		TP_ERR(NULL, "reset_gpio = %d\n", ts->reset_gpio);
+	ts->irq_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,irq-gpio", 0);
+	if (ts->irq_gpio < 0)
+		TP_ERR(NULL, "irq_gpio = %d\n", ts->irq_gpio);
+#endif
+#endif
+#else
+	ts->reset_gpio = ILITEK_RESET_GPIO;
+	ts->irq_gpio = ILITEK_IRQ_GPIO;
+#endif
+
+	TP_MSG(NULL, "reset_gpio = %d irq_gpio = %d\n", ts->reset_gpio, ts->irq_gpio);
+
+
+#if defined(ILITEK_GPIO_DEBUG)
+	do {
+		ts->test_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,test-gpio", 0);
+		if (ts->test_gpio < 0) {
+			TP_ERR(NULL, "test_gpio: %d\n", ts->test_gpio);
+			break;
+		}
+
+		TP_MSG(NULL, "test_gpio: %d\n", ts->test_gpio);
+
+		if (gpio_request(ts->test_gpio, "ilitek-test-gpio")) {
+			TP_ERR(NULL, "request test_gpio failed\n");
+			break;
+		}
+
+		gpio_direction_output(ts->test_gpio, 1);
+
+	} while (0);
+#endif
+}
+
+static int ilitek_request_gpio(void)
+{
+	int ret = 0;
+
+	ts->irq_gpio = -ENODEV;
+	ts->reset_gpio = -ENODEV;
+
+	ilitek_get_gpio_num();
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+	if (ts->reset_gpio > 0) {
+		ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio so free retry\n");
+			gpio_free(ts->reset_gpio);
+			ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request reset_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio\n");
+		} else {
+			ret = gpio_direction_output(ts->reset_gpio, 1);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction output rest gpio err\n");
+		}
+	}
+	if (ts->irq_gpio > 0) {
+		ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio so free retry\n");
+			gpio_free(ts->irq_gpio);
+			ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request irq_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio\n");
+		} else {
+			ret = gpio_direction_input(ts->irq_gpio);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction input irq gpio err\n");
+		}
+	}
+#endif
+	return ret;
+}
+
+int ilitek_create_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to create esd workqueue\n");
+
+	INIT_DELAYED_WORK(&ts->esd_work, ilitek_esd_check);
+	ts->esd_workq = create_singlethread_workqueue("ilitek_esd_wq");
+	if (!ts->esd_workq)
+		return -ENOMEM;
+
+	ts->esd_skip = false;
+	ts->esd_delay = 2 * HZ;
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	return 0;
+}
+
+void ilitek_remove_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to remove esd workqueue\n");
+
+	if (ts->esd_workq) {
+		cancel_delayed_work_sync(&ts->esd_work);
+		destroy_workqueue(ts->esd_workq);
+		ts->esd_workq = NULL;
+	}
+}
+
+static int ilitek_register_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+	int error;
+
+	ts->fb_notif.notifier_call = ilitek_notifier_callback;
+
+#ifdef CONFIG_QCOM_DRM
+	error = msm_drm_register_client(&ts->fb_notif);
+#else
+	error = fb_register_client(&ts->fb_notif);
+#endif
+	if (error)
+		TP_ERR(NULL, "register fb_notifier failed, err: %d\n", error);
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	ts->early_suspend.suspend = ilitek_early_suspend;
+	ts->early_suspend.resume = ilitek_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+#endif /* ILITEK_PLAT != ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	device_enable_async_suspend(ts->device);
+	pm_runtime_set_active(ts->device);
+	pm_runtime_get(ts->device);
+	pm_runtime_enable(ts->device);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+#ifdef CONFIG_QCOM_DRM
+	msm_drm_unregister_client(&ts->fb_notif);
+#else
+	fb_unregister_client(&ts->fb_notif);
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+}
+
+int ilitek_netlink_init(u8 unit)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	NETLINK_KERNEL_CFG_DECLARE(cfg, udp_receive);
+
+	if (ilitek_netlink_sock)
+		ilitek_netlink_exit();
+
+	ilitek_netlink_sock = NETLINK_KERNEL_CREATE(unit, &cfg, udp_receive);
+
+	if (!ilitek_netlink_sock) {
+		TP_ERR(NULL, "netlink_kernel_create failed\n");
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+#endif
+	return -EPERM;
+}
+
+void ilitek_netlink_exit(void)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	if (ilitek_netlink_sock)
+		netlink_kernel_release(ilitek_netlink_sock);
+
+	ilitek_netlink_sock = NULL;
+#endif
+}
+
+static int __maybe_unused ilitek_alloc_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	I2CDMABuf_va = (u8 *) dma_alloc_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+	if (!I2CDMABuf_va) {
+		TP_ERR(NULL, "ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n");
+		I2CDMABuf_va = (u8 *) dma_alloc_coherent(NULL, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+		if (!I2CDMABuf_va) {
+			TP_ERR(NULL, "ilitek [TPD] NULL dma_alloc_coherent error\n");
+			return -ENOMEM;
+		}
+	}
+	memset(I2CDMABuf_va, 0, ILITEK_DMA_SIZE);
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_free_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	if (I2CDMABuf_va) {
+		dma_free_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE,
+				  I2CDMABuf_va, I2CDMABuf_pa);
+
+		I2CDMABuf_va = NULL;
+		I2CDMABuf_pa = 0;
+
+	}
+#endif
+	return 0;
+}
+
+static int __maybe_unused ilitek_power_on(bool status)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int error;
+
+	TP_MSG(NULL, "%s\n", status ? "POWER ON" : "POWER OFF");
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	input_set_power_enable(&(config_info.input_type), status);
+#else
+
+	if (status) {
+		if (ts->vdd) {
+			error = regulator_enable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_enable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	} else {
+		if (ts->vdd) {
+			error = regulator_disable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_disable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	}
+
+#ifdef MTK_UNDTS
+	if (status)
+		hwPowerOn(PMIC_APP_CAP_TOUCH_VDD, VOL_3300, "TP");
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_request_regulator(struct ilitek_ts_data *ts)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int ret = 0;
+	char *vdd_name = "vdd";
+	char *vcc_i2c_name = "vcc_i2c";
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	vdd_name = "vtouch";
+	ts->vdd = regulator_get(tpd->tpd_dev, vdd_name);
+	tpd->reg = ts->vdd;
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+	}
+#elif ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	ts->vdd = regulator_get(ts->device, vdd_name);
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+
+	}
+
+	ts->vdd_i2c = regulator_get(ts->device, vcc_i2c_name);
+	if (IS_ERR(ts->vdd_i2c)) {
+		TP_ERR(NULL, "regulator_get vdd_i2c fail\n");
+		ts->vdd_i2c = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd_i2c, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set i2c to 3000~3300mv.\n");
+	}
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+#endif /* ILITEK_ENABLE_REGULATOR_POWER_ON */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_regulator(void)
+{
+#if defined(ILITEK_ENABLE_REGULATOR_POWER_ON) && ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	if (ts->vdd)
+		regulator_put(ts->vdd);
+	if (ts->vdd_i2c)
+		regulator_put(ts->vdd_i2c);
+#endif
+}
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init)
+{
+	if (init) {
+		device_init_wakeup(ts->device, 1);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+		wake_lock_init(&ilitek_wake_lock, WAKE_LOCK_SUSPEND, "ilitek wakelock");
+#endif
+		return;
+	}
+
+	device_init_wakeup(ts->device, 0);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_destroy(&ilitek_wake_lock);
+#endif
+}
+
+static int _ilitek_write_then_read(unsigned char *wbuf, int wlen,
+				   unsigned char *rbuf, int rlen, void *data)
+{
+	return ilitek_write_and_read(wbuf, wlen, 1, rbuf, rlen);
+}
+
+static int ilitek_read_interrupt_in(unsigned char *rbuf, int rlen,
+				    unsigned int timeout_ms, void *data)
+{
+	return ilitek_write_and_read(NULL, 0, 0, rbuf, rlen);
+}
+
+static void _ilitek_init_ack(unsigned int tout_ms, void *data)
+{
+	UNUSED(tout_ms);
+
+	ilitek_irq_enable();
+	ts->unhandle_irq = true;
+	atomic_set(&ts->get_INT, 0);
+}
+
+static int _ilitek_wait_ack(u8 cmd, unsigned int tout_ms, void *data)
+{
+	unsigned int t_ms = 0;
+	int tmp, error = -ETIME;
+
+	UNUSED(cmd);
+
+	do {
+		tmp = atomic_read(&ts->get_INT);
+		if (tmp) {
+			error = 0;
+			break;
+		}
+
+		udelay(1000);
+		t_ms++;
+	} while (t_ms < tout_ms);
+
+	ts->unhandle_irq = false;
+	ilitek_irq_disable();
+
+	return error;
+}
+
+static void _ilitek_delay(unsigned int delay_ms)
+{
+	mdelay(delay_ms);
+}
+
+static int _ilitek_reset(unsigned int delay_ms, void *data)
+{
+	/* return error if no reset gpio found */
+	if (ts->reset_gpio < 0)
+		return -ENODEV;
+
+	ilitek_reset(delay_ms);
+	return 0;
+}
+
+struct ilitek_ts_callback dev_cb = {
+	.write_then_read = _ilitek_write_then_read,
+	.read_interrupt_in = ilitek_read_interrupt_in,
+	.init_ack = _ilitek_init_ack,
+	.wait_ack = _ilitek_wait_ack,
+	.hw_reset = _ilitek_reset,
+	.re_enum = NULL,
+	.delay_ms = _ilitek_delay,
+	.msg = NULL,
+
+	.write_then_read_direct = NULL,
+	.mode_switch_notify = NULL,
+};
+
+int ilitek_main_probe(void *client, struct device *device)
+{
+	struct ilitek_ts_settings setting;
+
+	TP_MSG(NULL, "driver version: %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu\n",
+		driver_ver[0], driver_ver[1], driver_ver[2], driver_ver[3],
+		driver_ver[4], driver_ver[5], driver_ver[6]);
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts) {
+		TP_ERR(NULL, "allocate ts failed\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	ts->device = device;
+
+	mutex_init(&ts->ilitek_mutex);
+	ts->unhandle_irq = false;
+
+	ilitek_alloc_dma();
+	ilitek_request_regulator(ts);
+	ilitek_power_on(true);
+	ilitek_request_gpio();
+
+	ilitek_reset(600);
+
+	ts->dev = ilitek_dev_init(interface_i2c, "0", false, &dev_cb, ts);
+	if (!ts->dev)
+		goto err_free_gpio;
+
+	memset(&setting, 0, sizeof(setting));
+	setting.sensor_id_mask = ILITEK_SENSOR_ID_MASK;
+	ilitek_dev_setting(ts->dev, &setting);
+
+	if (api_update_ts_info(ts->dev) < 0)
+		goto err_dev_exit;
+
+	ts->irq_trigger_type = (ts->dev->protocol.flag == PTL_V6) ?
+		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+	if (ilitek_request_irq())
+		goto err_dev_exit;
+
+#ifdef ILITEK_BOOT_UPDATE
+	ts->update_thread = kthread_run(ilitek_update_thread, NULL,
+					"ilitek_update_thread");
+	if (IS_ERR(ts->update_thread))
+		goto err_free_irq;
+#else
+	if (ilitek_request_input_dev())
+		goto err_free_irq;
+#endif
+
+	ilitek_register_resume_suspend();
+	ilitek_create_sysfsnode();
+	ilitek_create_tool_node();
+	ilitek_netlink_init(NETLINK_USERSOCK);
+
+	ts->esd_check = ILITEK_ESD_CHECK_ENABLE;
+	if (ts->esd_check)
+		ilitek_create_esd_check_workqueue();
+
+	ts->gesture_status = ILITEK_GESTURE_DEFAULT;
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, true);
+
+	ts->low_power_status = ILITEK_LOW_POWER_DEFAULT;
+
+	return 0;
+
+err_free_irq:
+	free_irq(ts->irq, ts);
+
+err_dev_exit:
+	ilitek_dev_exit(ts->dev);
+
+err_free_gpio:
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+	kfree(ts);
+
+	return -ENODEV;
+}
+
+int ilitek_main_remove(void *client)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (!ts)
+		return 0;
+
+	ilitek_netlink_exit();
+
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, false);
+
+	ilitek_remove_esd_check_workqueue();
+	ilitek_remove_tool_node();
+	ilitek_remove_sys_node();
+	ilitek_release_resume_suspend();
+
+	if (ts->pen_input_dev)
+		input_unregister_device(ts->pen_input_dev);
+
+	if (ts->input_dev)
+		input_unregister_device(ts->input_dev);
+
+#ifndef MTK_UNDTS
+	free_irq(ts->irq, ts);
+#endif
+
+	ilitek_dev_exit(ts->dev);
+
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+
+	kfree(ts);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_platform_init.c b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
new file mode 100644
index 000000000000..0b7f97ab7746
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+
+#define TPD_OK (0)
+
+#ifdef MTK_UNDTS
+static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
+static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
+struct touch_vitual_key_map_t touch_key_point_maping_array[] = { {key_1}, {key_2}, {key_3}, {key_4} };
+
+static struct i2c_board_info __initdata ilitek_i2c_tpd = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41)
+};
+#endif
+
+/* probe function is used for matching and initializing input device */
+static int /*__devinit*/ tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	TP_MSG(NULL, "TPD probe\n");
+
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -1;
+	}
+
+	ret = ilitek_main_probe(client, &client->dev);
+	if (ret == 0)	// If probe is success, then enable the below flag.
+		tpd_load_status = 1;
+
+	TP_MSG(NULL, "TPD probe done\n");
+	return TPD_OK;
+}
+
+static int tpd_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	strcpy(info->type, TPD_DEVICE);
+	return TPD_OK;
+}
+
+static int /*__devexit*/ tpd_remove(struct i2c_client *client)
+{
+	TP_MSG(NULL, "TPD removed\n");
+	return ilitek_main_remove(client);
+}
+
+/* The I2C device list is used for matching I2C device and I2C device driver. */
+static const struct i2c_device_id tpd_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+
+MODULE_DEVICE_TABLE(i2c, tpd_device_id);
+
+const struct of_device_id touch_dt_match_table[] = {
+	{ .compatible = "mediatek,cap_touch",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, touch_dt_match_table);
+
+static struct i2c_driver tpd_i2c_driver = {
+
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.of_match_table = of_match_ptr(touch_dt_match_table),
+	},
+	.probe = tpd_probe,
+	.remove = tpd_remove,
+	.id_table = tpd_device_id,
+	.detect = tpd_detect,
+};
+
+static int tpd_local_init(void)
+{
+	TP_MSG(NULL, "TPD init device driver\n");
+
+	if (i2c_add_driver(&tpd_i2c_driver) != 0) {
+		TP_ERR(NULL, "Unable to add i2c driver.\n");
+		return -1;
+	}
+	if (tpd_load_status == 0) {
+		TP_ERR(NULL, "Add error touch panel driver.\n");
+		i2c_del_driver(&tpd_i2c_driver);
+		return -1;
+	}
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button)
+		tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, tpd_dts_data.tpd_key_dim_local);
+
+#else
+	tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);	// initialize tpd button data
+#endif
+	tpd_type_cap = 1;
+	TP_MSG(NULL, "TPD init done\n");
+	return TPD_OK;
+}
+
+#ifdef MTK_UNDTS
+static void tpd_resume(struct early_suspend *h)
+{
+#else
+static void tpd_resume(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD wake up\n");
+	ilitek_resume();
+	TP_MSG(NULL, "TPD wake up done\n");
+}
+
+#ifdef MTK_UNDTS
+static void tpd_suspend(struct early_suspend *h)
+{
+#else
+static void tpd_suspend(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD enter sleep\n");
+	ilitek_suspend();
+	TP_MSG(NULL, "TPD enter sleep done\n");
+}
+
+static struct tpd_driver_t tpd_device_driver = {
+	.tpd_device_name = ILITEK_TS_NAME,
+	.tpd_local_init = tpd_local_init,
+	.suspend = tpd_suspend,
+	.resume = tpd_resume,
+	.tpd_have_button = 1,
+};
+
+static int __init ilitek_touch_driver_init(void)
+{
+	TP_MSG(NULL, "touch panel driver init\n");
+
+#ifdef MTK_UNDTS
+	i2c_register_board_info(2, &ilitek_i2c_tpd, 1);
+#else
+	tpd_get_dts_info();
+#endif
+	if (tpd_driver_add(&tpd_device_driver) < 0)
+		TP_ERR(NULL, "TPD add TP driver failed\n");
+
+	return 0;
+
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "touch panel driver exit\n");
+	tpd_driver_remove(&tpd_device_driver);
+}
+
+#else
+
+#ifdef ILITEK_SPI_INTERFACE
+static int ilitek_touch_driver_probe(struct spi_device *spi)
+{
+	int error;
+
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		TP_ERR(NULL, "SPI setup failed, err: %d\n", error);
+		return error;
+	}
+
+	TP_MSG(NULL, "SPI start probe, max_speed_hz: %d\n", spi->max_speed_hz);
+
+	return ilitek_main_probe(spi, &spi->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct spi_device *spi)
+{
+	REMOVE_RETURN(ilitek_main_remove(spi));
+}
+#else
+static I2C_PROBE_FUNC(ilitek_touch_driver_probe, struct i2c_client *client)
+{
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -ENODEV;
+	}
+
+	TP_MSG(NULL, "ILITEK client->addr: 0x%x, client->irq: %d\n",
+		client->addr, client->irq);
+
+	return ilitek_main_probe(client, &client->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct i2c_client *client)
+{
+	REMOVE_RETURN(ilitek_main_remove(client));
+}
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id ilitek_touch_match_table[] = {
+	{.compatible = "tchip,ilitek",},
+	{},
+};
+#endif
+
+static const struct i2c_device_id ilitek_touch_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+MODULE_DEVICE_TABLE(i2c, ilitek_touch_device_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ilitekts_acpi_id[] = {
+	{"ILIT2901", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
+#endif
+
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+static int __maybe_unused ilitek_ts_suspend(struct device *dev)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+static int __maybe_unused ilitek_ts_resume(struct device *dev)
+{
+	ilitek_resume();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_ts_suspend, ilitek_ts_resume);
+#endif
+
+#ifdef ILITEK_SPI_INTERFACE
+static struct spi_driver ilitek_touch_device_driver = {
+#else
+static struct i2c_driver ilitek_touch_device_driver = {
+#endif
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = ilitek_touch_match_table,
+#endif
+#ifdef CONFIG_ACPI
+		.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
+#endif
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+		.pm = &ilitek_pm_ops,
+#endif
+	},
+
+	.probe = ilitek_touch_driver_probe,
+	.remove = ilitek_touch_driver_remove,
+
+#if !defined(ILITEK_SPI_INTERFACE)
+	.id_table = ilitek_touch_device_id,
+#endif
+};
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+static const unsigned short normal_i2c[2] = { 0x41, I2C_CLIENT_END };
+struct ctp_config_info config_info = {
+	.input_type = CTP_TYPE,
+	.name = NULL,
+	.int_number = 0,
+};
+
+static int twi_id;
+static int screen_max_x;
+static int screen_max_y;
+static int revert_x_flag;
+static int revert_y_flag;
+static int exchange_x_y_flag;
+static int ctp_get_system_config(void)
+{
+	twi_id = config_info.twi_id;
+	screen_max_x = config_info.screen_max_x;
+	screen_max_y = config_info.screen_max_y;
+	TP_MSG(NULL, "Ilitek: screen_max_x = %d\n", screen_max_x);
+	revert_x_flag = config_info.revert_x_flag;
+	revert_y_flag = config_info.revert_y_flag;
+	exchange_x_y_flag = config_info.exchange_x_y_flag;
+	if ((screen_max_x == 0) || (screen_max_y == 0)) {
+		TP_ERR(NULL, "%s:read config error!\n", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+int ctp_ts_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+
+	if (twi_id == adapter->nr) {
+		strlcpy(info->type, ILITEK_TS_NAME, I2C_NAME_SIZE);
+		return 0;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static struct i2c_board_info i2c_info_dev = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41),
+	.platform_data = NULL,
+};
+
+static int add_ctp_device(void)
+{
+	struct i2c_adapter *adap;
+	//script_parser_fetch("ctp_para", "ctp_twi_id", &twi_id, 1);
+	adap = i2c_get_adapter(twi_id);
+	i2c_new_device(adap, &i2c_info_dev);
+	return 0;
+}
+
+static int ilitek_init_allwin(void)
+{
+
+	int ret = 0;
+
+	if (input_fetch_sysconfig_para(&(config_info.input_type))) {
+		TP_ERR(NULL, "Ilitek:  ctp_fetch_sysconfig_para err.\n");
+		return -1;
+	} else {
+		ret = input_init_platform_resource(&(config_info.input_type));
+		if (0 != ret)
+			TP_ERR(NULL, "Ilitek: ctp_ops.init_platform_resource err.\n");
+	}
+
+	if (config_info.ctp_used == 0) {
+		TP_ERR(NULL, "Ilitek: *** if use ctp,please put the sys_config.fex ctp_used set to 1.\n");
+		return -1;
+	}
+
+	if (ctp_get_system_config() < 0)
+		TP_ERR(NULL, "Ilitek: %s:read config fail!\n", __func__);
+
+	add_ctp_device();
+	ilitek_touch_device_driver.address_list = normal_i2c;
+	ilitek_touch_device_driver.detect = ctp_ts_detect;
+	return 0;
+
+}
+
+#endif
+static int __init ilitek_touch_driver_init(void)
+{
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	if (ilitek_init_allwin() < 0) {
+		TP_ERR(NULL, "ilitek_init_allwin failed.\n");
+		return -ENODEV;
+	}
+#endif
+
+	TP_MSG(NULL, "add ILITEK touch device driver\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	return spi_register_driver(&ilitek_touch_device_driver);
+#else
+	return i2c_add_driver(&ilitek_touch_device_driver);
+#endif
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "remove touch device driver i2c driver.\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	spi_unregister_driver(&ilitek_touch_device_driver);
+#else
+	i2c_del_driver(&ilitek_touch_device_driver);
+#endif
+}
+#endif
+
+module_init(ilitek_touch_driver_init);
+module_exit(ilitek_touch_driver_exit);
+MODULE_AUTHOR("ILITEK");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.c b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
new file mode 100644
index 000000000000..945bab688405
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
@@ -0,0 +1,3644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_protocol.h"
+
+typedef int (*protocol_func_t)(struct ilitek_ts_device *, void *);
+
+struct protocol_map {
+	u8 cmd;
+	u8 flag;
+	protocol_func_t func;
+	const char *desc;
+};
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} touch_fmts[touch_fmt_max];
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} pen_fmts[pen_fmt_max];
+
+#define X(_cmd, _protocol, _cmd_id, _api) \
+	static int _api(struct ilitek_ts_device *, void *);
+ILITEK_CMD_MAP
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api) {_cmd, _protocol, _api, #_cmd_id},
+struct protocol_map protocol_maps[] = { ILITEK_CMD_MAP };
+#undef X
+
+u16 le16(const u8 *p)
+{
+	return p[0] | p[1] << 8;
+}
+
+u16 be16(const u8 *p)
+{
+	return p[1] | p[0] << 8;
+}
+
+u32 le32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val += (p[bytes] << (8 * bytes));
+
+	return val;
+}
+
+u32 be32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val = (val << 8) | (*p++);
+
+	return val;
+}
+
+static bool is_2501x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "25011") ||
+	    !strcmp(dev->mcu_info.ic_name, "25012"))
+		return true;
+
+	return false;
+}
+
+bool is_29xx(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "2900") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901") ||
+	    !strcmp(dev->mcu_info.ic_name, "2910") ||
+	    !strcmp(dev->mcu_info.ic_name, "2911") ||
+	    !strcmp(dev->mcu_info.ic_name, "2531") ||
+	    !strcmp(dev->mcu_info.ic_name, "2532") ||
+	    !strcmp(dev->mcu_info.ic_name, "2921") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901M") ||
+	    is_2501x(handle))
+		return true;
+
+	return false;
+}
+
+bool _is_231x(char *ic_name)
+{
+	if (!strcmp(ic_name, "2312") || !strcmp(ic_name, "2315"))
+		return true;
+
+	return false;
+}
+
+bool is_231x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	return _is_231x(dev->mcu_info.ic_name);
+}
+
+bool has_hw_key(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle || !dev->tp_info.key_num)
+		return false;
+
+	if (dev->key.info.mode == key_hw ||
+	    dev->key.info.mode == key_hsw)
+		return true;
+
+	return false;
+}
+
+u8 get_protocol_ver_flag(u32 ver)
+{
+	if (((ver >> 16) & 0xFF) == 0x3 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_6 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_7)
+		return PTL_V3;
+
+	if (((ver >> 16) & 0xFF) == 0x6 ||
+	    (ver & 0xFFFF00) == BL_PROTOCOL_V1_8)
+		return PTL_V6;
+
+	return PTL_ANY;
+}
+
+void grid_reset(struct grids *grid)
+{
+	grid->mc.need_update = false;
+	grid->sc_x.need_update = false;
+	grid->sc_y.need_update = false;
+	grid->pen_x.need_update = false;
+	grid->pen_y.need_update = false;
+
+	grid->key_mc.need_update = false;
+	grid->key_x.need_update = false;
+	grid->key_y.need_update = false;
+
+	grid->self.need_update = false;
+
+	if (grid->mc.data)
+		_memset(grid->mc.data, 0,
+		       grid->mc.X * grid->mc.Y * sizeof(s32));
+	if (grid->sc_x.data)
+		_memset(grid->sc_x.data, 0,
+		grid->sc_x.X * grid->sc_x.Y * sizeof(s32));
+	if (grid->sc_y.data)
+		_memset(grid->sc_y.data, 0,
+		grid->sc_y.X * grid->sc_y.Y * sizeof(s32));
+	if (grid->pen_x.data)
+		_memset(grid->pen_x.data, 0,
+		grid->pen_x.X * grid->pen_x.Y * sizeof(s32));
+	if (grid->pen_y.data)
+		_memset(grid->pen_y.data, 0,
+		grid->pen_y.X * grid->pen_y.Y * sizeof(s32));
+
+	if (grid->key_mc.data)
+		_memset(grid->key_mc.data, 0,
+		grid->key_mc.X * grid->key_mc.Y * sizeof(s32));
+	if (grid->key_x.data)
+		_memset(grid->key_x.data, 0,
+		grid->key_x.X * grid->key_x.Y * sizeof(s32));
+	if (grid->key_y.data)
+		_memset(grid->key_y.data, 0,
+		grid->key_y.X * grid->key_y.Y * sizeof(s32));
+
+	if (grid->self.data)
+		_memset(grid->self.data, 0,
+		grid->self.X * grid->self.Y * sizeof(s32));
+
+	grid->dmsg.pen_need_update = false;
+	grid->dmsg.touch_need_update = false;
+	_memset(grid->dmsg.touch, 0, sizeof(grid->dmsg.touch));
+	_memset(grid->dmsg.pen, 0, sizeof(grid->dmsg.pen));
+}
+
+void grid_free(struct grids *grid)
+{
+	if (grid->mc.data)
+		CFREE(grid->mc.data);
+	if (grid->sc_x.data)
+		CFREE(grid->sc_x.data);
+	if (grid->sc_y.data)
+		CFREE(grid->sc_y.data);
+	if (grid->pen_x.data)
+		CFREE(grid->pen_x.data);
+	if (grid->pen_y.data)
+		CFREE(grid->pen_y.data);
+
+	if (grid->key_mc.data)
+		CFREE(grid->key_mc.data);
+	if (grid->key_x.data)
+		CFREE(grid->key_x.data);
+	if (grid->key_y.data)
+		CFREE(grid->key_y.data);
+
+	if (grid->self.data)
+		CFREE(grid->self.data);
+}
+
+int grid_alloc(void *handle, struct grids *grid)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int X, Y, key;
+
+	_memset(grid, 0, sizeof(*grid));
+
+	if (!dev)
+		return -EINVAL;
+
+	X = dev->tp_info.x_ch;
+	Y = dev->tp_info.y_ch;
+	key = dev->tp_info.key_num;
+
+	grid->mc.data = (s32 *)CALLOC(X * Y, sizeof(s32));
+	grid->sc_x.data = (s32 *)CALLOC(X, sizeof(s32));
+	grid->sc_y.data = (s32 *)CALLOC(Y, sizeof(s32));
+	grid->pen_x.data = (s32 *)CALLOC(X * 8, sizeof(s32));
+	grid->pen_y.data = (s32 *)CALLOC(Y * 8, sizeof(s32));
+	grid->key_mc.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_x.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_y.data = (s32 *)CALLOC(1, sizeof(s32));
+	grid->self.data = (s32 *)CALLOC(4, sizeof(s32));
+	if (!grid->mc.data || !grid->sc_x.data || !grid->sc_y.data ||
+	    !grid->pen_x.data || !grid->pen_y.data || !grid->key_mc.data ||
+	    !grid->key_x.data || !grid->key_y.data || !grid->self.data)
+		goto err_free;
+
+	grid->mc.X = X; grid->mc.Y = Y;
+	grid->sc_x.X = X; grid->sc_x.Y = 1;
+	grid->sc_y.X = 1; grid->sc_y.Y = Y;
+	grid->pen_x.X = X; grid->pen_x.Y = 8;
+	grid->pen_y.X = 8; grid->pen_y.Y = Y;
+
+	grid->key_mc.X = key; grid->key_mc.Y = 1;
+	grid->key_x.X = key; grid->key_x.Y = 1;
+	grid->key_y.X = 1; grid->key_y.Y = 1;
+
+	grid->self.X = 4, grid->self.Y = 1;
+
+	grid_reset(grid);
+
+	return 0;
+
+err_free:
+	grid_free(grid);
+
+	return -ENOMEM;
+}
+
+static u16 update_crc(u16 crc, u8 newbyte)
+{
+	char i;
+		const u16 crc_poly = 0x8408;
+
+	crc ^= newbyte;
+
+	for (i = 0; i < 8; i++) {
+		if (crc & 0x01)
+			crc = (crc >> 1) ^ crc_poly;
+		else
+			crc = crc >> 1;
+	}
+
+	return crc;
+}
+
+u16 get_crc(u32 start, u32 end,
+	    u8 *buf, u32 buf_size)
+{
+	u16 crc = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		crc = update_crc(crc, buf[i]);
+
+	return crc;
+}
+
+u32 get_checksum(u32 start, u32 end,
+		 u8 *buf, u32 buf_size)
+{
+	u32 sum = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		sum += buf[i];
+
+	return sum;
+}
+
+bool is_checksum_matched(u8 checksum, int start, int end,
+			 u8 *buf, int buf_size)
+{
+	u8 check;
+
+	check = ~(get_checksum(start, end, buf, buf_size)) + 1;
+	if (check != checksum) {
+		TP_ERR_ARR(NULL, "[data]", TYPE_U8, end - start, buf + start);
+		TP_ERR(NULL, "checksum : 0x%02x/0x%02x not matched\n",
+			check, checksum);
+		return false;
+	}
+
+	return true;
+}
+
+bool support_mcu_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060009))
+		return false;
+
+	return true;
+}
+
+bool support_sensor_id(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060004))
+		return false;
+
+	return true;
+}
+
+bool support_production_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_fwid(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010802) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_power_status(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x06000a))
+		return false;
+
+	return true;
+}
+
+int bridge_set_int_monitor(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf3;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int bridge_set_test_mode(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf2;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int reset_helper(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool need_re_enum = true;
+
+	if (dev->_interface == interface_i2c) {
+		/* sw reset if no reset-gpio found */
+		if (!dev->cb.hw_reset ||
+		    dev->cb.hw_reset(dev->reset_time, dev->_private) < 0)
+			return api_protocol_set_cmd(dev, SET_SW_RST,
+						    &need_re_enum);
+
+		return 0;
+	}
+
+	return api_protocol_set_cmd(dev, SET_SW_RST, &need_re_enum);
+}
+
+static int re_enum_helper(struct ilitek_ts_device *dev, u8 enum_type)
+{
+	int error;
+	int retry = 5;
+
+	if (!dev->cb.re_enum)
+		return -EINVAL;
+
+	do {
+		error = dev->cb.re_enum(enum_type, dev->_private);
+		if (!error)
+			return 0;
+
+		TP_WARN(dev->id, "re-enum failed, error: %d, retry: %d\n", error, retry);
+		dev->cb.delay_ms(500);
+	} while (!dev->setting.no_retry && retry--);
+
+	TP_ERR(dev->id, "re-enum retry failed\n");
+
+	return -ENODEV;
+}
+
+int read_interrupt_in(void *handle, u8 *buf, int rlen,
+		      unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_interrupt_in)
+		return -EINVAL;
+
+	error = dev->cb.read_interrupt_in(buf, rlen, timeout_ms,
+					   dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[int-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		 unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_ctrl_in)
+		return -EINVAL;
+
+	if (dev->quirks & QUIRK_BRIDGE) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA4;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = 0;
+		dev->wbuf[4] = (rlen + 6) & 0xFF;
+		dev->wbuf[5] = ((rlen + 6) >> 8) & 0xFF;
+		dev->wbuf[6] = cmd;
+
+		error = dev->cb.write_then_read_direct(dev->wbuf, 64,
+							dev->rbuf, 64, dev->_private);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.read_ctrl_in(buf, rlen, timeout_ms, dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[ctrl-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int write_then_read(void *handle, u8 *cmd, int wlen,
+		    u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[wbuf]:", TYPE_U8, wlen, cmd);
+
+	if (!wlen && (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+	    dev->quirks & QUIRK_BRIDGE)) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA3;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = rlen;
+
+		error = write_then_read_direct(dev, dev->wbuf, 64, NULL, 0);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.write_then_read(cmd, wlen, buf, rlen, dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[rbuf]:", TYPE_U8, rlen, buf);
+
+	return (error < 0) ? error : 0;
+}
+
+int write_then_read_direct(void *handle, u8 *cmd, int wlen,
+			   u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read_direct)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-wbuf]:", TYPE_U8, wlen, cmd);
+
+	error = dev->cb.write_then_read_direct(cmd, wlen, buf, rlen,
+					       dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-rbuf]:", TYPE_U8, rlen, buf);
+
+	return error;
+}
+
+int write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct ilitek_ts_callback *cb = &dev->cb;
+
+	u8 ack_cmd;
+
+	TP_DBG(dev->id, "cmd: 0x" PFMT_X8 ", tout_ms: %d\n",
+		cmd[0], timeout_ms);
+
+	if (dev->quirks & QUIRK_WAIT_ACK_DELAY) {
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		cb->delay_ms(dev->setting.wait_ack_delay);
+		return 0;
+	}
+
+	if (dev->setting.no_INT_ack) {
+		/* prevent bridge int handling flow affecting the following read */
+		if (dev->quirks & QUIRK_BRIDGE)
+			bridge_set_int_monitor(dev, false);
+
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		/*
+		* for no-INT-ack flow, add delay to prevent
+		* interrupting FW flow too soon, while FW should
+		* be handling previous write command. ex. 0xcd/ 0xc3
+		*/
+		cb->delay_ms(5);
+
+		goto check_busy;
+	}
+
+	if (!cb->init_ack || !cb->wait_ack)
+		return -EINVAL;
+
+	cb->init_ack(timeout_ms, dev->_private);
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, true);
+
+	error = write_then_read(dev, cmd, wlen, NULL, 0);
+	if (error < 0)
+		return error;
+
+	ack_cmd = (cmd[0] == CMD_ACCESS_SLAVE) ? cmd[2] : cmd[0];
+	error = cb->wait_ack(ack_cmd, timeout_ms, dev->_private);
+
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, false);
+
+	/* cmd[0] should be ILITEK cmd code */
+	if (error < 0) {
+		TP_WARN(dev->id, "wait 0x" PFMT_X8 " ack %d ms timeout, err: %d\n",
+			cmd[0], timeout_ms, error);
+
+		if (dev->setting.no_retry)
+			return -EILITIME;
+
+		goto check_busy;
+	}
+
+	return 0;
+
+check_busy:
+	return api_check_busy(dev, timeout_ms, 10);
+}
+
+/* Common APIs */
+static int api_protocol_get_scrn_res(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_screen_info *screen_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 28);
+	if (error < 0)
+		return error;
+
+	screen_info = (struct ilitek_screen_info *)dev->rbuf;
+
+	dev->screen_info.x_min = screen_info->x_min;
+	dev->screen_info.y_min = screen_info->y_min;
+	dev->screen_info.x_max = screen_info->x_max;
+	dev->screen_info.y_max = screen_info->y_max;
+
+	TP_DBG(dev->id, "screen x: " PFMT_U16 "~" PFMT_U16 ", screen y: " PFMT_U16 "~" PFMT_U16 "\n",
+		dev->screen_info.x_min, dev->screen_info.x_max,
+		dev->screen_info.y_min, dev->screen_info.y_max);
+
+	dev->screen_info.pressure_min = 0;
+	dev->screen_info.pressure_max = 0;
+	dev->screen_info.x_tilt_min = 0;
+	dev->screen_info.x_tilt_max = 0;
+	dev->screen_info.y_tilt_min = 0;
+	dev->screen_info.y_tilt_max = 0;
+	if (dev->protocol.ver > 0x60006) {
+		dev->screen_info.pressure_min = screen_info->pressure_min;
+		dev->screen_info.pressure_max = screen_info->pressure_max;
+		dev->screen_info.x_tilt_min = screen_info->x_tilt_min;
+		dev->screen_info.x_tilt_max = screen_info->x_tilt_max;
+		dev->screen_info.y_tilt_min = screen_info->y_tilt_min;
+		dev->screen_info.y_tilt_max = screen_info->y_tilt_max;
+
+		dev->screen_info.pen_x_min = screen_info->pen_x_min;
+		dev->screen_info.pen_y_min = screen_info->pen_y_min;
+		dev->screen_info.pen_x_max = screen_info->pen_x_max;
+		dev->screen_info.pen_y_max = screen_info->pen_y_max;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v3 *tp_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 15);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v3 *)dev->rbuf;
+
+	dev->tp_info.block_num = 2;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+
+	dev->tp_info.support_modes = tp_info->support_modes;
+	if (dev->tp_info.support_modes > 3 || !dev->tp_info.support_modes)
+		dev->tp_info.support_modes = 1;
+
+	TP_DBG(dev->id, "touch ch.(start/end) x: " PFMT_U8 "/" PFMT_U8 ", y: " PFMT_U8 "/" PFMT_U8 "\n",
+		tp_info->touch_start_y, tp_info->touch_end_y,
+		tp_info->touch_start_x, tp_info->touch_end_x);
+
+	if (dev->tp_info.key_num) {
+		/* check v3 key is virtual or hw keys */
+		dev->key.info.mode =
+			(tp_info->touch_start_y == 0xff &&
+			 tp_info->touch_end_y == 0xff &&
+			 tp_info->touch_start_x == 0xff &&
+			 tp_info->touch_end_x == 0xff) ?
+			key_hw : key_vitual;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v6 *tp_info;
+	u8 i;
+
+#define X(_enum, _code, _name) {_code, _name},
+	const struct {
+		const int code;
+		const char *str;
+	} pen_modes[] = { STYLUS_MODES };
+#undef X
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 21);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v6 *)dev->rbuf;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+	dev->tp_info.ic_num = tp_info->ic_num;
+	dev->tp_info.format = tp_info->format;
+	dev->tp_info.support_modes = tp_info->support_modes;
+	dev->tp_info.die_num = tp_info->die_num;
+
+	if (dev->tp_info.format == 5)
+		api_protocol_set_cmd(dev, GET_CRYPTO_INFO, NULL);
+
+	if (dev->tp_info.ic_num > ARRAY_SIZE(dev->ic)) {
+		TP_ERR(dev->id, "invalid ic_num: " PFMT_U8 "\n", dev->tp_info.ic_num);
+		return -EINVAL;
+	}
+	TP_MSG(dev->id, "[Panel Information] Chip count: %u\n",
+		dev->tp_info.ic_num);
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max tp: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	if (dev->protocol.ver < 0x60003)
+		return 0;
+
+	dev->tp_info.block_num = tp_info->block_num;
+	TP_MSG(dev->id, "[Panel Information] Block Number: " PFMT_U8 "\n",
+		dev->tp_info.block_num);
+
+	if (dev->protocol.ver < 0x60007)
+		return 0;
+
+	dev->tp_info.pen_modes = tp_info->pen_modes;
+
+	_memset(dev->pen_mode, 0, sizeof(dev->pen_mode));
+	if (!dev->tp_info.pen_modes)
+		_strcpy(dev->pen_mode, "Disable",
+			sizeof(dev->pen_mode));
+	for (i = 0; i < ARRAY_SIZE(pen_modes); i++) {
+		if (!(tp_info->pen_modes & pen_modes[i].code))
+			continue;
+
+		if (_strlen(dev->pen_mode))
+			_strcat(dev->pen_mode, ",", sizeof(dev->pen_mode));
+
+		_strcat(dev->pen_mode, pen_modes[i].str,
+			sizeof(dev->pen_mode));
+	}
+
+	TP_DBG(dev->id, "pen_modes: " PFMT_U8 "\n", dev->tp_info.pen_modes);
+	TP_MSG(dev->id, "[Panel Information] Pen Mode: " PFMT_C8 "\n",
+		dev->pen_mode);
+
+	dev->tp_info.pen_format = tp_info->pen_format;
+	dev->tp_info.pen_x_resolution = tp_info->pen_x_resolution;
+	dev->tp_info.pen_y_resolution = tp_info->pen_y_resolution;
+	TP_MSG(dev->id, "[Panel Information] Pen Format: 0x" PFMT_X8 "\n",
+		dev->tp_info.pen_format);
+	TP_MSG(dev->id, "[Panel Information] Pen X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.pen_x_resolution,
+		dev->tp_info.pen_y_resolution);
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+#define X(_enum, _id, _size, _cnt)	\
+	pen_fmts[_id].size = _size;	\
+	pen_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_PEN_REPORT_FORMAT;
+#undef X
+
+	if (dev->protocol.flag == PTL_V3)
+		error = api_protocol_get_tp_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		error = api_protocol_get_tp_info_v6(dev, data);
+	else
+		return -EINVAL;
+
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max fingers: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	switch (dev->tp_info.format) {
+	case touch_fmt_0x1:
+	case touch_fmt_0x2:
+	case touch_fmt_0x3:
+	case touch_fmt_0x4:
+	case touch_fmt_0x10:
+		if (dev->setting.default_format_enabled)
+			goto default_fmt_enabled;
+
+		dev->fmt.touch_size = touch_fmts[dev->tp_info.format].size;
+		dev->fmt.touch_max_cnt = touch_fmts[dev->tp_info.format].max_cnt;
+		break;
+
+default_fmt_enabled:
+	default:
+	case touch_fmt_0x11:
+	case touch_fmt_0x0:
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+		break;
+	}
+
+	switch (dev->tp_info.pen_format) {
+	case pen_fmt_0x1:
+	case pen_fmt_0x2:
+		dev->fmt.pen_size = pen_fmts[dev->tp_info.pen_format].size;
+		dev->fmt.pen_max_cnt = pen_fmts[dev->tp_info.pen_format].max_cnt;
+		break;
+	default:
+	case pen_fmt_0x0:
+		dev->fmt.pen_size = pen_fmts[pen_fmt_0x0].size;
+		dev->fmt.pen_max_cnt = pen_fmts[pen_fmt_0x0].max_cnt;
+		break;
+	}
+
+	TP_MSG(dev->id, "[Panel Information] X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_resolution, dev->tp_info.y_resolution);
+	TP_MSG(dev->id, "[Panel Information] X/Y channel: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_ch, dev->tp_info.y_ch);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Fingers\n",
+		dev->tp_info.max_fingers);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Keys\n",
+		dev->tp_info.key_num);
+
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " modes\n",
+		dev->tp_info.support_modes);
+
+	TP_DBG(dev->id, "touch format: 0x" PFMT_X8 ", size: %u bytes, max cnt: %u per packet\n",
+		dev->tp_info.format, dev->fmt.touch_size,
+		dev->fmt.touch_max_cnt);
+
+	if (dev->tp_info.key_num > 0) {
+		error = api_protocol_set_cmd(dev, GET_KEY_INFO, NULL);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v3 *key_info;
+	unsigned int i;
+
+	UNUSED(data);
+
+	/* Only i2c interface has key for V3 */
+	if (dev->_interface != interface_i2c)
+		return 0;
+
+	if (dev->tp_info.key_num > 20) {
+		TP_ERR(dev->id, "key count: " PFMT_U8 " invalid\n", dev->tp_info.key_num);
+		return -EINVAL;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 29);
+	if (error < 0)
+		return error;
+
+	for (i = 0; dev->tp_info.key_num > 5U &&
+	     i < DIV_ROUND_UP(dev->tp_info.key_num, 5U) - 1U; i++) {
+		TP_MSG(dev->id, "read keyinfo again, i: %u\n", i);
+		error = write_then_read(dev, NULL, 0,
+					dev->rbuf + 29 + 5 * i, 25);
+		if (error < 0)
+			return error;
+	}
+
+	key_info = (struct ilitek_key_info_v3 *)dev->rbuf;
+	dev->key.info.x_len = be16(key_info->x_len);
+	dev->key.info.y_len = be16(key_info->y_len);
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = be16(key_info->keys[i].x);
+		dev->key.info.keys[i].y = be16(key_info->keys[i].y);
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v6 *key_info;
+	unsigned int i, offset;
+
+	UNUSED(data);
+
+	if (dev->tp_info.key_num > ARRAY_SIZE(dev->key.info.keys)) {
+		TP_ERR(dev->id, "exception keycount " PFMT_U8 " > %d\n", dev->tp_info.key_num,
+			(int)ARRAY_SIZE(dev->key.info.keys));
+		return -EINVAL;
+	}
+
+	switch (dev->_interface) {
+	case interface_i2c:
+		if (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+		    dev->quirks & QUIRK_BRIDGE) {
+			error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+			if (error < 0)
+				return error;
+			error = read_ctrl_in(dev, CMD_GET_KEY_INFO,
+					     dev->rbuf,
+					     5 + dev->tp_info.key_num * 5, 2000);
+			if (error < 0)
+				return error;
+			offset = (dev->quirks & QUIRK_BRIDGE) ? 1 : 0;
+		} else {
+			error = write_then_read(dev, dev->wbuf, 1,
+						dev->rbuf, 5 + dev->tp_info.key_num * 5);
+			if (error < 0)
+				return error;
+			offset = 0;
+		}
+		break;
+
+	case interface_usb:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 6;
+		break;
+	case interface_hid_over_i2c:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 4;
+		break;
+	default: return -EINVAL;
+	};
+
+	key_info = (struct ilitek_key_info_v6 *)(dev->rbuf + offset);
+	dev->key.info.mode = key_info->mode;
+	TP_MSG(dev->id, "[Panel Information] key mode: " PFMT_U8 "\n", dev->key.info.mode);
+
+	dev->key.info.x_len = key_info->x_len;
+	dev->key.info.y_len = key_info->y_len;
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = key_info->keys[i].x;
+		dev->key.info.keys[i].y = key_info->keys[i].y;
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_key_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_key_info_v6(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_get_ptl_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->protocol.flag = PTL_V6;
+	dev->reset_time = 1000;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->protocol.ver = (dev->rbuf[0] << 16) + (dev->rbuf[1] << 8) +
+			     dev->rbuf[2];
+	TP_MSG(dev->id, "[Protocol Version]: %x.%x.%x\n",
+		(dev->protocol.ver >> 16) & 0xFF,
+		(dev->protocol.ver >> 8) & 0xFF,
+		dev->protocol.ver & 0xFF);
+
+	dev->protocol.flag = get_protocol_ver_flag(dev->protocol.ver);
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->reset_time = 200;
+		break;
+	case PTL_V6:
+		dev->reset_time = 600;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol ver.: 0x%x\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_fw_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->fw_ver, dev->rbuf, 8);
+
+	if (dev->ic[0].mode == BL_MODE) {
+		TP_MSG_ARR(dev->id, "[BL Firmware Version]", TYPE_U8,
+			   8, dev->fw_ver);
+	} else {
+		TP_MSG_ARR(dev->id, "[FW Version]", TYPE_U8, 4, dev->fw_ver);
+		TP_MSG_ARR(dev->id, "[Customer Version]", TYPE_U8,
+			   4, dev->fw_ver + 4);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < ic_num; i++) {
+		dev->ic[i].mode = dev->rbuf[i * 2];
+
+		if (dev->ic[i].mode == AP_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "AP");
+		else if (dev->ic[i].mode == BL_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "BL");
+		else
+			_sprintf(dev->ic[i].mode_str, 0, "UNKNOWN");
+	}
+
+	TP_MSG(dev->id, "[Current Mode] Master: 0x" PFMT_X8 " " PFMT_C8 "\n",
+		dev->ic[0].mode, dev->ic[0].mode_str);
+	for (i = 1; i < ic_num; i++)
+		TP_MSG(dev->id, "[Current Mode] Slave[" PFMT_U8 "]: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, dev->ic[i].mode, dev->ic[i].mode_str);
+
+	return 0;
+}
+
+static int api_protocol_power_status(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 func, lvd_level_sel;
+
+	if (!data)
+		return -EFAULT;
+
+	if (!support_power_status(dev)) {
+		_memset(&dev->pwr, 0, sizeof(dev->pwr));
+		return 0;
+	}
+
+	func = ((*(u16 *)data) >> 8) & 0xff;
+	lvd_level_sel = (*(u16 *)data) & 0xff;
+
+	dev->wbuf[1] = func;
+	switch (func) {
+	/* get level select */
+	case 0x02:
+		dev->wbuf[2] = lvd_level_sel;
+		return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+
+	/* clear flag */
+	case 0x00:
+		return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+
+	/* get flag */
+	case 0x01:
+		error = write_then_read(dev, dev->wbuf, 2,
+					dev->rbuf, 4);
+		if (error < 0)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev->pwr.header = be16(dev->rbuf);
+	dev->pwr.vdd33_lvd_flag = dev->rbuf[2];
+	dev->pwr.vdd33_lvd_level_sel = dev->rbuf[3];
+
+	TP_DBG(dev->id, "[Power-Status] header: 0x" PFMT_X16 ", flag: 0x" PFMT_X8 ", level_sel: 0x" PFMT_X8 "\n",
+		dev->pwr.header, dev->pwr.vdd33_lvd_flag,
+		dev->pwr.vdd33_lvd_level_sel);
+
+	return 0;
+}
+
+static int api_protocol_get_sensor_id(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_sensor_id(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->sensor.header = be16(dev->rbuf);
+	dev->sensor.id = dev->rbuf[2];
+
+	TP_MSG(dev->id, "[Sensor ID] header: 0x" PFMT_X16 ", id: 0x" PFMT_X8 "\n",
+		dev->sensor.header,
+		(u8)(dev->sensor.id & dev->setting.sensor_id_mask));
+
+	return 0;
+}
+
+static int api_protocol_get_product_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_production_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->product_info, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[Production Info]", TYPE_U8, 8, dev->product_info);
+
+	return 0;
+}
+
+static int api_protocol_get_fwid(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_fwid(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->customer_id = le16(dev->rbuf);
+	dev->fwid = le16(dev->rbuf + 2);
+
+	TP_MSG(dev->id, "[Customer ID] 0x%04x\n", dev->customer_id);
+	TP_MSG(dev->id, "[FWID] 0x%04x\n", dev->fwid);
+
+	return 0;
+}
+
+static int api_protocol_get_crypto_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	u16 __MAYBE_UNUSED crypto_ver;
+	u32 crypto_opt;
+
+	UNUSED(data);
+
+	/*
+	 * encrypted report format should be supported after AP v6.0.8
+	 * set report format to 0 if protocol version not matched or
+	 * crypto info say it's not supported.
+	 */
+	if (dev->protocol.ver < 0x060008 ||
+	    write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6) < 0) {
+		dev->tp_info.format = 0;
+		return 0;
+	}
+
+	crypto_ver = le16(dev->rbuf);
+	crypto_opt = le32(dev->rbuf + 2, 4);
+
+	TP_MSG(dev->id, "[Encrypt Ver.] 0x%x\n", crypto_ver);
+	TP_MSG(dev->id, "[Encrypt Options] 0x%x\n", crypto_opt);
+
+	if (!(crypto_opt & 1))
+		dev->tp_info.format = 0;
+
+	return 0;
+}
+
+static int api_protocol_get_hid_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060009)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6);
+	if (error < 0)
+		return error;
+
+	_memcpy(&dev->hid_info, dev->rbuf, sizeof(dev->hid_info));
+
+	TP_MSG(dev->id, "vid/pid/rev: 0x%x/0x%x/0x%x\n",
+		dev->hid_info.vid, dev->hid_info.pid, dev->hid_info.rev);
+
+	return 0;
+}
+
+static bool is_special_char(char c)
+{
+	return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+		(c >= '0' && c <= '9')) ? false : true;
+}
+
+static int api_protocol_get_mcu_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_ver {
+		u16 ic_name;
+		u8 df_start_addr[3];
+		u8 df_size;
+
+		char module_name[26];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, use GET_MCU_VER (0x61) cmd
+	 */
+	if (dev->protocol.flag == PTL_V6 && support_mcu_info(dev)) {
+		error = api_protocol_set_cmd(dev, GET_MCU_INFO, NULL);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1,
+					dev->rbuf, 32);
+		if (error < 0)
+			return error;
+
+		parser = (struct mcu_ver *)dev->rbuf;
+
+		_memset(dev->mcu_info.ic_name, 0,
+			sizeof(dev->mcu_info.ic_name));
+		_sprintf(dev->mcu_info.ic_name, 0, "%04x", parser->ic_name);
+
+		_memset(dev->mcu_info.module_name, 0,
+			sizeof(dev->mcu_info.module_name));
+		_memcpy(dev->mcu_info.module_name, parser->module_name,
+			sizeof(parser->module_name));
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		if (is_29xx(dev)) {
+			/* modify reset time to 100ms for 29xx ICs */
+			dev->reset_time = 100;
+
+			/* set mm_addr for bin file update */
+			dev->mcu_info.mm_addr =
+				is_2501x(dev) ? MM_ADDR_2501X : MM_ADDR_29XX;
+			dev->mcu_info.min_addr = START_ADDR_29XX;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		} else {
+			dev->mcu_info.mm_addr = MM_ADDR_LEGO;
+			dev->mcu_info.min_addr = START_ADDR_LEGO;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		}
+	}
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+	if (!strcmp(dev->mcu_info.ic_name, "2133"))
+		_sprintf(dev->mcu_info.ic_name, 0, "2132S");
+
+	_memset(dev->mcu_info.ic_full_name, 0,
+		sizeof(dev->mcu_info.ic_full_name));
+	_sprintf(dev->mcu_info.ic_full_name, 0,
+		"ILI" PFMT_C8, dev->mcu_info.ic_name);
+
+	TP_MSG(dev->id, "[MCU Kernel Version] " PFMT_C8 "\n",
+		dev->mcu_info.ic_full_name);
+	TP_MSG(dev->id, "[Module Name]: [" PFMT_C8 "]\n",
+		dev->mcu_info.module_name);
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_info {
+		char ic_name[5];
+		char mask_ver[2];
+		u8 mm_addr[3];
+		char module_name[18];
+		u8 reserve[4];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd only support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, return 0 to skip this command.
+	 */
+	if (dev->protocol.flag != PTL_V6 || !support_mcu_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	if (error < 0)
+		return error;
+
+	parser = (struct mcu_info *)dev->rbuf;
+
+	_memset(dev->mcu_info.ic_name, 0, sizeof(dev->mcu_info.ic_name));
+	_memcpy(dev->mcu_info.ic_name, parser->ic_name,
+		sizeof(parser->ic_name));
+
+	_memcpy(dev->mcu_info.module_name, parser->module_name,
+		sizeof(parser->module_name));
+	dev->mcu_info.mm_addr = le32(parser->mm_addr, 3);
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_fs_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->sine.start & 0xFF;
+	dev->wbuf[2] = freq->sine.start >> 8;
+	dev->wbuf[3] = freq->sine.end & 0xFF;
+	dev->wbuf[4] = freq->sine.end >> 8;
+	dev->wbuf[5] = freq->sine.step;
+	dev->wbuf[6] = freq->mc_swcap.start & 0xFF;
+	dev->wbuf[7] = freq->mc_swcap.start >> 8;
+	dev->wbuf[8] = freq->mc_swcap.end & 0xFF;
+	dev->wbuf[9] = freq->mc_swcap.end >> 8;
+	dev->wbuf[10] = freq->mc_swcap.step;
+	dev->wbuf[11] = freq->sc_swcap.start & 0xFF;
+	dev->wbuf[12] = freq->sc_swcap.start >> 8;
+	dev->wbuf[13] = freq->sc_swcap.end & 0xFF;
+	dev->wbuf[14] = freq->sc_swcap.end >> 8;
+	dev->wbuf[15] = freq->sc_swcap.step;
+	dev->wbuf[16] = freq->frame_cnt & 0xFF;
+	dev->wbuf[17] = freq->frame_cnt >> 8;
+	dev->wbuf[18] = freq->scan_type;
+
+	if (dev->protocol.ver < 0x60005)
+		return write_then_read(dev, dev->wbuf, 19, NULL, 0);
+
+	do {
+		if (dev->protocol.ver < 0x60009) {
+			error = write_then_read(dev, dev->wbuf, 19,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[16] = freq->dump1.start & 0xFF;
+		dev->wbuf[17] = freq->dump1.start >> 8;
+		dev->wbuf[18] = freq->dump1.end & 0xFF;
+		dev->wbuf[19] = freq->dump1.end >> 8;
+		dev->wbuf[20] = freq->dump1.step;
+		dev->wbuf[21] = freq->dump1_val;
+		dev->wbuf[22] = freq->dump2.start & 0xFF;
+		dev->wbuf[23] = freq->dump2.start >> 8;
+		dev->wbuf[24] = freq->dump2.end & 0xFF;
+		dev->wbuf[25] = freq->dump2.end >> 8;
+		dev->wbuf[26] = freq->dump2.step;
+		dev->wbuf[27] = freq->dump2_val;
+		dev->wbuf[28] = freq->frame_cnt & 0xFF;
+		dev->wbuf[29] = freq->frame_cnt >> 8;
+		dev->wbuf[30] = freq->scan_type;
+
+		if (dev->protocol.ver < 0x6000a) {
+			error = write_then_read(dev, dev->wbuf, 31,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[31] = freq->mc_frame_cnt & 0xFF;
+		dev->wbuf[32] = freq->mc_frame_cnt >> 8;
+		dev->wbuf[33] = freq->dump_frame_cnt & 0xFF;
+		dev->wbuf[34] = freq->dump_frame_cnt >> 8;
+
+		error = write_then_read(dev, dev->wbuf, 35, dev->rbuf, 5);
+
+	} while (0);
+
+	if (error < 0)
+		return error;
+
+	freq->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != 0x5a || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			freq->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_short_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct short_settings *_short = (struct short_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[short info] dump1: 0x" PFMT_X8 ", dump2: 0x" PFMT_X8 ", vref: 0x" PFMT_X8 ", postidle: 0x" PFMT_X16 "\n",
+		_short->dump_1, _short->dump_2,
+		_short->v_ref_L, _short->post_idle);
+
+	dev->wbuf[1] = _short->dump_1;
+	dev->wbuf[2] = _short->dump_2;
+	dev->wbuf[3] = _short->v_ref_L;
+	dev->wbuf[4] = _short->post_idle & 0xFF;
+	dev->wbuf[5] = (_short->post_idle >> 8) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 6, NULL, 0);
+}
+
+static int api_protocol_set_open_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct open_settings *open = (struct open_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id,
+		"[open info] freq.: " PFMT_U16 ", gain: 0x" PFMT_X8 ", gain_rfb: 0x" PFMT_X8
+		", afe_res_sel: 0x" PFMT_X8 ", mc_fsel: 0x" PFMT_X8 "\n",
+		open->freq, open->gain, open->gain_rfb,
+		open->afe_res_sel, open->mc_fsel);
+
+	dev->wbuf[wlen++] = open->freq & 0xFF;
+	dev->wbuf[wlen++] = (open->freq >> 8) & 0xFF;
+	dev->wbuf[wlen++] = open->gain;
+	dev->wbuf[wlen++] = open->gain_rfb;
+	dev->wbuf[wlen++] = open->afe_res_sel;
+	dev->wbuf[wlen++] = open->mc_fsel;
+
+	if (dev->protocol.ver > 0x060009) {
+		TP_DBG(dev->id, "[open info] frame: " PFMT_U16 "\n",
+			open->frame);
+
+		dev->wbuf[wlen++] = open->frame & 0xFF;
+		dev->wbuf[wlen++] = (open->frame >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_charge_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error, i;
+	struct charge_curve_settings *curve =
+		(struct charge_curve_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "charge-curve info. scan mode: 0x" PFMT_U8 "\n",
+		curve->scan_mode);
+
+	dev->wbuf[1] = curve->scan_mode;
+	dev->wbuf[2] = curve->dump.start & 0xFF;
+	dev->wbuf[3] = (curve->dump.start >> 8) & 0xFF;
+	dev->wbuf[4] = curve->dump.end & 0xFF;
+	dev->wbuf[5] = (curve->dump.end >> 8) & 0xFF;
+	dev->wbuf[6] = curve->dump.step;
+	dev->wbuf[7] = curve->dump.post_idle & 0xFF;
+	dev->wbuf[8] = (curve->dump.post_idle >> 8) & 0xFF;
+	dev->wbuf[9] = curve->dump.fix_val & 0xFF;
+	dev->wbuf[10] = (curve->dump.fix_val >> 8) & 0xFF;
+	dev->wbuf[11] = curve->charge.start & 0xFF;
+	dev->wbuf[12] = (curve->charge.start >> 8) & 0xFF;
+	dev->wbuf[13] = curve->charge.end & 0xFF;
+	dev->wbuf[14] = (curve->charge.end >> 8) & 0xFF;
+	dev->wbuf[15] = curve->charge.step;
+	dev->wbuf[16] = curve->charge.post_idle & 0xFF;
+	dev->wbuf[17] = (curve->charge.post_idle >> 8) & 0xFF;
+	dev->wbuf[18] = curve->charge.fix_val & 0xFF;
+	dev->wbuf[19] = (curve->charge.fix_val >> 8) & 0xFF;
+	dev->wbuf[20] = curve->c_sub & 0xFF;
+	dev->wbuf[21] = (curve->c_sub >> 8) & 0xFF;
+	dev->wbuf[22] = curve->frame_cnt & 0xFF;
+	dev->wbuf[23] = (curve->frame_cnt >> 8) & 0xFF;
+
+	for (i = 0; i < (int)ARRAY_SIZE(curve->pt); i++) {
+		dev->wbuf[24 + i * 4] = curve->pt[i].x & 0xFF;
+		dev->wbuf[24 + i * 4 + 1] = (curve->pt[i].x >> 8) & 0xFF;
+		dev->wbuf[24 + i * 4 + 2] = curve->pt[i].y & 0xFF;
+		dev->wbuf[24 + i * 4 + 3] = (curve->pt[i].y >> 8) & 0xFF;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 60, dev->rbuf, 5);
+	if (error < 0)
+		return error;
+
+	curve->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != AP_MODE || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			curve->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_p2p_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct p2p_settings *p2p = (struct p2p_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[p2p info] frame_cnt.: " PFMT_U16 ", type: 0x" PFMT_X8 "\n",
+		p2p->frame_cnt, p2p->type);
+
+	dev->wbuf[wlen++] = p2p->frame_cnt & 0xFF;
+	dev->wbuf[wlen++] = (p2p->frame_cnt >> 8) & 0xFF;
+
+	if (dev->protocol.ver > 0x060009) {
+		dev->wbuf[wlen++] = p2p->type & 0xFF;
+		dev->wbuf[wlen++] = p2p->freq & 0xFF;
+		dev->wbuf[wlen++] = (p2p->freq >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_pen_fs_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->pen.mode;
+	dev->wbuf[2] = freq->pen.start & 0xFF;
+	dev->wbuf[3] = freq->pen.start >> 8;
+	dev->wbuf[4] = freq->pen.end & 0xFF;
+	dev->wbuf[5] = freq->pen.end >> 8;
+	dev->wbuf[6] = freq->frame_cnt & 0xFF;
+	dev->wbuf[7] = freq->frame_cnt >> 8;
+
+	error = write_then_read(dev, dev->wbuf, 8, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int api_protocol_get_core_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->core_ver, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[CoreVersion]", TYPE_U8, 4, dev->core_ver);
+
+	return 0;
+}
+
+static int api_protocol_get_tuning_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->tuning_ver, dev->rbuf, 4);
+
+	TP_MSG_ARR(dev->id, "[TurningVersion]", TYPE_U8, 4, dev->tuning_ver);
+
+	return 0;
+}
+
+static int api_protocol_set_sw_reset(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	int wlen = 1;
+	bool need_re_enum = (data) ? *(bool *)data : false;
+	bool force_reset = (!data) ? true : false;
+
+	/* make sure touch report in default I2C-HID mode after force reset */
+	if (dev->_interface == interface_hid_over_i2c && !force_reset)
+		return 0;
+
+	dev->wbuf[1] = 0;
+	error = write_then_read(dev, dev->wbuf, wlen, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(dev->reset_time);
+
+	if (dev->_interface == interface_usb && need_re_enum)
+		return re_enum_helper(dev, enum_sw_reset);
+
+	return 0;
+}
+
+static int api_protocol_get_sys_busy(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	if (data)
+		*(u8 *)data = 0;
+
+	_memset(dev->rbuf, 0, 64);
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 1);
+	if (error < 0)
+		return error;
+
+	if (data)
+		*(u8 *)data = dev->rbuf[0];
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	/*
+	 * No need to get/print AP CRC by 0xC7 in BL mode,
+	 * and 2501x ICs would get wrong crc in BL.
+	 */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1,
+				dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	TP_MSG(dev->id, "[FW CRC] Master: 0x%x\n", dev->ic[0].crc[0]);
+
+	for (i = 1; i < ic_num; i++) {
+		dev->ic[i].crc[0] = le16(dev->rbuf + 2 * i);
+		TP_MSG(dev->id, "[FW CRC] Slave[" PFMT_U8 "]: 0x%x\n",
+			i, dev->ic[i].crc[0]);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error, rlen;
+
+	UNUSED(data);
+
+	rlen = (is_231x(dev)) ? 4 : 2;
+
+	if (dev->_interface == interface_i2c) {
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		dev->cb.delay_ms(600);
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	}
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	if (is_231x(dev))
+		dev->ic[0].crc[0] |= (le16(dev->rbuf + 2) << 16);
+
+	TP_MSG(dev->id, "[Check Code] AP: 0x%x\n", dev->ic[0].crc[0]);
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_ap_crc_v6(dev, data);
+	else if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_ap_crc_v3(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_set_mode_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 mode = dev->wbuf[1];
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * Bridge with V3 IC need to set bridge into/out test mode additionally.
+	 */
+	if (dev->quirks & QUIRK_BRIDGE)
+		return bridge_set_test_mode(dev, (mode) ? true : false);
+
+	return 0;
+}
+
+static int api_protocol_write_enable(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool in_ap = (data) ? *(bool *)data : true;
+
+	error = write_then_read(dev, dev->wbuf,
+				(in_ap) ? 3 : 10, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * V3 need AP/BL mode switch delay
+	 */
+	if (in_ap)
+		dev->cb.delay_ms(is_231x(dev) ? 1000 : 100);
+	else
+		dev->cb.delay_ms(10);
+
+	return 0;
+}
+
+static int api_protocol_write_data_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 33, NULL, 0);
+}
+
+static int api_protocol_get_df_crc(struct ilitek_ts_device *dev,  void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->ic[0].crc[1] = 0;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[1] = le16(dev->rbuf + 2) << 16 | le16(dev->rbuf);
+	TP_MSG(dev->id, "[Check Code] Data: 0x%x\n", dev->ic[0].crc[1]);
+
+	return 0;
+}
+
+static int api_protocol_set_mode_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_get_crc_by_addr(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 start, end, t_ms;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		start = le32(dev->wbuf + 2, 3);
+		end = le32(dev->wbuf + 5, 3);
+		t_ms = ((end - start) / 4096 + 1) * TOUT_CD * TOUT_CD_RATIO;
+
+		error = write_then_wait_ack(dev, dev->wbuf, 8, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_get_crc_by_num(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 t_ms = (dev->wbuf[2] == 0) ? TOUT_CF_BLOCK_0 : TOUT_CF_BLOCK_N;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		error = write_then_wait_ack(dev, dev->wbuf, 3, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_read_flash(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 code = *(u32 *)data;
+	bool prepare;
+	int rlen;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		if ((dev->protocol.ver & 0xFFFF00) == BL_PROTOCOL_V1_7 &&
+		     dev->fw_ver[3] < 3) {
+			TP_ERR(dev->id, "BL: 0x%x, FW: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 " not support cmd: 0x" PFMT_X8 "\n",
+				dev->protocol.ver, dev->fw_ver[0],
+				dev->fw_ver[1], dev->fw_ver[2], dev->fw_ver[3],
+				dev->wbuf[0]);
+			return -EINVAL;
+		}
+
+		return write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	}
+
+	if (!data)
+		return -EINVAL;
+
+	prepare = (code >> 16) ? true : false;
+	rlen = code & 0xFFFF;
+
+	if (prepare) {
+		error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+		dev->cb.delay_ms(100);
+
+		return error;
+	}
+
+	if (dev->_interface == interface_i2c)
+		error = write_then_read(dev, dev->wbuf, 2, dev->rbuf, rlen);
+	else
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+
+	return error;
+}
+
+static int api_protocol_set_flash_addr(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 addr = *(u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		dev->wbuf[3] = addr & 0xFF;
+		dev->wbuf[2] = (addr >> 8) & 0xFF;
+		dev->wbuf[1] = (addr >> 16) & 0xFF;
+
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		return 0;
+	}
+
+	dev->wbuf[1] = addr & 0xFF;
+	dev->wbuf[2] = (addr >> 8) & 0xFF;
+	dev->wbuf[3] = (addr >> 16) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 4, NULL, 0);
+}
+
+static int api_protocol_set_data_len(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_set_flash_enable(struct ilitek_ts_device *dev,
+					 void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	int wlen, rlen;
+	bool in_ap = ((type & 0x1) != 0) ? true : false;
+	bool is_slave = ((type & 0x2) != 0) ? true : false;
+
+	u32 set_start, set_end, get_start, get_end;
+
+	if (!is_slave) {
+		wlen = (in_ap) ? 3 : 9;
+		rlen = (in_ap || dev->protocol.ver < 0x010803) ? 0 : 6;
+
+		set_start = le32(dev->wbuf + 3, 3);
+		set_end = le32(dev->wbuf + 6, 3);
+
+		error = write_then_read(dev, dev->wbuf, wlen,
+					dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+
+		if (in_ap || dev->protocol.ver < 0x010803)
+			return 0;
+
+		get_start = le32(dev->rbuf, 3);
+		get_end = le32(dev->rbuf + 3, 3);
+
+		if (set_start != get_start || set_end != get_end) {
+			TP_ERR(dev->id, "start/end addr.: 0x%x/0x%x vs. 0x%x/0x%x not match\n",
+				set_start, set_end, get_start, get_end);
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	error = write_then_wait_ack(dev, dev->wbuf, 9,
+				    TOUT_CC_SLAVE * TOUT_CC_SLAVE_RATIO);
+	if (error < 0)
+		return error;
+	dev->cb.delay_ms(2000);
+
+	return (dev->_interface == interface_usb) ?
+		re_enum_helper(dev, enum_sw_reset) : 0;
+}
+
+static int api_protocol_write_data_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, TOUT_C3 * TOUT_C3_RATIO);
+}
+
+static int api_protocol_write_data_m2v(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, 30000);
+}
+
+static int api_protocol_access_slave(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_slave_access *access;
+
+	if (!data)
+		return -EINVAL;
+
+	access = (struct ilitek_slave_access *)data;
+
+	dev->wbuf[1] = access->slave_id;
+	dev->wbuf[2] = access->func;
+	_memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (access->func) {
+	case CMD_GET_AP_CRC:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 4);
+		*((u32 *)access->data) = le32(dev->rbuf, 4);
+		break;
+
+	case CMD_GET_MCU_MOD:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 1);
+		*((u8 *)access->data) = dev->rbuf[0];
+		break;
+
+	case CMD_GET_FW_VER:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 8);
+		_memcpy((u8 *)access->data, dev->rbuf, 8);
+
+		break;
+
+	case CMD_WRITE_ENABLE:
+		dev->wbuf[3] = ((u8 *)access->data)[0];
+		dev->wbuf[4] = ((u8 *)access->data)[1];
+		dev->wbuf[5] = ((u8 *)access->data)[2];
+		dev->wbuf[6] = ((u8 *)access->data)[3];
+		dev->wbuf[7] = ((u8 *)access->data)[4];
+		dev->wbuf[8] = ((u8 *)access->data)[5];
+
+		error = write_then_wait_ack(dev, dev->wbuf, 9, 5000);
+		break;
+
+	default:
+		error = write_then_wait_ack(dev, dev->wbuf, 3, 5000);
+		break;
+	};
+
+	return error;
+}
+
+static int api_protocol_set_ap_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, false, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_bl_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, true, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_idle(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_set_sleep(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_wakeup(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_func_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool get = (data) ? *(bool *)data : true;
+
+	if (!data)
+		return -EINVAL;
+
+	if (get) {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+		if (error < 0)
+			return error;
+
+		dev->func.header = be16(dev->rbuf);
+		dev->func.mode = dev->rbuf[2];
+		TP_MSG(dev->id, "[FW Mode] 0x" PFMT_X8 "\n", dev->func.mode);
+
+		return 0;
+	}
+
+	if (dev->protocol.flag == PTL_V3) {
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+		return 0;
+	} else if (dev->protocol.flag == PTL_V6) {
+		error = write_then_wait_ack(dev, dev->wbuf, 4,
+					    TOUT_68 * TOUT_68_RATIO);
+		if (error < 0)
+			return error;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int api_protocol_c_model_info(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060008)
+		return write_then_read(dev, dev->wbuf, 12, NULL, 0);
+
+	return write_then_read(dev, dev->wbuf, 18, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct tuning_para_settings tuning =
+		*(struct tuning_para_settings *)data;
+	u32 wlen;
+
+	int header;
+	int tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = tuning.func;
+	dev->wbuf[2] = tuning.ctrl;
+	dev->wbuf[3] = tuning.type;
+
+	if (tuning.func == 0x0) {
+		wlen = 4;
+		tout_ms = TOUT_65_READ * TOUT_65_READ_RATIO;
+
+		switch (tuning.ctrl) {
+		case 0x3: case 0x5: case 0x10:
+			wlen += tuning.len;
+			tout_ms = TOUT_65_WRITE * TOUT_65_WRITE_RATIO;
+
+			//TODO: add memory range check
+			_memcpy(dev->wbuf + 4, tuning.buf, tuning.len);
+			break;
+		}
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	switch (tuning.ctrl) {
+	case 0x2: case 0x4:
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = read_ctrl_in(dev, CMD_TUNING_PARA_V6,
+				     dev->rbuf, 1024, 5000);
+		if (error < 0)
+			return error;
+
+		header = (dev->_interface == interface_i2c) ? 5 : 6;
+		header = (dev->quirks & QUIRK_BRIDGE) ? 6 : header;
+
+		_memcpy(tuning.buf, dev->rbuf + header, tuning.len);
+
+		break;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_cdc_init_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	int wlen;
+	struct cdc_settings *set = (struct cdc_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (set->is_freq) {
+		dev->wbuf[1] = 0x0F;
+		dev->wbuf[2] = set->freq.sine.start;
+		dev->wbuf[3] = set->freq.sine.end;
+		dev->wbuf[4] = set->freq.sine.step;
+
+		error = write_then_read(dev, dev->wbuf, 5, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(200);
+	} else {
+		dev->wbuf[1] = set->cmd;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = set->config & 0xFF;
+		wlen = 4;
+
+		if (set->config & 0xFF00) {
+			dev->wbuf[3] = (set->config >> 8) & 0xFF;
+			dev->wbuf[4] = set->config & 0xFF;
+			wlen = 5;
+		}
+
+		error = write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(10);
+	}
+
+	return api_check_busy(dev, 15000, 10);
+}
+
+static int api_protocol_get_cdc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_wait_ack(dev, dev->wbuf, 1, TOUT_F2 * TOUT_F2_RATIO);
+}
+
+static int api_protocol_set_cdc_init_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	struct cdc_settings *set = (struct cdc_settings *)data;
+	int wlen = 1, tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[wlen++] = set->cmd;
+
+	if (set->is_freq) {
+		tout_ms = set->freq.sine.steps * TOUT_F1_FREQ_MC;
+		tout_ms += (set->freq.mc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.sc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump1.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump2.steps * TOUT_F1_FREQ_SC);
+
+		tout_ms += (set->freq.pen.steps * TOUT_F1_FREQ_MC);
+
+		tout_ms *= MAX(set->freq.frame_cnt, 1);
+		tout_ms *= TOUT_F1_FREQ_RATIO;
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (set->is_p2p) {
+		tout_ms = MAX(set->p2p.frame_cnt, 1) *
+			TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (dev->protocol.ver > 0x60008)
+		dev->wbuf[wlen++] = set->config & 0xFF;
+
+	tout_ms = TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+	if (set->is_curve) {
+		tout_ms = set->curve.dump.steps * TOUT_F1_CURVE;
+		tout_ms += (set->curve.charge.steps * TOUT_F1_CURVE);
+		tout_ms *= MAX(set->curve.frame_cnt, 1);
+		tout_ms *= TOUT_F1_CURVE_RATIO;
+	} else if (set->is_short) {
+		tout_ms = TOUT_F1_SHORT * TOUT_F1_SHORT_RATIO;
+	} else if (set->is_open) {
+		tout_ms = MAX(set->open.frame, 1) *
+			TOUT_F1_OPEN * TOUT_F1_OPEN_RATIO;
+	} else if (set->is_key && dev->protocol.ver < 0x6000a) {
+		tout_ms = TOUT_F1_KEY;
+	}
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+}
+
+static int api_protocol_get_cdc_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u32 *cdc_info = (u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	*cdc_info = le32(dev->rbuf, 4);
+
+	return 0;
+}
+
+int api_protocol_set_cmd(void *handle, u8 idx, void *data)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev || idx >= ARRAY_SIZE(protocol_maps))
+		return -EINVAL;
+
+	if (!(dev->protocol.flag & protocol_maps[idx].flag) &&
+	    protocol_maps[idx].flag != PTL_ANY) {
+		TP_ERR(dev->id, "Unexpected cmd: " PFMT_C8 " for 0x" PFMT_X8 " only, now is 0x" PFMT_X8 "\n",
+			protocol_maps[idx].desc, protocol_maps[idx].flag,
+			dev->protocol.flag);
+		return -EINVAL;
+	}
+
+	dev->wbuf[0] = protocol_maps[idx].cmd;
+	error = protocol_maps[idx].func(dev, data);
+	if (error < 0) {
+		TP_ERR(dev->id, "failed to execute cmd: 0x" PFMT_X8 " " PFMT_C8 ", err: %d\n",
+			protocol_maps[idx].cmd, protocol_maps[idx].desc, error);
+		return error;
+	}
+
+	return 0;
+}
+
+int api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	u8 cmd = 0;
+
+	if (!force && dev->fw_mode == mode)
+		return 0;
+
+	_memset(dev->wbuf, 0, sizeof(dev->wbuf));
+
+	if (dev->protocol.flag == PTL_V3) {
+		/* V3 only support suspend and normal mode */
+		if (mode != mode_normal &&
+		    mode != mode_suspend &&
+		    mode != mode_test)
+			return -EPROTONOSUPPORT;
+		dev->wbuf[1] = (mode == mode_normal) ? 0x00 : 0x01;
+		cmd = SET_TEST_MOD;
+	} else if (dev->protocol.flag == PTL_V6) {
+		dev->wbuf[1] = mode;
+		dev->wbuf[2] = (eng) ? 0x01 : 0x00;
+		cmd = SET_MOD_CTRL;
+	}
+
+	error = api_protocol_set_cmd(dev, cmd, NULL);
+	if (error < 0)
+		return error;
+
+	/* switch from test to normal mode should wait 1 sec. delay */
+	if (dev->protocol.flag == PTL_V6 &&
+	    dev->fw_mode == mode_test && mode == mode_normal)
+		dev->cb.delay_ms(1000);
+	else
+		dev->cb.delay_ms(100);
+
+	dev->fw_mode = mode;
+
+	return 0;
+}
+
+u16 api_get_block_crc_by_addr(void *handle, u8 type,
+			      u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = start;
+	dev->wbuf[3] = (start >> 8) & 0xFF;
+	dev->wbuf[4] = (start >> 16) & 0xFF;
+	dev->wbuf[5] = end & 0xFF;
+	dev->wbuf[6] = (end >> 8) & 0xFF;
+	dev->wbuf[7] = (end >> 16) & 0xFF;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+u16 api_get_block_crc_by_num(void *handle, u8 type,
+			     u8 block_num)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = block_num;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+int api_set_data_len(void *handle, u16 data_len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[1] = data_len & 0xFF;
+	dev->wbuf[2] = (data_len >> 8) & 0xFF;
+
+	return api_protocol_set_cmd(dev, SET_DATA_LEN, NULL);
+}
+
+int api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 type;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = start & 0xFF;
+	dev->wbuf[4] = (start >> 8) & 0xFF;
+	dev->wbuf[5] = start >> 16;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (end >> 8) & 0xFF;
+	dev->wbuf[8] = end >> 16;
+
+	type = (in_ap) ? 0x1 : 0x0;
+	type |= (is_slave) ? 0x2 : 0x0;
+
+	return api_protocol_set_cmd(dev, SET_FLASH_EN, &type);
+}
+
+int api_write_data_v6(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V6, &wlen);
+}
+
+int api_access_slave(void *handle, u8 id, u8 func, void *data)
+{
+	struct ilitek_slave_access access;
+
+	access.slave_id = id;
+	access.func = func;
+	access.data = data;
+
+	return api_protocol_set_cmd(handle, ACCESS_SLAVE, &access);
+}
+
+int api_write_enable_v3(void *handle, bool in_ap, bool write_ap,
+			u32 end, u32 checksum)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = (write_ap) ? 0x0 : 0x1;
+	dev->wbuf[4] = (end >> 16) & 0xFF;
+	dev->wbuf[5] = (end >> 8) & 0xFF;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (checksum >> 16) & 0xFF;
+	dev->wbuf[8] = (checksum >> 8) & 0xFF;
+	dev->wbuf[9] = checksum & 0xFF;
+
+	return api_protocol_set_cmd(dev, WRITE_ENABLE, &in_ap);
+}
+
+int api_write_data_v3(void *handle)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V3, NULL);
+}
+
+int api_check_busy(void *handle, int timeout_ms, int delay_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 busy;
+
+	/* retry 2 times at least */
+	int i = MAX(DIV_ROUND_UP(timeout_ms, delay_ms), 2);
+
+	_memset(dev->wbuf, 0, 64);
+
+	while (i--) {
+		api_protocol_set_cmd(dev, GET_SYS_BUSY, &busy);
+		if (busy == ILITEK_TP_SYSTEM_READY)
+			return 0;
+
+		/* delay ms for each check busy */
+		dev->cb.delay_ms(delay_ms);
+
+		/* if caller set no_retry then skip check busy retry */
+		if (dev->setting.no_retry)
+			break;
+	}
+
+	TP_WARN(dev->id, "check busy timeout: %d ms, state: 0x" PFMT_X8 "\n",
+		timeout_ms, busy);
+
+	return -EILIBUSY;
+}
+
+int api_to_bl_mode(void *handle, bool to_bl,
+		   u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+
+	do {
+		if (api_protocol_set_cmd(dev, GET_MCU_MOD, NULL) < 0)
+			continue;
+
+		if (dev->ic[0].mode == target_mode)
+			goto success_change_mode;
+
+		if (to_bl) {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, true, false,
+						     0, 0) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_BL_MODE, NULL);
+		} else {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, false, false,
+						     start, end) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_AP_MODE, NULL);
+		}
+
+		switch (dev->_interface) {
+		case interface_hid_over_i2c:
+		case interface_i2c:
+			dev->cb.delay_ms(1000 + 100 * cnt);
+			break;
+		case interface_usb:
+			re_enum_helper(dev, enum_ap_bl);
+			break;
+		}
+	} while (!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+
+	/* update fw ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+
+	/* update protocol ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	return 0;
+}
+
+int api_set_idle(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = (enable) ? 1 : 0;
+	return api_protocol_set_cmd(dev, SET_MCU_IDLE, NULL);
+}
+
+int api_set_func_mode(void *handle, u8 mode)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	bool get = false;
+
+	_memset(dev->wbuf, 0, 64);
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->wbuf[1] = 0x55;
+		dev->wbuf[2] = 0xAA;
+		break;
+	case PTL_V6:
+		dev->wbuf[1] = 0x5A;
+		dev->wbuf[2] = 0xA5;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol: %x, flag: " PFMT_U8 "",
+			dev->protocol.ver, dev->protocol.flag);
+		return -EINVAL;
+	}
+	dev->wbuf[3] = mode;
+
+	if (dev->protocol.ver < 0x30400) {
+		TP_ERR(dev->id, "protocol: 0x%x not support\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	error = api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+	if (error < 0)
+		return error;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		return error;
+
+	return (dev->func.mode == mode) ? 0 : -EFAULT;
+}
+
+int api_get_func_mode(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool get = true;
+
+	return api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+}
+
+int api_erase_data_v3(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+
+	TP_INFO(dev->id, "erase data flash for " PFMT_C8 ", mode: " PFMT_X8 "\n",
+		dev->mcu_info.ic_name, dev->ic[0].mode);
+
+	if (is_231x(dev)) {
+		/* V3 231x only support erase data flash in AP mode */
+		if (dev->ic[0].mode != AP_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, true, false, 0, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(100);
+
+		dev->wbuf[1] = 0x02;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V3, NULL);
+		if (error < 0)
+			return error;
+
+		switch (dev->_interface) {
+		case interface_usb:
+			return re_enum_helper(dev, enum_ap_bl);
+		default:
+			dev->cb.delay_ms(1500);
+			break;
+		}
+	} else {
+		/* V3 251x only support erase data flash in BL mode */
+		if (dev->ic[0].mode != BL_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, false, false, 0xf01f, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		_memset(dev->wbuf + 1, 0xFF, 32);
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(500);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v3(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 addr, end = start + len, copied;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += 32, copied += 32) {
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, READ_FLASH, NULL);
+		if (error < 0)
+			return error;
+
+		_memcpy(buf + copied, dev->rbuf, 32);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v6(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 code, addr, end = start + len, copied;
+	u16 data_len;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += data_len, copied += data_len) {
+		if (end - addr > 1024)
+			data_len = 2048;
+		else if (end - addr > 256)
+			data_len = 1024;
+		else if (end - addr > 64)
+			data_len = 256;
+		else
+			data_len = 64;
+
+		error = api_set_data_len(dev, data_len);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x1; code = 1 << 16;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x0; code = data_len & 0xFFFF;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		if (dev->_interface == interface_hid_over_i2c)
+			_memcpy(buf + copied, dev->rbuf + 5, data_len);
+		else
+			_memcpy(buf + copied, dev->rbuf, data_len);
+	}
+
+	return 0;
+}
+
+int api_read_flash(void *handle, u8 *buf,
+		   u32 start, u32 len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (dev->protocol.flag == PTL_V3)
+		return api_read_flash_v3(dev, buf, start, len);
+
+	return api_read_flash_v6(dev, buf, start, len);
+}
+
+int _api_read_mp_result(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)(&dev->mp);
+	u32 addr = (is_29xx(dev)) ? 0x2e000 : 0x3e000;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return api_read_flash_v6(dev, buf, addr, 1000);
+
+	/* 1000 bytes data/ 2 bytes crc/ 1 bytes checksum */
+	tuning.len = 1003;
+	tuning.buf = buf;
+
+	tuning.func = 0x0; tuning.ctrl = 0x4; tuning.type = 0x10;
+	error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+	if (error < 0)
+		return error;
+
+	tuning.func = 0x1; tuning.ctrl = 0x4; tuning.type = 0x10;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_read_mp_result(void *handle)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_read_mp_result(handle);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+int _api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)mp;
+	u16 crc;
+
+	crc = get_crc(0, 1000, buf, sizeof(struct mp_station));
+	mp->crc = crc;
+
+	tuning.func = 0x0;
+	tuning.ctrl = 0x5;
+	tuning.type = 0x10;
+	tuning.buf = buf;
+	tuning.len = 1002;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_write_mp_result(handle, mp);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+static void mp_result(const char *item, u8 data)
+{
+	if (data == 1)
+		TP_INFO(NULL, PFMT_C8 " Result: PASS\n", item);
+	else if (data == 2)
+		TP_INFO(NULL, PFMT_C8 " Result: NG\n", item);
+	else
+		TP_INFO(NULL, PFMT_C8 " Result: N/A\n", item);
+}
+
+void api_decode_mp_result(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct mp_station *mp = &dev->mp;
+	int i, j;
+	struct mp_station_old *_mp;
+	char module_name[32];
+	char bar_code[256];
+
+	TP_DBG_ARR(NULL, "[MpResult Raw]:", TYPE_U8, 1002, (u8 *)mp);
+
+	TP_MSG(NULL, "mp result ver: 0x%08x\n", mp->info.mp_result_ver);
+
+	/* For Old Mp Result Format */
+	if (mp->info.mp_result_ver == 0xFFFFFFFF ||
+	    mp->info.mp_result_ver < 0x01000000) {
+		_mp = (struct mp_station_old *)mp;
+
+		for (i = 0; i < 9; i++) {
+			TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+			if (!_mp->station[i].week ||
+			    _mp->station[i].week == 0xFF) {
+				TP_INFO(NULL, "No Test\n");
+				continue;
+			}
+
+			TP_INFO(NULL, "Week of year : " PFMT_U8 "\n",
+				_mp->station[i].week);
+			TP_INFO(NULL, "Year : 20%02u\n",
+				_mp->station[i].year);
+			TP_INFO_ARR(NULL, "Firmware Version : ",
+				   TYPE_U8, 8, _mp->station[i].fw_ver);
+
+			_memset(module_name, 0, sizeof(module_name));
+			_memcpy(module_name, _mp->station[i].module,
+				sizeof(_mp->station[i].module));
+			module_name[sizeof(_mp->station[i].module) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].module);
+			     j++) {
+				if ((u8)module_name[j] == 0xFF) {
+					module_name[j] = '\0';
+					break;
+				}
+			}
+			TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n",
+				module_name);
+
+			mp_result("Short Test", _mp->station[i].short_test);
+			mp_result("Open Test", _mp->station[i].open_test);
+			mp_result("Self Cap Test", _mp->station[i].self_test);
+			mp_result("Uniformity Test",
+				_mp->station[i].uniform_test);
+			mp_result("DAC Test", _mp->station[i].dac_test);
+			mp_result("Key Raw Test", _mp->station[i].key_test);;
+			mp_result("Painting Test", _mp->station[i].paint_test);
+			mp_result("MicroOpen Test", _mp->station[i].mopen_test);
+			mp_result("GPIO Test", _mp->station[i].gpio_test);
+			mp_result("Final", _mp->station[i].final_result);
+
+			_memset(bar_code, 0, sizeof(bar_code));
+			_memcpy(bar_code, _mp->station[i].bar_code,
+				sizeof(_mp->station[i].bar_code));
+			bar_code[sizeof(_mp->station[i].bar_code) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].bar_code);
+			     j++) {
+				if ((u8)bar_code[j] == 0xFF) {
+					bar_code[j] = '\0';
+					break;
+				}
+			}
+
+			TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+			TP_INFO(NULL, "Customer ID : 0x%04x\n",
+				_mp->station[i].custom_id);
+			TP_INFO(NULL, "FWID : 0x%04x\n",
+				_mp->station[i].fwid);
+		}
+
+		return;
+	}
+
+	TP_INFO(NULL, "[MP Result]\n");
+	TP_INFO(NULL, "***Customer Info***\n");
+
+	TP_INFO(NULL, "MPResult Version:%02X.%02X.%02X.%02X\n",
+		(mp->info.mp_result_ver >> 24) & 0xFF,
+		(mp->info.mp_result_ver >> 16) & 0xFF,
+		(mp->info.mp_result_ver >> 8) & 0xFF,
+		mp->info.mp_result_ver & 0xFF);
+	TP_INFO(NULL, "Customer ID : 0x%04X\n", mp->info.customer_id);
+	TP_INFO(NULL, "FW ID : 0x%04X\n", mp->info.fwid);
+
+	for (i = 0; i < (int)ARRAY_SIZE(mp->station); i++) {
+		TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+		if (!mp->station[i].week || mp->station[i].week == 0xFF) {
+			TP_INFO(NULL, "No Test\n");
+			continue;
+		}
+
+		TP_INFO(NULL, "Week of year : %d\n", mp->station[i].week);
+		TP_INFO(NULL, "Year : 20%02d\n", mp->station[i].year);
+		TP_INFO(NULL, "Firmware Version : 0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+			mp->station[i].fw_ver[7], mp->station[i].fw_ver[6],
+			mp->station[i].fw_ver[5], mp->station[i].fw_ver[4],
+			mp->station[i].fw_ver[3], mp->station[i].fw_ver[2],
+			mp->station[i].fw_ver[1], mp->station[i].fw_ver[0]);
+
+		_memset(module_name, 0, sizeof(module_name));
+		_memcpy(module_name, mp->station[i].module,
+			sizeof(mp->station[i].module));
+		module_name[sizeof(mp->station[i].module) + 1] = '\0';
+		for (j = 0; j < (int)sizeof(mp->station[i].module); j++) {
+			if ((u8)module_name[j] == 0xFF) {
+				module_name[j] = '\0';
+				break;
+			}
+		}
+		TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n", module_name);
+
+		mp_result("Short Test", mp->station[i].short_test);
+		mp_result("Open Test", mp->station[i].open_test);
+		mp_result("Self Cap Test", mp->station[i].self_test);
+		mp_result("Uniformity Test", mp->station[i].uniform_test);
+		mp_result("DAC Test", mp->station[i].dac_test);
+		mp_result("Key Raw Test", mp->station[i].key_test);;
+		mp_result("Painting Test", mp->station[i].paint_test);
+		mp_result("MicroOpen Test", mp->station[i].mopen_test);
+		mp_result("GPIO Test", mp->station[i].gpio_test);
+		mp_result("Final", mp->station[i].final_result);
+
+		TP_INFO(NULL, "Tool Version : " PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "\n",
+			mp->station[i].tool_ver[7], mp->station[i].tool_ver[6],
+			mp->station[i].tool_ver[5], mp->station[i].tool_ver[4],
+			mp->station[i].tool_ver[3], mp->station[i].tool_ver[2],
+			mp->station[i].tool_ver[1], mp->station[i].tool_ver[0]);
+
+		_memset(bar_code, 0, sizeof(bar_code));
+		_memcpy(bar_code, mp->station[i].bar_code,
+			sizeof(mp->station[i].bar_code));
+		bar_code[sizeof(mp->station[i].bar_code) + 1] = '\0';
+		TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+		TP_INFO(NULL, "Customer ID : 0x%04x\n",
+			mp->station[i].custom_id);
+		TP_INFO(NULL, "FWID : 0x%04x\n", mp->station[i].fwid);
+	}
+}
+
+int api_read_tuning(void *handle, u8 *buf, int rlen)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	int got, need;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return -EINVAL;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	for (got = 0, need = rlen; need > 0; got += 1024, need -= 1024) {
+		tuning.len = MIN(need, 1024);
+		tuning.buf = buf + got;
+
+		tuning.func = 0x0; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+
+		tuning.func = 0x1; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+	}
+
+	TP_DBG_ARR(dev->id, "[tuning]:", TYPE_U8, rlen, buf);
+
+	return api_set_ctrl_mode(dev, mode_normal, false, true);
+}
+
+int api_write_data_m2v(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_M2V, &wlen);
+}
+
+int api_to_bl_mode_m2v(void *handle, bool to_bl)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+	u8 mode;
+
+	if (dev->_interface != interface_usb)
+		return -EINVAL;
+
+	do {
+		dev->cb.delay_ms(100);//Reed Add : 20230927
+
+		if (api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) < 0)
+			continue;
+
+		if (mode == target_mode)
+			goto success_change_mode;
+
+		dev->cb.delay_ms(300);//Reed Add : 20230927
+
+		if (to_bl && api_access_slave(dev, 0x80, CMD_SET_BL_MODE,
+					      NULL) < 0)
+			continue;
+		else if (!to_bl && api_access_slave(dev, 0x80, CMD_SET_AP_MODE,
+						    NULL) < 0)
+			continue;
+
+		do {
+			dev->cb.delay_ms(100);//Reed Add : 20230927
+			if (!api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) &&
+			    mode == target_mode)
+				goto success_change_mode;
+			dev->cb.delay_ms(5000);
+		} while (!dev->setting.no_retry && cnt++ < retry);
+		break;
+	} while (!!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "M2V current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "M2V current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		mode, (to_bl) ? "BL" : "AP");
+
+	return 0;
+}
+
+int api_get_ic_crc(void *handle, u8 final_fw_mode)
+{
+	int error;
+	u8 i;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		break;
+	case PTL_V6:
+		for (i = 1; i < dev->tp_info.block_num; i++) {
+			dev->ic[0].crc[i] = api_get_block_crc_by_num(dev,
+				CRC_CALCULATE, i);
+		}
+		break;
+	default:
+		error = -EINVAL; break;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, final_fw_mode, false, true);
+
+	return error;
+}
+
+void api_print_ts_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 i;
+
+	TP_INFO(dev->id, "[Protocol Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X\n",
+		(dev->protocol.ver >> 16) & 0xff,
+		(dev->protocol.ver >> 8) & 0xff,
+		dev->protocol.ver & 0xff);
+
+	TP_INFO(dev->id, "[Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[0], dev->fw_ver[1],
+		dev->fw_ver[2], dev->fw_ver[3]);
+	TP_INFO(dev->id, "[Customer Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[4], dev->fw_ver[5],
+		dev->fw_ver[6], dev->fw_ver[7]);
+
+	TP_INFO(dev->id, "[Kernel Version]\n");
+	if (dev->protocol.flag == PTL_V6 &&
+	    support_mcu_info(dev)) {
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X (" PFMT_C8 ")\n",
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1],
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[4], dev->mcu_info.ic_name);
+	} else {
+		TP_INFO(dev->id, "0x%c%c.0x%c%c\n",
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1]);
+	}
+
+	TP_INFO(dev->id, "[Current Mode]\n");
+	TP_INFO(dev->id, "Master : " PFMT_C8 "\n", dev->ic[0].mode_str);
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	for (i = 1; i < dev->tp_info.ic_num; i++)
+		TP_INFO(dev->id, "Slave : " PFMT_C8 "\n", dev->ic[i].mode_str);
+
+	TP_INFO(dev->id, "[Module Name]\n");
+	TP_INFO(dev->id, PFMT_C8 "\n", dev->mcu_info.module_name);
+
+	TP_INFO(dev->id, "[Core Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->core_ver[0], dev->core_ver[1],
+		dev->core_ver[2], dev->core_ver[3]);
+
+	if (is_231x(dev)) {
+		switch (dev->core_ver[1]) {
+		case 0x00:
+			TP_INFO(dev->id, "(Dual Interface)\n"); break;
+		case 0x01:
+			TP_INFO(dev->id, "(USB Interface)\n"); break;
+		case 0x02:
+			TP_INFO(dev->id, "(I2C Interface)\n"); break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[Tuning Version]\n");
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+			dev->tuning_ver[0], dev->tuning_ver[1],
+			dev->tuning_ver[2], dev->tuning_ver[3]);
+	}
+
+	TP_INFO(dev->id, "[Panel Information]\n");
+	TP_INFO(dev->id, "X resolution : " PFMT_U16 "\n", dev->tp_info.x_resolution);
+	TP_INFO(dev->id, "Y resolution : " PFMT_U16 "\n", dev->tp_info.y_resolution);
+	TP_INFO(dev->id, "AA X Channel : " PFMT_U16 "\n", dev->tp_info.x_ch);
+	TP_INFO(dev->id, "AA Y Channel : " PFMT_U16 "\n", dev->tp_info.y_ch);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Fingers\n", dev->tp_info.max_fingers);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Touch Keys\n", dev->tp_info.key_num);
+
+	if (dev->tp_info.key_num) {
+		switch (dev->key.info.mode) {
+		case key_disable:
+			TP_INFO(dev->id, "Key Mode : NO_Key\n");
+			break;
+		case key_hw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_1\n");
+			break;
+		case key_hsw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_2\n");
+			break;
+		case key_vitual:
+			TP_INFO(dev->id, "Key Mode : Virtual_Key\n");
+			break;
+		case key_fw_disable:
+			TP_INFO(dev->id, "Key Mode : FW_disable\n");
+			break;
+		default:
+			TP_INFO(dev->id, "Key Mode : Unknown(0x" PFMT_X8 ")\n",
+				dev->key.info.mode);
+			break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "Support Pen Type : " PFMT_C8 "\n",
+			dev->pen_mode);
+		TP_INFO(dev->id, "Chip Counts : " PFMT_U8 "\n", dev->tp_info.ic_num);
+		TP_INFO(dev->id, "Report Format : " PFMT_U8 "\n", dev->tp_info.format);
+		TP_INFO(dev->id, "Block Number : " PFMT_U8 "\n", dev->tp_info.block_num);
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[FW CRC]\n");
+		TP_INFO(dev->id, "Master : 0x%04X\n", dev->ic[0].crc[0]);
+		for (i = 1; i < dev->tp_info.ic_num; i++)
+			TP_INFO(dev->id, "Slave : 0x%04X\n", dev->ic[i].crc[0]);
+	} else {
+		TP_INFO(dev->id, "[Check Code]\n");
+		TP_INFO(dev->id, "AP : 0x%08X\n", dev->ic[0].crc[0]);
+		TP_INFO(dev->id, "DATA : 0x%08X\n", dev->ic[0].crc[1]);
+	}
+}
+
+void api_read_then_print_m2v_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 m2v_mode;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	api_set_ctrl_mode(dev, mode_suspend, false, true);
+	api_access_slave(dev, 0x80, CMD_GET_FW_VER, m2v_fw_ver);
+	api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &m2v_mode);
+	api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &m2v_checksum);
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	TP_INFO(dev->id, "[M2V Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+		m2v_fw_ver[0], m2v_fw_ver[1], m2v_fw_ver[2], m2v_fw_ver[3],
+		m2v_fw_ver[4], m2v_fw_ver[5], m2v_fw_ver[6], m2v_fw_ver[7]);
+
+	TP_INFO(dev->id, "[M2V Current Mode]\n");
+	TP_INFO(dev->id, "Mode : " PFMT_C8 " Mode\n",
+		(m2v_mode == AP_MODE) ? "AP" : "BL");
+
+	TP_INFO(dev->id, "[M2V FW CheckSum]\n");
+	TP_INFO(dev->id, "FW CheckSum : 0x%08X\n", m2v_checksum);
+}
+
+int api_update_ts_info(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	/*
+	 * previous set suspend mode is in V6 format.
+	 * set suspend mode again if device protocol is checked as V3.
+	 */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_PRODUCT_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_FWID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_SENSOR_ID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_HID_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	/* BL mode should perform FW upgrade afterward */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	/* V3 need to get DF CRC */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_TUNING_VER, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_CORE_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_SCRN_RES, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->tp_info.ic_num > 1) {
+		error = api_protocol_set_cmd(dev, GET_AP_CRC,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_MCU_MOD,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	return error;
+}
+
+void __ilitek_get_info(void *handle, struct ilitek_common_info *info)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!info || !dev)
+		return;
+
+	_memcpy(info, &dev->quirks, sizeof(struct ilitek_common_info));
+}
+
+void ilitek_dev_set_quirks(void *handle, u32 quirks)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	dev->quirks = quirks;
+}
+
+void ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->sys, sys, sizeof(struct ilitek_sys_info));
+}
+
+void ilitek_dev_setting(void *handle, struct ilitek_ts_settings *setting)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->setting, setting, sizeof(struct ilitek_ts_settings));
+
+	if (dev->setting.default_format_enabled) {
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+	}
+
+	TP_MSG(dev->id, "no-retry: %d, no-INT-ack: %d\n",
+		dev->setting.no_retry, dev->setting.no_INT_ack);
+}
+
+void ilitek_dev_bind_callback(void *handle, struct ilitek_ts_callback *callback)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (callback) {
+		_memcpy(&dev->cb, callback, sizeof(struct ilitek_ts_callback));
+		if (dev->cb.msg)
+			g_msg = dev->cb.msg;
+	}
+}
+
+void *ilitek_dev_init(u8 _interface, const char *id,
+		      bool need_update_ts_info,
+		      struct ilitek_ts_callback *callback, void *_private)
+{
+	struct ilitek_ts_device *dev;
+
+	dev = (struct ilitek_ts_device *)MALLOC(sizeof(*dev));
+	if (!dev)
+		return NULL;
+
+	TP_MSG(NULL, "commonflow code version: 0x%x\n",
+		COMMONFLOW_CODE_VERSION);
+
+	TP_DBG(NULL, "sizeof(ilitek_ts_device): %u\n",
+		(unsigned int)sizeof(struct ilitek_ts_device));
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(dev, 0, sizeof(*dev));
+
+	_strcpy(dev->id, id, sizeof(dev->id));
+	ilitek_dev_bind_callback(dev, callback);
+
+	dev->_interface = _interface;
+	dev->_private = _private;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	dev->fw_mode = mode_unknown;
+
+	if (need_update_ts_info && api_update_ts_info(dev) < 0) {
+		ilitek_dev_exit(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+void ilitek_dev_exit(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/*
+	 * LIBUSB would kill /dev/hidraw* and make system stop handling
+	 * device's usb. sw reset is required to re-enum usb then /dev/hidraw*
+	 * would be created and system would start to handle touch event.
+	 */
+	if (dev->quirks & QUIRK_LIBUSB || dev->setting.sw_reset_at_last)
+		api_protocol_set_cmd(dev, SET_SW_RST, NULL);
+
+	if (dev)
+		FREE(dev);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.h b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
new file mode 100644
index 000000000000..ad9c5c18cd36
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
@@ -0,0 +1,916 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_PROTOCOL_H__
+#define __ILITEK_PROTOCOL_H__
+
+#include "ilitek_def.h"
+
+/* quirks definition */
+#define QUIRK_WAIT_ACK_DELAY		0x1
+#define QUIRK_BRIDGE			0x2
+#define QUIRK_DAEMON_I2C		0x4
+#define QUIRK_WIFI_ITS_I2C		0x8
+#define QUIRK_LIBUSB			0x10
+
+#define START_ADDR_LEGO			0x3000
+#define START_ADDR_29XX			0x4000
+#define END_ADDR_LEGO			0x40000
+
+#define MM_ADDR_LEGO			0x3020
+#define MM_ADDR_29XX			0x4020
+#define MM_ADDR_2501X			0x4038
+
+#define DF_START_ADDR_LEGO		0x3C000
+#define DF_START_ADDR_29XX		0x2C000
+
+#define ILITEK_TP_SYSTEM_READY		0x50
+
+#define CRC_CALCULATE			0
+#define CRC_GET				1
+
+#define ILTIEK_MAX_BLOCK_NUM		20
+
+#define PTL_ANY				0x00
+#define PTL_V3				0x03
+#define PTL_V6				0x06
+
+#define BL_PROTOCOL_V1_8		0x10800
+#define BL_PROTOCOL_V1_7		0x10700
+#define BL_PROTOCOL_V1_6		0x10600
+
+#define TOUT_CF_BLOCK_0			2500
+#define TOUT_CF_BLOCK_N			500
+#define TOUT_F1_SHORT			1600
+#define TOUT_F1_OPEN			12
+#define TOUT_F1_FREQ_MC			2
+#define TOUT_F1_FREQ_SC			1
+#define TOUT_F1_CURVE			13
+#define TOUT_F1_KEY			400
+#define TOUT_F1_OTHER			27
+#define TOUT_F2				7
+#define TOUT_CD				27
+#define TOUT_C3				100
+#define TOUT_65_WRITE			135
+#define TOUT_65_READ			3
+#define TOUT_68				24
+#define TOUT_CC_SLAVE			16000
+
+#define TOUT_F1_SHORT_RATIO		2
+#define TOUT_F1_OPEN_RATIO		3
+#define TOUT_F1_FREQ_RATIO		3
+#define TOUT_F1_CURVE_RATIO		3
+#define TOUT_F1_OTHER_RATIO		3
+#define TOUT_F2_RATIO			3
+#define TOUT_CD_RATIO			3
+#define TOUT_C3_RATIO			3
+#define TOUT_65_WRITE_RATIO		3
+#define TOUT_65_READ_RATIO		3
+#define TOUT_68_RATIO			3
+#define TOUT_CC_SLAVE_RATIO		2
+
+#define AP_MODE		0x5A
+#define BL_MODE		0x55
+
+#define STYLUS_MODES			\
+	X(STYLUS_WGP,	0x1,	"WGP")	\
+	X(STYLUS_USI,	0x2,	"USI")	\
+	X(STYLUS_MPP,	0x4,	"MPP")
+
+#define ILITEK_CMD_MAP							\
+	X(0x20, PTL_ANY, GET_TP_INFO, api_protocol_get_tp_info)		\
+	X(0x21, PTL_ANY, GET_SCRN_RES, api_protocol_get_scrn_res)	\
+	X(0x22, PTL_ANY, GET_KEY_INFO, api_protocol_get_key_info)	\
+	X(0x30, PTL_ANY, SET_IC_SLEEP, api_protocol_set_sleep)		\
+	X(0x31, PTL_ANY, SET_IC_WAKE, api_protocol_set_wakeup)		\
+	X(0x34, PTL_ANY, SET_MCU_IDLE, api_protocol_set_idle)		\
+	X(0x40, PTL_ANY, GET_FW_VER, api_protocol_get_fw_ver)		\
+	X(0x42, PTL_ANY, GET_PTL_VER, api_protocol_get_ptl_ver)		\
+	X(0x43, PTL_ANY, GET_CORE_VER, api_protocol_get_core_ver)	\
+	X(0x60, PTL_ANY, SET_SW_RST, api_protocol_set_sw_reset)		\
+	X(0x61, PTL_ANY, GET_MCU_VER, api_protocol_get_mcu_ver)		\
+	X(0x68, PTL_ANY, SET_FUNC_MOD, api_protocol_set_func_mode)	\
+	X(0x80, PTL_ANY, GET_SYS_BUSY, api_protocol_get_sys_busy)	\
+	X(0xC0, PTL_ANY, GET_MCU_MOD, api_protocol_get_mcu_mode)	\
+	X(0xC1, PTL_ANY, SET_AP_MODE, api_protocol_set_ap_mode)		\
+	X(0xC2, PTL_ANY, SET_BL_MODE, api_protocol_set_bl_mode)		\
+	X(0xC5, PTL_ANY, READ_FLASH, api_protocol_read_flash)		\
+	X(0xC7, PTL_ANY, GET_AP_CRC, api_protocol_get_ap_crc)		\
+	X(0xC8, PTL_ANY, SET_ADDR, api_protocol_set_flash_addr)		\
+									\
+	/* v3 only cmds */						\
+	X(0x25, PTL_V3, GET_CDC_INFO_V3, api_protocol_get_cdc_info_v3)	\
+	X(0x63, PTL_V3, TUNING_PARA_V3, api_protocol_tuning_para_v3)	\
+	X(0xC3, PTL_V3, WRITE_DATA_V3, api_protocol_write_data_v3)	\
+	X(0xC4, PTL_V3, WRITE_ENABLE, api_protocol_write_enable)	\
+	X(0xCA, PTL_V3, GET_DF_CRC, api_protocol_get_df_crc)		\
+	X(0xF2, PTL_V3, SET_TEST_MOD, api_protocol_set_mode_v3)		\
+	X(0xF3, PTL_V3, INIT_CDC_V3, api_protocol_set_cdc_init_v3)	\
+									\
+	/* v6 only cmds */						\
+	X(0x24, PTL_V6, POWER_STATUS, api_protocol_power_status)	\
+	X(0x27, PTL_V6, GET_SENSOR_ID, api_protocol_get_sensor_id)	\
+	X(0x44, PTL_V6, GET_TUNING_VER, api_protocol_get_tuning_ver)	\
+	X(0x45, PTL_V6, GET_PRODUCT_INFO, api_protocol_get_product_info)\
+	X(0x46, PTL_V6, GET_FWID, api_protocol_get_fwid)		\
+	X(0x47, PTL_V6, GET_CRYPTO_INFO, api_protocol_get_crypto_info)	\
+	X(0x48, PTL_V6, GET_HID_INFO, api_protocol_get_hid_info)	\
+	X(0x62, PTL_V6, GET_MCU_INFO, api_protocol_get_mcu_info)	\
+	X(0x65, PTL_V6, TUNING_PARA_V6, api_protocol_tuning_para_v6)	\
+	X(0x69, PTL_V6, SET_FS_INFO, api_protocol_set_fs_info)		\
+	X(0x6A, PTL_V6, SET_SHORT_INFO, api_protocol_set_short_info)	\
+	X(0x6B, PTL_V6, C_MODEL_INFO, api_protocol_c_model_info)	\
+	X(0x6C, PTL_V6, SET_P2P_INFO, api_protocol_set_p2p_info)	\
+	X(0x6D, PTL_V6, SET_OPEN_INFO, api_protocol_set_open_info)	\
+	X(0x6E, PTL_V6, SET_CHARGE_INFO, api_protocol_set_charge_info)	\
+	X(0x6F, PTL_V6, SET_PEN_FS_INFO, api_protocol_set_pen_fs_info)	\
+	X(0xB0, PTL_V6, WRITE_DATA_M2V, api_protocol_write_data_m2v)	\
+	X(0xC3, PTL_V6, WRITE_DATA_V6, api_protocol_write_data_v6)	\
+	X(0xC9, PTL_V6, SET_DATA_LEN, api_protocol_set_data_len)	\
+	X(0xCB, PTL_V6, ACCESS_SLAVE, api_protocol_access_slave)	\
+	X(0xCC, PTL_V6, SET_FLASH_EN, api_protocol_set_flash_enable)	\
+	X(0xCD, PTL_V6, GET_BLK_CRC_ADDR, api_protocol_get_crc_by_addr)	\
+	X(0xCF, PTL_V6, GET_BLK_CRC_NUM, api_protocol_get_crc_by_num)	\
+	X(0xF0, PTL_V6, SET_MOD_CTRL, api_protocol_set_mode_v6)		\
+	X(0xF1, PTL_V6, INIT_CDC_V6, api_protocol_set_cdc_init_v6)	\
+	X(0xF2, PTL_V6, GET_CDC_V6, api_protocol_get_cdc_v6)
+
+
+#define X(_cmd, _protocol, _cmd_id, _api)	_cmd_id,
+enum ilitek_cmd_ids {
+	ILITEK_CMD_MAP
+	/* ALWAYS keep at the end */
+	MAX_CMD_CNT
+};
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api)	CMD_##_cmd_id = _cmd,
+enum ilitek_cmds { ILITEK_CMD_MAP };
+#undef X
+
+enum ilitek_hw_interfaces {
+	interface_i2c = 0,
+	interface_hid_over_i2c,
+	interface_usb,
+};
+
+enum ilitek_fw_modes {
+	mode_unknown = -1,
+	mode_normal = 0,
+	mode_test,
+	mode_debug,
+	mode_suspend,
+};
+
+enum ilitek_key_modes {
+	key_disable = 0,
+	key_hw = 1,
+	key_hsw = 2,
+	key_vitual = 3,
+	key_fw_disable = 0xff,
+};
+
+#define ILITEK_TOUCH_REPORT_FORMAT			\
+	X(touch_fmt_0x0,	0x0, 5, 10)	\
+	X(touch_fmt_0x1,	0x1, 6, 10)	\
+	X(touch_fmt_0x2,	0x2, 10, 5)	\
+	X(touch_fmt_0x3,	0x3, 10, 5)	\
+	X(touch_fmt_0x4,	0x4, 10, 5)	\
+	X(touch_fmt_0x10,	0x10, 10, 6)	\
+	X(touch_fmt_0x11,	0x11, 5, 10)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_touch_fmts {
+	ILITEK_TOUCH_REPORT_FORMAT
+	touch_fmt_max = 0x100,
+};
+#undef X
+
+#define ILITEK_PEN_REPORT_FORMAT		\
+	X(pen_fmt_0x0, 0x0, 12, 1)	\
+	X(pen_fmt_0x1, 0x1, 18, 1)	\
+	X(pen_fmt_0x2, 0x2, 22, 1)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_pen_fmts {
+	ILITEK_PEN_REPORT_FORMAT
+	pen_fmt_max = 0x100,
+};
+#undef X
+
+struct ilitek_slave_access {
+	u8 slave_id;
+	u8 func;
+	void *data;
+};
+
+struct tuning_para_settings {
+	u8 func;
+	u8 ctrl;
+	u8 type;
+
+	u8 *buf;
+	u32 len;
+};
+
+struct reports {
+	bool touch_need_update;
+	bool pen_need_update;
+
+	u8 touch[64];
+	u8 pen[64];
+};
+
+struct grid_data {
+	bool need_update;
+	unsigned int X, Y;
+
+	s32 *data;
+};
+
+struct grids {
+	struct grid_data mc;
+	struct grid_data sc_x;
+	struct grid_data sc_y;
+	struct grid_data pen_x;
+	struct grid_data pen_y;
+
+	struct grid_data key_mc;
+	struct grid_data key_x;
+	struct grid_data key_y;
+
+	struct grid_data self;
+
+	/* touch/pen debug message along with frame update */
+	struct reports dmsg;
+};
+
+enum ilitek_enum_type {
+	enum_ap_bl = 0,
+	enum_sw_reset,
+};
+
+typedef void (*update_grid_t)(u32, u32, struct grids *, void *);
+typedef void (*update_report_rate_t)(unsigned int);
+
+typedef int (*write_then_read_t)(u8 *, int, u8 *, int, void *);
+typedef int (*read_ctrl_in_t)(u8 *, int, unsigned int, void *);
+typedef int (*read_interrupt_in_t)(u8 *, int, unsigned int, void *);
+typedef void (*init_ack_t)(unsigned int, void *);
+typedef int (*wait_ack_t)(u8, unsigned int, void *);
+typedef int (*hw_reset_t)(unsigned int, void *);
+typedef int (*re_enum_t)(u8, void *);
+typedef void (*delay_ms_t)(unsigned int);
+
+
+typedef int (*write_then_read_direct_t)(u8 *, int, u8 *, int, void *);
+typedef void (*mode_switch_notify_t)(bool, bool, void *);
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_fmt {
+	u8 id : 6;
+	u8 status : 1;
+	u8 reserve : 1;
+	u16 x;
+	u16 y;
+	u8 pressure;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ touch_iwb_fmt {
+	u8 status : 3;
+	u8 reserve : 5;
+	u8 id : 6;
+	u8 reserve_1 : 2;
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ pen_fmt {
+	union __PACKED__ {
+		u8 modes;
+		struct __PACKED__ {
+			u8 tip_sw : 1;
+			u8 barrel_sw : 1;
+			u8 eraser : 1;
+			u8 invert : 1;
+			u8 in_range : 1;
+			u8 reserve : 3;
+		};
+	};
+	u16 x;
+	u16 y;
+	u16 pressure;
+	s16 x_tilt;
+	s16 y_tilt;
+
+	u8 battery;
+
+	union __PACKED__ {
+		/* usi v1.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 width;
+			u8 style;
+		} usi_1;
+
+		/* usi v2.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 color_24[3];
+			u8 no_color;
+			u8 width;
+			u8 style;
+		} usi_2;
+	};
+};
+
+struct __PACKED__ ilitek_report_fmt_info {
+	u32 touch_size;
+	u32 touch_max_cnt;
+
+	u32 pen_size;
+	u32 pen_max_cnt;
+};
+
+struct __PACKED__ ilitek_screen_info {
+	u16 x_min;
+	u16 y_min;
+	u16 x_max;
+	u16 y_max;
+	u16 pressure_min;
+	u16 pressure_max;
+	s16 x_tilt_min;
+	s16 x_tilt_max;
+	s16 y_tilt_min;
+	s16 y_tilt_max;
+	u16 pen_x_min;
+	u16 pen_y_min;
+	u16 pen_x_max;
+	u16 pen_y_max;
+};
+
+struct __PACKED__ ilitek_tp_info_v6 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u16 x_ch;
+	u16 y_ch;
+	u8 max_fingers;
+	u8 key_num;
+	u8 ic_num;
+	u8 support_modes;
+	u8 format;
+	u8 die_num;
+	u8 block_num;
+	u8 pen_modes;
+	u8 pen_format;
+	u16 pen_x_resolution;
+	u16 pen_y_resolution;
+};
+
+struct __PACKED__ ilitek_tp_info_v3 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u8 x_ch;
+	u8 y_ch;
+	u8 max_fingers;
+	u8 reserve;
+	u8 key_num;
+	u8 reserve_1;
+	u8 touch_start_y;
+	u8 touch_end_y;
+	u8 touch_start_x;
+	u8 touch_end_x;
+	u8 support_modes;
+};
+
+struct __PACKED__ ilitek_key_info_v6 {
+	u8 mode;
+	u16 x_len;
+	u16 y_len;
+
+	struct __PACKED__ _ilitek_key_info_v6 {
+		u8 id;
+		u16 x;
+		u16 y;
+	} keys[50];
+};
+
+struct __PACKED__ ilitek_key_info_v3 {
+	u8 x_len[2];
+	u8 y_len[2];
+
+	struct __PACKED__ _ilitek_key_info_v3 {
+		u8 id;
+		u8 x[2];
+		u8 y[2];
+	} keys[20];
+};
+
+struct __PACKED__ ilitek_ts_kernel_info {
+	char ic_name[6];
+	char mask_ver[2];
+	u32 mm_addr;
+	u32 min_addr;
+	u32 max_addr;
+	char module_name[32];
+
+	char ic_full_name[16];
+};
+
+struct __PACKED__ ilitek_key_info {
+	struct ilitek_key_info_v6 info;
+	bool clicked[50];
+};
+
+struct __PACKED__ ilitek_power_status {
+	u16 header;
+	u8 vdd33_lvd_flag;
+	u8 vdd33_lvd_level_sel;
+};
+
+struct __PACKED__ ilitek_sensor_id {
+	u16 header;
+	u8 id;
+};
+
+struct __PACKED__ ilitek_func_mode {
+	u16 header;
+	u8 mode;
+};
+
+struct __PACKED__ ilitek_ts_protocol {
+	u32 ver;
+	u8 flag;
+};
+
+struct __PACKED__ ilitek_ts_ic {
+	u8 mode;
+	u32 crc[ILTIEK_MAX_BLOCK_NUM];
+
+	char mode_str[32];
+};
+
+struct __PACKED__ ilitek_hid_info {
+	u16 pid;
+	u16 vid;
+	u16 rev;
+};
+
+struct __PACKED__ freq_category {
+	u32 start;
+	u32 end;
+	u32 step;
+	u32 steps;
+
+		u32 size;
+	char limit[1024];
+
+	u8 mode;
+
+	s32 *data;
+};
+
+struct __PACKED__ freq_settings {
+	bool prepared;
+
+	unsigned int frame_cnt;
+
+	/* add from v6.0.A */
+	unsigned int mc_frame_cnt;
+	unsigned int dump_frame_cnt;
+
+	unsigned int scan_type;
+
+	struct freq_category sine;
+	struct freq_category mc_swcap;
+	struct freq_category sc_swcap;
+	struct freq_category pen;
+
+	struct freq_category dump1;
+	struct freq_category dump2;
+	u8 dump1_val;
+	u8 dump2_val;
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ short_settings {
+	bool prepared;
+
+	u8 dump_1;
+	u8 dump_2;
+	u8 v_ref_L;
+	u16 post_idle;
+};
+
+struct __PACKED__ open_settings {
+	bool prepared;
+
+	u16 freq;
+	u8 gain;
+	u8 gain_rfb;
+	u8 afe_res_sel;
+	u8 mc_fsel;
+	u16 frame;
+};
+
+struct __PACKED__ p2p_settings {
+	bool prepared;
+
+	u16 frame_cnt;
+	u8 type;
+
+	/* add from v6.0.A */
+	u16 freq;
+};
+
+struct __PACKED__ charge_curve_sweep {
+	u16 start;
+	u16 end;
+	u8 step;
+	u16 post_idle;
+	u16 fix_val;
+
+	u16 steps;
+};
+
+struct __PACKED__ charge_curve_settings {
+	bool prepared;
+
+	u8 scan_mode;
+
+	struct charge_curve_sweep dump;
+	struct charge_curve_sweep charge;
+
+	u16 c_sub;
+	u16 frame_cnt;
+
+	struct __PACKED__ charge_curve_point {
+		u16 x;
+		u16 y;
+		u16 *dump_max;
+		u16 *dump_avg;
+		u16 *charge_max;
+		u16 *charge_avg;
+	} pt[9];
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ cdc_settings {
+	u8 cmd;
+	u16 config;
+
+	bool skip_checksum;
+
+	/* freq. */
+	struct freq_settings freq;
+	/* short */
+	struct short_settings _short;
+	/* open */
+	struct open_settings open;
+	/* p2p */
+	struct p2p_settings p2p;
+	/* charge curve */
+	struct charge_curve_settings curve;
+
+	/* status only writable by CDC commonflow */
+	bool is_key;
+	bool is_p2p;
+	bool is_freq;
+	bool is_curve;
+	bool is_short;
+	bool is_open;
+	bool is_16bit;
+	bool is_sign;
+	bool is_fast_mode;
+	unsigned int total_bytes;
+
+	/* error code during cdc data collection */
+	s32 error;
+};
+
+struct __PACKED__ mp_station_old {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test:2;
+		u8 open_test:2;
+		u8 self_test:2;
+		u8 uniform_test:2;
+
+		u8 dac_test:2;
+		u8 key_test:2;
+		u8 final_result:2;
+		u8 paint_test:2;
+
+		u8 mopen_test:2;
+		u8 gpio_test:2;
+		u8 reserve_1:4;
+
+		char bar_code[28];
+		u8 reserve_2[35];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[10];
+};
+
+struct __PACKED__ mp_station {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test : 2;
+		u8 open_test : 2;
+		u8 self_test : 2;
+		u8 uniform_test : 2;
+
+		u8 dac_test : 2;
+		u8 key_test : 2;
+		u8 final_result : 2;
+		u8 paint_test : 2;
+
+		u8 mopen_test : 2;
+		u8 gpio_test : 2;
+		u8 reserve : 4;
+
+		u8 tool_ver[8];
+		char bar_code[135];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[5];
+
+	struct __PACKED__ {
+		u8 reserve_1[91];
+		u32 mp_result_ver;
+		u16 customer_id;
+		u16 fwid;
+		u8 reserve_2;
+	} info;
+
+	u16 crc;
+};
+
+struct __PACKED__ ilitek_ts_settings {
+	bool no_retry;
+	bool no_INT_ack;
+
+	bool sw_reset_at_last;
+
+	u8 sensor_id_mask;
+
+	/* only used for QUIRK_WAIT_ACK_DELAY */
+	u32 wait_ack_delay;
+
+	/*
+	 * engineer mode would likely report default format
+	 * ex. IWB-format
+	 */
+	bool default_format_enabled;
+};
+
+struct __PACKED__ ilitek_sys_info {
+	u16 pid;
+};
+
+struct __PACKED__ ilitek_ts_callback {
+	/* Please don't use "repeated start" for I2C interface */
+	write_then_read_t write_then_read;
+	read_ctrl_in_t read_ctrl_in;
+	read_interrupt_in_t read_interrupt_in;
+	init_ack_t init_ack;
+	wait_ack_t wait_ack;
+	hw_reset_t hw_reset;
+	re_enum_t re_enum;
+	delay_ms_t delay_ms;
+	msg_t msg;
+
+	/* write cmd without adding any hid header */
+	write_then_read_direct_t write_then_read_direct;
+	/* notify caller after AP/BL mode switch command */
+	mode_switch_notify_t mode_switch_notify;
+};
+
+struct __PACKED__ ilitek_common_info {
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen;
+	struct ilitek_tp_info_v6 tp;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu;
+	struct ilitek_hid_info hid;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+};
+
+struct __PACKED__ ilitek_ts_device {
+	void *_private;
+	char id[64];
+	u32 reset_time;
+
+	struct ilitek_ts_settings setting;
+
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen_info;
+	struct ilitek_tp_info_v6 tp_info;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu_info;
+	struct ilitek_hid_info hid_info;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+
+	u8 fw_mode;
+	struct mp_station mp;
+
+	u8 wbuf[4096];
+	u8 rbuf[4096];
+	struct ilitek_ts_callback cb;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16 __DLL le16(const u8 *p);
+u16 __DLL be16(const u8 *p);
+u32 __DLL le32(const u8 *p, int bytes);
+u32 __DLL be32(const u8 *p, int bytes);
+
+bool __DLL is_29xx(void *handle);
+
+bool __DLL _is_231x(char *ic_name);
+bool __DLL is_231x(void *handle);
+
+bool __DLL has_hw_key(void *handle);
+
+u8 __DLL get_protocol_ver_flag(u32 ver);
+
+int __DLL grid_alloc(void *handle, struct grids *grid);
+void __DLL grid_free(struct grids *grid);
+void __DLL grid_reset(struct grids *grid);
+
+u16 __DLL get_crc(u32 start, u32 end,
+		  u8 *buf, u32 buf_size);
+
+u32 __DLL get_checksum(u32 start, u32 end,
+		       u8 *buf, u32 buf_size);
+
+bool __DLL is_checksum_matched(u8 checksum, int start, int end,
+			       u8 *buf, int buf_size);
+
+bool __DLL support_sensor_id(void *handle);
+bool __DLL support_production_info(void *handle);
+bool __DLL support_fwid(void *handle);
+bool __DLL support_mcu_info(void *handle);
+bool __DLL support_power_status(void *handle);
+int __DLL write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms);
+
+int __DLL bridge_set_int_monitor(void *handle, bool enable);
+int __DLL bridge_set_test_mode(void *handle, bool enable);
+
+int __DLL reset_helper(void *handle);
+
+int __DLL write_then_read(void *handle, u8 *cmd, int wlen,
+			  u8 *buf, int rlen);
+int __DLL write_then_read_direct(void *handle, u8 *cmd, int wlen,
+				 u8 *buf, int rlen);
+int __DLL read_interrupt_in(void *handle, u8 *buf, int rlen,
+			    unsigned int timeout_ms);
+int __DLL read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		       unsigned int timeout_ms);
+
+void __DLL __ilitek_get_info(void *handle,
+			     struct ilitek_common_info *info);
+
+void __DLL ilitek_dev_set_quirks(void *handle, u32 quirks);
+void __DLL ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys);
+void __DLL ilitek_dev_setting(void *handle,
+			      struct ilitek_ts_settings *setting);
+
+void __DLL ilitek_dev_bind_callback(void *handle,
+				    struct ilitek_ts_callback *callback);
+
+void __DLL *ilitek_dev_init(u8 _interface, const char *id,
+			    bool need_update_ts_info,
+			    struct ilitek_ts_callback *callback,
+			    void *_private);
+void __DLL ilitek_dev_exit(void *handle);
+
+void __DLL api_print_ts_info(void *handle);
+void __DLL api_read_then_print_m2v_info(void *handle);
+
+int __DLL api_update_ts_info(void *handle);
+
+int __DLL api_protocol_set_cmd(void *handle, u8 idx, void *data);
+int __DLL api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force);
+
+u16 __DLL api_get_block_crc_by_addr(void *handle, u8 type,
+				    u32 start, u32 end);
+u16 __DLL api_get_block_crc_by_num(void *handle, u8 type,
+				   u8 block_num);
+
+int __DLL api_set_data_len(void *handle, u16 data_len);
+int __DLL api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			      u32 start, u32 end);
+int __DLL api_write_data_v6(void *handle, int wlen);
+int __DLL api_access_slave(void *handle, u8 id, u8 func, void *data);
+int __DLL api_check_busy(void *handle, int timeout_ms, int delay_ms);
+int __DLL api_write_enable_v3(void *handle, bool in_ap,
+			      bool write_ap, u32 end, u32 checksum);
+int __DLL api_write_data_v3(void *handle);
+
+int __DLL api_to_bl_mode(void *handle, bool bl, u32 start, u32 end);
+
+int __DLL api_write_data_m2v(void *handle, int wlen);
+int __DLL api_to_bl_mode_m2v(void *handle, bool to_bl);
+
+int __DLL api_set_idle(void *handle, bool enable);
+int __DLL api_set_func_mode(void *handle, u8 mode);
+int __DLL api_get_func_mode(void *handle);
+
+int __DLL api_erase_data_v3(void *handle);
+
+int __DLL api_read_flash(void *handle, u8 *buf,
+			 u32 start_addr, u32 len);
+
+int __DLL _api_read_mp_result(void *handle);
+int __DLL api_read_mp_result(void *handle);
+int __DLL _api_write_mp_result(void *handle, struct mp_station *mp);
+int __DLL api_write_mp_result(void *handle, struct mp_station *mp);
+void __DLL api_decode_mp_result(void *handle);
+
+int __DLL api_read_tuning(void *handle, u8 *buf, int rlen);
+
+int __DLL api_get_ic_crc(void *handle, u8 final_fw_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.c b/drivers/input/touchscreen/ilitek/ilitek_report.c
new file mode 100644
index 000000000000..dbe8abfd64ef
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_report.h"
+#include "ilitek_crypto.h"
+
+static bool is_debug_packet_id(u8 id, u8 _interface)
+{
+	return ((_interface == interface_i2c && id == 0xdb) ||
+		(_interface == interface_usb && id == 0xaa));
+}
+
+static bool is_pen_packet_id(u8 id)
+{
+	return (id == 0x0c || id == 0x0d);
+}
+
+static void touch_decode(struct ilitek_ts_device *dev,
+			 struct ilitek_report *report,
+			 u8 *buf, u8 cnt)
+{
+	struct touch_fmt *parser, *finger;
+	struct touch_iwb_fmt *parser_iwb;
+	u8 i, j, offset;
+	u32 size = dev->fmt.touch_size;
+
+	offset = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	for (i = 0; i < cnt && i < dev->tp_info.max_fingers; i++) {
+		finger = &report->touch.finger[i];
+
+		do {
+			if (dev->protocol.flag == PTL_V3 &&
+			    dev->_interface == interface_i2c) {
+				finger->id = i;
+				finger->status = buf[1 + offset + i * 5] >> 7;
+				finger->x = ((buf[1 + offset + i * 5] & 0x3F) << 8) +
+					buf[2 + offset + i * 5];
+				finger->y = ((buf[3 + offset + i * 5] & 0x3F) << 8) +
+					buf[4 + offset + i * 5];
+				finger->pressure = buf[5 + offset + i * 5];
+				finger->height = 128;
+				finger->width = 1;
+
+				break;
+			}
+
+			parser = (struct touch_fmt *)
+				(buf + 1 + i * size);
+			parser_iwb = (struct touch_iwb_fmt *)
+				(buf + 1 + i * size);
+
+			finger->id = parser->id;
+			finger->status = parser->status;
+			finger->x = parser->x;
+			finger->y = parser->y;
+
+			if (dev->setting.default_format_enabled)
+				break;
+
+			switch (dev->tp_info.format) {
+			case touch_fmt_0x1:
+				finger->pressure = parser->pressure;
+				break;
+			case touch_fmt_0x2:
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x3:
+				finger->pressure = parser->pressure;
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x4:
+				finger->id = parser_iwb->id;
+				finger->status =
+					(parser_iwb->status == 0x7) ? 1 : 0;
+				finger->x = parser_iwb->x;
+				finger->y = parser_iwb->y;
+				finger->width = parser_iwb->width;
+				finger->height = parser_iwb->height;
+				break;
+			case touch_fmt_0x10:
+				finger->width = le16(buf + i * size + 6);
+				finger->height = le16(buf + i * size + 8);
+				finger->algo = buf[i * size + 10];
+				report->touch.algo = 0;
+				break;
+			case touch_fmt_0x11:
+				finger->id =
+					(!finger->id) ? 0 : finger->id - 1;
+				break;
+			}
+		} while (false);
+
+		TP_DBG(dev->id,
+		       "[touch-report] id:%hhu, status:%hhu, x:%hu, y:%hu, p:%hhu, w:%hu, h:%hu, algo: 0x%hhx\n",
+			finger->id, finger->status, finger->x, finger->y,
+			finger->pressure, finger->width, finger->height,
+			finger->algo);
+
+		if (finger->id >= dev->tp_info.max_fingers) {
+			TP_ERR(dev->id, "invalid touch id: %hhu >= %hhu\n",
+				finger->id, dev->tp_info.max_fingers);
+			return;
+		}
+
+		/*
+		 * if x/y within key's range, skip touch range check.
+		 */
+		for (j = 0; j < dev->tp_info.key_num; j++) {
+			if ((finger->x < dev->key.info.keys[j].x &&
+			     finger->x > dev->key.info.keys[j].x +
+				     dev->key.info.x_len) &&
+			    (finger->y < dev->key.info.keys[j].y &&
+			     finger->y > dev->key.info.keys[j].y +
+				     dev->key.info.y_len))
+				continue;
+
+			goto skip_touch_range_check;
+		}
+
+		if (finger->status &&
+		    (finger->x - 1 > dev->screen_info.x_max ||
+		     finger->y - 1 > dev->screen_info.y_max ||
+		     finger->x < dev->screen_info.x_min ||
+		     finger->y < dev->screen_info.y_min)) {
+			TP_ERR(dev->id, "Point[%d]: (%d, %d), Limit: (%d:%d, %d:%d) OOB\n",
+				finger->id, finger->x, finger->y,
+				dev->screen_info.x_min, dev->screen_info.x_max,
+				dev->screen_info.y_min, dev->screen_info.y_max);
+			return;
+		}
+
+skip_touch_range_check:
+		continue;
+	}
+
+	report->touch.cnt = i;
+
+	/*
+	 * report touch event callback,
+	 * which includes actual count of finger report just parsed above.
+	 */
+	if (report->cb.report_touch_event)
+		report->cb.report_touch_event(&report->touch, report->_private);
+}
+
+static void pen_decode(struct ilitek_ts_device *dev,
+		       struct ilitek_report *report,
+		       u8 *buf)
+{
+	struct pen_fmt *parser = (struct pen_fmt *)(buf + 1);
+	struct pen_fmt *pen = &report->pen.pen;
+
+	report->pen.cnt = buf[61];
+	report->pen.algo = buf[62];
+
+	memcpy(pen, parser, sizeof(struct pen_fmt));
+
+	switch (dev->tp_info.pen_format) {
+	default:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery);
+		break;
+	case 1:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_1.barrel_pressure,
+			pen->usi_1.idx, pen->usi_1.color, pen->usi_1.width,
+			pen->usi_1.style);
+		break;
+	case 2:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color24: %u, no_color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_2.barrel_pressure,
+			pen->usi_2.idx, le32(pen->usi_2.color_24, 3),
+			pen->usi_2.no_color, pen->usi_2.width,
+			pen->usi_2.style);
+		break;
+	}
+
+	/* report pen event callback */
+	if (report->cb.report_pen_event)
+		report->cb.report_pen_event(&report->pen, report->_private);
+}
+
+static void dmsg_decode(struct ilitek_ts_device *dev,
+			struct ilitek_report *report,
+			u8 *buf, int buf_size)
+{
+	if ((int)buf[1] >= buf_size)
+		return;
+
+	buf[buf[1]] = '\0';
+	TP_DBG(dev->id, "%s\n", (char *)(buf + 2));
+
+	if (report->cb.report_dmsg)
+		report->cb.report_dmsg((char *)(buf + 2),
+					buf_size - 2, report->_private);
+}
+
+static void report_buf(struct ilitek_report *report, u8 *buf,
+		       bool is_last)
+{
+	if (!report->cb.report_buf)
+		return;
+
+	report->cb.report_buf(buf, 64, is_last, report->_private);
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v3(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+
+	u8 cnt;
+	int idx = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	UNUSED(buf_size);
+
+	if (dev->_interface == interface_i2c) {
+		if (dev->quirks & QUIRK_BRIDGE) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+
+			if (buf[0] != 0x03 || buf[1] != 0xa3 || buf[2] != 0x10)
+				return -EAGAIN;
+
+			/* move algo byte to index 62 */
+			buf[62] = buf[35];
+		} else if (dev->quirks & QUIRK_DAEMON_I2C ||
+			   dev->quirks & QUIRK_BRIDGE ||
+			   dev->quirks & QUIRK_WIFI_ITS_I2C) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+		} else {
+			dev->wbuf[0] = 0x10;
+			error = write_then_read(dev, dev->wbuf, 1, buf, 32);
+			if (error < 0)
+				return error;
+
+			report->touch.algo = buf[31];
+			buf[31] = 0;
+
+			if (buf[0] == 2) {
+				error = write_then_read(dev, NULL, 0,
+							 buf + 31, 20);
+				if (error < 0)
+					return error;
+			}
+
+			/* move algo byte to index 62 */
+			buf[62] = report->touch.algo;
+		}
+
+		switch (dev->rbuf[idx]) {
+		default:
+		case 1:
+			cnt = 6;
+			break;
+		case 0:
+			cnt = 0;
+			break;
+		case 2:
+			cnt = 10;
+			break;
+		}
+	} else {
+		error = read_interrupt_in(dev, buf, 64, 1000);
+		if (error < 0)
+			return error;
+
+		cnt = buf[55];
+
+		/* move algo byte to index 62 */
+		buf[62] = buf[56];
+	}
+
+	report->touch.algo = buf[62];
+
+	report->touch.dbg_size = 64;
+	memcpy(report->touch.dbg, buf, 64);
+
+	report_buf(report, buf, true);
+
+	return cnt;
+}
+
+static bool is_iwb_fmt(struct ilitek_ts_device *dev)
+{
+	return dev->tp_info.format == touch_fmt_0x4 &&
+		!dev->setting.default_format_enabled;
+}
+
+static bool need_skip_checksum(struct ilitek_ts_device *dev,
+			       struct ilitek_report *report,
+			       u8 packet_id, bool is_first)
+{
+	/*
+	 * don't check checksum for below situation:
+	 *   1. debug packet
+	 *   2. normal mode pen packet w/ HID.
+	 *   3. IWB format packet
+	 */
+
+	if (report->skip_checksum)
+		return true;
+
+	if (is_first) {
+		if (is_debug_packet_id(packet_id, dev->_interface))
+			return true;
+
+		if ((dev->_interface == interface_usb ||
+		     dev->_interface == interface_hid_over_i2c) &&
+		    packet_id == 0x0d)
+			return true;
+	}
+
+	if (is_iwb_fmt(dev))
+		return true;
+
+	return false;
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v6(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+	u8 i, cnt;
+	u8 size = dev->fmt.touch_size, max_cnt = dev->fmt.touch_max_cnt;
+	u8 tmp[64];
+
+	UNUSED(buf_size);
+
+	error = read_interrupt_in(dev, tmp, 64, 1000);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.format == 5)
+		ilitek_decrypt(tmp + 1, 48);
+
+	/* set packet id to 0x48 forcely for using BRIDGE finger report */
+	if (dev->quirks & QUIRK_BRIDGE && !is_pen_packet_id(tmp[0]))
+		tmp[0] = 0x48;
+
+	if (!need_skip_checksum(dev, report, tmp[0], true) &&
+	    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+		return -EILIPROTO;
+
+	report->pen.algo = tmp[62];
+	report->touch.algo = tmp[62];
+	report->pen.dbg_size = 64;
+	report->touch.dbg_size = 64;
+	memcpy(report->pen.dbg, tmp, 64);
+	memcpy(report->touch.dbg, tmp, 64);
+	memcpy(buf, tmp, 64);
+
+	/*
+	 * no need to check contact count byte for debug packet and pen packet.
+	 */
+	if (is_pen_packet_id(tmp[0]) ||
+	    is_debug_packet_id(tmp[0], dev->_interface)) {
+		report_buf(report, tmp, true);
+		return 0;
+	}
+
+	cnt = is_iwb_fmt(dev) ? tmp[55] : tmp[61];
+	for (i = 1; i < DIV_ROUND_UP(cnt, max_cnt); i++) {
+		report_buf(report, tmp, false);
+
+		error = read_interrupt_in(dev, tmp, 64, 1000);
+		if (error < 0)
+			return error;
+
+		if (dev->tp_info.format == 5)
+			ilitek_decrypt(tmp + 1, 48);
+
+		if (!need_skip_checksum(dev, report, tmp[0], false) &&
+		    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+			return -EILIPROTO;
+
+		/* copy and skip the first rid byte */
+		memcpy(buf + i * size * max_cnt + 1, tmp + 1, 63);
+	}
+	report_buf(report, tmp, true);
+
+	return cnt;
+}
+
+int ilitek_report_update(void *handle, struct ilitek_report *report)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt;
+
+	if (!dev)
+		return -EINVAL;
+
+	/* initial report event */
+	memset(&report->touch, 0, sizeof(report->touch));
+	memset(&report->pen, 0, sizeof(report->pen));
+
+	memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (dev->protocol.flag) {
+	default: return -EPERM;
+	case PTL_V3:
+		cnt = report_get_raw_v3(dev, report, dev->rbuf,
+					 sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		break;
+
+	case PTL_V6:
+		cnt = report_get_raw_v6(dev, report, dev->rbuf,
+					sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		/* pen packet (V6 only) */
+		if (is_pen_packet_id(dev->rbuf[0])) {
+			pen_decode(dev, report, dev->rbuf);
+			return 0;
+		}
+
+		break;
+	}
+
+	/* debug message packet */
+	if (is_debug_packet_id(dev->rbuf[0], dev->_interface)) {
+		dmsg_decode(dev, report, dev->rbuf, sizeof(dev->rbuf));
+		return 0;
+	}
+
+	touch_decode(dev, report, dev->rbuf, cnt);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.h b/drivers/input/touchscreen/ilitek/ilitek_report.h
new file mode 100644
index 000000000000..e49333f9618b
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_REPORT_H__
+#define __ILITEK_REPORT_H__
+
+#include "ilitek_def.h"
+#include "ilitek_protocol.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_data {
+	struct touch_fmt finger[40];
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+struct __PACKED__ pen_data {
+	struct pen_fmt pen;
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return touch event */
+typedef void(*report_touch_event_t)(struct touch_data *, void *);
+/* return pen event */
+typedef void(*report_pen_event_t)(struct pen_data *, void *);
+/* return debug msg */
+typedef void(*report_dmsg_t)(char *, int, void *);
+/* return raw data buf */
+typedef void(*report_buf_t)(u8 *, int, bool, void *);
+
+struct ilitek_report_callback {
+	report_touch_event_t report_touch_event;
+	report_pen_event_t report_pen_event;
+	report_dmsg_t report_dmsg;
+	report_buf_t report_buf;
+};
+
+struct ilitek_report {
+	struct touch_data touch;
+	struct pen_data pen;
+	struct ilitek_report_callback cb;
+
+	bool skip_checksum;
+
+	void *_private;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	int __DLL ilitek_report_update(void *handle, struct ilitek_report *report);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_tool.c b/drivers/input/touchscreen/ilitek/ilitek_tool.c
new file mode 100644
index 000000000000..689c1c09d397
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_tool.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+#include <linux/firmware.h>
+
+#include <linux/miscdevice.h>
+
+static struct proc_dir_entry *ilitek_proc;
+static struct proc_dir_entry *ilitek_proc_entry;
+
+#define ILITEK_IOCTL_BASE                       100
+#define ILITEK_IOCTL_I2C_WRITE_DATA             _IOWR(ILITEK_IOCTL_BASE, 0, uint64_t)
+#define ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT      _IOWR(ILITEK_IOCTL_BASE, 0, uint32_t)
+#define ILITEK_IOCTL_I2C_WRITE_LENGTH           _IOWR(ILITEK_IOCTL_BASE, 1, int32_t)
+#define ILITEK_IOCTL_I2C_READ_DATA              _IOWR(ILITEK_IOCTL_BASE, 2, uint64_t)
+#define ILITEK_IOCTL_I2C_READ_DATA_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 2, uint32_t)
+#define ILITEK_IOCTL_I2C_READ_LENGTH            _IOWR(ILITEK_IOCTL_BASE, 3, int32_t)
+#define ILITEK_IOCTL_DRIVER_INFORMATION		_IOWR(ILITEK_IOCTL_BASE, 8, int32_t)
+#define ILITEK_IOCTL_USB_UPDATE_RESOLUTION      _IOWR(ILITEK_IOCTL_BASE, 9, int32_t)
+#define ILITEK_IOCTL_I2C_INT_FLAG	        _IOWR(ILITEK_IOCTL_BASE, 10, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE                 _IOWR(ILITEK_IOCTL_BASE, 11, int32_t)
+#define ILITEK_IOCTL_STOP_READ_DATA             _IOWR(ILITEK_IOCTL_BASE, 12, int32_t)
+#define ILITEK_IOCTL_START_READ_DATA            _IOWR(ILITEK_IOCTL_BASE, 13, int32_t)
+#define ILITEK_IOCTL_I2C_SWITCH_IRQ		_IOWR(ILITEK_IOCTL_BASE, 15, int32_t)
+#define ILITEK_IOCTL_UPDATE_FLAG		_IOWR(ILITEK_IOCTL_BASE, 16, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE_FW		_IOWR(ILITEK_IOCTL_BASE, 18, int32_t)
+#define ILITEK_IOCTL_RESET			_IOWR(ILITEK_IOCTL_BASE, 19, int32_t)
+#define ILITEK_IOCTL_INT_STATUS			_IOWR(ILITEK_IOCTL_BASE, 20, int32_t)
+
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#define ILITEK_IOCTL_DEBUG_SWITCH		_IOWR(ILITEK_IOCTL_BASE, 21, int32_t)
+#endif
+
+#define ILITEK_IOCTL_I2C_INT_CLR		_IOWR(ILITEK_IOCTL_BASE, 22, int32_t)
+#define ILITEK_IOCTL_I2C_INT_POLL		_IOWR(ILITEK_IOCTL_BASE, 23, uint64_t)
+#define ILITEK_IOCTL_I2C_INT_POLL_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 23, uint32_t)
+#define ILITEK_IOCTL_I2C_ISR_TYPE		_IOWR(ILITEK_IOCTL_BASE, 24, uint32_t)
+#define ILITEK_IOCTL_I2C_NETLINK		_IOWR(ILITEK_IOCTL_BASE, 25, uint16_t)
+
+
+#define ILITEK_DEVICE_NODE_PERMISSON			0666
+
+static s32 ilitek_file_open(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = true;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static s32 ilitek_file_close(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = false;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static ssize_t ilitek_file_write(struct file *filp,
+				 const char *buf, size_t size, loff_t *f_pos)
+{
+	s32 ret = 0, count = 0;
+	u8 buffer[512];
+	u32 *data;
+	char *token = NULL, *cur = NULL;
+
+	if (size > sizeof(buffer)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(buffer));
+		return -EINVAL;
+	}
+
+	ret = copy_from_user(buffer, buf, size);
+	if (ret < 0) {
+		TP_ERR(NULL, "copy data from user space, failed");
+		return -1;
+	}
+
+	token = cur = buffer;
+
+	data = kcalloc(size, sizeof(u32), GFP_KERNEL);
+
+	while ((token = strsep(&cur, ",")) != NULL) {
+		//data[count] = str2hex(token);
+		sscanf(token, "%x", &data[count]);
+		TP_MSG(NULL, "data[%d] = %x\n", count, data[count]);
+		count++;
+	}
+
+	if (buffer[size - 2] == 'I' && (size == 20 || size == 52) && buffer[0] == 0x77 && buffer[1] == 0x77) {
+
+		TP_MSG(NULL, "IOCTL_WRITE CMD = %d\n", buffer[2]);
+		switch (buffer[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+#ifdef ILITEK_TUNING_MESSAGE
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", buffer[3]);
+			if (buffer[3] == 0) {
+				ilitek_debug_flag = false;
+			} else if (buffer[3] == 1) {
+				ilitek_debug_flag = true;
+			}
+			break;
+#endif
+		case 15:
+			if (buffer[3] == 0)
+				ilitek_irq_disable();
+			else
+				ilitek_irq_enable();
+
+			break;
+		case 16:
+			ts->operation_protection = buffer[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 18:
+			ilitek_irq_disable();
+			mutex_lock(&ts->ilitek_mutex);
+			ret = ilitek_write(&buffer[3], 33);
+			mutex_unlock(&ts->ilitek_mutex);
+			ilitek_irq_enable();
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+
+			return ret;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	if (buffer[size - 2] == 'W') {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write(buffer, size - 2);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			return ret;
+		}
+	} else if (!strncmp(buffer, "unhandle_irq", strlen("unhandle_irq"))) {
+		ts->unhandle_irq = !ts->unhandle_irq;
+		TP_MSG(NULL, "ts->unhandle_irq = %d.\n", ts->unhandle_irq);
+	} else if (!strncmp(buffer, "dbg_pkt", strlen("dbg_pkt"))) {
+		set_log_level(log_level_pkt);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_pkt);
+	} else if (!strncmp(buffer, "dbg_debug", strlen("dbg_debug"))) {
+		set_log_level(log_level_dbg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_dbg);
+	} else if (!strncmp(buffer, "dbg_info", strlen("dbg_info"))) {
+		set_log_level(log_level_msg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_msg);
+	} else if (!strncmp(buffer, "dbg_err", strlen("dbg_err"))) {
+		set_log_level(log_level_err);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_err);
+	} else if (!strncmp(buffer, "dbg_num", strlen("dbg_num"))) {
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", tp_log_level);
+	}
+#ifdef ILITEK_TUNING_MESSAGE
+	else if (!strncmp(buffer, "truning_dbg_flag", strlen("truning_dbg_flag"))) {
+		ilitek_debug_flag = !ilitek_debug_flag;
+		TP_MSG(NULL, " %s debug_flag message(%X).\n", ilitek_debug_flag ? "Enabled" : "Disabled", ilitek_debug_flag);
+	}
+#endif
+	else if (!strncmp(buffer, "irq_status", strlen("irq_status"))) {
+		TP_MSG(NULL, "gpio_get_value(i2c.irq_gpio) = %d.\n", gpio_get_value(ts->irq_gpio));
+	} else if (!strncmp(buffer, "enable", strlen("enable"))) {
+		ilitek_irq_enable();
+		TP_MSG(NULL, "irq enable\n");
+	} else if (!strncmp(buffer, "disable", strlen("disable"))) {
+		ilitek_irq_disable();
+		TP_MSG(NULL, "irq disable\n");
+	} else if (!strncmp(buffer, "info", strlen("info"))) {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		api_update_ts_info(ts->dev);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+	} else if (!strncmp(buffer, "reset", strlen("reset"))) {
+		ilitek_reset(ts->dev->reset_time);
+	}
+
+	TP_DBG(NULL, "ilitek return count = %zu\n", size);
+	kfree(data);
+	return size;
+}
+
+static FOPS_IOCTL_FUNC(ilitek_file_ioctl, uint32_t cmd, unsigned long arg)
+{
+	static u8 *buffer;
+	static unsigned long len;
+	s32 ret = 0;
+	int tmp;
+
+	buffer = kmalloc(ILITEK_IOCTL_MAX_TRANSFER, GFP_KERNEL);
+	memset(buffer, 0, ILITEK_IOCTL_MAX_TRANSFER);
+
+	switch (cmd) {
+	case ILITEK_IOCTL_I2C_WRITE_DATA:
+	case ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid write len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(buffer, (u8 *)arg, len)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, len, 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write failed, cmd: %x\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_I2C_READ_DATA:
+	case ILITEK_IOCTL_I2C_READ_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid read len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(NULL, 0, 0, buffer, len);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c read failed, buf: %x\n", buffer[0]);
+			break;
+		}
+
+		if (copy_to_user((u8 *)arg, buffer, len)) {
+			ret = -EFAULT;
+			TP_ERR(NULL, "copy data to user space, failed\n");
+		}
+		break;
+	case ILITEK_IOCTL_I2C_WRITE_LENGTH:
+	case ILITEK_IOCTL_I2C_READ_LENGTH:
+		len = arg;
+		break;
+	case ILITEK_IOCTL_DRIVER_INFORMATION:
+		memcpy(buffer, driver_ver, 7);
+		if (copy_to_user((u8 *)arg, buffer, 7))
+			ret = -EFAULT;
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE:
+		break;
+	case ILITEK_IOCTL_I2C_INT_FLAG:
+		buffer[0] = !(gpio_get_value(ts->irq_gpio));
+		if (copy_to_user((u8 *)arg, buffer, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_FLAG = %d.\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_START_READ_DATA:
+		ilitek_irq_enable();
+		ts->unhandle_irq = false;
+		TP_MSG(NULL, "enable_irq and ts->unhandle_irq = false.\n");
+		break;
+	case ILITEK_IOCTL_STOP_READ_DATA:
+		ilitek_irq_disable();
+		ts->unhandle_irq = true;
+		TP_MSG(NULL, "disable_irq and ts->unhandle_irq = true.\n");
+		break;
+	case ILITEK_IOCTL_RESET:
+		ilitek_reset(ts->dev->reset_time);
+		break;
+	case ILITEK_IOCTL_INT_STATUS:
+		if (put_user(gpio_get_value(ts->irq_gpio), (s32 *)arg))
+			ret = -EFAULT;
+		break;
+#ifdef ILITEK_TUNING_MESSAGE
+	case ILITEK_IOCTL_DEBUG_SWITCH:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+		TP_MSG(NULL, "ilitek The debug_flag = %d.\n", buffer[0]);
+		if (buffer[0] == 0)
+			ilitek_debug_flag = false;
+		else if (buffer[0] == 1)
+			ilitek_debug_flag = true;
+		break;
+#endif
+	case ILITEK_IOCTL_I2C_SWITCH_IRQ:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		if (buffer[0] == 0)
+			ilitek_irq_disable();
+		else
+			ilitek_irq_enable();
+
+		break;
+	case ILITEK_IOCTL_UPDATE_FLAG:
+		ts->operation_protection = arg;
+		TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE_FW:
+		if (copy_from_user(buffer, (u8 *)arg, 35)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, buffer[34], 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write, failed\n");
+
+		break;
+	case ILITEK_IOCTL_I2C_INT_CLR:
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_CLR, set get_INT false\n");
+		atomic_set(&ts->get_INT, 0);
+		break;
+	case ILITEK_IOCTL_I2C_INT_POLL:
+	case ILITEK_IOCTL_I2C_INT_POLL_COMPAT:
+		tmp = atomic_read(&ts->get_INT);
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_POLL, get_INT: %d\n", tmp);
+
+		if (copy_to_user((u8 *)arg, &tmp, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+		}
+		break;
+	case ILITEK_IOCTL_I2C_ISR_TYPE:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_ISR_TYPE, set ISR type: %lu\n", arg);
+		ts->irq_handle_type = (arg >> 16);
+		ts->irq_read_len = arg & 0xFFFF;
+		break;
+	case ILITEK_IOCTL_I2C_NETLINK:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_NETLINK, set netlink: %s with ETH: %hhu\n",
+			(arg >> 8) ? "ON" : "OFF", (u8)(arg & 0xFF));
+
+		if (arg >> 8)
+			ret = ilitek_netlink_init(arg & 0xFF);
+		else
+			ilitek_netlink_exit();
+
+		break;
+	default:
+		TP_ERR(NULL, "unrecognized ioctl cmd: 0x%04x\n", cmd);
+		ret = -EINVAL;
+		break;
+	}
+
+	kfree(buffer);
+	return (ret < 0) ? ret : 0;
+}
+
+static ssize_t ilitek_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	u8 *tmp;
+	s32 ret;
+
+	if (count > 8192)
+		count = 8192;
+
+	tmp = kmalloc(count, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = ilitek_read(tmp, count);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+	if (ret < 0) {
+		TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		goto err_free;
+	}
+
+	if (copy_to_user(buf, tmp, count)) {
+		ret = -EFAULT;
+		goto err_free;
+	}
+
+err_free:
+	kfree(tmp);
+
+	return ret > 0 ? count : ret;
+}
+
+/* compat ioctl for 32/64 bit user program compatibility */
+#ifdef CONFIG_COMPAT
+static long ilitek_file_compat_ioctl(struct file *fp, uint32_t cmd,
+				     unsigned long arg)
+{
+	return ilitek_file_ioctl(fp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+
+static struct file_operations ilitek_fops = {
+	.FOPS_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = ilitek_file_compat_ioctl,
+#endif
+
+	.read = ilitek_file_read,
+	.write = ilitek_file_write,
+	.open = ilitek_file_open,
+	.release = ilitek_file_close,
+};
+
+static struct PROC_FOPS_T ilitek_proc_fops = {
+	.PROC_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.PROC_COMPAT_IOCTL = ilitek_file_compat_ioctl,
+#endif
+
+	.PROC_READ = ilitek_file_read,
+	.PROC_WRITE = ilitek_file_write,
+	.PROC_OPEN = ilitek_file_open,
+	.PROC_RELEASE = ilitek_file_close,
+};
+
+static ssize_t ilitek_driver_version_read(struct file *fp, char __user *buf,
+					  size_t size, loff_t *off)
+{
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	cnt = scnprintf(str, sizeof(str), "driver-version-tag: [%*phD]\n",
+			7, driver_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_drv_version = {
+	.PROC_READ = ilitek_driver_version_read,
+};
+
+static ssize_t ilitek_update_fw_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int error;
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.bin");
+		if (error < 0)
+			cnt += scnprintf(str, sizeof(str),
+					 "upgrade failed, err: %d\n", error);
+	} else {
+		cnt += scnprintf(str, sizeof(str),
+				 "upgrade success, fw version: %*phD\n",
+				 8, ts->dev->fw_ver);
+	}
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwupdate = {
+	.PROC_READ = ilitek_update_fw_read,
+};
+
+static ssize_t ilitek_firmware_version_read(struct file *fp, char __user *buf,
+					    size_t size, loff_t *off)
+{
+	int error;
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (error < 0)
+		cnt = scnprintf(str, sizeof(str), "read failed, err: %d\n",
+				error);
+	else
+		cnt = scnprintf(str, sizeof(str), "fw-version-tag: [%*phD]\n",
+				8, ts->dev->fw_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwversion = {
+	.PROC_READ = ilitek_firmware_version_read,
+};
+
+static ssize_t ilitek_console_write(struct file *filp, const char *buf,
+				    size_t size, loff_t *f_pos)
+{
+	int error;
+	char tmp[256], str[256];
+	char *ptr, *cur = str;
+	size_t wlen, rlen, _wlen = 0;
+	u8 cmd[64], data[64];
+
+	if (size > sizeof(tmp)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(tmp));
+		return -EINVAL;
+	}
+
+	error = copy_from_user(tmp, buf, size);
+	if (error < 0) {
+		TP_ERR(NULL, "copy_from_user failed, err: %d", error);
+		return error;
+	}
+
+	memset(str, 0, sizeof(str));
+	sscanf(tmp, "%zu:%zu:%s", &wlen, &rlen, str);
+	TP_MSG(NULL, "wlen: %zu, rlen: %zu, command: %s\n", wlen, rlen, str);
+
+	while ((ptr = strsep(&cur, "-")) && _wlen < sizeof(cmd))
+		sscanf(ptr, "%hhx", &cmd[_wlen++]);
+
+	if (wlen > 0 && wlen != _wlen) {
+		TP_ERR(NULL, "write cmd length: %zu not match with %s\n",
+			wlen, str);
+		return -EINVAL;
+	} else if (rlen > sizeof(data)) {
+		TP_ERR(NULL, "invalid read cmd length: %zu > %zu too large\n",
+			rlen, sizeof(data));
+		return -EINVAL;
+	}
+
+	if (wlen > 0)
+		TP_MSG(NULL, "[write]: %*phD, len: %zu\n",
+			(int)wlen, cmd, wlen);
+
+	error = ilitek_write_and_read(cmd, wlen, 1, data, rlen);
+	if (error < 0)
+		return error;
+
+	if (rlen > 0)
+		TP_MSG(NULL, "[read]: %*phD, len: %zu\n",
+			(int)rlen, data, rlen);
+
+	return size;
+}
+
+static struct PROC_FOPS_T ilitek_fops_console = {
+	.PROC_WRITE = ilitek_console_write,
+};
+
+static ssize_t ilitek_func_mode_write(struct file *fp, const char *buf,
+				      size_t size, loff_t *off)
+{
+	int error, cnt;
+	char str[64];
+	u8 func_mode;
+
+	cnt = MIN(size, sizeof(str));
+	memset(str, 0, sizeof(str));
+	if (copy_from_user(str, buf, cnt))
+		return -EFAULT;
+
+	sscanf(str, "%hhu", &func_mode);
+	TP_MSG(NULL, "set func mode: %hhu, support max: %hhu modes\n",
+		func_mode, ts->dev->tp_info.support_modes);
+
+	if (func_mode >= ts->dev->tp_info.support_modes)
+		return -EINVAL;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	error |= api_set_func_mode(ts->dev, func_mode);
+	error |= api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return (error < 0) ? error : cnt;
+}
+
+static ssize_t ilitek_func_mode_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 i;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_protocol_set_cmd(ts->dev, GET_TP_INFO, NULL);
+	api_get_func_mode(ts->dev);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "function mode: ");
+	for (i = 0; i < ts->dev->tp_info.support_modes; i++) {
+		if (i == ts->dev->func.mode)
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "[%hhu] ", i);
+		else
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "%hhu ", i);
+	}
+	cnt += scnprintf(str + strlen(str), sizeof(str) - cnt, "\n");
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_func_mode = {
+	.PROC_READ = ilitek_func_mode_read,
+	.PROC_WRITE = ilitek_func_mode_write,
+};
+
+static ssize_t ilitek_crypto_key_write(struct file *fp, const char *buf,
+				       size_t size, loff_t *off)
+{
+	u8 crypto[AES_KEY_LEN * 2];
+
+	if (size != sizeof(crypto) ||
+	    copy_from_user(crypto, buf, sizeof(crypto)))
+		return -EFAULT;
+
+	TP_MSG(NULL, "set crypto_key: %*phD\n", AES_KEY_LEN, crypto);
+	TP_MSG(NULL, "set crypto_iv: %*phD\n", AES_KEY_LEN,
+		crypto + AES_KEY_LEN);
+
+	memcpy(crypto_key, crypto, AES_KEY_LEN);
+	memcpy(crypto_iv, crypto + AES_KEY_LEN, AES_KEY_LEN);
+
+	return sizeof(crypto);
+}
+
+static ssize_t ilitek_crypto_key_read(struct file *fp, char __user *buf,
+				      size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "key: %*phD\n",
+			 AES_KEY_LEN, crypto_key);
+	cnt += scnprintf(str + strlen(str), sizeof(str), "iv: %*phD\n",
+			 AES_KEY_LEN, crypto_iv);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_crypto_key = {
+	.PROC_READ = ilitek_crypto_key_read,
+	.PROC_WRITE = ilitek_crypto_key_write,
+};
+
+static ssize_t ilitek_setmode_0_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 0);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_0 = {
+	.PROC_READ = ilitek_setmode_0_read,
+};
+
+static ssize_t ilitek_setmode_1_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 1);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_1 = {
+	.PROC_READ = ilitek_setmode_1_read,
+};
+
+static ssize_t ilitek_setmode_2_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 2);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_2 = {
+	.PROC_READ = ilitek_setmode_2_read,
+};
+
+static ssize_t ilitek_driver_version_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "driver-version-tag: [%*phD]\n",
+			 7, driver_ver);
+}
+
+static DEVICE_ATTR(driver_version, 0664, ilitek_driver_version_show, NULL);
+
+static ssize_t ilitek_eds_check_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	if (ts->esd_check)
+		return scnprintf(buf, PAGE_SIZE, "[enable] disable\n");
+
+	return scnprintf(buf, PAGE_SIZE, "enable [disable]\n");
+}
+
+static ssize_t ilitek_esd_check_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	TP_MSG(NULL, "set esd check: %hhx to %s\n", ts->esd_check, buf);
+
+	if (!strncmp(buf, "enable", 6) && !ts->esd_check) {
+		ilitek_create_esd_check_workqueue();
+		ts->esd_check = true;
+	} else if (!strncmp(buf, "disable", 7) && ts->esd_check) {
+		ilitek_remove_esd_check_workqueue();
+		ts->esd_check = false;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(esd_check, 0664,
+		   ilitek_eds_check_show,
+		   ilitek_esd_check_store);
+
+
+static ssize_t ilitek_low_power_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->low_power_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_low_power_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	int i;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set low power: %hhx to %s\n", ts->low_power_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		ts->low_power_status = modes[i].id;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(low_power, 0664,
+		   ilitek_low_power_show,
+		   ilitek_low_power_store);
+
+static ssize_t ilitek_gesture_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->gesture_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_gesture_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int i;
+	u8 type = ts->gesture_status;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set gesture: %hhx to %s\n", ts->gesture_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		type = modes[i].id;
+	}
+
+	if (!ts->gesture_status && type)
+		ilitek_register_gesture(ts, true);
+	else if (ts->gesture_status && !type)
+		ilitek_register_gesture(ts, false);
+
+	ts->gesture_status = type;
+
+	return size;
+}
+static DEVICE_ATTR(gesture, 0664, ilitek_gesture_show, ilitek_gesture_store);
+
+static ssize_t ilitek_firmware_version_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	int ret;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (ret < 0) {
+		TP_ERR(NULL, "read tp info failed, err: %d\n", ret);
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n", ret);
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "fw-version-tag: [%*phD]\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(firmware_version, 0664, ilitek_firmware_version_show, NULL);
+
+static ssize_t ilitek_module_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_protocol_set_cmd(ts->dev, GET_MCU_VER, NULL);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	if (error < 0)
+		TP_ERR(NULL, "read mcu ver. failed, err: %d\n", error);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+out:
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE, "module-name-tag: [%s]\n",
+			 ts->dev->mcu_info.module_name);
+}
+
+static DEVICE_ATTR(product_id, 0664, ilitek_module_name_show, NULL);
+
+static ssize_t ilitek_update_fw_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0)
+		error = ilitek_upgrade_firmware("ilitek.bin");
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "upgrade failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE,
+			 "upgrade success, current fw version: %*phD\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(update_fw, 0664, ilitek_update_fw_show, NULL);
+
+static struct attribute *ilitek_sysfs_attrs_ctrl[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_gesture.attr,
+	&dev_attr_low_power.attr,
+	&dev_attr_esd_check.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static struct attribute_group ilitek_attribute_group[] = {
+	{ .attrs = ilitek_sysfs_attrs_ctrl },
+};
+
+int ilitek_create_sysfsnode(void)
+{
+	int error;
+
+	error = sysfs_create_group(&ts->device->kobj, ilitek_attribute_group);
+	if (error < 0) {
+		TP_ERR(NULL, "sysfs_create_group failed, err: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+void ilitek_remove_sys_node(void)
+{
+	sysfs_remove_group(&ts->device->kobj, ilitek_attribute_group);
+}
+
+static struct miscdevice ilitek_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ilitek_ctrl",
+	.fops = &ilitek_fops,
+	.mode = 0666,
+};
+
+int ilitek_create_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+	misc_register(&ilitek_misc);
+
+	ilitek_proc = proc_create("ilitek_ctrl", ILITEK_DEVICE_NODE_PERMISSON,
+				  NULL, &ilitek_proc_fops);
+
+	if (!ilitek_proc)
+		TP_ERR(NULL, "proc_create(ilitek_ctrl, ILITEK_DEVICE_NODE_PERMISSON, NULL, &ilitek_fops) fail\n");
+
+	ilitek_proc_entry = proc_mkdir("ilitek", NULL);
+	if (!ilitek_proc_entry) {
+		TP_ERR(NULL, "Error, failed to creat procfs.\n");
+		return -EINVAL;
+	}
+
+	if (!proc_create("driver_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_drv_version))
+		TP_ERR(NULL, "failed to create procfs driver_version.\n");
+
+	if (!proc_create("firmware_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwversion))
+		TP_ERR(NULL, "failed to create procfs firmware_version.\n");
+
+	if (!proc_create("console", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_console))
+		TP_ERR(NULL, "failed to create procfs console.\n");
+
+	if (!proc_create("update_fw", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwupdate))
+		TP_ERR(NULL, "failed to create procfs update_fw.\n");
+
+	if (!proc_create("func_mode", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_func_mode))
+		TP_ERR(NULL, "failed to create procfs func_mode.\n");
+
+	if (!proc_create("crypto_key", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_crypto_key))
+		TP_ERR(NULL, "failed to create procfs crypto_key.\n");
+
+	/*
+	 * below setmode_X is historical setting for some customer need
+	 * to set function mode by cat procfs node.
+	 * please make sure it's not risky to modifiy below.
+	 */
+	if (!proc_create("setmode_0", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_0))
+		TP_ERR(NULL, "failed to create procfs setmode_0.\n");
+	if (!proc_create("setmode_1", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_1))
+		TP_ERR(NULL, "failed to create procfs setmode_1.\n");
+	if (!proc_create("setmode_2", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_2))
+		TP_ERR(NULL, "failed to create procfs setmode_2.\n");
+#endif
+
+	return 0;
+}
+
+int ilitek_remove_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+
+	misc_deregister(&ilitek_misc);
+
+	if (ilitek_proc) {
+		TP_MSG(NULL, "remove procfs ilitek_ctrl.\n");
+		remove_proc_entry("ilitek_ctrl", NULL);
+		ilitek_proc = NULL;
+	}
+
+	if (ilitek_proc_entry) {
+		TP_MSG(NULL, "remove procfs inode\n");
+		remove_proc_entry("driver_version", ilitek_proc_entry);
+		remove_proc_entry("firmware_version", ilitek_proc_entry);
+		remove_proc_entry("console", ilitek_proc_entry);
+		remove_proc_entry("update_fw", ilitek_proc_entry);
+		remove_proc_entry("func_mode", ilitek_proc_entry);
+		remove_proc_entry("crypto_key", ilitek_proc_entry);
+
+		remove_proc_entry("setmode_0", ilitek_proc_entry);
+		remove_proc_entry("setmode_1", ilitek_proc_entry);
+		remove_proc_entry("setmode_2", ilitek_proc_entry);
+
+		remove_proc_entry("ilitek", NULL);
+		ilitek_proc_entry = NULL;
+	}
+#endif
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_ts.h b/drivers/input/touchscreen/ilitek/ilitek_ts.h
new file mode 100644
index 000000000000..9ee0b13e08cd
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_ts.h
@@ -0,0 +1,268 @@
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ *
+ */
+
+#ifndef __ILITEK_TS_H__
+#define __ILITEK_TS_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include <linux/init.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+//#if defined(CONFIG_FB)
+#if 0
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#define ILITEK_BLANK_POWERDOWN			FB_BLANK_POWERDOWN
+#define	ILITEK_BLANK_UNBLANK			FB_BLANK_UNBLANK
+#define	ILITEK_EVENT_BLANK			FB_EVENT_BLANK
+#define	ILITEK_BLANK_NORMAL			FB_BLANK_NORMAL
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#define ILITEK_BL_ADDR				0x41
+#define ILITEK_SENSOR_ID_MASK			0xff
+
+#define ILITEK_PLAT_QCOM			1
+#define ILITEK_PLAT_MTK				2
+#define ILITEK_PLAT_ROCKCHIP			3
+#define ILITEK_PLAT_ALLWIN			4
+#define ILITEK_PLAT_AMLOGIC			5
+#define ILITEK_PLAT				ILITEK_PLAT_ROCKCHIP
+//#define CONFIG_QCOM_DRM
+#if ILITEK_PLAT == ILITEK_PLAT_QCOM
+#ifdef CONFIG_QCOM_DRM
+#include <linux/msm_drm_notify.h>
+#define ILITEK_BLANK_POWERDOWN			MSM_DRM_BLANK_POWERDOWN
+#define ILITEK_BLANK_UNBLANK			MSM_DRM_BLANK_UNBLANK
+#define ILITEK_EVENT_BLANK			MSM_DRM_EVENT_BLANK
+#define ILITEK_BLANK_NORMAL			MSM_DRM_BLANK_UNBLANK
+#endif
+#endif
+
+#define ILITEK_GESTURE_TYPES			\
+	X(Disable, 0, "disable")		\
+	X(Single_Click, 1, "single-click")	\
+	X(Double_Click, 2, "double-click")
+#define ILITEK_GESTURE_DEFAULT			Gesture_Disable
+
+#define X(_type, _id, _str)	Gesture_##_type = _id,
+enum Gesture_Type {
+	ILITEK_GESTURE_TYPES
+};
+#undef X
+
+#define ILITEK_LOW_POWER_TYPES		\
+	X(Sleep,	0, "sleep")	\
+	X(Idle,		1, "idle")	\
+	X(PowerOff,	2, "poweroff")
+#define ILITEK_LOW_POWER_DEFAULT		Low_Power_Sleep
+
+#define X(_type, _id, _str)	Low_Power_##_type = _id,
+enum Low_Power_Type {
+	ILITEK_LOW_POWER_TYPES
+};
+#undef X
+
+#define ILITEK_CHECKSUM_FAILED_RELEASE		true
+
+#define ILITEK_TOUCH_PROTOCOL_B
+//#define ILITEK_REPORT_PRESSURE
+//#define ILITEK_USE_LCM_RESOLUTION
+//#define ILITEK_ISR_PROTECT
+
+#define ILITEK_TUNING_MESSAGE
+#define ILITEK_REGISTER_SUSPEND_RESUME
+#define ILITEK_ESD_CHECK_ENABLE			0
+
+#define ILITEK_TOOL
+
+#define ILITEK_ROTATE_FLAG			0
+#define ILITEK_REVERT_X				0
+#define ILITEK_REVERT_Y				0
+#define TOUCH_SCREEN_X_MAX			1080	//LCD_WIDTH
+#define TOUCH_SCREEN_Y_MAX			1920	//LCD_HEIGHT
+#define ILITEK_RESOLUTION_MAX			16384
+//#define ILITEK_ENABLE_REGULATOR_POWER_ON
+#define ILITEK_GET_GPIO_NUM
+
+#define ILITEK_GET_TIME_FUNC_WITH_TIME		0
+#define ILITEK_GET_TIME_FUNC_WITH_JIFFIES	1
+#define ILITEK_GET_TIME_FUNC			ILITEK_GET_TIME_FUNC_WITH_JIFFIES
+
+#define DOUBLE_CLICK_DISTANCE			1000
+#define DOUBLE_CLICK_ONE_CLICK_USED_TIME	800
+#define DOUBLE_CLICK_NO_TOUCH_TIME		1000
+#define DOUBLE_CLICK_TOTAL_USED_TIME		(DOUBLE_CLICK_NO_TOUCH_TIME + (DOUBLE_CLICK_ONE_CLICK_USED_TIME * 2))
+
+//#define ILITEK_WAKELOCK_SUPPORT
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+#include <linux/wakelock.h>
+#endif
+
+#define ILITEK_TS_NAME				"ilitek_ts"
+
+#define ABSSUB(a, b) ((a > b) ? (a - b) : (b - a))
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+//#define MTK_UNDTS //no use dts and for mtk old version
+
+#define ILITEK_ENABLE_DMA
+#define ILITEK_DMA_SIZE				4096
+#define ILITEK_USE_MTK_INPUT_DEV
+
+#if define(ILITEK_USE_MTK_INPUT_DEV) && !defined(ILITEK_USE_LCM_RESOLUTION)
+#define ILITEK_USE_LCM_RESOLUTION
+#endif
+
+#ifdef ILITEK_GET_GPIO_NUM
+#undef ILITEK_GET_GPIO_NUM
+#endif
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include <linux/namei.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#ifdef MTK_UNDTS
+
+#define TPD_KEY_COUNT   4
+#define key_1           {60, 17000}	//auto define
+#define key_2           {180, 17000}
+#define key_3           {300, 17000}
+#define key_4           {420, 17000}
+
+#define TPD_KEYS        {KEY_MENU, KEY_HOMEPAGE, KEY_BACK, KEY_SEARCH}	//change for you panel key info
+#define TPD_KEYS_DIM    {{key_1, 50, 30 }, {key_2, 50, 30 }, {key_3, 50, 30 }, {key_4, 50, 30 } }
+
+struct touch_vitual_key_map_t {
+	int point_x;
+	int point_y;
+};
+
+extern struct touch_vitual_key_map_t touch_key_point_maping_array[];
+
+#include <mach/mt_pm_ldo.h>
+#include <cust_eint.h>
+#include "cust_gpio_usage.h"
+#include <mach/mt_gpio.h>
+#include <mach/mt_typedefs.h>
+#include <pmic_drv.h>
+#include <mach/mt_boot.h>
+#include <linux/dma-mapping.h>
+
+#else
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_MTK_BOOT
+#include "mt_boot_common.h"
+#endif
+
+#endif /* MTK_UNDTS */
+
+#include "tpd.h"
+extern struct tpd_device *tpd;
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#include <linux/irq.h>
+#include <linux/init-input.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+extern struct ctp_config_info config_info;
+#endif
+
+
+#ifndef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef MTK_UNDTS
+#define ILITEK_IRQ_GPIO				GPIO_CTP_EINT_PIN
+#define ILITEK_RESET_GPIO			GPIO_CTP_RST_PIN
+#else
+#define ILITEK_IRQ_GPIO				GTP_INT_PORT
+#define ILITEK_RESET_GPIO			GTP_RST_PORT
+#endif
+#elif ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#define ILITEK_IRQ_GPIO				config_info.int_number
+#define ILITEK_RESET_GPIO			config_info.wakeup_gpio.gpio
+#else
+#define ILITEK_IRQ_GPIO				9
+#define ILITEK_RESET_GPIO			10
+#endif
+#endif
+
+#define ILITEK_I2C_RETRY_COUNT			3
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+extern int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg);
+extern int ilitek_resume_allwin(struct i2c_client *client);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.c b/drivers/input/touchscreen/ilitek/ilitek_update.c
new file mode 100644
index 000000000000..d7e873a3c204
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_update.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifndef __KERNEL__
+static int hex_to_bin(u8 ch)
+{
+	u8 cu = ch & 0xdf;
+	return -1 +
+		((ch - '0' +  1) & (unsigned)((ch - '9' - 1) &
+		('0' - 1 - ch)) >> 8) +
+		((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) &
+		('A' - 1 - cu)) >> 8);
+}
+
+static int hex2bin(u8 *dst, const u8 *src, size_t count)
+{
+	int hi = 0, lo = 0;
+
+	while (count--) {
+		hi = hex_to_bin(*src++);
+		lo = hex_to_bin(*src++);
+		if (hi < 0 || lo < 0) {
+			TP_ERR(NULL, "hex_to_bin failed, hi: %d, lo: %d\n",
+				hi, lo);
+			return -EINVAL;
+		}
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
+#endif
+
+static u32 get_tag_addr(u32 start, u32 end,
+			const u8 *buf, unsigned int buf_size,
+			const u8 *tag, unsigned int tag_size)
+{
+	unsigned int i;
+
+	for (i = start; i <= end - tag_size && i < buf_size - tag_size; i++) {
+		if (!memcmp(buf + i, tag, tag_size))
+			return i + tag_size + 1;
+	}
+
+	return end;
+}
+
+static u32 get_endaddr(u32 start, u32 end, const u8 *buf,
+		       unsigned int buf_size, bool is_AP)
+{
+	u32 addr;
+	u8 tag[32];
+	const u8 ap_tag[] = "ILITek AP CRC   ";
+	const u8 blk_tag[] = "ILITek END TAG  ";
+
+	_memset(tag, 0xFF, sizeof(tag));
+	_memcpy(tag + 16, (is_AP) ? ap_tag : blk_tag, 16);
+
+	addr = get_tag_addr(start, end, buf, buf_size, tag, sizeof(tag));
+	TP_DBG(NULL, "find tag in start/end: 0x%x/0x%x, tag addr: 0x%x\n",
+		start, end, addr);
+
+	return addr;
+}
+
+static int decode_mm(struct ilitek_fw_handle *fw, u32 addr,
+		     u8 *buf, u32 buf_size)
+{
+	u8 i;
+	union mapping_info *mapping;
+
+	TP_INFO(NULL, "------------Memory Mapping information------------\n");
+	TP_INFO(NULL, "memory-mapping-info addr: 0x%x\n", addr);
+
+	mapping = (union mapping_info *)(buf + addr);
+	_memset(fw->file.ic_name, 0, sizeof(fw->file.ic_name));
+
+	switch (mapping->mapping_ver[2]) {
+	case 0x2:
+		_memcpy(fw->file.ic_name, mapping->ic_name,
+			sizeof(mapping->ic_name));
+		break;
+	default:
+	case 0x1:
+		_sprintf(fw->file.ic_name, 0, "%02x%02x",
+			mapping->ic_name[1], mapping->ic_name[0]);
+		break;
+	}
+
+	if (!strcmp(fw->file.ic_name, "2133"))
+		_sprintf(fw->file.ic_name, 0, "2132S");
+
+	if (fw->dev && strcmp(fw->dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " not matched\n",
+			fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EINVAL;
+	}
+
+	TP_MSG(NULL, "Hex Mapping Ver.: 0x%x\n",
+		le32(mapping->mapping_ver, 3));
+	TP_MSG(NULL, "Hex Protocol: 0x%x\n",
+		le32(mapping->protocol_ver, 3));
+	TP_MSG(NULL, "Hex MCU Ver.: " PFMT_C8 "\n", fw->file.ic_name);
+
+	_memset(fw->file.fw_ver, 0, sizeof(fw->file.fw_ver));
+	fw->file.fwid = 0xffff;
+
+	fw->file.mm_addr = addr;
+	switch (addr) {
+	case 0x4038:
+	case 0x4020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x2C007];
+		fw->file.fw_ver[5] = buf[0x2C006];
+		fw->file.fw_ver[6] = buf[0x2C005];
+		fw->file.fw_ver[7] = buf[0x2C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x3020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x3C007];
+		fw->file.fw_ver[5] = buf[0x3C006];
+		fw->file.fw_ver[6] = buf[0x3C005];
+		fw->file.fw_ver[7] = buf[0x3C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x2020:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x2033];
+		fw->file.fw_ver[1] = buf[0x2032];
+		fw->file.fw_ver[2] = buf[0x2031];
+		fw->file.fw_ver[3] = buf[0x2030];
+		fw->file.fw_ver[4] = buf[0xF004];
+		fw->file.fw_ver[5] = buf[0xF005];
+		fw->file.fw_ver[6] = buf[0xF006];
+		fw->file.fw_ver[7] = buf[0xF007];
+
+		/* for V3 251x IC, get AP crc and DF checksum */
+		fw->file.blocks[0].check = get_crc(fw->file.blocks[0].start,
+			fw->file.blocks[0].end - 1, buf, buf_size);
+
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+
+	case 0x500:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x52D];
+		fw->file.fw_ver[1] = buf[0x52C];
+		fw->file.fw_ver[2] = buf[0x52B];
+		fw->file.fw_ver[3] = buf[0x52A];
+		fw->file.fw_ver[4] = buf[0x1F404];
+		fw->file.fw_ver[5] = buf[0x1F405];
+		fw->file.fw_ver[6] = buf[0x1F406];
+		fw->file.fw_ver[7] = buf[0x1F407];
+
+		/* for V3 231x IC, get AP checksum and DF checksum */
+		fw->file.blocks[0].check = get_checksum(
+			fw->file.blocks[0].start,
+			fw->file.blocks[0].end + 1, buf, buf_size);
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+	default:
+		fw->file.mm_size = 0;
+		break;
+	}
+
+	TP_MSG(NULL, "file fwid: 0x%04x\n", fw->file.fwid);
+
+	TP_INFO(NULL, "File FW Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[0], fw->file.fw_ver[1],
+		fw->file.fw_ver[2], fw->file.fw_ver[3]);
+	TP_INFO(NULL, "File Customer Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[4], fw->file.fw_ver[5],
+		fw->file.fw_ver[6], fw->file.fw_ver[7]);
+
+	if (le32(mapping->mapping_ver, 3) < 0x10000)
+		goto memory_mapping_end;
+
+	TP_INFO(NULL, "File Tuning Version: %02x-%02x-%02x-%02x\n",
+		mapping->_lego.tuning_ver[3], mapping->_lego.tuning_ver[2],
+		mapping->_lego.tuning_ver[1], mapping->_lego.tuning_ver[0]);
+
+	if (mapping->_lego.block_num > ARRAY_SIZE(fw->file.blocks)) {
+		TP_ERR(NULL, "Unexpected block num: " PFMT_U8 " > %u\n",
+			mapping->_lego.block_num,
+			(unsigned int)ARRAY_SIZE(fw->file.blocks));
+		goto memory_mapping_end;
+	}
+
+	fw->file.block_num = mapping->_lego.block_num;
+
+	TP_MSG(NULL, "Total " PFMT_U8 " blocks\n", fw->file.block_num);
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].start =
+			le32(mapping->_lego.blocks[i].addr, 3);
+		fw->file.blocks[i].end = (i == fw->file.block_num - 1) ?
+			le32(mapping->_lego.end_addr, 3) :
+			le32(mapping->_lego.blocks[i + 1].addr, 3);
+
+		/*
+		 * get end addr. of block,
+		 * i.e. address of block's final byte of crc.
+		 */
+		fw->file.blocks[i].end = get_endaddr(
+			fw->file.blocks[i].start, fw->file.blocks[i].end,
+			buf, buf_size, i == 0);
+
+		fw->file.blocks[i].check = get_crc(fw->file.blocks[i].start,
+			fw->file.blocks[i].end - 1,
+			buf, buf_size);
+
+		TP_MSG(NULL, "Block[%u], start:0x%x end:0x%x, crc:0x%x\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			fw->file.blocks[i].check);
+	}
+
+memory_mapping_end:
+	TP_INFO(NULL, "--------------------------------------------------\n");
+
+	return 0;
+}
+
+static int decode_hex(struct ilitek_fw_handle *fw, u8 *hex,
+		      u32 start, u32 end,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	u8 info[4], data[16];
+	unsigned int i, len, addr, type, exaddr = 0;
+	u32 mapping_info_addr = 0;
+
+	/* m2v hex has another block at the end of hex file */
+	u8 j = (fw->m2v) ? fw->file.block_num : 0;
+
+	fw->file.blocks[j].start = (~0U);
+	fw->file.blocks[j].end = 0x0;
+	fw->file.blocks[j].check = 0x0;
+	fw->file.blocks[j + 1].start = (~0U);
+	fw->file.blocks[j + 1].end = 0x0;
+	fw->file.blocks[j + 1].check = 0x0;
+
+	for (i = start; i < end; i++) {
+		/* filter out non-hexadecimal characters */
+		if (hex_to_bin(hex[i]) < 0)
+			continue;
+
+		error = hex2bin(info, hex + i, sizeof(info));
+		if (error < 0)
+			return error;
+
+		len = info[0];
+		addr = be32(info + 1, 2);
+		type = info[3];
+
+		error = hex2bin(data, hex + i + 8, len);
+		if (error < 0)
+			return error;
+
+		switch (type) {
+		case 0xAC:
+			mapping_info_addr = be32(data, len);
+			break;
+
+		case 0xAD:
+			fw->file.blocks[1].start = be32(data, len);
+			_memset(buf + fw->file.blocks[1].start, 0, 0x1000);
+			break;
+
+		case 0xBA:
+			if (be32(data, len) != 2U)
+				break;
+
+			TP_MSG(NULL, "start to decode M2V part of hex file\n");
+			fw->m2v = true;
+			////Reed Add : 20230721��������2326��������Decode_mm,�ټ�������M2V���֡���
+			if (mapping_info_addr)
+				decode_mm(fw, mapping_info_addr, buf, buf_size);
+			return decode_hex(fw, hex, i + 10 + len * 2 + 1, end,
+					  fw->m2v_buf, ILITEK_FW_BUF_SIZE);
+
+		case 0x01:
+			goto success_return;
+
+		case 0x02:
+			exaddr = be32(data, len) << 4;
+			break;
+
+		case 0x04:
+			exaddr = be32(data, len) << 16;
+			break;
+
+		case 0x05:
+			TP_MSG(NULL, "hex data type: 0x%x, start linear address: 0x%x\n",
+				type, be32(data, len));
+			break;
+
+		case 0x00:
+			addr += exaddr;
+
+			if (addr + len > buf_size) {
+				TP_ERR(NULL, "hex addr: 0x%x, buf size: 0x%x OOB\n",
+					addr + len, buf_size);
+				return -ENOBUFS;
+			}
+			_memcpy(buf + addr, data, len);
+
+			fw->file.blocks[j].start =
+				MIN(fw->file.blocks[j].start, addr);
+
+			if (addr + len < fw->file.blocks[j + 1].start) {
+				fw->file.blocks[j].end =
+					MAX(fw->file.blocks[j].end,
+					    addr + len - 1);
+				fw->file.blocks[j].check += get_checksum(
+					0, len, data, sizeof(data));
+			} else {
+				fw->file.blocks[j + 1].end =
+					MAX(fw->file.blocks[j + 1].end,
+					    addr + len - 1);
+				fw->file.blocks[j + 1].check += get_checksum(
+					0, len, data, sizeof(data));
+			}
+
+			break;
+		default:
+			TP_ERR(NULL, "unexpected type:0x%x in hex, len:%u, addr:0x%x\n",
+				type, len, addr);
+			return -EINVAL;
+		}
+
+		i = i + 10 + len * 2;
+	}
+
+success_return:
+	if (fw->m2v)
+		fw->m2v_checksum = fw->file.blocks[fw->file.block_num].check;
+	if (mapping_info_addr)
+		return decode_mm(fw, mapping_info_addr, fw->file.buf, buf_size);
+
+	return 0;
+}
+
+static int decode_bin(struct ilitek_fw_handle *fw,
+		      u8 *bin, u32 bin_size,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 mapping_info_addr;
+
+	if (!dev) {
+		TP_ERR(NULL, "offline decode bin file is not supported\n");
+		return -EINVAL;
+	}
+
+	if (bin_size > buf_size) {
+		TP_ERR(dev->id, "bin file size: 0x%x, buf size: 0x%x OOB\n",
+			bin_size, buf_size);
+		return -ENOBUFS;
+	}
+	_memcpy(buf, bin, bin_size);
+
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		return error;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		return error;
+
+	switch (dev->protocol.flag) {
+	case PTL_V6:
+		mapping_info_addr = dev->mcu_info.mm_addr;
+		break;
+
+	case PTL_V3:
+		/*
+		 * For 231x: AP checksum and DF checksum, DF start addr: 0x1f000
+		 * For 251x: AP crc and DF checksum, DF start addr: 0xf000
+		 */
+		if (is_231x(dev)) {
+			mapping_info_addr = 0x500;
+
+			fw->file.blocks[1].start = 0x1f000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x0;
+
+			/* +2 to get end addr. of last byte of 4 bytes checksum */
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true) + 2;
+
+		} else {
+			mapping_info_addr = 0x2020;
+			fw->file.blocks[1].start = 0xf000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x2000;
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true);
+		}
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * take the whole "buf" into decode_mm, "buf" should be
+	 * properly initialized, and the size should be
+	 * larger than "bin", which reduce OOB issue.
+	 */
+	return decode_mm(fw, mapping_info_addr, buf, buf_size);
+}
+
+#ifdef ILITEK_BOOT_UPDATE
+#include "ilitek_fw.h"
+
+static int decode_ili(struct ilitek_fw_handle *fw,
+		      u8 *buf, u32 buf_size)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 *ili_buf = CTPM_FW;
+	int size = sizeof(CTPM_FW);
+	u8 id;
+
+#if defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		if (!support_sensor_id(dev)) {
+			TP_WARN(dev->id,
+				"protocol: 0x%x, mode: 0x" PFMT_X8 ", sensor-id not supported, take default fw(id: 0x%x)\n",
+				dev->protocol.ver, dev->ic[0].mode,
+				ILITEK_BOOT_UPDATE_DEF_ID);
+			fw->file.id = ILITEK_BOOT_UPDATE_DEF_ID;
+			break;
+		}
+
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id >= ARRAY_SIZE(ili_arr)) {
+			TP_ERR(dev->id, "invalid sensor id: " PFMT_U8 " >= %d\n",
+				dev->sensor.id, (int)ARRAY_SIZE(ili_arr));
+			return -EINVAL;
+		}
+
+		fw->file.id = id;
+		ili_buf = ili_arr[id].buf;
+		size = ili_arr[id].size;
+		break;
+	case ili_default:
+		break;
+	default:
+		TP_ERR(dev->id, "unexpected ili-select-type: %u\n",
+			__ili_select_type__);
+		return -EINVAL;
+	}
+#endif
+
+	if (!ili_buf || size < 32)
+		return -EINVAL;
+
+	fw->setting.fw_ver_check = true;
+	fw->setting.fw_ver_policy = 0;
+	_memcpy(fw->setting.fw_ver, ili_buf + 18, 8);
+
+	TP_MSG_ARR(dev->id, "IC  fw ver:", TYPE_U8, 8, dev->fw_ver);
+	TP_MSG_ARR(dev->id, "Hex fw ver:", TYPE_U8, 8, fw->setting.fw_ver);
+
+	return decode_bin(fw, ili_buf + 32, size - 32, buf, buf_size);
+}
+#endif
+
+static bool need_retry(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 id;
+
+#if defined(ILITEK_BOOT_UPDATE) && defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	if (fw->file.type != fw_ili)
+		return false;
+
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id == fw->file.id)
+			break;
+
+		/* reload correct fw if sensor-id not matched with default fw */
+		TP_MSG(dev->id, "sensor id: 0x%x, file: 0x%x not matched, "
+			"reload fw again\n",
+			id, fw->file.id);
+
+		if (ilitek_update_load_fw(fw, "ilitek.ili") < 0) {
+			TP_ERR(dev->id, "reload ilitek.ili failed\n");
+			return false;
+		}
+
+		/* MUST DISABLE fw ver check for the second try */
+		fw->setting.fw_ver_check = false;
+
+		return true;
+	default:
+		break;
+	}
+#endif
+
+	UNUSED(dev);
+	UNUSED(id);
+
+	return false;
+}
+
+static int decode_firmware(struct ilitek_fw_handle *fw, WCHAR *filename)
+{
+	int error;
+	int size = 0;
+	u8 *buf;
+	WCHAR *file_ext;
+
+	/* initialization */
+	_memset(fw->file.buf, 0xFF, fw->file.buf_size);
+	_memset(fw->m2v_buf, 0xFF, ILITEK_FW_BUF_SIZE);
+	fw->m2v = false;
+	/*
+	 * set block num 2 for V3 AP and Data Flash as default,
+	 * for V6, block num would be updated after decoding memory mapping.
+	 */
+	fw->file.block_num = 2;
+	fw->file.blocks[0].start = (~0U);
+	fw->file.blocks[0].end = 0x0;
+	fw->file.blocks[0].check = 0x0;
+	fw->file.blocks[1].start = (~0U);
+	fw->file.blocks[1].end = 0x0;
+	fw->file.blocks[1].check = 0x0;
+
+	TP_MSG(NULL, "start to read fw file: " PFMT_C16 "\n", filename);
+
+	buf = (u8 *)CALLOC(ILITEK_FW_FILE_SIZE, 1);
+	if (!buf)
+		return -ENOMEM;
+
+	file_ext = WCSRCHR(filename, '.');
+	if (!file_ext)
+		return -ENOENT;
+
+	/* no need to read .ili file */
+	if (WCSCASECMP(file_ext, ".ili")) {
+		if (!fw->cb.read_fw) {
+			error = -EFAULT;
+			TP_ERR(NULL, "read fw callback not registered\n");
+			goto err_free;
+		}
+
+		size = fw->cb.read_fw(filename, buf, ILITEK_FW_FILE_SIZE,
+				      fw->_private);
+
+		error = size;
+		if (error < 0) {
+			TP_ERR(NULL, "read fw file: " PFMT_C16 " failed, err: %d\n",
+				filename, error);
+			goto err_free;
+		}
+	}
+
+	if (!WCSCASECMP(file_ext, ".hex")) {
+		fw->file.type = fw_hex;
+		error = decode_hex(fw, buf, 0, size, fw->file.buf,
+				   fw->file.buf_size);
+	} else if (!WCSCASECMP(file_ext, ".bin")) {
+		fw->file.type = fw_bin;
+		error = decode_bin(fw, buf, size, fw->file.buf,
+				   fw->file.buf_size);
+	}
+#ifdef ILITEK_BOOT_UPDATE
+	else if (!WCSCASECMP(file_ext, ".ili")) {
+		fw->file.type = fw_ili;
+		error = decode_ili(fw, fw->file.buf, fw->file.buf_size);
+	}
+#endif
+	else {
+		error = -EINVAL;
+	}
+
+err_free:
+	CFREE(buf);
+
+	return error;
+}
+
+static bool need_fw_update_v3(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool fw_file_has_data_flash =
+		(fw->file.blocks[1].start < fw->file.blocks[1].end);
+	bool need = false;
+
+	TP_INFO(dev->id, "------------V3 AP/DF Info.------------\n");
+
+	fw->file.blocks[0].check_match = (fw->setting.force_update) ?
+		false : dev->ic[0].crc[0] == fw->file.blocks[0].check;
+
+	TP_INFO(dev->id, "AP block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+		fw->file.blocks[0].start, fw->file.blocks[0].end,
+		dev->ic[0].crc[0], fw->file.blocks[0].check,
+		(fw->file.blocks[0].check_match) ? "matched" : "not matched");
+
+	fw->file.blocks[1].check_match = true;
+	if (fw_file_has_data_flash) {
+		fw->file.blocks[1].check_match = (fw->setting.force_update) ?
+			false : dev->ic[0].crc[1] == fw->file.blocks[1].check;
+
+		TP_INFO(dev->id, "DF block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(fw->file.blocks[1].check_match) ?
+			"matched" : "not matched");
+	} else if (!is_231x(dev)) {
+		/*
+		 * for 251x ICs, if no data flash in fw file,
+		 * need to switch to BL mode then erase data flash forcely.
+		 */
+		need = true;
+	}
+
+	TP_INFO(dev->id, "--------------------------------------\n");
+
+	need |= (!fw->file.blocks[0].check_match ||
+		 !fw->file.blocks[1].check_match);
+
+	if (dev->ic[0].mode == BL_MODE)
+		return true;
+
+	return need;
+}
+
+static bool need_fw_update_v6(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	bool need = false;
+
+	TP_INFO(dev->id, "------------Lego Block Info.------------\n");
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].check_match = (fw->setting.force_update) ?
+			false : (dev->ic[0].crc[i] == fw->file.blocks[i].check);
+
+		need = (!fw->file.blocks[i].check_match) ? true : need;
+
+		TP_INFO(dev->id, "Block[" PFMT_U8 "]: Start/End Addr.: 0x%x/0x%x, IC/File CRC: 0x%x/0x%x " PFMT_C8 "\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			dev->ic[0].crc[i], fw->file.blocks[i].check,
+			(fw->file.blocks[i].check_match) ?
+			"matched" : "not matched");
+	}
+
+	/* check BL mode firstly before AP-cmd related varaible, ex: ic_num */
+	if (dev->ic[0].mode == BL_MODE) {
+		need = true;
+		goto force_return;
+	}
+
+	for (i = 1; i < dev->tp_info.ic_num; i++) {
+		TP_INFO(dev->id, "Master/Slave[" PFMT_U8 "] CRC: 0x%x/0x%x " PFMT_C8 ", Slave Mode: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, fw->file.blocks[0].check, dev->ic[i].crc[0],
+			(fw->file.blocks[0].check == dev->ic[i].crc[0]) ?
+			"matched" : "not matched",
+			dev->ic[i].mode, dev->ic[i].mode_str);
+
+		if (dev->ic[i].crc[0] == fw->file.blocks[0].check &&
+			dev->ic[i].mode == AP_MODE)
+			continue;
+		need = true;
+	}
+
+	if (fw->m2v) {
+		api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &fw->m2v_checksum);
+
+		fw->file.blocks[fw->file.block_num].check_match =
+			fw->file.blocks[fw->file.block_num].check ==
+			fw->m2v_checksum;
+		TP_INFO(dev->id, "M2V IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->m2v_checksum,
+			fw->file.blocks[fw->file.block_num].check,
+			(fw->file.blocks[fw->file.block_num].check_match) ?
+			"matched" : "not matched");
+
+		fw->m2v_need_update =
+			!fw->file.blocks[fw->file.block_num].check_match ||
+			fw->setting.force_update;
+		need |= fw->m2v_need_update;
+	}
+
+force_return:
+	TP_INFO(dev->id, "----------------------------------------\n");
+
+	return need;
+}
+
+static bool need_fw_update(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool need = false;
+
+	struct ilitek_fw_settings *set = &fw->setting;
+	u64 dev_fw_ver, file_fw_ver;
+
+	if (dev->protocol.flag == PTL_V3)
+		need = need_fw_update_v3(fw);
+	else if (dev->protocol.flag == PTL_V6)
+		need = need_fw_update_v6(fw);
+
+	if (fw->cb.update_fw_ic_info)
+		fw->cb.update_fw_ic_info(false,
+					 dev->fw_ver, dev->ic[0].crc,
+					 fw->file.block_num, fw->_private);
+
+	if (set->force_update)
+		return true;
+	else if (set->fw_check_only)
+		return false;
+
+	if (set->fw_ver_check && dev->ic[0].mode == AP_MODE) {
+		TP_INFO(dev->id, "IC FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			dev->fw_ver[0], dev->fw_ver[1], dev->fw_ver[2],
+			dev->fw_ver[3], dev->fw_ver[4], dev->fw_ver[5],
+			dev->fw_ver[6], dev->fw_ver[7]);
+		TP_INFO(dev->id, "File FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			set->fw_ver[0], set->fw_ver[1],
+			set->fw_ver[2], set->fw_ver[3],
+			set->fw_ver[4], set->fw_ver[5],
+			set->fw_ver[6], set->fw_ver[7]);
+
+		dev_fw_ver =
+			U82U64(dev->fw_ver[0], 7) + U82U64(dev->fw_ver[1], 6) +
+			U82U64(dev->fw_ver[2], 5) + U82U64(dev->fw_ver[3], 4) +
+			U82U64(dev->fw_ver[4], 3) + U82U64(dev->fw_ver[5], 2) +
+			U82U64(dev->fw_ver[6], 1) + U82U64(dev->fw_ver[7], 0);
+		file_fw_ver =
+			U82U64(set->fw_ver[0], 7) + U82U64(set->fw_ver[1], 6) +
+			U82U64(set->fw_ver[2], 5) + U82U64(set->fw_ver[3], 4) +
+			U82U64(set->fw_ver[4], 3) + U82U64(set->fw_ver[5], 2) +
+			U82U64(set->fw_ver[6], 1) + U82U64(set->fw_ver[7], 0);
+
+		TP_MSG(dev->id, "IC fw ver: 0x" PFMT_X64 ", File fw ver: 0x" PFMT_X64 "\n",
+			(long long unsigned int)dev_fw_ver,
+			(long long unsigned int)file_fw_ver);
+
+		if (file_fw_ver > dev_fw_ver) {
+			TP_INFO(dev->id, "IC FW version is older than File FW version\n");
+			return true;
+		} else if (file_fw_ver == dev_fw_ver) {
+			TP_INFO(dev->id, "File FW version is the same, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_same) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_same) ?
+				need : false;
+		} else {
+			TP_INFO(dev->id, "File FW version is older, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				need : false;
+		}
+	}
+
+	return need;
+}
+
+static int update_master(struct ilitek_fw_handle *fw, int idx, u32 len)
+{
+	int error = 0;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+	u16 file_crc;
+	int retry = 3;
+
+	TP_MSG(dev->id, "updating block[%d], data len: %u, start/end addr: 0x%x/0x%x\n",
+		idx, len, fw->file.blocks[idx].start, fw->file.blocks[idx].end);
+
+err_retry:
+	if ((dev->setting.no_retry && error < 0) || retry-- < 0)
+		return (error < 0) ? error : -EINVAL;
+
+	error = api_write_enable_v6(dev, false, false,
+				     fw->file.blocks[idx].start,
+				     fw->file.blocks[idx].end);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[idx].start;
+	     i < fw->file.blocks[idx].end; i += len) {
+		/*
+		 * check end addr. of data write buffer is within valid range.
+		 */
+		if (i + len > END_ADDR_LEGO) {
+			TP_ERR(dev->id, "block[%d] write addr. 0x%x + 0x%x > 0x%x OOB\n",
+				idx, i, len, END_ADDR_LEGO);
+			return -EINVAL;
+		}
+
+		_memcpy(dev->wbuf + 1, fw->file.buf + i, len);
+		error = api_write_data_v6(dev, len + 1);
+
+		if (error < 0)
+			goto err_retry;
+
+		fw->progress_curr = MIN(i + len - fw->file.blocks[idx].offset,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "block[%d] update progress: " PFMT_U8 "%%\n",
+			idx, fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	file_crc = get_crc(fw->file.blocks[idx].start,
+			   fw->file.blocks[idx].end - 1,
+			   fw->file.buf, fw->file.buf_size);
+	dev->ic[0].crc[idx] =
+		api_get_block_crc_by_addr(dev, CRC_GET,
+					  fw->file.blocks[idx].start,
+					  fw->file.blocks[idx].end);
+
+	TP_INFO(dev->id, "block[%d]: start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+		idx, fw->file.blocks[idx].start, fw->file.blocks[idx].end,
+		dev->ic[0].crc[idx], file_crc,
+		(file_crc == dev->ic[0].crc[idx]) ?
+		"matched" : "not matched");
+
+	if (file_crc != dev->ic[0].crc[idx]) {
+		error = -EINVAL;
+		goto err_retry;
+	}
+
+	return 0;
+}
+
+static int update_slave(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[0].crc[0] == dev->ic[i].crc[0] &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "updating slave, master/slave[" PFMT_U8 "] crc: 0x%x/0x%x\n",
+			i, dev->ic[0].crc[0], dev->ic[i].crc[0]);
+		error = api_access_slave(dev, 0x3, CMD_WRITE_DATA_V6, NULL);
+		if (error < 0)
+			return error;
+		error = api_write_enable_v6(dev, false, true,
+					    fw->file.blocks[0].start,
+					    fw->file.blocks[0].end);
+		if (error < 0)
+			return error;
+
+		goto success_return;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[i].mode == AP_MODE &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "changing slave[" PFMT_U8 "]: 0x" PFMT_X8 " to AP mode\n",
+			i, dev->ic[i].mode);
+		error = api_access_slave(dev, 0x3, CMD_SET_AP_MODE, NULL);
+		if (error < 0)
+			return error;
+		break;
+	}
+
+success_return:
+	return 0;
+}
+
+static int update_M3_M2V(struct ilitek_fw_handle *fw, u32 len)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 i;
+	u8 buf[6];
+	u32 AdressRange, M2V_Buf_Len, CheckSumRange;
+	u16 AddDataCount;
+
+	u8 j = fw->file.block_num;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	//Reed Add : 20230721
+	AdressRange = fw->file.blocks[j].end - fw->file.blocks[j].start;
+	M2V_Buf_Len = fw->file.blocks[j].end - fw->file.blocks[j].start + 1;
+	CheckSumRange = fw->file.blocks[j].check;
+	AddDataCount = len - M2V_Buf_Len % len;
+	AddDataCount = (AddDataCount >= 8) ? AddDataCount : AddDataCount + len;
+	AdressRange += AddDataCount;
+	CheckSumRange += AddDataCount * 0xFF;
+
+	error = api_to_bl_mode_m2v(dev, true);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_set_data_len(dev, len);
+	if (error < 0)
+		return error;
+
+	TP_INFO(dev->id, "updating M2V, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+		fw->file.blocks[j].start, fw->file.blocks[j].end,
+		fw->file.blocks[j].check);
+
+	//Reed Add : 20230721
+	buf[0] = (AdressRange >> 16) & 0xFF;
+	buf[1] = (AdressRange >> 8) & 0xFF;
+	buf[2] = AdressRange & 0xFF;
+	buf[3] = (CheckSumRange >> 16) & 0xFF;
+	buf[4] = (CheckSumRange >> 8) & 0xFF;
+	buf[5] = CheckSumRange & 0xFF;
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+	error = api_access_slave(dev, 0x80, CMD_WRITE_ENABLE, buf);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[j].start;
+	     i < fw->file.blocks[j].end; i += len) {
+		_memcpy(dev->wbuf + 1, fw->m2v_buf + i, len);
+
+		error = api_write_data_m2v(dev, len + 1);
+		if (error < 0)
+			return error;
+
+		fw->progress_curr = MIN(fw->progress_curr + len,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "m2v update progress: " PFMT_U8 "%%\n", fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	error = api_to_bl_mode_m2v(dev, false);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_access_slave(dev, 0x80, CMD_GET_FW_VER, fw->m2v_fw_ver);
+	if (error < 0)
+		return error;
+
+	TP_MSG_ARR(dev->id, "update M2V success, fw version:", TYPE_U8,
+		   8, fw->m2v_fw_ver);
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_8(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	error = api_set_data_len(dev, fw->update_len);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		if (fw->file.blocks[i].check_match)
+			continue;
+
+		error = update_master(fw, i, fw->update_len);
+		if (error < 0) {
+			TP_ERR(dev->id, "Upgrade Block:" PFMT_U8 " failed, err: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	error = api_to_bl_mode(dev, false, fw->file.blocks[0].start,
+			       fw->file.blocks[0].end);
+	if (error < 0)
+		return error;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	/* get tp info. for updating ic_num */
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.ic_num > 1) {
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(true, fw->_private);
+		error = update_slave(fw);
+
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(false, fw->_private);
+
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade slave failed, err: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	if (fw->m2v && fw->m2v_need_update) {
+		error = update_M3_M2V(fw, 1024);
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade m2v slave failed, err: %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_7(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+
+	/*
+	 * Erase data initially for the case that hex w/o data flash section.
+	 * Due to historical factor, please double confirm before modify here.
+	 */
+	error = api_erase_data_v3(dev);
+	if (error < 0)
+		return error;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false,
+					     fw->file.blocks[1].end + 1,
+					     fw->file.blocks[1].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start;
+		     i <= fw->file.blocks[1].end; i += 32) {
+			_memset(dev->wbuf + 1, 0, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->cb.delay_ms(50);
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[1] = (le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+
+		TP_INFO(dev->id, "DF block, start/end addr.: 0x%x/0x%x, ic/file checkksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(dev->ic[0].crc[1] == fw->file.blocks[1].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[1] != fw->file.blocks[1].check)
+			return -EFAULT;
+	}
+
+	/*
+	 * Update AP code forcely if AP crc/checksum not match or
+	 * Data Flash has been updated.
+	 */
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true,
+					     fw->file.blocks[0].end + 1,
+					     fw->file.blocks[0].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start;
+		     i <= fw->file.blocks[0].end; i += 32) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[0] =
+			(le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+		TP_INFO(dev->id, "AP block, start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			dev->ic[0].crc[0], fw->file.blocks[0].check,
+			(dev->ic[0].crc[0] == fw->file.blocks[0].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[0] != fw->file.blocks[0].check)
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_6(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	struct ilitek_ts_callback *cb = &dev->cb;
+	unsigned int i, j;
+
+	u32 bytes, end, check;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[1].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[1].end + 1) % 32) : 31;
+		end = fw->file.blocks[1].end + bytes;
+		check = fw->file.blocks[1].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) DF start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[1].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start, j = 0;
+		     i <= fw->file.blocks[1].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+	}
+
+	/*
+	* Update AP code forcely if AP crc/checksum not match or
+	* Data Flash has been updated.
+	*/
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[0].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[0].end + 1) % 32) : 31;
+		end = fw->file.blocks[0].end + bytes;
+		check = fw->file.blocks[0].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) AP start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[0].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start, j = 0;
+			i <= fw->file.blocks[0].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[0].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+
+#ifdef ILITEK_BOOT_UPDATE
+		/*
+		 * .ili file fill DF section with default 0xFF, but not 0
+		 * which make flow stuck in BL mode.
+		 * so HW-reset is need before switching to AP mode.
+		 * remove HW-reset after fixing .ili converter bug.
+		 */
+		reset_helper(dev);
+#endif
+	}
+
+	return 0;
+}
+
+static void update_progress(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	unsigned int last_end = 0, last_offset = 0;
+
+	fw->progress = 0;
+	fw->progress_max = 0;
+	fw->progress_curr = 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		fw->progress_max +=
+			(fw->file.blocks[1].end > fw->file.blocks[1].start) ?
+			fw->file.blocks[1].end - fw->file.blocks[1].start : 0;
+		fw->progress_max +=
+			(fw->file.blocks[0].end > fw->file.blocks[0].start) ?
+			fw->file.blocks[0].end - fw->file.blocks[0].start : 0;
+		break;
+
+	case PTL_V6:
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->file.blocks[i].check_match)
+				continue;
+
+			fw->progress_max +=
+				fw->file.blocks[i].end -
+				fw->file.blocks[i].start;
+			last_offset += fw->file.blocks[i].start - last_end;
+			fw->file.blocks[i].offset = last_offset;
+
+			last_end = fw->file.blocks[i].end;
+		}
+
+		if (fw->m2v_need_update) {
+			fw->progress_max +=
+				fw->file.blocks[fw->file.block_num].end -
+				fw->file.blocks[fw->file.block_num].start;
+		}
+
+		break;
+	}
+}
+
+void *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			 struct ilitek_update_callback *cb, void *_private)
+{
+	struct ilitek_fw_handle *fw;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)_dev;
+
+	if (need_update_ts_info && dev && api_update_ts_info(dev) < 0)
+		return NULL;
+
+	fw = (struct ilitek_fw_handle *)MALLOC(sizeof(*fw));
+	if (!fw)
+		return NULL;
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(fw, 0, sizeof(*fw));
+	fw->dev = (dev) ? dev : NULL;
+
+	/* initial update-len to default UPDATE_LEN */
+	fw->update_len = UPDATE_LEN;
+
+	fw->dev = dev;
+	fw->_private = _private;
+	fw->file.buf_size = ILITEK_FW_BUF_SIZE;
+	fw->file.buf = (u8 *)CALLOC(fw->file.buf_size, 1);
+	if (!fw->file.buf)
+		goto err_free_fw;
+
+	fw->m2v_buf = (u8 *)CALLOC(ILITEK_FW_BUF_SIZE, 1);
+	if (!fw->m2v_buf)
+		goto err_free_fw_buf;
+
+	if (cb)
+		_memcpy(&fw->cb, cb, sizeof(*cb));
+
+	return fw;
+
+err_free_fw_buf:
+	CFREE(fw->file.buf);
+err_free_fw:
+	FREE(fw);
+
+	return NULL;
+}
+
+void ilitek_update_exit(void *handle)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	if (fw->file.buf)
+		CFREE(fw->file.buf);
+
+	if (fw->m2v_buf)
+		CFREE(fw->m2v_buf);
+
+	if (fw)
+		FREE(fw);
+}
+
+void ilitek_update_set_data_length(void *handle, u16 len)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!fw)
+		return;
+
+	fw->update_len = len;
+}
+
+int ilitek_update_load_fw(void *handle, WCHAR *fw_name)
+{
+	int error;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	u32 i;
+
+	if (!handle)
+		return -EINVAL;
+
+	error = decode_firmware(fw, fw_name);
+	if (error < 0)
+		return error;
+
+	if (fw->cb.update_fw_file_info)
+		fw->cb.update_fw_file_info(&fw->file, fw->_private);
+
+	if (!fw->dev)
+		return 0;
+
+	/* for Lego and V6 IC, check block's start/end address validity */
+	if (fw->dev->protocol.flag == PTL_V6) {
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->dev->mcu_info.min_addr <=
+				fw->file.blocks[i].start &&
+			    fw->dev->mcu_info.max_addr >
+				fw->file.blocks[i].end)
+				continue;
+
+			if (!(fw->file.blocks[i].start % 0x1000))
+				continue;
+
+			TP_ERR(fw->dev->id, "Block[%u] addr. OOB (0x%x <= 0x%x/0x%x < 0x%x) or invalid start addr\n",
+				i, fw->dev->mcu_info.min_addr,
+				fw->file.blocks[i].start,
+				fw->file.blocks[i].end,
+				fw->dev->mcu_info.max_addr);
+			return -EINVAL;
+		}
+	}
+
+	TP_MSG(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " matched\n",
+		fw->dev->mcu_info.ic_name, fw->file.ic_name);
+
+	return 0;
+}
+
+int ilitek_update_start(void *handle)
+{
+	int error;
+	s8 retry = 0;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+	struct ilitek_ts_device *dev;
+
+	if (!handle)
+		return -EINVAL;
+	dev = fw->dev;
+
+	/*
+	 * Some platform (ITS-Bridge) might change touch controller
+	 * after loading fw file, get panel info. forcely and
+	 * re-check the ic/file are matched.
+	 */
+	error = api_update_ts_info(dev);
+	if (error < 0 || strcmp(dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "get ic info failed, err: %d or ic/file (" PFMT_C8 "/" PFMT_C8 ") not matched\n",
+			error, fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EPERM;
+	}
+
+	TP_INFO(dev->id, "[ilitek_update_start] start\n");
+
+	do {
+		TP_DBG(dev->id, "retry: %hhd, retry_limit: %hhd\n",
+			retry, fw->setting.retry);
+		if (retry)
+			reset_helper(dev);
+
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			continue;
+
+		if (!need_fw_update(fw)) {
+			if (is_231x(dev))
+				goto erase_data_flash_231x;
+			goto success_return;
+		}
+
+		update_progress(fw);
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(0, fw->_private);
+
+		error = api_to_bl_mode(dev, true, 0, 0);
+		if (error < 0)
+			continue;
+
+		TP_INFO_ARR(dev->id, "[BL Firmware Version]",
+			    TYPE_U8, 8, dev->fw_ver);
+		TP_INFO(dev->id, "[ilitek_update_start] start to program\n");
+
+		switch (dev->protocol.ver & 0xFFFF00) {
+		case BL_PROTOCOL_V1_8:
+			error = ilitek_update_BL_v1_8(fw);
+			break;
+		case BL_PROTOCOL_V1_7:
+			error = ilitek_update_BL_v1_7(fw);
+			break;
+		case BL_PROTOCOL_V1_6:
+			error = ilitek_update_BL_v1_6(fw);
+			break;
+		default:
+			TP_ERR(dev->id, "BL protocol ver: 0x%x not supported\n",
+				dev->protocol.ver);
+			continue;
+		}
+		if (error < 0)
+			continue;
+
+		error = api_to_bl_mode(dev, false,
+				       fw->file.blocks[0].start,
+				       fw->file.blocks[0].end);
+		if (error < 0)
+			continue;
+
+erase_data_flash_231x:
+		/*
+		 * If no data flash section in firmware file,
+		 * 231x need to erase data flash after change to AP mode.
+		 */
+		if (fw->file.blocks[1].end < fw->file.blocks[1].start &&
+		    is_231x(dev)) {
+			error = api_erase_data_v3(dev);
+			if (error < 0)
+				continue;
+		}
+
+success_return:
+		error = api_update_ts_info(dev);
+		if (error < 0)
+			continue;
+
+		error = api_set_ctrl_mode(dev, mode_normal, false, true);
+		if (error < 0)
+			continue;
+
+		if (fw->cb.update_fw_ic_info)
+			fw->cb.update_fw_ic_info(true,
+						 dev->fw_ver, dev->ic[0].crc,
+						 fw->file.block_num,
+						 fw->_private);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(100, fw->_private);
+
+		if (need_retry(fw))
+			continue;
+
+		TP_INFO(dev->id, "[ilitek_update_start] success\n");
+
+		return 0;
+	} while (!dev->setting.no_retry && ++retry < fw->setting.retry);
+
+	TP_ERR(dev->id, "[ilitek_update_start] fw update failed, err: %d\n",
+		error);
+
+	return (error < 0) ? error : -EFAULT;
+}
+
+void ilitek_update_setting(void *handle, struct ilitek_fw_settings *setting)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&fw->setting, setting, sizeof(struct ilitek_fw_settings));
+}
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.h b/drivers/input/touchscreen/ilitek/ilitek_update.h
new file mode 100644
index 000000000000..b7987b9fb121
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.h
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_UPDATE_H__
+#define __ILITEK_UPDATE_H__
+
+#include "ilitek_protocol.h"
+
+#define UPDATE_LEN			1024
+
+#define ILITEK_FW_FILE_SIZE		(512 * 1024)
+#define ILITEK_FW_BUF_SIZE		(256 * 1024)
+
+enum fw_file_type {
+	fw_hex = 0,
+	fw_bin,
+	fw_ili,
+};
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ mapping_info_lego {
+	u8 tuning_ver[4];
+	u8 fw_ver[4];
+	u8 core_test;
+	u8 core_day;
+	u8 core_month;
+	u8 core_year;
+	u32 core_ver;
+	u8 vendor_ver[6];
+	u8 _reserve_1[8];
+	u16 customer_id;
+	u16 fwid;
+	u16 i2c_addr;
+	u8 _reserve_2[2];
+	char model_name[16];
+	u8 _reserve_3[2];
+	u8 ic_num;
+	u8 total_tuning_num;
+	u16 sizeof_tuning;
+	u16 sizeof_tp_param;
+	u16 sizeof_sys_info;
+	u16 sizeof_sys_algo;
+	u16 sizeof_key_info;
+	u8 block_num;
+	u8 support_tuning_num;
+	u8 _reserve_4[2];
+
+	struct __PACKED__ {
+		u8 addr[3];
+	} blocks[10];
+
+	u8 _reserve_5[9];
+	u8 end_addr[3];
+	u8 _reserve_6[2];
+};
+
+union __PACKED__ mapping_info {
+	struct __PACKED__ {
+		u8 mapping_ver[3];
+		u8 protocol_ver[3];
+		u8 ic_name[6];
+
+		struct mapping_info_lego _lego;
+	};
+};
+
+/*
+ * for V3, "check" is checksum, block[0] for AP and block[1] for Data Flash.
+ * for V6, "check" is CRC.
+ */
+struct __PACKED__ ilitek_block {
+	bool check_match;
+	u32 start;
+	u32 end;
+	u32 check;
+	u32 offset;
+};
+
+struct __PACKED__ ilitek_fw_file_info {
+	char ic_name[8];
+	u8 fw_ver[8];
+	u16 fwid;
+
+	u8 block_num;
+	struct ilitek_block blocks[ILTIEK_MAX_BLOCK_NUM];
+
+	u32 mm_addr;
+	u32 mm_size;
+
+	u32 buf_size;
+	u8 *buf;
+
+	/* fw file's sensor-id or fwid or other id */
+	u32 id;
+	u8 type;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return file size in # of bytes, or negative error code */
+typedef int (*read_fw_t)(WCHAR *, u8 *, int, void *);
+/* update progress of fw updating */
+typedef void (*update_progress_t)(u8, void *);
+/* update fw info to callers */
+typedef void (*update_fw_file_info_t)(struct ilitek_fw_file_info *, void *);
+
+/* notify caller before/after slave upgrade (for ITS only) */
+typedef void (*slave_update_notify_t)(bool, void *);
+/* update fw version and crc/checksum before/after update (for ITS only) */
+typedef void (*update_fw_ic_info_t)(bool, u8 *, u32 *, int, void *);
+
+struct ilitek_update_callback {
+	read_fw_t read_fw;
+	update_progress_t update_progress;
+	update_fw_file_info_t update_fw_file_info;
+
+	slave_update_notify_t slave_update_notify;
+	update_fw_ic_info_t update_fw_ic_info;
+};
+
+enum fw_ver_check_policy {
+	allow_fw_ver_downgrade = 1,
+	allow_fw_ver_same = 2,
+};
+
+struct ilitek_fw_settings {
+	s8 retry;
+	bool fw_check_only;
+	bool force_update;
+
+	bool fw_ver_check;
+	u8 fw_ver_policy;
+	u8 fw_ver[8];
+};
+
+struct ilitek_fw_handle {
+	struct ilitek_ts_device *dev;
+	void *_private;
+
+	/* upgrade options */
+	struct ilitek_fw_settings setting;
+
+	/* common variable */
+	int update_len;
+
+	struct ilitek_fw_file_info file;
+
+	/* M3 + M2V */
+	bool m2v;
+	bool m2v_need_update;
+	u8 *m2v_buf;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	/* upgrade status */
+	unsigned int progress_curr;
+	unsigned int progress_max;
+	u8 progress;
+
+	/* callbacks */
+	struct ilitek_update_callback cb;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			       struct ilitek_update_callback *callback,
+			       void *_private);
+
+void __DLL ilitek_update_exit(void *handle);
+
+void __DLL ilitek_update_set_data_length(void *handle, u16 len);
+
+void __DLL ilitek_update_setting(void *handle,
+				 struct ilitek_fw_settings *setting);
+
+int __DLL ilitek_update_load_fw(void *handle, WCHAR *fw_name);
+
+int __DLL ilitek_update_start(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git "a/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" "b/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"
new file mode 100644
index 0000000000000000000000000000000000000000..54af506a4b7c4546aaa12ec99f136c9083b62a83
GIT binary patch
literal 162
zcmZQCuPRR|FHUA439vJiGgJX_3J@1FfHc>ZFflPOG_>^}{sH8{XrBE*K_md<!NeuG
f)w8dM-eF8(e4Ne9!oaZdltBfE57ScAbow{|sQnt1

literal 0
HcmV?d00001

-- 
2.25.1


^ permalink raw reply related

* [PATCH] input: touchscreen: Add ilitek touchscreen driver support
From: 2724853925 @ 2025-11-16 13:30 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg
  Cc: linux-input, linux-kernel, linux-gpio, 2724853925, weisicheng

From: weisicheng <7855009+weisicheng@user.noreply.gitee.com>

Add basic support for Ilitek I2C touchscreen controllers (e.g., ILitek2511, ILitek2701).
This patch includes:
1. Kconfig entry for Ilitek driver configuration
2. Makefile entry to compile the ilitek driver module
3. Core driver files under drivers/input/touchscreen/ilitek/ (I2C communication,
   touch event handling, power management)

The driver is compatible with standard input subsystem and supports:
- Multi-touch detection
- Pressure sensing
- Dynamic power saving mode

Signed-off-by: weisicheng <2724853925@qq.com>
---
 drivers/input/touchscreen/Kconfig             |   13 +
 drivers/input/touchscreen/Makefile            |    1 +
 drivers/input/touchscreen/ilitek/Makefile     |   34 +
 .../input/touchscreen/ilitek/ilitek_common.h  |  264 ++
 .../input/touchscreen/ilitek/ilitek_crypto.c  |  465 +++
 .../input/touchscreen/ilitek/ilitek_crypto.h  |   61 +
 drivers/input/touchscreen/ilitek/ilitek_def.c |  235 ++
 drivers/input/touchscreen/ilitek/ilitek_def.h |  554 +++
 .../input/touchscreen/ilitek/ilitek_main.c    | 2198 ++++++++++
 .../touchscreen/ilitek/ilitek_platform_init.c |  404 ++
 .../touchscreen/ilitek/ilitek_protocol.c      | 3644 +++++++++++++++++
 .../touchscreen/ilitek/ilitek_protocol.h      |  916 +++++
 .../input/touchscreen/ilitek/ilitek_report.c  |  455 ++
 .../input/touchscreen/ilitek/ilitek_report.h  |   78 +
 .../input/touchscreen/ilitek/ilitek_tool.c    | 1156 ++++++
 drivers/input/touchscreen/ilitek/ilitek_ts.h  |  268 ++
 .../input/touchscreen/ilitek/ilitek_update.c  | 1657 ++++++++
 .../input/touchscreen/ilitek/ilitek_update.h  |  199 +
 ...344\272\244\346\265\201\347\250\213 .docx" |  Bin 0 -> 162 bytes
 19 files changed, 12602 insertions(+)
 create mode 100644 drivers/input/touchscreen/ilitek/Makefile
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_common.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_main.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_platform_init.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_tool.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_ts.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.h
 create mode 100644 "drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7d5b72ee07fa..3552629c4d4d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -515,6 +515,19 @@ config TOUCHSCREEN_ILITEK
 	  To compile this driver as a module, choose M here: the
 	  module will be called ilitek_ts_i2c.
 
+config TOUCHSCREEN_ILI2511
+	tristate "Ilitek I2C or SPI Touch ICs"
+	depends on I2C
+	depends on SPI
+	help
+	  Say Y here if you have touchscreen with ILITEK touch IC,
+	  it supports 213X/23XX/25XX and other Lego series.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ilitek.
+
 config TOUCHSCREEN_IPROC
 	tristate "IPROC touch panel driver support"
 	depends on ARCH_BCM_IPROC || COMPILE_TEST
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ab9abd151078..3124c4dd8ee1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX)	+= hynitron_cstxxx.o
 obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X)	+= hynitron-cst816x.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_ILITEK)	+= ilitek_ts_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ILI2511)	+= ilitek/
 obj-$(CONFIG_TOUCHSCREEN_IMAGIS)	+= imagis.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/ilitek/Makefile b/drivers/input/touchscreen/ilitek/Makefile
new file mode 100644
index 000000000000..c912438a3b3e
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/Makefile
@@ -0,0 +1,34 @@
+ccflags-y += -Wall
+
+ifndef __KERNEL__
+ccflags-y += -D__KERNEL__
+endif
+
+#Un-mark below item to enable FW upgrade on boot
+#ccflags-y += -DILITEK_BOOT_UPDATE
+
+ILITEK_INTERFACE = i2c
+#ILITEK_INTERFACE = spi
+
+
+ifeq ($(ILITEK_INTERFACE), spi)
+$(info start to build ilitek SPI driver)
+ccflags-y += -DILITEK_SPI_INTERFACE
+ilitek := ilitek_spi
+else
+$(info start to build ilitek I2C driver)
+ilitek := ilitek_i2c
+endif
+
+$(ilitek)-objs += \
+	ilitek_def.o \
+	ilitek_main.o \
+	ilitek_platform_init.o \
+	ilitek_update.o \
+	ilitek_tool.o \
+	ilitek_protocol.o \
+	ilitek_crypto.o \
+	ilitek_report.o
+
+obj-$(CONFIG_TOUCHSCREEN_ILI2511) += $(ilitek).o
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_common.h b/drivers/input/touchscreen/ilitek/ilitek_common.h
new file mode 100644
index 000000000000..7f058c3e4914
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_common.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _ILITEK_COMMON_H_
+#define _ILITEK_COMMON_H_
+/* Includes of headers ------------------------------------------------------*/
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "ilitek_ts.h"
+#include "ilitek_protocol.h"
+#include "ilitek_update.h"
+
+#include "ilitek_crypto.h"
+#include "ilitek_report.h"
+
+/* Extern define ------------------------------------------------------------*/
+//driver information
+#define DRIVER_VERSION_0				5
+#define DRIVER_VERSION_1				9
+#define DRIVER_VERSION_2				3
+#define DRIVER_VERSION_3				0
+#define CUSTOMER_H_ID					0
+#define CUSTOMER_L_ID					0
+#define TEST_VERSION					0
+
+#define ILITEK_IOCTL_MAX_TRANSFER			5000UL
+
+#define set_arr(arr, idx, val)			\
+	do {					\
+		if ((idx) < ARRAY_SIZE(arr))	\
+			(arr)[(idx)] = (val);	\
+	} while (0)
+
+/* i2c clock rate for rk3288 */
+#if ILITEK_PLAT == ILITEK_PLAT_ROCKCHIP && \
+	KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE
+#define SCL_RATE(rate)	.scl_rate = (rate),
+#else
+#define SCL_RATE(rate)
+#endif
+
+/* netlink */
+#if KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)	\
+	struct netlink_kernel_cfg cfg = {	\
+		.groups = 0,			\
+		.input = func,			\
+	}
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), (cfg_ptr))
+#else
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), THIS_MODULE, (cfg_ptr))
+#endif
+#else
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), 0, (func), NULL, THIS_MODULE)
+#endif
+
+/* input_dev */
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define INPUT_MT_INIT_SLOTS(dev, num)	\
+		input_mt_init_slots((dev), (num), INPUT_MT_DIRECT)
+#else
+#define INPUT_MT_INIT_SLOTS(dev, num)	input_mt_init_slots((dev), (num))
+#endif
+
+/* file_operations ioctl */
+#if KERNEL_VERSION(2, 6, 36) <= LINUX_VERSION_CODE
+#define FOPS_IOCTL	unlocked_ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		long func(struct file *fp, cmd, arg)
+#else
+#define FOPS_IOCTL	ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		s32 func(struct inode *np, struct file *fp,	cmd, arg)
+
+#endif
+
+#if KERNEL_VERSION(6, 3, 0) > LINUX_VERSION_CODE
+#define I2C_PROBE_FUNC(func, client_arg)	\
+	int func(client_arg, const struct i2c_device_id *id)
+#else
+#define I2C_PROBE_FUNC(func, client_arg)	int func(client_arg)
+#endif
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+#define REMOVE_FUNC(func, client_arg)	int func(client_arg)
+#define REMOVE_RETURN(val)		({ __typeof__(val) _val = (val); return _val; })
+#else
+#define REMOVE_FUNC(func, client_arg)	void func(client_arg)
+#define REMOVE_RETURN(val)		(val)
+#endif
+
+#if KERNEL_VERSION(6, 4, 0) > LINUX_VERSION_CODE
+#define CLASS_CREATE(name)	class_create(THIS_MODULE, (name))
+#else
+#define CLASS_CREATE(name)	class_create((name))
+#endif
+
+/* procfs */
+#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
+#define PROC_FOPS_T	file_operations
+#define PROC_READ	read
+#define PROC_WRITE	write
+#define PROC_IOCTL		FOPS_IOCTL
+#define PROC_COMPAT_IOCTL	compat_ioctl
+#define PROC_OPEN	open
+#define PROC_RELEASE	release
+#else
+#define PROC_FOPS_T	proc_ops
+#define PROC_READ	proc_read
+#define PROC_WRITE	proc_write
+#define PROC_IOCTL		proc_ioctl
+#define PROC_COMPAT_IOCTL	proc_compat_ioctl
+#define PROC_OPEN	proc_open
+#define PROC_RELEASE	proc_release
+#endif
+
+#ifdef MTK_UNDTS
+#define ISR_FUNC(func)	void func(void)
+#define ISR_RETURN(val)
+#else
+#define ISR_FUNC(func)	irqreturn_t func(int irq, void *dev_id)
+#define ISR_RETURN(val)	({ __typeof__(val) _val = (val); return _val; })
+#endif
+
+enum ilitek_irq_handle_type {
+	irq_type_normal = 0,
+	irq_type_debug,
+	irq_type_c_model,
+};
+
+struct ilitek_ts_data {
+	void *client;
+	struct device *device;
+	struct ilitek_ts_device *dev;
+
+	/* should > 2K for C-Model */
+	u8 buf[4096];
+
+	struct input_dev *input_dev;
+	struct input_dev *pen_input_dev;
+	struct regulator *vdd;
+	struct regulator *vdd_i2c;
+	struct regulator *vcc_io;
+
+	int irq;
+	int irq_gpio;
+	int reset_gpio;
+	int test_gpio;
+
+	bool system_suspend;
+	bool power_key_triggered;
+
+	u8 irq_trigger_type;
+
+	bool is_touched;
+	bool touch_key_hold_press;
+	int touch_flag[40];
+
+#if 0
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+	struct task_struct *update_thread;
+
+	atomic_t firmware_updating;
+	bool operation_protection;
+	bool unhandle_irq;
+	unsigned int irq_handle_type;
+	unsigned int irq_read_len;
+
+	u8 gesture_status;
+	u8 low_power_status;
+
+	bool esd_check;
+	bool esd_skip;
+	struct workqueue_struct *esd_workq;
+	struct delayed_work esd_work;
+	unsigned long esd_delay;
+
+	/* Mutex for protecting concurrent access */
+	struct mutex ilitek_mutex;
+
+	atomic_t irq_enabled;
+	atomic_t get_INT;
+
+	bool wake_irq_enabled;
+
+	bool irq_registered;
+};
+
+/* Extern macro -------------------------------------------------------------*/
+
+#define CEIL(n, d) (((n) % (d)) ? ((n) / (d)) + 1 : ((n) / (d)))
+
+/* Extern variables ---------------------------------------------------------*/
+
+extern u8 driver_ver[];
+
+extern struct ilitek_ts_data *ts;
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#endif
+/* Extern function prototypes -----------------------------------------------*/
+/* Extern functions ---------------------------------------------------------*/
+void ilitek_resume(void);
+void ilitek_suspend(void);
+int ilitek_main_probe(void *client, struct device *dev);
+int ilitek_main_remove(void *client);
+void ilitek_reset(int delay);
+
+int ilitek_write(u8 *cmd, int len);
+int ilitek_read(u8 *buf, int len);
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len);
+
+void ilitek_irq_enable(void);
+void ilitek_irq_disable(void);
+
+int ilitek_upgrade_firmware(char *filename);
+
+int ilitek_create_tool_node(void);
+int ilitek_remove_tool_node(void);
+
+int ilitek_create_sysfsnode(void);
+void ilitek_remove_sys_node(void);
+
+int ilitek_netlink_init(u8 unit);
+void ilitek_netlink_exit(void);
+
+void ilitek_gpio_dbg(void);
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init);
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y);
+
+int ilitek_create_esd_check_workqueue(void);
+void ilitek_remove_esd_check_workqueue(void);
+
+int ilitek_read_data_and_report(void);
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.c b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
new file mode 100644
index 000000000000..2ba6105266d2
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_crypto.h"
+
+/*
+ * The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+ * The numbers below can be computed dynamically trading ROM for RAM -
+ * This can be useful in (embedded) bootloader applications, where ROM is often limited.
+ */
+static const u8 sbox[256] = {
+	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const u8 rsbox[256] = {
+	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+/*
+ * The round constant word array, contains the values given by
+ * x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+ */
+static const u8 rcon[11] = {
+	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+/* array holding the intermediate results during decryption. */
+typedef u8 state_t[4][4];
+
+struct crypto_aes_ctx {
+	u8 key_dec[240];
+};
+
+static u8 xtime(u8 x)
+{
+	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
+}
+
+static void inv_shift_rows(state_t *state)
+{
+	u8 tmp;
+
+	/* Rotate first row 1 columns to right */
+	tmp = (*state)[3][1];
+	(*state)[3][1] = (*state)[2][1];
+	(*state)[2][1] = (*state)[1][1];
+	(*state)[1][1] = (*state)[0][1];
+	(*state)[0][1] = tmp;
+
+	/* Rotate second row 2 columns to right */
+	tmp = (*state)[0][2];
+	(*state)[0][2] = (*state)[2][2];
+	(*state)[2][2] = tmp;
+	tmp = (*state)[1][2];
+	(*state)[1][2] = (*state)[3][2];
+	(*state)[3][2] = tmp;
+
+	/* Rotate third row 3 columns to right */
+	tmp = (*state)[0][3];
+	(*state)[0][3] = (*state)[1][3];
+	(*state)[1][3] = (*state)[2][3];
+	(*state)[2][3] = (*state)[3][3];
+	(*state)[3][3] = tmp;
+}
+
+static void inv_sub_bytes(state_t *state)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[j][i] = rsbox[(*state)[j][i]];
+}
+
+/*
+ * multiply is used to multiply numbers in the field GF(2^8)
+ * Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+ *       The compiler seems to be able to vectorize the operation better this way.
+ *       See https://github.com/kokke/tiny-AES-c/pull/34
+ */
+static u8 multiply(u8 x, u8 y)
+{
+	return (((y & 1) * x) ^
+		(((y >> 1) & 1) * xtime(x)) ^
+		(((y >> 2) & 1) * xtime(xtime(x))) ^
+		(((y >> 3) & 1) * xtime(xtime(xtime(x)))) ^
+		(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
+}
+
+/*
+ * inv_mix_cols function mixes the columns of the state matrix.
+ */
+static void inv_mix_cols(state_t *state)
+{
+	int i;
+	u8 a, b, c, d;
+
+	for (i = 0; i < 4; ++i) {
+		a = (*state)[i][0];
+		b = (*state)[i][1];
+		c = (*state)[i][2];
+		d = (*state)[i][3];
+
+		(*state)[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^
+			multiply(c, 0x0d) ^ multiply(d, 0x09);
+		(*state)[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^
+			multiply(c, 0x0b) ^ multiply(d, 0x0d);
+		(*state)[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^
+			multiply(c, 0x0e) ^ multiply(d, 0x0b);
+		(*state)[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^
+			multiply(c, 0x09) ^ multiply(d, 0x0e);
+	}
+}
+
+/*
+ * This function adds the round key to state.
+ * The round key is added to the state by an XOR function.
+ */
+static void add_round_key(u8 round, state_t *state, const u8 *key)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[i][j] ^= key[(round * Nb * 4) + (i * Nb) + j];
+}
+
+static void aes_decrypt(state_t *state, const u8 *key)
+{
+	u8 round;
+
+	/* Add the first round key to the state before starting the rounds. */
+	add_round_key(Nr, state, key);
+
+	/*
+	 * There will be Nr rounds.
+	 * The first Nr-1 rounds are identical.
+	 * These Nr rounds are executed in the loop below.
+	 * Last one without InvMixColumn()
+	 */
+	for (round = Nr - 1; ; --round) {
+		inv_shift_rows(state);
+		inv_sub_bytes(state);
+		add_round_key(round, state, key);
+
+		if (!round)
+			break;
+
+		inv_mix_cols(state);
+	}
+}
+
+/*
+ * Produces Nb(Nr+1) round keys, used in each round to decrypt the states.
+ */
+static void aes_expandkey(u8 *round_key, const u8 *key)
+{
+	unsigned int i, j, k;
+	u8 tmp_a[4];
+	u8 tmp_b;
+
+	/* The first round key is the key itself. */
+	for (i = 0; i < Nk; ++i) {
+		round_key[(i * 4) + 0] = key[(i * 4) + 0];
+		round_key[(i * 4) + 1] = key[(i * 4) + 1];
+		round_key[(i * 4) + 2] = key[(i * 4) + 2];
+		round_key[(i * 4) + 3] = key[(i * 4) + 3];
+	}
+
+	/* All other round keys are found from the previous round keys. */
+	for (i = Nk; i < Nb * (Nr + 1); ++i) {
+		k = (i - 1) * 4;
+		tmp_a[0] = round_key[k + 0];
+		tmp_a[1] = round_key[k + 1];
+		tmp_a[2] = round_key[k + 2];
+		tmp_a[3] = round_key[k + 3];
+
+		if (!(i % Nk)) {
+			/*
+			 * Shifts the 4 bytes in a word to the left once.
+			 * [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+			 */
+			tmp_b = tmp_a[0];
+			tmp_a[0] = tmp_a[1];
+			tmp_a[1] = tmp_a[2];
+			tmp_a[2] = tmp_a[3];
+			tmp_a[3] = tmp_b;
+
+			tmp_a[0] = sbox[tmp_a[0]];
+			tmp_a[1] = sbox[tmp_a[1]];
+			tmp_a[2] = sbox[tmp_a[2]];
+			tmp_a[3] = sbox[tmp_a[3]];
+
+			tmp_a[0] = tmp_a[0] ^ rcon[i / Nk];
+		}
+
+		j = i * 4;
+		k = (i - Nk) * 4;
+		round_key[j + 0] = round_key[k + 0] ^ tmp_a[0];
+		round_key[j + 1] = round_key[k + 1] ^ tmp_a[1];
+		round_key[j + 2] = round_key[k + 2] ^ tmp_a[2];
+		round_key[j + 3] = round_key[k + 3] ^ tmp_a[3];
+	}
+}
+
+u8 crypto_key[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			       0x4, 0x5, 0x6, 0x7,
+			       0x8, 0x9, 0xa, 0xb,
+			       0xc, 0xd, 0xe, 0xf };
+
+u8 crypto_iv[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			      0x4, 0x5, 0x6, 0x7,
+			      0x8, 0x9, 0xa, 0xb,
+			      0xc, 0xd, 0xe, 0xf };
+
+void ilitek_decrypt(u8 *buf, u32 len)
+{
+	u8 key[AES_KEY_LEN];
+	u8 iv[AES_KEY_LEN];
+	u8 *tmp = buf;
+	u8 iv_tmp[AES_KEY_LEN];
+	struct crypto_aes_ctx ctx;
+
+	u32 i, j;
+
+	memcpy(key, crypto_key, AES_KEY_LEN);
+	memcpy(iv, crypto_iv, AES_KEY_LEN);
+
+	aes_expandkey(ctx.key_dec, key);
+
+	for (i = 0; i < len; i += AES_KEY_LEN, tmp += AES_KEY_LEN) {
+		memcpy(iv_tmp, tmp, AES_KEY_LEN);
+
+		aes_decrypt((state_t *)tmp, ctx.key_dec);
+		for (j = 0; j < AES_KEY_LEN; j++)
+			tmp[j] ^= iv[j];
+
+		memcpy(iv, iv_tmp, AES_KEY_LEN);
+	}
+}
+
+static void sha256_init(struct sha256_ctx *ctx)
+{
+	ctx->datalen = 0;
+	ctx->bitlen[0] = 0;
+	ctx->bitlen[1] = 0;
+
+	/*
+	 * 8 hash value for sha256
+	 * square root of prime number 2,3,5,7,11,13,17,19
+	 * then take 32 bit numbers after the decimal point
+	 */
+	ctx->state[0] = 0x6a09e667;
+	ctx->state[1] = 0xbb67ae85;
+	ctx->state[2] = 0x3c6ef372;
+	ctx->state[3] = 0xa54ff53a;
+	ctx->state[4] = 0x510e527f;
+	ctx->state[5] = 0x9b05688c;
+	ctx->state[6] = 0x1f83d9ab;
+	ctx->state[7] = 0x5be0cd19;
+}
+
+static void sha256_transform(struct sha256_ctx *ctx, u8 *data)
+{
+	static u32 key[64] = {
+		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	};
+
+	u32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+	for (i = 0, j = 0; i < 16; ++i, j += 4)
+		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+
+	for (; i < 64; ++i)
+		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+
+	a = ctx->state[0];
+	b = ctx->state[1];
+	c = ctx->state[2];
+	d = ctx->state[3];
+	e = ctx->state[4];
+	f = ctx->state[5];
+	g = ctx->state[6];
+	h = ctx->state[7];
+
+	for (i = 0; i < 64; ++i) {
+		t1 = h + EP1(e) + CH(e, f, g) + key[i] + m[i];
+		t2 = EP0(a) + MAJ(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + t1;
+		d = c;
+		c = b;
+		b = a;
+		a = t1 + t2;
+	}
+
+	ctx->state[0] += a;
+	ctx->state[1] += b;
+	ctx->state[2] += c;
+	ctx->state[3] += d;
+	ctx->state[4] += e;
+	ctx->state[5] += f;
+	ctx->state[6] += g;
+	ctx->state[7] += h;
+}
+
+static void sha256_update(struct sha256_ctx *ctx, u8 byte)
+{
+	ctx->data[ctx->datalen] = byte;
+	ctx->datalen++;
+	if (ctx->datalen == 64) {
+		sha256_transform(ctx, ctx->data);
+		DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], 512);
+		ctx->datalen = 0;
+	}
+}
+
+static void sha256_final(struct sha256_ctx *ctx, u8 *hash)
+{
+	u32 i;
+
+	i = ctx->datalen;
+
+	/* Pad whatever data is left in the buffer. */
+	if (ctx->datalen < 56) {
+		ctx->data[i++] = 0x80;
+		while (i < 56)
+			ctx->data[i++] = 0x00;
+	} else {
+		ctx->data[i++] = 0x80;
+		while (i < 64)
+			ctx->data[i++] = 0x00;
+		sha256_transform(ctx, ctx->data);
+		memset(ctx->data, 0, 56);
+	}
+
+	/* Append to the padding the total message's length in bits and transform. */
+	DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8);
+	ctx->data[63] = ctx->bitlen[0];
+	ctx->data[62] = ctx->bitlen[0] >> 8;
+	ctx->data[61] = ctx->bitlen[0] >> 16;
+	ctx->data[60] = ctx->bitlen[0] >> 24;
+	ctx->data[59] = ctx->bitlen[1];
+	ctx->data[58] = ctx->bitlen[1] >> 8;
+	ctx->data[57] = ctx->bitlen[1] >> 16;
+	ctx->data[56] = ctx->bitlen[1] >> 24;
+	sha256_transform(ctx, ctx->data);
+
+	/*
+	 * Since this implementation uses little endian byte ordering and SHA uses big endian,
+	 * reverse all the bytes when copying the final state to the output hash.
+	 */
+	for (i = 0; i < 4; ++i) {
+		hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+	}
+}
+
+void get_sha256(u32 start, u32 end, u8 *buf, u32 buf_size, u8 sha256[32])
+{
+	struct sha256_ctx ctx;
+	u32 i;
+
+	sha256_init(&ctx);
+
+	if (end >= buf_size || start > buf_size || end < start) {
+		TP_ERR(NULL, "start/end addr: %#x/%#x buf size: %#x OOB\n", start, end, buf_size);
+		return;
+	}
+
+	for (i = start; i <= end && i < buf_size; i++)
+		sha256_update(&ctx, buf[i]);
+
+	sha256_final(&ctx, sha256);
+
+	TP_MSG_ARR(NULL, "sha256:", TYPE_U8, 32, sha256);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.h b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
new file mode 100644
index 000000000000..ef01ca434776
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_CRYPTO_H__
+#define __ILITEK_CRYPTO_H__
+
+#include "ilitek_def.h"
+
+#define Nb	(4)
+#define Nk	(4)       /* The number of 32 bit words in a key. */
+#define Nr	(10)      /* The number of rounds in AES Cipher. */
+
+#define AES_KEY_LEN      (16) /* Key length in bytes */
+
+extern u8 crypto_key[AES_KEY_LEN];
+extern u8 crypto_iv[AES_KEY_LEN];
+
+#define DBL_INT_ADD(a, b, c)			\
+	do {					\
+		if ((a) > 0xffffffff - (c))	\
+			++(b);			\
+		(a) += (c);			\
+	} while (0)
+
+#define ROTLEFT(a, b)	(((a) << (b)) | ((a) >> (32 - (b))))
+#define ROTRIGHT(a, b)	(((a) >> (b)) | ((a) << (32 - (b))))
+
+#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
+#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
+#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
+
+struct sha256_ctx {
+	u8 data[64];
+	u32 datalen;
+	u32 bitlen[2];
+	u32 state[8];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL ilitek_decrypt(u8 *buf, u32 len);
+
+void __DLL get_sha256(u32 start, u32 end,
+		      u8 *buf, u32 buf_size, u8 sha256[32]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.c b/drivers/input/touchscreen/ilitek/ilitek_def.c
new file mode 100644
index 000000000000..e9934cac240d
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_def.h"
+
+int tp_log_level = log_level_msg;
+bool tp_print_en = true;
+FILE *tp_fp;
+
+char g_str[4096];
+msg_t g_msg;
+
+#if defined(__KERNEL__) || defined(__UEFI_DXE__)
+
+int get_time_ms(u32 *t_ms)
+{
+	*t_ms = 0;
+
+	return -EINVAL;
+}
+
+#else
+#ifdef _WIN32
+
+static int gettimeofday(struct timeval *tp, void *tzp)
+{
+	time_t clock;
+	struct tm tm;
+	SYSTEMTIME wtm;
+
+	GetLocalTime(&wtm);
+	tm.tm_year = wtm.wYear - 1900;
+	tm.tm_mon = wtm.wMonth - 1;
+	tm.tm_mday = wtm.wDay;
+	tm.tm_hour = wtm.wHour;
+	tm.tm_min = wtm.wMinute;
+	tm.tm_sec = wtm.wSecond;
+	tm.tm_isdst = -1;
+	clock = mktime(&tm);
+	tp->tv_sec = (long)clock;
+	tp->tv_usec = wtm.wMilliseconds * 1000;
+	return 0;
+}
+
+#endif
+
+int get_time_ms(u32 *t_ms)
+{
+	static u32 time_ms_init;
+	struct timeval t;
+	u32 time_ms;
+
+	gettimeofday(&t, NULL);
+	time_ms = t.tv_sec * 1000 + t.tv_usec / 1000;
+	time_ms_init = (!time_ms_init) ? time_ms : time_ms_init;
+
+	*t_ms = time_ms - time_ms_init;
+
+	return 0;
+}
+
+#endif
+
+void tp_log_arr(char *id, int level, const char *header, const char *tag,
+		int type, int len, void *buf)
+{
+	const int num = 64;
+	int i, idx = 0;
+	u32 time_ms;
+	int error;
+
+	if (level > tp_log_level || !buf)
+		return;
+
+	error = get_time_ms(&time_ms);
+
+	do {
+		_memset(g_str, 0, sizeof(g_str));
+
+		if (!error)
+			_sprintf(g_str, 0, "[%7u.%03u]",
+				time_ms / 1000, time_ms % 1000);
+
+		if (id)
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 "[" PFMT_C8 "] " PFMT_C8 " ",
+				 header, id, tag);
+		else
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 " " PFMT_C8 " ",
+				 header, tag);
+
+		for (i = 0; i < num && idx < len; i++, idx++) {
+			switch (type) {
+			default:
+			case TYPE_U8:
+				_sprintf(g_str, _strlen(g_str), "%02x-",
+					((u8 *)buf)[idx]);
+				break;
+			case TYPE_INT:
+				_sprintf(g_str, _strlen(g_str), "%d-",
+					((int *)buf)[idx]);
+				break;
+			}
+		}
+		_sprintf(g_str, _strlen(g_str) - 1, ", len: [%d/%d]\n",
+			idx, len);
+
+		if (tp_print_en)
+			TP_PRINTF(PFMT_C8, g_str);
+		if (g_msg)
+			g_msg(level, g_str);
+		TP_LOG(tp_fp, g_str);
+	} while (idx < len);
+}
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items)
+{
+	int error = 0;
+
+	MUTEX_INIT(q->mutex);
+
+	MUTEX_LOCK(q->mutex);
+
+	do {
+		q->item_size = item_size;
+		q->curr_size = 0;
+		q->max_size = max_items;
+
+		q->buf = (u8 *)CALLOC(max_items, item_size);
+		if (!q->buf) {
+			error = -ENOMEM;
+			break;
+		}
+
+		q->push_ptr = q->buf;
+		q->pop_ptr = q->buf;
+		q->end_ptr = q->buf + (max_items - 1) * item_size;
+	} while (false);
+
+	MUTEX_UNLOCK(q->mutex);
+
+	return error;
+}
+
+void queue_exit(struct queue *q)
+{
+	if (q->buf)
+		CFREE(q->buf);
+	MUTEX_EXIT(q->mutex);
+}
+
+void queue_push(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	/* Stop push data when queue is full */
+	if (q->curr_size >= q->max_size)
+		goto release_push_lock;
+
+	q->curr_size++;
+	if (q->push_ptr == q->end_ptr)
+		q->push_ptr = q->buf;
+	else
+		q->push_ptr += q->item_size;
+
+	if (q->push_ptr == q->pop_ptr)
+		TP_ERR(NULL, "[Warn]Queue overload, queue size: %u\n", q->curr_size);
+
+release_push_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void queue_pop(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	if (!q->curr_size)
+		goto release_pop_lock;
+
+	q->curr_size--;
+	if (q->pop_ptr == q->end_ptr)
+		q->pop_ptr = q->buf;
+	else
+		q->pop_ptr += q->item_size;
+
+release_pop_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void set_print_en(bool enable)
+{
+	tp_print_en = enable;
+}
+
+void set_log_level(int level)
+{
+	tp_log_level = level;
+}
+
+int set_log_fopen(WCHAR *filename)
+{
+	int error;
+
+	if (tp_fp)
+		return -EINVAL;
+
+	error = WFOPEN(&tp_fp, filename, "w+");
+	if (error < 0) {
+		tp_fp = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+void set_log_fclose(void)
+{
+	if (!tp_fp)
+		return;
+
+	_fclose(tp_fp);
+	tp_fp = NULL;
+}
+
+void set_log_fwrite(char *str)
+{
+	TP_LOG(tp_fp, str);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.h b/drivers/input/touchscreen/ilitek/ilitek_def.h
new file mode 100644
index 000000000000..d6f2077d7760
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.h
@@ -0,0 +1,554 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_DEF_H__
+#define __ILITEK_DEF_H__
+
+#define COMMONFLOW_CODE_VERSION		0x00000301
+
+/*
+ * Windows
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+#define __MAYBE_UNUSED
+
+#ifdef _WINDLL
+#define __DLL __declspec(dllexport)
+#else
+#define __DLL __declspec(dllimport)
+#endif
+
+#define __PACKED__
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	sprintf_s((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size)	strncpy_s((dst), (dst_size), (src), (n))
+#define _strcasecmp(l, r)		_stricmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat_s((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)		sscanf_s(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy_s((dst), (dst_size), (src))
+#define _strtok(str, del, next_token)	strtok_s((str), (del), (next_token))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				wchar_t
+#define WSTRING				wstring
+#define WCSCPY(dst, src, dst_size)	wcscpy_s((dst), (dst_size), (src))
+#define WCSCASECMP(str, tag)		_wcsicmp((str), (L##tag))
+#define WCSRCHR(str, ch)		wcsrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	swprintf_s((buf), (size), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)					\
+	((!((*(pfp)) = _wfsopen((filename), (const wchar_t *)(L##mode), \
+				_SH_DENYWR))) ? -EFAULT : 0)
+#define WACCESS(filename, mode)		_waccess((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)					\
+	do {							\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_U8TEXT);		\
+		fwprintf((fp), (fmt), ##__VA_ARGS__);		\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_TEXT);		\
+	} while (false)
+
+#define PFMT_C16			"%ls"
+#define PFMT_C8				"%hs"
+
+#include <codecvt>
+#define TO_WCHAR(x)	\
+	std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t > ().from_bytes(x).c_str()
+
+#define _localtime(ptm, ptime)		localtime_s((ptm), (ptime))
+
+#define _fopen(pfp, filename, mode)	\
+	(fopen_s((pfp), (filename), (const char *)(mode)))
+#define _fclose(pfp)			fclose((pfp))
+
+#define MUTEX_T				HANDLE
+#define MUTEX_INIT(x)			((x) = CreateMutex(NULL, false, NULL))
+#define MUTEX_LOCK(x)			(WaitForSingleObject((x), INFINITE))
+#define MUTEX_UNLOCK(x)			(ReleaseMutex((x)))
+#define MUTEX_EXIT(x)			(CloseHandle((x)))
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+/*
+ * UEFI Driver
+ */
+#elif defined(__UEFI_DXE__)
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+
+#define UEFI_MAX_STR_SIZE	0x1000
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	AsciiSPrint((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) \
+	AsciiStrnCpyS(dst, dst_size, src, (UINTN)n)
+#define _strcasecmp(l, r)		AsciiStriCmp((l), (r))
+#define _strcat(dst, src, dst_size)	\
+	AsciiStrCatS((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)
+#define _strlen(str)			AsciiStrnLenS((str), UEFI_MAX_STR_SIZE)
+#define _strcpy(dst, src, dst_size)	\
+	_strncpy((dst), (src), _strlen(src), (dst_size))
+#define _strtok(str, del, next_token)
+
+static __MAYBE_UNUSED char *_strrchr(const char *s, char c)
+{
+	char *found = NULL;
+
+	do {
+		if (*s == c)
+			found = (char *)s;
+	} while (*s++ != '\0');
+
+	return found;
+}
+
+#define _memset(ptr, ch, size)		\
+	SetMem((ptr), (UINTN)(size), (UINT8)(ch))
+#define _memcpy(dst, src, size)		\
+	CopyMem((dst), (src), (UINTN)(size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		_strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	(-EINVAL)
+#define WACCESS(filename, mode)		(-EINVAL)
+#define WFPRINTF(fp, fmt, ...)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%a"
+#define PFMT_C8				"%a"
+#define PFMT_U8				"%u"
+#define PFMT_U16			"%u"
+#define PFMT_X8				"%02x"
+#define PFMT_X16			"%04x"
+#define PFMT_X64			"%016llx"
+
+#define _localtime(ptm, ptime)
+#define _fclose(pfp)
+
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define MALLOC(size)		AllocatePool(size)
+#define CALLOC(num, size)	AllocateZeroPool(num * size)
+#define FREE(ptr)			\
+	do {				\
+		FreePool((ptr));	\
+		(ptr) = NULL;		\
+	} while (0)
+
+#define CFREE(ptr)		FREE(ptr)
+#define TP_PRINTF(fmt, ...)	DebugPrint(DEBUG_INFO, fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+/*
+ * Linux-Based System
+ */
+#else
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+#define _sprintf(buf, idx, fmt, ...)	sprintf((buf) + (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) strncpy((dst), (src), (n))
+#define _strcasecmp(l, r)		strcasecmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat((dst), (src))
+#define _sscanf(str, fmt, ...)		sscanf(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy((dst), (src))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define WTEXT(str)			str
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	_fopen(pfp, filename, mode)
+#define WACCESS(filename, mode)		_access((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)		fprintf((fp), (fmt), ##__VA_ARGS__)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%s"
+#define PFMT_C8				"%s"
+
+#define _localtime(ptm, ptime)						\
+		do {							\
+			struct tm *__tm__;				\
+									\
+			__tm__ = localtime((ptime));			\
+			_memcpy((ptm), __tm__, sizeof(struct tm));	\
+		} while (false)
+
+#ifdef __KERNEL__
+#define FILE				void
+#define _fopen(pfp, filename, mode)	(-EINVAL)
+#define _fclose(pfp)
+#define _access(filename, mode)		(-EINVAL)
+
+#define _strtok(str, del, next_token)
+
+#include <linux/spinlock.h>
+#define MUTEX_T				spinlock_t
+#define MUTEX_INIT(x)			spin_lock_init(&(x))
+#define MUTEX_LOCK(x)			spin_lock(&(x))
+#define MUTEX_UNLOCK(x)			spin_unlock(&(x))
+#define MUTEX_EXIT(x)
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#define MALLOC(size)		kmalloc(size, GFP_KERNEL)
+#define CALLOC(num, size)	vmalloc(num * size)
+#define FREE(ptr)		\
+	do {			\
+		kfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		\
+	do {			\
+		vfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+
+#define TP_PRINTF(fmt, ...)	printk(fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+#else
+#include <sys/time.h>
+#include <unistd.h>
+
+#define _fopen(pfp, filename, mode)	\
+	((!((*(pfp)) = fopen((filename), (mode)))) ? -EFAULT : 0)
+#define _fclose(pfp)			fclose((pfp))
+#define _access(filename, mode)		access((filename), (mode))
+
+#define _strtok(str, del, next_token)	strtok((str), (del))
+
+#ifdef __UEFI_APP__
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+#else
+#include <pthread.h>
+#define MUTEX_T				pthread_mutex_t
+#define MUTEX_INIT(x)			(pthread_mutex_init(&(x), NULL))
+#define MUTEX_LOCK(x)			(pthread_mutex_lock(&(x)))
+#define MUTEX_UNLOCK(x)			(pthread_mutex_unlock(&(x)))
+#define MUTEX_EXIT(x)			(pthread_mutex_destroy(&(x)))
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#define TP_PRINTF(fmt, ...)							\
+	__android_log_print(ANDROID_LOG_INFO, "ILITEK COMMON", "[%s][%d]" fmt,	\
+			    __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _WIN32 */
+
+#define U82U64(byte, order)	\
+	((u64)((u64)(byte) << ((order) * 8)))
+
+#ifndef PFMT_C16
+#define PFMT_C16			"%ls"
+#endif
+#ifndef PFMT_C8
+#define PFMT_C8				"%hs"
+#endif
+#ifndef PFMT_U8
+#define PFMT_U8				"%hhu"
+#endif
+#ifndef PFMT_U16
+#define PFMT_U16			"%hu"
+#endif
+#ifndef PFMT_X8
+#define PFMT_X8				"%hhx"
+#endif
+#ifndef PFMT_X16
+#define PFMT_X16			"%hx"
+#endif
+
+#ifndef PFMT_X64
+#define PFMT_X64			"%llx"
+#endif
+
+#ifndef TP_PRINTF
+#define TP_PRINTF(fmt, ...)				\
+	do {						\
+		printf(fmt, ##__VA_ARGS__);		\
+		fflush(stdout);				\
+	} while (0)
+#endif
+
+#ifndef TP_LOG
+#define TP_LOG(fp, str)				\
+	do {					\
+		if (!fp)			\
+			break;			\
+		fprintf((fp), PFMT_C8, (str));	\
+		fflush((fp));			\
+	} while (0)
+#endif
+
+#ifndef TP_PRINT
+#define TP_PRINT(_id, level, need_tag, tag, fmt, ...)			\
+	do {								\
+		char *__id__ = (_id);					\
+		u32 __time_ms__;					\
+									\
+		if (level > tp_log_level)				\
+			break;						\
+									\
+		g_str[0] = '\0';					\
+									\
+		if (need_tag) {						\
+			if (!get_time_ms(&__time_ms__))			\
+				_sprintf(g_str, _strlen(g_str),		\
+					"[%7u.%03u]",			\
+					__time_ms__ / 1000,		\
+					__time_ms__ % 1000);		\
+			_sprintf(g_str, _strlen(g_str), PFMT_C8, tag);	\
+		}							\
+									\
+		if (__id__) {						\
+			_sprintf(g_str, _strlen(g_str),			\
+				 "[" PFMT_C8 "] " fmt,			\
+				 __id__, ##__VA_ARGS__);		\
+		} else {						\
+			_sprintf(g_str, _strlen(g_str), " " fmt,	\
+				 ##__VA_ARGS__);			\
+		}							\
+									\
+		if (tp_print_en)					\
+			TP_PRINTF(PFMT_C8, g_str);			\
+		if (g_msg)						\
+			g_msg(level, g_str);				\
+		TP_LOG(tp_fp, g_str);					\
+	} while (0)
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d)	(((n) + (d) - 1) / (d))
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x)		((void)(x))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a)		((sizeof(a) / sizeof(*(a))))
+#endif
+
+#ifndef MIN
+#define MIN(l, r)		(((l) > (r)) ? (r) : (l))
+#endif
+
+#ifndef MAX
+#define MAX(l, r)		(((l) > (r)) ? (l) : (r))
+#endif
+
+#define EILICOMM		200
+#define EILIBUSY		201
+#define EILITIME		202
+#define EILIPROTO		203
+
+enum ilitek_log_level {
+	log_level_none = -1,	/* no log displayed */
+	log_level_err = 0,	/* critical errors */
+	log_level_warn,		/* warnings */
+	log_level_tag,		/* special-required tags */
+	log_level_info,		/* important/UI messages */
+	log_level_msg,		/* non-important messages */
+	log_level_dbg,		/* debugging messages */
+	log_level_pkt,		/* tx/rx packets */
+
+	log_level_max,		/* sentinel */
+};
+
+#define _TP_ERR(fmt, ...)	\
+	TP_PRINT(NULL, log_level_err, false, "", fmt, ##__VA_ARGS__)
+#define _TP_WARN(fmt, ...)	\
+	TP_PRINT(NULL, log_level_warn, false, "", fmt, ##__VA_ARGS__)
+#define _TP_TAG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_tag, false, "", fmt, ##__VA_ARGS__)
+#define _TP_INFO(fmt, ...)	\
+	TP_PRINT(NULL, log_level_info, false, "", fmt, ##__VA_ARGS__)
+#define _TP_MSG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_msg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_DBG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_dbg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_PKT(fmt, ...)	\
+	TP_PRINT(NULL, log_level_pkt, false, "", fmt, ##__VA_ARGS__)
+
+
+#define TP_ERR(id, fmt, ...)	\
+	TP_PRINT(id, log_level_err, true, "[ILITEK][ERR]", fmt, ##__VA_ARGS__)
+#define TP_WARN(id, fmt, ...)	\
+	TP_PRINT(id, log_level_warn, true, "[ILITEK][WARN]", fmt, ##__VA_ARGS__)
+#define TP_TAG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_tag, true, "[ILITEK][TAG]", fmt, ##__VA_ARGS__)
+#define TP_INFO(id, fmt, ...)	\
+	TP_PRINT(id, log_level_info, true, "[ILITEK][INFO]", fmt, ##__VA_ARGS__)
+#define TP_MSG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_msg, true, "[ILITEK][MSG]", fmt, ##__VA_ARGS__)
+#define TP_DBG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_dbg, true, "[ILITEK][DBG]", fmt, ##__VA_ARGS__)
+#define TP_PKT(id, fmt, ...)	\
+	TP_PRINT(id, log_level_pkt, true, "[ILITEK][PKT]", fmt, ##__VA_ARGS__)
+
+enum ilitek_array_type {
+	TYPE_U8 = 0,
+	TYPE_INT,
+	TYPE_U32,
+};
+
+#define TP_ERR_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_err, "[ILITEK][ERR]", tag, type, len, buf)
+#define TP_WARN_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_warn, "[ILITEK][WARN]", tag, type, len, buf)
+#define TP_TAG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_tag, "[ILITEK][TAG]", tag, type, len, buf)
+#define TP_INFO_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_info, "[ILITEK][INFO]", tag, type, len, buf)
+#define TP_MSG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_msg, "[ILITEK][MSG]", tag, type, len, buf)
+#define TP_DBG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_dbg, "[ILITEK][DBG]", tag, type, len, buf)
+#define TP_PKT_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_pkt, "[ILITEK][PKT]", tag, type, len, buf)
+
+extern int tp_log_level;
+extern bool tp_print_en;
+extern char g_str[4096];
+extern FILE *tp_fp;
+
+typedef void (*msg_t)(int, char *);
+extern msg_t g_msg;
+
+struct queue {
+	u32 curr_size;
+	u32 max_size;
+	u8 *buf;
+	u8 *push_ptr;
+	u8 *pop_ptr;
+	u8 *end_ptr;
+	u32 item_size;
+
+	MUTEX_T mutex;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL tp_log_arr(char *id, int level, const char *header,
+		      const char *tag, int type, int len, void *buf);
+
+int __DLL get_time_ms(u32 *t_ms);
+void __DLL set_print_en(bool enable);
+void __DLL set_log_level(int level);
+int __DLL set_log_fopen(WCHAR *filename);
+void __DLL set_log_fclose(void);
+void __DLL set_log_fwrite(char *str);
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items);
+void queue_exit(struct queue *q);
+void queue_push(struct queue *q);
+void queue_pop(struct queue *q);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_main.c b/drivers/input/touchscreen/ilitek/ilitek_main.c
new file mode 100644
index 000000000000..9bb1fe6aa4b7
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_main.c
@@ -0,0 +1,2198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+struct ilitek_ts_data *ts;
+
+u8 driver_ver[] = {
+	DRIVER_VERSION_0, DRIVER_VERSION_1, DRIVER_VERSION_2, DRIVER_VERSION_3,
+	CUSTOMER_H_ID, CUSTOMER_L_ID, TEST_VERSION,
+};
+
+static bool checksum_failed_release = ILITEK_CHECKSUM_FAILED_RELEASE;
+module_param(checksum_failed_release, bool, 0664);
+MODULE_PARM_DESC(checksum_failed_release,
+		 "When packet's checksum is wrong, (default)release all touch point or ignore the packet");
+
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+extern struct tpd_device *tpd;
+#ifdef ILITEK_ENABLE_DMA
+static u8 *I2CDMABuf_va;
+static dma_addr_t I2CDMABuf_pa;
+#endif
+#endif
+
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+struct wake_lock ilitek_wake_lock;
+#endif
+
+#ifdef ILITEK_TUNING_MESSAGE
+static struct sock *ilitek_netlink_sock;
+bool ilitek_debug_flag;
+static u_int ilitek_pid = 100, ilitek_seq = 23;
+#endif
+
+static void __maybe_unused ilitek_udp_reply(void *payload, int size)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int len = NLMSG_SPACE(size);
+	int ret;
+	int pid = ilitek_pid, seq = ilitek_seq;
+
+	TP_DBG(NULL, "[%s] ilitek_debug_flag: %d\n", __func__, ilitek_debug_flag);
+	if (!ilitek_debug_flag)
+		return;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		TP_ERR(NULL, "alloc skb error\n");
+		return;
+	}
+
+	nlh = nlmsg_put(skb, pid, seq, 0, size, 0);
+	if (!nlh)
+		goto nlmsg_failure;
+
+	nlh->nlmsg_flags = 0;
+	memcpy(NLMSG_DATA(nlh), payload, size);
+
+	NETLINK_CB(skb).portid = 0;	/* from kernel */
+	NETLINK_CB(skb).dst_group = 0;	/* unicast */
+
+	ret = netlink_unicast(ilitek_netlink_sock, skb, pid, MSG_DONTWAIT);
+	if (ret < 0)
+		TP_ERR(NULL, "ilitek send failed, ret: %d\n", ret);
+	return;
+
+nlmsg_failure:
+	kfree_skb(skb);
+
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void __maybe_unused udp_receive(struct sk_buff *skb)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	int count = 0, ret = 0, i = 0;
+	u8 *data;
+	struct nlmsghdr *nlh;
+
+	nlh = (struct nlmsghdr *)skb->data;
+	ilitek_pid = NETLINK_CREDS(skb)->pid;
+	ilitek_seq = nlh->nlmsg_seq;
+
+	TP_DBG(NULL, "netlink received, pid: %d, seq: %d\n",
+		     ilitek_pid, ilitek_seq);
+
+	data = (u8 *) NLMSG_DATA(nlh);
+	count = nlmsg_len(nlh);
+	if (!strcmp(data, "Open!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = true;
+		ilitek_udp_reply(data, sizeof("Open!"));
+	} else if (!strcmp(data, "Close!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = false;
+	} else if (!strcmp(data, "Wifi_Paint_Start") ||
+		   !strcmp(data, "Daemon_Debug_Start")) {
+		ilitek_debug_flag = true;
+	} else if (!strcmp(data, "Wifi_Paint_End") ||
+		   !strcmp(data, "Daemon_Debug_End")) {
+		ilitek_debug_flag = false;
+	}
+
+
+	TP_DBG(NULL, "count = %d  data[count -3] = %d data[count -2] = %c\n",
+	       count, data[count - 3], data[count - 2]);
+	for (i = 0; i < count; i++)
+		//TP_MSG(NULL, "data[%d] = 0x%x\n", i, data[i]);
+	if (data[count - 2] == 'I' && (count == 20 || count == 52) &&
+	    data[0] == 0x77 && data[1] == 0x77) {
+		TP_DBG(NULL, "IOCTL_WRITE CMD = %d\n", data[2]);
+		switch (data[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", data[3]);
+			if (data[3] == 0)
+				ilitek_debug_flag = false;
+			else if (data[3] == 1)
+				ilitek_debug_flag = true;
+			break;
+		case 15:
+			if (data[3] == 0) {
+				ilitek_irq_disable();
+				TP_DBG(NULL, "ilitek_irq_disable.\n");
+			} else {
+				ilitek_irq_enable();
+				TP_DBG(NULL, "ilitek_irq_enable.\n");
+			}
+			break;
+		case 16:
+			ts->operation_protection = data[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 8:
+			TP_MSG(NULL, "get driver version\n");
+			ilitek_udp_reply(driver_ver, 7);
+			break;
+		case 18:
+			TP_DBG(NULL, "firmware update write 33 bytes data\n");
+			ret = ilitek_write(&data[3], 33);
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			if (ret < 0)
+				data[0] = 1;
+			else
+				data[0] = 0;
+			ilitek_udp_reply(data, 1);
+			return;
+		default:
+			return;
+		}
+	} else if (data[count - 2] == 'W') {
+		ret = ilitek_write(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+		if (ret < 0)
+			data[0] = 1;
+		else
+			data[0] = 0;
+		ilitek_udp_reply(data, 1);
+	} else if (data[count - 2] == 'R') {
+		ret = ilitek_read(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		if (ret < 0)
+			data[count - 2] = 1;
+		else
+			data[count - 2] = 0;
+		ilitek_udp_reply(data, count - 1);
+	}
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void ilitek_esd_check(struct work_struct *work)
+{
+	int retry = 3;
+	static bool is_first_run = true;
+	static u32 protocol_ver;
+
+	/*
+	 * update protocol version at the first run
+	 */
+	if (is_first_run) {
+		is_first_run = false;
+		protocol_ver = ts->dev->protocol.ver;
+		TP_MSG(NULL, "[ESD] firstly loading protocol ver: %x as ref.\n",
+			protocol_ver);
+	}
+
+	if (ts->operation_protection || ts->esd_skip) {
+		TP_MSG(NULL, "[ESD] operation_protection: %hhu, esd_skip: %hhu\n",
+			ts->operation_protection, ts->esd_skip);
+		goto skip_return;
+	}
+
+	mutex_lock(&ts->ilitek_mutex);
+
+	for (; retry-- > 0;) {
+		if (api_protocol_set_cmd(ts->dev, GET_PTL_VER, NULL) < 0) {
+			TP_ERR(NULL, "[ESD] i2c comm. failed\n");
+			continue;
+		}
+
+		if (protocol_ver != ts->dev->protocol.ver) {
+			TP_ERR(NULL, "unexpected ptl ver (referance)%x vs. %x\n",
+				protocol_ver, ts->dev->protocol.ver);
+			continue;
+		}
+
+		goto pass_return;
+	}
+
+	ilitek_reset(ts->dev->reset_time);
+
+pass_return:
+	mutex_unlock(&ts->ilitek_mutex);
+
+skip_return:
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+}
+
+void ilitek_irq_enable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	enable_irq(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 1);
+}
+
+void ilitek_irq_disable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (!atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	disable_irq_nosync(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 0);
+}
+
+#ifdef ILITEK_ENABLE_DMA
+static int ilitek_dma_i2c_read(u8 *buf, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int err;
+
+
+	if (len < 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_recv(client, buf, len);
+	}
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+	err = i2c_master_recv(client, (u8 *)I2CDMABuf_pa, len);
+	if (err < 0)
+		return err;
+
+	memcpy(buf, I2CDMABuf_va, len);
+
+	return 0;
+}
+
+static int ilitek_dma_i2c_write(u8 *cmd, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+
+	if (len <= 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_send(client, cmd, len);
+	}
+
+	memcpy(I2CDMABuf_va, cmd, len);
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+
+	return i2c_master_send(client, (u8 *)I2CDMABuf_pa, len);
+}
+#endif
+
+static int ilitek_i2c_transfer(struct i2c_msg *msgs, int cnt)
+{
+	int err = 0;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int count = ILITEK_I2C_RETRY_COUNT;
+
+#ifdef ILITEK_ENABLE_DMA
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		count = ILITEK_I2C_RETRY_COUNT;
+		while (count-- >= 0) {
+			msgs[i].ext_flag = 0;
+			if (msgs[i].flags == I2C_M_RD)
+				err = ilitek_dma_i2c_read(msgs[i].buf, msgs[i].len);
+			else if (!msgs[i].flags)
+				err = ilitek_dma_i2c_write(msgs[i].buf, msgs[i].len);
+
+			if (err < 0) {
+				TP_ERR(NULL, "i2c[0x%hx] dma tx/rx failed, err: %d\n",
+					msgs[i].addr, err);
+				mdelay(20);
+				continue;
+			}
+
+			break;
+		}
+	}
+#else
+	while (count-- >= 0) {
+		err = i2c_transfer(client->adapter, msgs, cnt);
+		if (err < 0) {
+			TP_ERR(NULL, "i2c[0x%hx] tx/rx failed, err: %d\n",
+				msgs[0].addr, err);
+			mdelay(20);
+			continue;
+		}
+		break;
+	}
+#endif
+
+	return err;
+}
+
+static int __maybe_unused ilitek_i2c_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error;
+
+	/*
+	 * Default ILITEK_BL_ADDR. is firstly used.
+	 * if communication failed, change between BL addr. and
+	 * other addr. defined by DTS, then retry.
+	 */
+	static unsigned short addr = ILITEK_BL_ADDR;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	struct i2c_msg msgs[2] = {
+		{.addr = addr, .flags = 0, .len = w_len,
+		 .buf = cmd, SCL_RATE(400000)},
+		{.addr = addr, .flags = I2C_M_RD, .len = r_len,
+		 .buf = buf, SCL_RATE(400000)}
+	};
+
+	/*
+	 * IMPORTANT: If I2C repeat start is required, please check with ILITEK.
+	 */
+	if (w_len > 0 && r_len > 0 && !delay_ms) {
+		if (ilitek_i2c_transfer(msgs, 2) < 0) {
+			/* try another i2c addr. (default: 0x41) */
+			addr = (addr == ILITEK_BL_ADDR) ?
+				client->addr : ILITEK_BL_ADDR;
+			msgs[0].addr = msgs[1].addr = addr;
+
+			return ilitek_i2c_transfer(msgs, 2);
+		}
+
+		return 0;
+	}
+
+	if (w_len > 0 && ilitek_i2c_transfer(msgs, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		error = ilitek_i2c_transfer(msgs, 1);
+		if (error < 0)
+			return error;
+	}
+
+	if (delay_ms > 0)
+		mdelay(delay_ms);
+
+	if (r_len > 0 && ilitek_i2c_transfer(msgs + 1, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		return ilitek_i2c_transfer(msgs + 1, 1);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_i2c_write(u8 *cmd, int len)
+{
+	return ilitek_i2c_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_i2c_read(u8 *buf, int len)
+{
+	return ilitek_i2c_write_and_read(NULL, 0, 0, buf, len);
+}
+
+static int __maybe_unused ilitek_spi_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error = 0;
+	u8 *wbuf, *rbuf;
+	struct spi_device *spi = (struct spi_device *)ts->client;
+	struct spi_transfer xfer = {
+		.len = r_len + 4,
+		.speed_hz = ((struct spi_device *)ts->client)->max_speed_hz,
+	};
+	struct spi_message msg;
+
+	if (w_len > 0 && r_len > 0) {
+		error = ilitek_spi_write_and_read(cmd, w_len, delay_ms,
+						  NULL, 0);
+		if (error < 0)
+			return error;
+
+		return ilitek_spi_write_and_read(NULL, 0, 0, buf, r_len);
+	}
+
+	wbuf = CALLOC(4096, sizeof(u8));
+	rbuf = CALLOC(4096, sizeof(u8));
+
+	if (!wbuf || !rbuf) {
+		error = -ENOMEM;
+		goto exit;
+	}
+
+	xfer.tx_buf = wbuf;
+	xfer.rx_buf = rbuf;
+
+	wbuf[1] = 0xAA;
+
+	/* wbuf[0] set as 0x83 for spi data read */
+	if (r_len > 0) {
+		wbuf[0] = 0x83;
+		memset(wbuf + 2, 0, xfer.len - 2);
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfer, &msg);
+		error = spi_sync(spi, &msg);
+		if (error < 0)
+			goto exit;
+
+		TP_DBG(NULL, "[rbuf]: %*phD, len: %d\n", xfer.len, rbuf, xfer.len);
+
+		memcpy(buf, rbuf + 4, r_len);
+	} else if (w_len > 0) {
+		wbuf[0] = 0x82;
+		wbuf[2] = cmd[0];
+		wbuf[3] = 0;
+		memcpy(wbuf + 4, cmd + 1, w_len - 1);
+
+		TP_DBG(NULL, "[wbuf]: %*phD, len: %d\n", 3 + w_len, wbuf, 3 + w_len);
+
+		error = spi_write(spi, wbuf, 3 + w_len);
+		if (error < 0)
+			goto exit;
+
+		if (delay_ms > 0)
+			mdelay(delay_ms);
+	}
+
+exit:
+	CFREE(wbuf);
+	CFREE(rbuf);
+
+	return (error < 0) ? error : 0;
+}
+
+static int __maybe_unused ilitek_spi_write(u8 *cmd, int len)
+{
+	return ilitek_spi_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_spi_read(u8 *buf, int len)
+{
+	return ilitek_spi_write_and_read(NULL, 0, 0, buf, len);
+}
+
+int ilitek_write(u8 *cmd, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write(cmd, len);
+#else
+	error = ilitek_i2c_write(cmd, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_read(u8 *buf, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_read(buf, len);
+#else
+	error = ilitek_i2c_read(buf, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#else
+	error = ilitek_i2c_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+void __maybe_unused ilitek_gpio_dbg(void)
+{
+#if defined(ILITEK_GPIO_DEBUG)
+	gpio_direction_output(ts->test_gpio, 0);
+	mdelay(1);
+	gpio_direction_output(ts->test_gpio, 1);
+#endif
+}
+
+void ilitek_reset(int delay)
+{
+	TP_MSG(NULL, "reset_gpio: %d, delay: %d\n", ts->reset_gpio, delay);
+
+	ilitek_irq_disable();
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK && defined(MTK_UNDTS)
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ZERO);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(delay);
+#elif ILITEK_PLAT == ILITEK_PLAT_MTK
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 0);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#else
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 0);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#endif
+
+	ilitek_irq_enable();
+}
+
+static int ilitek_free_gpio(void)
+{
+
+#ifndef MTK_UNDTS
+	if (gpio_is_valid(ts->reset_gpio)) {
+		TP_MSG(NULL, "reset_gpio is valid so free\n");
+		gpio_free(ts->reset_gpio);
+	}
+	if (gpio_is_valid(ts->irq_gpio)) {
+		TP_MSG(NULL, "irq_gpio is valid so free\n");
+		gpio_free(ts->irq_gpio);
+	}
+#endif
+
+#if defined(ILITEK_GPIO_DEBUG)
+	if (gpio_is_valid(ts->test_gpio)) {
+		TP_MSG(NULL, "test_gpio is valid so free\n");
+		gpio_free(ts->test_gpio);
+	}
+#endif
+
+	return 0;
+}
+
+static int ilitek_request_pen_input_dev(void)
+{
+	int error;
+	struct input_dev *input;
+
+	int x_min = ts->dev->screen_info.pen_x_min;
+	int y_min = ts->dev->screen_info.pen_y_min;
+	int x_max = ts->dev->screen_info.pen_x_max;
+	int y_max = ts->dev->screen_info.pen_y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	TP_DBG(NULL, "registering pen input device\n");
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOOL_PEN, input->keybit);		/* In Range */
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);	/* Invert */
+	__set_bit(BTN_STYLUS, input->keybit);		/* Barrel Switch */
+	__set_bit(BTN_TOUCH, input->keybit);		/* Tip Switch */
+
+	input->name = "ILITEK STYLUS";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
+
+	input_set_abs_params(input, ABS_PRESSURE,
+			     ts->dev->screen_info.pressure_min,
+			     ts->dev->screen_info.pressure_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_X,
+			     ts->dev->screen_info.x_tilt_min,
+			     ts->dev->screen_info.x_tilt_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_Y,
+			     ts->dev->screen_info.y_tilt_min,
+			     ts->dev->screen_info.y_tilt_max, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "register pen device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+
+	ts->pen_input_dev = input;
+
+	return 0;
+}
+
+static int ilitek_request_input_dev(void)
+{
+	int error;
+	int i;
+	struct input_dev *input;
+
+#ifdef ILITEK_USE_MTK_INPUT_DEV
+	input = tpd->dev;
+	if (!input)
+		return -ENOMEM;
+#ifdef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
+			input_set_capability(input, EV_KEY,
+					     tpd_dts_data.tpd_key_local[i]);
+	}
+#endif
+#else
+	int x_min = ts->dev->screen_info.x_min;
+	int y_min = ts->dev->screen_info.y_min;
+	int x_max = ts->dev->screen_info.x_max;
+	int y_max = ts->dev->screen_info.y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+#endif
+
+	TP_DBG(NULL, "registering touch input device\n");
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	INPUT_MT_INIT_SLOTS(input, MAX(2, ts->dev->tp_info.max_fingers));
+#else
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0,
+			     ts->dev->tp_info.max_fingers, 0, 0);
+#endif
+
+#ifdef ILITEK_REPORT_PRESSURE
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+#endif
+
+	for (i = 0; i < ts->dev->tp_info.key_num; i++)
+		set_bit(ts->dev->key.info.keys[i].id & KEY_MAX, input->keybit);
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+#ifndef ILITEK_USE_MTK_INPUT_DEV
+	input->name = ILITEK_TS_NAME;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x_min = 0; y_min = 0;
+	x_max = TOUCH_SCREEN_X_MAX; y_max = TOUCH_SCREEN_Y_MAX;
+#endif
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 32767, 0, 0);
+	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 32767, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "input_register_device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+#endif
+
+	ts->input_dev = input;
+
+	if (ts->dev->tp_info.pen_modes)
+		ilitek_request_pen_input_dev();
+
+	return 0;
+}
+
+static int ilitek_touch_down(int id, int x, int y, int p, int h, int w)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x = (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX /
+		(ts->dev->screen_info.x_max - ts->dev->screen_info.x_min);
+	y = (y - ts->dev->screen_info.y_min) * TOUCH_SCREEN_Y_MAX /
+		(ts->dev->screen_info.y_max - ts->dev->screen_info.y_min);
+#endif
+
+	input_report_key(input, BTN_TOUCH, 1);
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_mt_slot(input, id);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+#endif
+	input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+	input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+	input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, h);
+	input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, w);
+#ifdef ILITEK_REPORT_PRESSURE
+	input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+#endif
+#ifndef ILITEK_TOUCH_PROTOCOL_B
+	input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id);
+	input_mt_sync(input);
+#endif
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(x, y, 1);
+			TP_DBG(NULL, "tpd_button(x, y, 1) = tpd_button(%d, %d, 1)\n", x, y);
+		}
+	}
+#endif
+#endif
+#endif
+	return 0;
+}
+
+static int ilitek_touch_release(int id)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	if (ts->touch_flag[id] == 1) {
+		TP_DBG(NULL, "release point id = %d\n", id);
+		input_mt_slot(input, id);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+#else
+	input_report_key(input, BTN_TOUCH, 0);
+	input_mt_sync(input);
+#endif
+	set_arr(ts->touch_flag, id, 0);
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() ||
+		    RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(0, 0, 0);
+			TP_DBG(NULL, "tpd_button(x, y, 0) = tpd_button(0, 0, 0)\n");
+		}
+	}
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int ilitek_touch_release_all_point(void)
+{
+	struct input_dev *input = ts->input_dev;
+	int i = 0;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_report_key(input, BTN_TOUCH, 0);
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		ilitek_touch_release(i);
+#else
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		set_arr(ts->touch_flag, i, 0);
+	ilitek_touch_release(0);
+#endif
+	ts->is_touched = false;
+	input_sync(input);
+	return 0;
+}
+
+static int ilitek_check_key_down(int x, int y)
+{
+	int j;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if ((x >= ts->dev->key.info.keys[j].x &&
+		     x <= ts->dev->key.info.keys[j].x +
+			ts->dev->key.info.x_len) &&
+		    (y >= ts->dev->key.info.keys[j].y &&
+		     y <= ts->dev->key.info.keys[j].y +
+			ts->dev->key.info.y_len)) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 1);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				x = tpd_dts_data.tpd_key_dim_local[j].key_x;
+				y = tpd_dts_data.tpd_key_dim_local[j].key_y;
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key down\n",
+					j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_down(0, x, y, 10, 128, 1);
+			}
+#else
+			x = touch_key_point_maping_array[j].point_x;
+			y = touch_key_point_maping_array[j].point_y;
+			ilitek_touch_down(0, x, y, 10, 128, 1);
+#endif
+#endif
+			ts->dev->key.clicked[j] = true;
+			ts->touch_key_hold_press = true;
+			ts->is_touched = true;
+			TP_DBG(NULL, "Key, Keydown ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int ilitek_check_key_release(int x, int y, int check_point)
+{
+	int j = 0;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if (!ts->dev->key.clicked[j])
+			continue;
+
+		if (check_point) {
+			if (x < ts->dev->key.info.keys[j].x ||
+			    x > ts->dev->key.info.keys[j].x + ts->dev->key.info.x_len ||
+			    y < ts->dev->key.info.keys[j].y ||
+			    y > ts->dev->key.info.keys[j].y + ts->dev->key.info.y_len) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+				input_report_key(ts->input_dev,
+						 ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+				if (tpd_dts_data.use_tpd_button) {
+					TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+					ilitek_touch_release(0);
+				}
+#else
+				ilitek_touch_release(0);
+#endif
+#endif
+				ts->dev->key.clicked[j] = false;
+				ts->touch_key_hold_press = false;
+				TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+					ts->dev->key.info.keys[j].id, x, y,
+					ts->dev->key.clicked[j]);
+				break;
+			}
+		} else {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_release(0);
+			}
+#else
+			ilitek_touch_release(0);
+#endif
+#endif
+			ts->dev->key.clicked[j] = false;
+			ts->touch_key_hold_press = false;
+			TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+int event_spacing;
+static u8 finger_state;
+static int start_x;
+static int start_y;
+static int current_x;
+static int current_y;
+
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+static struct timeval start_event_time;
+#else
+unsigned long start_event_time_jiffies;
+#endif
+
+static int ilitek_get_time_diff(void)
+{
+	int diff_milliseconds = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+	struct timeval time_now;
+
+	do_gettimeofday(&time_now);
+	diff_milliseconds += (time_now.tv_sec - start_event_time.tv_sec) * 1000;
+
+	if (time_now.tv_usec < start_event_time.tv_usec) {
+		diff_milliseconds -= 1000;
+		diff_milliseconds += (1000 * 1000 + time_now.tv_usec - start_event_time.tv_usec) / 1000;
+	} else
+		diff_milliseconds += (time_now.tv_usec - start_event_time.tv_usec) / 1000;
+
+	if (diff_milliseconds < (-10000))
+		diff_milliseconds = 10000;
+	TP_MSG(NULL, "time_now.tv_sec = %d start_event_time.tv_sec = %d time_now.tv_usec = %d start_event_time.tv_usec = %d diff_milliseconds = %d\n",
+			(int)time_now.tv_sec, (int)start_event_time.tv_sec, (int)time_now.tv_usec, (int)start_event_time.tv_usec, diff_milliseconds);
+#else
+	diff_milliseconds = jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_event_time_jiffies);
+	TP_MSG(NULL, "jiffies_to_msecs(jiffies) = %u jiffies_to_msecs(start_event_time_jiffies) = %u diff_milliseconds = %d\n", jiffies_to_msecs(jiffies),
+			jiffies_to_msecs(start_event_time_jiffies), diff_milliseconds);
+#endif
+	return diff_milliseconds;
+}
+
+static u8 ilitek_double_click_touch(int finger_id, int x, int y,
+					 u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_id > 0) {
+		finger_state = 0;
+		goto out;
+	}
+	if (finger_state == 0 || finger_state == 5) {
+
+		finger_state = 1;
+		start_x = x;
+		start_y = y;
+		current_x = 0;
+		current_y = 0;
+		event_spacing = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+		do_gettimeofday(&start_event_time);
+#else
+		start_event_time_jiffies = jiffies;
+#endif
+	} else if (finger_state == 1) {
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 4;
+	} else if (finger_state == 2) {
+		finger_state = 3;
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > (DOUBLE_CLICK_ONE_CLICK_USED_TIME + DOUBLE_CLICK_NO_TOUCH_TIME))
+			finger_state = 0;
+	} else if (finger_state == 3) {
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_TOTAL_USED_TIME) {
+			start_x = current_x;
+			start_y = current_y;
+			finger_state = 4;
+		}
+	}
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+static u8 ilitek_double_click_release(u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_state == 1) {
+		finger_state = 2;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 0;
+	}
+	if (finger_state == 3) {
+		event_spacing = ilitek_get_time_diff();
+		if ((event_spacing < DOUBLE_CLICK_TOTAL_USED_TIME && event_spacing > 50) && (ABSSUB(current_x, start_x) < DOUBLE_CLICK_DISTANCE)
+				&& ((ABSSUB(current_y, start_y) < DOUBLE_CLICK_DISTANCE))) {
+			finger_state = 5;
+			goto out;
+		} else
+			finger_state = 0;
+	} else if (finger_state == 4)
+		finger_state = 0;
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y)
+{
+	struct input_dev *input = ts->input_dev;
+
+	if (ts->gesture_status == Gesture_Double_Click) {
+		if (touch) {
+			finger_state = ilitek_double_click_touch(idx, x, y, finger_state);
+			return;
+		}
+		finger_state = ilitek_double_click_release(finger_state);
+
+		if (finger_state != 5)
+			return;
+	}
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_timeout(&ilitek_wake_lock, 5 * HZ);
+#endif
+
+	/* prevent power key being triggered multiple times */
+	if (ts->power_key_triggered)
+		return;
+
+	input_report_key(input, KEY_POWER, 1);
+	input_sync(input);
+	input_report_key(input, KEY_POWER, 0);
+	input_sync(input);
+
+	ts->power_key_triggered = true;
+}
+
+static void ilitek_report_touch_event(struct touch_data *touch, void *_private)
+{
+	struct input_dev *input = ts->input_dev;
+	u8 i, released_cnt = 0;
+
+	u16 x_max = ts->dev->screen_info.x_max;
+	u16 x_min = ts->dev->screen_info.x_min;
+	u16 y_max = ts->dev->screen_info.y_max;
+	u16 y_min = ts->dev->screen_info.y_min;
+
+	/*
+	 * ISR may be activated after registering irq and
+	 * before creating input_dev
+	 */
+	if (!input) {
+		TP_ERR(NULL, "input_dev is not registerred\n");
+		return;
+	}
+
+	for (i = 0; i < touch->cnt; i++) {
+		if (!touch->finger[i].status) {
+			released_cnt++;
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+			ilitek_touch_release(touch->finger[i].id);
+#endif
+			continue;
+		}
+
+		set_arr(ts->touch_flag, touch->finger[i].id, 1);
+
+		touch->finger[i].x = (ILITEK_REVERT_X) ?
+			x_max - touch->finger[i].x + x_min : touch->finger[i].x;
+		touch->finger[i].y = (ILITEK_REVERT_Y) ?
+			y_max - touch->finger[i].y + y_min : touch->finger[i].y;
+
+#if ILITEK_ROTATE_FLAG
+		swap(touch->finger[i].x, touch->finger[i].y);
+#endif
+
+		if (ts->system_suspend) {
+			TP_MSG(NULL, "system is suspend not report point\n");
+			ilitek_gesture_handle(true, i, touch->finger[i].x,
+					      touch->finger[i].y);
+			continue;
+		}
+
+		if (!ts->is_touched)
+			ilitek_check_key_down(touch->finger[i].x,
+					      touch->finger[i].y);
+
+		if (!ts->touch_key_hold_press) {
+			ts->is_touched = true;
+			ilitek_touch_down(touch->finger[i].id,
+					  touch->finger[i].x,
+					  touch->finger[i].y,
+					  touch->finger[i].pressure,
+					  touch->finger[i].height,
+					  touch->finger[i].width);
+		} else {
+			ilitek_check_key_release(touch->finger[i].x,
+						 touch->finger[i].y, 1);
+		}
+	}
+
+	if (touch->cnt == released_cnt) {
+		if (ts->is_touched)
+			ilitek_touch_release_all_point();
+
+		ilitek_check_key_release(0, 0, 0);
+		ts->is_touched = false;
+
+		if (ts->system_suspend)
+			ilitek_gesture_handle(false, 0, 0, 0);
+	}
+
+	input_sync(input);
+}
+
+static void ilitek_report_pen_event(struct pen_data *pen, void *_private)
+{
+	static int curr_tool = BTN_TOOL_PEN;
+	struct input_dev *pen_input = ts->pen_input_dev;
+	int tool;
+
+	u16 x_max = ts->dev->screen_info.pen_x_max;
+	u16 x_min = ts->dev->screen_info.pen_x_min;
+	u16 y_max = ts->dev->screen_info.pen_y_max;
+	u16 y_min = ts->dev->screen_info.pen_y_min;
+
+	if (!pen_input)
+		return;
+
+	tool = (pen->pen.in_range && pen->pen.invert) ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	if (curr_tool != tool) {
+		input_report_key(pen_input, curr_tool, 0);
+		input_sync(pen_input);
+		curr_tool = tool;
+	}
+
+	pen->pen.x = (ILITEK_REVERT_X) ?
+		x_max - pen->pen.x + x_min : pen->pen.x;
+	pen->pen.y = (ILITEK_REVERT_Y) ?
+		y_max - pen->pen.y + y_min : pen->pen.y;
+
+#if ILITEK_ROTATE_FLAG
+	swap(pen->pen.x, pen->pen.y);
+#endif
+
+	input_report_key(pen_input, BTN_TOUCH,
+			 pen->pen.tip_sw || pen->pen.eraser);
+	input_report_key(pen_input, curr_tool, pen->pen.in_range);
+	input_report_key(pen_input, BTN_STYLUS, pen->pen.barrel_sw);
+	input_event(pen_input, EV_ABS, ABS_X, pen->pen.x);
+	input_event(pen_input, EV_ABS, ABS_Y, pen->pen.y);
+	input_event(pen_input, EV_ABS, ABS_PRESSURE, pen->pen.pressure);
+	input_event(pen_input, EV_ABS, ABS_TILT_X, pen->pen.x_tilt);
+	input_event(pen_input, EV_ABS, ABS_TILT_Y, pen->pen.y_tilt);
+
+	input_sync(pen_input);
+}
+
+
+static void ilitek_report_buf(u8 *buf, int size,
+			      bool is_last, void *_private)
+{
+	UNUSED(is_last);
+
+	ilitek_udp_reply(buf, size);
+}
+
+int ilitek_read_data_and_report(void)
+{
+	int error;
+	u8 i, count;
+	struct ilitek_report report;
+
+	memset(&report, 0, sizeof(report));
+	report.cb.report_touch_event = ilitek_report_touch_event;
+	report.cb.report_pen_event = ilitek_report_pen_event;
+	report.cb.report_buf = ilitek_report_buf;
+
+	switch (ts->irq_handle_type) {
+	case irq_type_c_model:
+		memset(ts->buf, 0, sizeof(ts->buf));
+
+		for (i = 0, count = 1; i < count; i++) {
+			ilitek_read(ts->buf, ts->irq_read_len);
+			ilitek_udp_reply(ts->buf, ts->irq_read_len);
+			count = ts->buf[ts->irq_read_len - 1];
+		}
+
+		break;
+
+	case irq_type_debug:
+	case irq_type_normal:
+	default:
+		error = ilitek_report_update(ts->dev, &report);
+		if (error < 0) {
+			if (error == -EILIPROTO && !checksum_failed_release)
+				break;
+
+			if (ts->is_touched) {
+				ilitek_touch_release_all_point();
+				ilitek_check_key_release(0, 0, 0);
+			}
+
+			return error;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+static ISR_FUNC(ilitek_i2c_isr)
+{
+	int error;
+
+	TP_DBG(NULL, "%s\n", __func__);
+
+	atomic_set(&ts->get_INT, 1);
+	ilitek_gpio_dbg();
+
+	ts->esd_skip = true;
+
+	if (atomic_read(&ts->firmware_updating)) {
+		TP_DBG(NULL, "firmware_updating return\n");
+		goto exit;
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_disable();
+#endif
+
+	if (!ts->unhandle_irq) {
+		mutex_lock(&ts->ilitek_mutex);
+		error = ilitek_read_data_and_report();
+		if (error < 0)
+			TP_ERR(NULL, "process error\n");
+		mutex_unlock(&ts->ilitek_mutex);
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_enable();
+#endif
+
+exit:
+	ts->esd_skip = false;
+
+	ISR_RETURN(IRQ_HANDLED);
+}
+
+static int ilitek_request_irq(void)
+{
+	int error;
+
+#ifdef MTK_UNDTS
+	mt_set_gpio_mode(ILITEK_IRQ_GPIO, GPIO_CTP_EINT_PIN_M_EINT);
+	mt_set_gpio_dir(ILITEK_IRQ_GPIO, GPIO_DIR_IN);
+	mt_set_gpio_pull_enable(ILITEK_IRQ_GPIO, GPIO_PULL_ENABLE);
+	mt_set_gpio_pull_select(ILITEK_IRQ_GPIO, GPIO_PULL_UP);
+
+	mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+	mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, ilitek_i2c_isr, 1);
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	struct device_node *node;
+
+	node = of_find_matching_node(NULL, touch_of_match);
+	if (node)
+		ts->irq = irq_of_parse_and_map(node, 0);
+#else
+	ts->irq = gpio_to_irq(ts->irq_gpio);
+#endif
+
+	TP_MSG(NULL, "ts->irq: %d\n", ts->irq);
+	if (ts->irq <= 0)
+		return -EINVAL;
+
+	error = request_threaded_irq(ts->irq, NULL, ilitek_i2c_isr,
+				     ts->irq_trigger_type | IRQF_ONESHOT,
+				     "ilitek_touch_irq", ts);
+	if (error) {
+		TP_ERR(NULL, "request threaded irq failed, err: %d\n", error);
+		return error;
+	}
+#endif
+
+	ts->irq_registered = true;
+	atomic_set(&ts->irq_enabled, 1);
+
+	return 0;
+}
+
+static int ilitek_read_fw(char *filename, unsigned char *buf, int size, void *data)
+{
+	int error, fw_size;
+	const struct firmware *fw;
+	struct device *device = (struct device *)data;
+
+	error = request_firmware(&fw, filename, device);
+	if (error) {
+		TP_ERR(NULL, "request fw: %s failed, err:%d\n", filename, error);
+		return error;
+	}
+
+	if (size < fw->size) {
+		fw_size = -EFBIG;
+		goto release_fw;
+	}
+
+	fw_size = fw->size;
+	memcpy(buf, fw->data, fw->size);
+
+release_fw:
+	release_firmware(fw);
+
+	return fw_size;
+}
+
+struct ilitek_update_callback update_cb = {
+	.read_fw = ilitek_read_fw,
+	.update_progress = NULL,
+	.update_fw_file_info = NULL,
+
+	.slave_update_notify = NULL,
+	.update_fw_ic_info = NULL,
+};
+
+int ilitek_upgrade_firmware(char *filename)
+{
+	int error;
+	struct ilitek_fw_handle *handle;
+	struct ilitek_fw_settings setting;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	atomic_set(&ts->firmware_updating, 1);
+	ts->operation_protection = true;
+
+	handle = ilitek_update_init(ts->dev, false, &update_cb, ts->device);
+
+	setting.force_update = false;
+	setting.fw_check_only = false;
+	setting.fw_ver_check = false;
+	setting.retry = 3;
+	ilitek_update_setting(handle, &setting);
+
+	error = ilitek_update_load_fw(handle, filename);
+	if (error < 0)
+		goto err_return;
+	error = ilitek_update_start(handle);
+	if (error < 0)
+		goto err_return;
+
+err_return:
+	ilitek_update_exit(handle);
+
+	ts->operation_protection = false;
+	atomic_set(&ts->firmware_updating, 0);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return error;
+}
+
+
+static int __maybe_unused ilitek_update_thread(void *arg)
+{
+#ifdef ILITEK_BOOT_UPDATE
+	int error;
+
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (kthread_should_stop()) {
+		TP_MSG(NULL, "ilitek_update_thread, stop\n");
+		return -1;
+	}
+
+	mdelay(100);
+
+	error = ilitek_upgrade_firmware("ilitek.ili");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.hex");
+		if (error < 0) {
+			error = ilitek_upgrade_firmware("ilitek.bin");
+			if (error < 0)
+				return error;
+		}
+	}
+
+	error = ilitek_request_input_dev();
+	if (error)
+		return (error < 0) ? error : -EFAULT;
+#endif
+
+	return 0;
+}
+
+void ilitek_suspend(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	ts->esd_skip = true;
+	if (ts->esd_check && ts->esd_workq)
+		cancel_delayed_work_sync(&ts->esd_work);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ts->wake_irq_enabled = (enable_irq_wake(ts->irq) == 0);
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_set_idle(ts->dev, true) < 0)
+				TP_ERR(NULL, "enable Idle mode failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	} else {
+		/*
+		 * Must disable irq before sleep cmd,
+		 * Avoid getting into ISR handling (and do i2c read),
+		 * after sending sleep cmd.
+		 */
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_protocol_set_cmd(ts->dev, SET_IC_SLEEP,
+						 NULL) < 0)
+				TP_ERR(NULL, "set tp sleep failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->power_key_triggered = false;
+	ts->system_suspend = true;
+}
+
+void ilitek_resume(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_set_idle(ts->dev, false);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+
+		if (ts->gesture_status == Gesture_Double_Click)
+			finger_state = 0;
+
+		if (ts->wake_irq_enabled) {
+			disable_irq_wake(ts->irq);
+			ts->wake_irq_enabled = false;
+		}
+	} else {
+		if (ts->dev->protocol.flag == PTL_V3) {
+			/*
+			 * If ILITEK_SLEEP is defined and FW support wakeup cmd,
+			 * the hw reset can be mark.
+			 */
+			ilitek_reset(ts->dev->reset_time);
+		}
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_protocol_set_cmd(ts->dev, SET_IC_WAKE, NULL);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->esd_skip = false;
+	if (ts->esd_check && ts->esd_workq)
+		queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	ilitek_touch_release_all_point();
+	ilitek_check_key_release(0, 0, 0);
+
+	ts->system_suspend = false;
+
+	ilitek_irq_enable();
+}
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+int ilitek_resume_allwin(struct i2c_client *client)
+{
+	ilitek_resume();
+	return 0;
+}
+#endif
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+static int __maybe_unused ilitek_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+#ifdef CONFIG_QCOM_DRM
+	struct msm_drm_notifier *ev_data = data;
+#else
+	struct fb_event *ev_data = data;
+#endif
+	int *blank;
+
+	TP_MSG(NULL, "FB EVENT event: %lu\n", event);
+
+#ifdef CONFIG_QCOM_DRM
+	if (!ev_data || (ev_data->id != 0))
+		return 0;
+#endif
+	if (ev_data && ev_data->data && event == ILITEK_EVENT_BLANK) {
+		blank = ev_data->data;
+		TP_MSG(NULL, "blank: %d\n", *blank);
+		if (*blank == ILITEK_BLANK_POWERDOWN) {
+			ilitek_suspend();
+		} else if (*blank == ILITEK_BLANK_UNBLANK ||
+			   *blank == ILITEK_BLANK_NORMAL) {
+			ilitek_resume();
+		}
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void __maybe_unused ilitek_early_suspend(struct early_suspend *h)
+{
+	ilitek_suspend();
+}
+
+static void __maybe_unused ilitek_late_resume(struct early_suspend *h)
+{
+	ilitek_resume();
+}
+#endif
+#endif
+
+static void ilitek_get_gpio_num(void)
+{
+#ifdef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	TP_MSG(NULL, "(config_info.wakeup_gpio.gpio) = %d (config_info.int_number) = %d\n", (config_info.wakeup_gpio.gpio), (config_info.int_number));
+	ts->reset_gpio = (config_info.wakeup_gpio.gpio);
+	ts->irq_gpio = (config_info.int_number);
+#else
+#ifdef CONFIG_OF
+	ts->reset_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,reset-gpio", 0);
+	if (ts->reset_gpio < 0)
+		TP_ERR(NULL, "reset_gpio = %d\n", ts->reset_gpio);
+	ts->irq_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,irq-gpio", 0);
+	if (ts->irq_gpio < 0)
+		TP_ERR(NULL, "irq_gpio = %d\n", ts->irq_gpio);
+#endif
+#endif
+#else
+	ts->reset_gpio = ILITEK_RESET_GPIO;
+	ts->irq_gpio = ILITEK_IRQ_GPIO;
+#endif
+
+	TP_MSG(NULL, "reset_gpio = %d irq_gpio = %d\n", ts->reset_gpio, ts->irq_gpio);
+
+
+#if defined(ILITEK_GPIO_DEBUG)
+	do {
+		ts->test_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,test-gpio", 0);
+		if (ts->test_gpio < 0) {
+			TP_ERR(NULL, "test_gpio: %d\n", ts->test_gpio);
+			break;
+		}
+
+		TP_MSG(NULL, "test_gpio: %d\n", ts->test_gpio);
+
+		if (gpio_request(ts->test_gpio, "ilitek-test-gpio")) {
+			TP_ERR(NULL, "request test_gpio failed\n");
+			break;
+		}
+
+		gpio_direction_output(ts->test_gpio, 1);
+
+	} while (0);
+#endif
+}
+
+static int ilitek_request_gpio(void)
+{
+	int ret = 0;
+
+	ts->irq_gpio = -ENODEV;
+	ts->reset_gpio = -ENODEV;
+
+	ilitek_get_gpio_num();
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+	if (ts->reset_gpio > 0) {
+		ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio so free retry\n");
+			gpio_free(ts->reset_gpio);
+			ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request reset_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio\n");
+		} else {
+			ret = gpio_direction_output(ts->reset_gpio, 1);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction output rest gpio err\n");
+		}
+	}
+	if (ts->irq_gpio > 0) {
+		ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio so free retry\n");
+			gpio_free(ts->irq_gpio);
+			ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request irq_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio\n");
+		} else {
+			ret = gpio_direction_input(ts->irq_gpio);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction input irq gpio err\n");
+		}
+	}
+#endif
+	return ret;
+}
+
+int ilitek_create_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to create esd workqueue\n");
+
+	INIT_DELAYED_WORK(&ts->esd_work, ilitek_esd_check);
+	ts->esd_workq = create_singlethread_workqueue("ilitek_esd_wq");
+	if (!ts->esd_workq)
+		return -ENOMEM;
+
+	ts->esd_skip = false;
+	ts->esd_delay = 2 * HZ;
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	return 0;
+}
+
+void ilitek_remove_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to remove esd workqueue\n");
+
+	if (ts->esd_workq) {
+		cancel_delayed_work_sync(&ts->esd_work);
+		destroy_workqueue(ts->esd_workq);
+		ts->esd_workq = NULL;
+	}
+}
+
+static int ilitek_register_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+	int error;
+
+	ts->fb_notif.notifier_call = ilitek_notifier_callback;
+
+#ifdef CONFIG_QCOM_DRM
+	error = msm_drm_register_client(&ts->fb_notif);
+#else
+	error = fb_register_client(&ts->fb_notif);
+#endif
+	if (error)
+		TP_ERR(NULL, "register fb_notifier failed, err: %d\n", error);
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	ts->early_suspend.suspend = ilitek_early_suspend;
+	ts->early_suspend.resume = ilitek_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+#endif /* ILITEK_PLAT != ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	device_enable_async_suspend(ts->device);
+	pm_runtime_set_active(ts->device);
+	pm_runtime_get(ts->device);
+	pm_runtime_enable(ts->device);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+#ifdef CONFIG_QCOM_DRM
+	msm_drm_unregister_client(&ts->fb_notif);
+#else
+	fb_unregister_client(&ts->fb_notif);
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+}
+
+int ilitek_netlink_init(u8 unit)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	NETLINK_KERNEL_CFG_DECLARE(cfg, udp_receive);
+
+	if (ilitek_netlink_sock)
+		ilitek_netlink_exit();
+
+	ilitek_netlink_sock = NETLINK_KERNEL_CREATE(unit, &cfg, udp_receive);
+
+	if (!ilitek_netlink_sock) {
+		TP_ERR(NULL, "netlink_kernel_create failed\n");
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+#endif
+	return -EPERM;
+}
+
+void ilitek_netlink_exit(void)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	if (ilitek_netlink_sock)
+		netlink_kernel_release(ilitek_netlink_sock);
+
+	ilitek_netlink_sock = NULL;
+#endif
+}
+
+static int __maybe_unused ilitek_alloc_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	I2CDMABuf_va = (u8 *) dma_alloc_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+	if (!I2CDMABuf_va) {
+		TP_ERR(NULL, "ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n");
+		I2CDMABuf_va = (u8 *) dma_alloc_coherent(NULL, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+		if (!I2CDMABuf_va) {
+			TP_ERR(NULL, "ilitek [TPD] NULL dma_alloc_coherent error\n");
+			return -ENOMEM;
+		}
+	}
+	memset(I2CDMABuf_va, 0, ILITEK_DMA_SIZE);
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_free_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	if (I2CDMABuf_va) {
+		dma_free_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE,
+				  I2CDMABuf_va, I2CDMABuf_pa);
+
+		I2CDMABuf_va = NULL;
+		I2CDMABuf_pa = 0;
+
+	}
+#endif
+	return 0;
+}
+
+static int __maybe_unused ilitek_power_on(bool status)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int error;
+
+	TP_MSG(NULL, "%s\n", status ? "POWER ON" : "POWER OFF");
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	input_set_power_enable(&(config_info.input_type), status);
+#else
+
+	if (status) {
+		if (ts->vdd) {
+			error = regulator_enable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_enable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	} else {
+		if (ts->vdd) {
+			error = regulator_disable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_disable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	}
+
+#ifdef MTK_UNDTS
+	if (status)
+		hwPowerOn(PMIC_APP_CAP_TOUCH_VDD, VOL_3300, "TP");
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_request_regulator(struct ilitek_ts_data *ts)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int ret = 0;
+	char *vdd_name = "vdd";
+	char *vcc_i2c_name = "vcc_i2c";
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	vdd_name = "vtouch";
+	ts->vdd = regulator_get(tpd->tpd_dev, vdd_name);
+	tpd->reg = ts->vdd;
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+	}
+#elif ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	ts->vdd = regulator_get(ts->device, vdd_name);
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+
+	}
+
+	ts->vdd_i2c = regulator_get(ts->device, vcc_i2c_name);
+	if (IS_ERR(ts->vdd_i2c)) {
+		TP_ERR(NULL, "regulator_get vdd_i2c fail\n");
+		ts->vdd_i2c = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd_i2c, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set i2c to 3000~3300mv.\n");
+	}
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+#endif /* ILITEK_ENABLE_REGULATOR_POWER_ON */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_regulator(void)
+{
+#if defined(ILITEK_ENABLE_REGULATOR_POWER_ON) && ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	if (ts->vdd)
+		regulator_put(ts->vdd);
+	if (ts->vdd_i2c)
+		regulator_put(ts->vdd_i2c);
+#endif
+}
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init)
+{
+	if (init) {
+		device_init_wakeup(ts->device, 1);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+		wake_lock_init(&ilitek_wake_lock, WAKE_LOCK_SUSPEND, "ilitek wakelock");
+#endif
+		return;
+	}
+
+	device_init_wakeup(ts->device, 0);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_destroy(&ilitek_wake_lock);
+#endif
+}
+
+static int _ilitek_write_then_read(unsigned char *wbuf, int wlen,
+				   unsigned char *rbuf, int rlen, void *data)
+{
+	return ilitek_write_and_read(wbuf, wlen, 1, rbuf, rlen);
+}
+
+static int ilitek_read_interrupt_in(unsigned char *rbuf, int rlen,
+				    unsigned int timeout_ms, void *data)
+{
+	return ilitek_write_and_read(NULL, 0, 0, rbuf, rlen);
+}
+
+static void _ilitek_init_ack(unsigned int tout_ms, void *data)
+{
+	UNUSED(tout_ms);
+
+	ilitek_irq_enable();
+	ts->unhandle_irq = true;
+	atomic_set(&ts->get_INT, 0);
+}
+
+static int _ilitek_wait_ack(u8 cmd, unsigned int tout_ms, void *data)
+{
+	unsigned int t_ms = 0;
+	int tmp, error = -ETIME;
+
+	UNUSED(cmd);
+
+	do {
+		tmp = atomic_read(&ts->get_INT);
+		if (tmp) {
+			error = 0;
+			break;
+		}
+
+		udelay(1000);
+		t_ms++;
+	} while (t_ms < tout_ms);
+
+	ts->unhandle_irq = false;
+	ilitek_irq_disable();
+
+	return error;
+}
+
+static void _ilitek_delay(unsigned int delay_ms)
+{
+	mdelay(delay_ms);
+}
+
+static int _ilitek_reset(unsigned int delay_ms, void *data)
+{
+	/* return error if no reset gpio found */
+	if (ts->reset_gpio < 0)
+		return -ENODEV;
+
+	ilitek_reset(delay_ms);
+	return 0;
+}
+
+struct ilitek_ts_callback dev_cb = {
+	.write_then_read = _ilitek_write_then_read,
+	.read_interrupt_in = ilitek_read_interrupt_in,
+	.init_ack = _ilitek_init_ack,
+	.wait_ack = _ilitek_wait_ack,
+	.hw_reset = _ilitek_reset,
+	.re_enum = NULL,
+	.delay_ms = _ilitek_delay,
+	.msg = NULL,
+
+	.write_then_read_direct = NULL,
+	.mode_switch_notify = NULL,
+};
+
+int ilitek_main_probe(void *client, struct device *device)
+{
+	struct ilitek_ts_settings setting;
+
+	TP_MSG(NULL, "driver version: %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu\n",
+		driver_ver[0], driver_ver[1], driver_ver[2], driver_ver[3],
+		driver_ver[4], driver_ver[5], driver_ver[6]);
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts) {
+		TP_ERR(NULL, "allocate ts failed\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	ts->device = device;
+
+	mutex_init(&ts->ilitek_mutex);
+	ts->unhandle_irq = false;
+
+	ilitek_alloc_dma();
+	ilitek_request_regulator(ts);
+	ilitek_power_on(true);
+	ilitek_request_gpio();
+
+	ilitek_reset(600);
+
+	ts->dev = ilitek_dev_init(interface_i2c, "0", false, &dev_cb, ts);
+	if (!ts->dev)
+		goto err_free_gpio;
+
+	memset(&setting, 0, sizeof(setting));
+	setting.sensor_id_mask = ILITEK_SENSOR_ID_MASK;
+	ilitek_dev_setting(ts->dev, &setting);
+
+	if (api_update_ts_info(ts->dev) < 0)
+		goto err_dev_exit;
+
+	ts->irq_trigger_type = (ts->dev->protocol.flag == PTL_V6) ?
+		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+	if (ilitek_request_irq())
+		goto err_dev_exit;
+
+#ifdef ILITEK_BOOT_UPDATE
+	ts->update_thread = kthread_run(ilitek_update_thread, NULL,
+					"ilitek_update_thread");
+	if (IS_ERR(ts->update_thread))
+		goto err_free_irq;
+#else
+	if (ilitek_request_input_dev())
+		goto err_free_irq;
+#endif
+
+	ilitek_register_resume_suspend();
+	ilitek_create_sysfsnode();
+	ilitek_create_tool_node();
+	ilitek_netlink_init(NETLINK_USERSOCK);
+
+	ts->esd_check = ILITEK_ESD_CHECK_ENABLE;
+	if (ts->esd_check)
+		ilitek_create_esd_check_workqueue();
+
+	ts->gesture_status = ILITEK_GESTURE_DEFAULT;
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, true);
+
+	ts->low_power_status = ILITEK_LOW_POWER_DEFAULT;
+
+	return 0;
+
+err_free_irq:
+	free_irq(ts->irq, ts);
+
+err_dev_exit:
+	ilitek_dev_exit(ts->dev);
+
+err_free_gpio:
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+	kfree(ts);
+
+	return -ENODEV;
+}
+
+int ilitek_main_remove(void *client)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (!ts)
+		return 0;
+
+	ilitek_netlink_exit();
+
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, false);
+
+	ilitek_remove_esd_check_workqueue();
+	ilitek_remove_tool_node();
+	ilitek_remove_sys_node();
+	ilitek_release_resume_suspend();
+
+	if (ts->pen_input_dev)
+		input_unregister_device(ts->pen_input_dev);
+
+	if (ts->input_dev)
+		input_unregister_device(ts->input_dev);
+
+#ifndef MTK_UNDTS
+	free_irq(ts->irq, ts);
+#endif
+
+	ilitek_dev_exit(ts->dev);
+
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+
+	kfree(ts);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_platform_init.c b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
new file mode 100644
index 000000000000..0b7f97ab7746
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+
+#define TPD_OK (0)
+
+#ifdef MTK_UNDTS
+static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
+static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
+struct touch_vitual_key_map_t touch_key_point_maping_array[] = { {key_1}, {key_2}, {key_3}, {key_4} };
+
+static struct i2c_board_info __initdata ilitek_i2c_tpd = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41)
+};
+#endif
+
+/* probe function is used for matching and initializing input device */
+static int /*__devinit*/ tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	TP_MSG(NULL, "TPD probe\n");
+
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -1;
+	}
+
+	ret = ilitek_main_probe(client, &client->dev);
+	if (ret == 0)	// If probe is success, then enable the below flag.
+		tpd_load_status = 1;
+
+	TP_MSG(NULL, "TPD probe done\n");
+	return TPD_OK;
+}
+
+static int tpd_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	strcpy(info->type, TPD_DEVICE);
+	return TPD_OK;
+}
+
+static int /*__devexit*/ tpd_remove(struct i2c_client *client)
+{
+	TP_MSG(NULL, "TPD removed\n");
+	return ilitek_main_remove(client);
+}
+
+/* The I2C device list is used for matching I2C device and I2C device driver. */
+static const struct i2c_device_id tpd_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+
+MODULE_DEVICE_TABLE(i2c, tpd_device_id);
+
+const struct of_device_id touch_dt_match_table[] = {
+	{ .compatible = "mediatek,cap_touch",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, touch_dt_match_table);
+
+static struct i2c_driver tpd_i2c_driver = {
+
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.of_match_table = of_match_ptr(touch_dt_match_table),
+	},
+	.probe = tpd_probe,
+	.remove = tpd_remove,
+	.id_table = tpd_device_id,
+	.detect = tpd_detect,
+};
+
+static int tpd_local_init(void)
+{
+	TP_MSG(NULL, "TPD init device driver\n");
+
+	if (i2c_add_driver(&tpd_i2c_driver) != 0) {
+		TP_ERR(NULL, "Unable to add i2c driver.\n");
+		return -1;
+	}
+	if (tpd_load_status == 0) {
+		TP_ERR(NULL, "Add error touch panel driver.\n");
+		i2c_del_driver(&tpd_i2c_driver);
+		return -1;
+	}
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button)
+		tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, tpd_dts_data.tpd_key_dim_local);
+
+#else
+	tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);	// initialize tpd button data
+#endif
+	tpd_type_cap = 1;
+	TP_MSG(NULL, "TPD init done\n");
+	return TPD_OK;
+}
+
+#ifdef MTK_UNDTS
+static void tpd_resume(struct early_suspend *h)
+{
+#else
+static void tpd_resume(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD wake up\n");
+	ilitek_resume();
+	TP_MSG(NULL, "TPD wake up done\n");
+}
+
+#ifdef MTK_UNDTS
+static void tpd_suspend(struct early_suspend *h)
+{
+#else
+static void tpd_suspend(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD enter sleep\n");
+	ilitek_suspend();
+	TP_MSG(NULL, "TPD enter sleep done\n");
+}
+
+static struct tpd_driver_t tpd_device_driver = {
+	.tpd_device_name = ILITEK_TS_NAME,
+	.tpd_local_init = tpd_local_init,
+	.suspend = tpd_suspend,
+	.resume = tpd_resume,
+	.tpd_have_button = 1,
+};
+
+static int __init ilitek_touch_driver_init(void)
+{
+	TP_MSG(NULL, "touch panel driver init\n");
+
+#ifdef MTK_UNDTS
+	i2c_register_board_info(2, &ilitek_i2c_tpd, 1);
+#else
+	tpd_get_dts_info();
+#endif
+	if (tpd_driver_add(&tpd_device_driver) < 0)
+		TP_ERR(NULL, "TPD add TP driver failed\n");
+
+	return 0;
+
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "touch panel driver exit\n");
+	tpd_driver_remove(&tpd_device_driver);
+}
+
+#else
+
+#ifdef ILITEK_SPI_INTERFACE
+static int ilitek_touch_driver_probe(struct spi_device *spi)
+{
+	int error;
+
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		TP_ERR(NULL, "SPI setup failed, err: %d\n", error);
+		return error;
+	}
+
+	TP_MSG(NULL, "SPI start probe, max_speed_hz: %d\n", spi->max_speed_hz);
+
+	return ilitek_main_probe(spi, &spi->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct spi_device *spi)
+{
+	REMOVE_RETURN(ilitek_main_remove(spi));
+}
+#else
+static I2C_PROBE_FUNC(ilitek_touch_driver_probe, struct i2c_client *client)
+{
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -ENODEV;
+	}
+
+	TP_MSG(NULL, "ILITEK client->addr: 0x%x, client->irq: %d\n",
+		client->addr, client->irq);
+
+	return ilitek_main_probe(client, &client->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct i2c_client *client)
+{
+	REMOVE_RETURN(ilitek_main_remove(client));
+}
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id ilitek_touch_match_table[] = {
+	{.compatible = "tchip,ilitek",},
+	{},
+};
+#endif
+
+static const struct i2c_device_id ilitek_touch_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+MODULE_DEVICE_TABLE(i2c, ilitek_touch_device_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ilitekts_acpi_id[] = {
+	{"ILIT2901", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
+#endif
+
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+static int __maybe_unused ilitek_ts_suspend(struct device *dev)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+static int __maybe_unused ilitek_ts_resume(struct device *dev)
+{
+	ilitek_resume();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_ts_suspend, ilitek_ts_resume);
+#endif
+
+#ifdef ILITEK_SPI_INTERFACE
+static struct spi_driver ilitek_touch_device_driver = {
+#else
+static struct i2c_driver ilitek_touch_device_driver = {
+#endif
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = ilitek_touch_match_table,
+#endif
+#ifdef CONFIG_ACPI
+		.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
+#endif
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+		.pm = &ilitek_pm_ops,
+#endif
+	},
+
+	.probe = ilitek_touch_driver_probe,
+	.remove = ilitek_touch_driver_remove,
+
+#if !defined(ILITEK_SPI_INTERFACE)
+	.id_table = ilitek_touch_device_id,
+#endif
+};
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+static const unsigned short normal_i2c[2] = { 0x41, I2C_CLIENT_END };
+struct ctp_config_info config_info = {
+	.input_type = CTP_TYPE,
+	.name = NULL,
+	.int_number = 0,
+};
+
+static int twi_id;
+static int screen_max_x;
+static int screen_max_y;
+static int revert_x_flag;
+static int revert_y_flag;
+static int exchange_x_y_flag;
+static int ctp_get_system_config(void)
+{
+	twi_id = config_info.twi_id;
+	screen_max_x = config_info.screen_max_x;
+	screen_max_y = config_info.screen_max_y;
+	TP_MSG(NULL, "Ilitek: screen_max_x = %d\n", screen_max_x);
+	revert_x_flag = config_info.revert_x_flag;
+	revert_y_flag = config_info.revert_y_flag;
+	exchange_x_y_flag = config_info.exchange_x_y_flag;
+	if ((screen_max_x == 0) || (screen_max_y == 0)) {
+		TP_ERR(NULL, "%s:read config error!\n", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+int ctp_ts_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+
+	if (twi_id == adapter->nr) {
+		strlcpy(info->type, ILITEK_TS_NAME, I2C_NAME_SIZE);
+		return 0;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static struct i2c_board_info i2c_info_dev = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41),
+	.platform_data = NULL,
+};
+
+static int add_ctp_device(void)
+{
+	struct i2c_adapter *adap;
+	//script_parser_fetch("ctp_para", "ctp_twi_id", &twi_id, 1);
+	adap = i2c_get_adapter(twi_id);
+	i2c_new_device(adap, &i2c_info_dev);
+	return 0;
+}
+
+static int ilitek_init_allwin(void)
+{
+
+	int ret = 0;
+
+	if (input_fetch_sysconfig_para(&(config_info.input_type))) {
+		TP_ERR(NULL, "Ilitek:  ctp_fetch_sysconfig_para err.\n");
+		return -1;
+	} else {
+		ret = input_init_platform_resource(&(config_info.input_type));
+		if (0 != ret)
+			TP_ERR(NULL, "Ilitek: ctp_ops.init_platform_resource err.\n");
+	}
+
+	if (config_info.ctp_used == 0) {
+		TP_ERR(NULL, "Ilitek: *** if use ctp,please put the sys_config.fex ctp_used set to 1.\n");
+		return -1;
+	}
+
+	if (ctp_get_system_config() < 0)
+		TP_ERR(NULL, "Ilitek: %s:read config fail!\n", __func__);
+
+	add_ctp_device();
+	ilitek_touch_device_driver.address_list = normal_i2c;
+	ilitek_touch_device_driver.detect = ctp_ts_detect;
+	return 0;
+
+}
+
+#endif
+static int __init ilitek_touch_driver_init(void)
+{
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	if (ilitek_init_allwin() < 0) {
+		TP_ERR(NULL, "ilitek_init_allwin failed.\n");
+		return -ENODEV;
+	}
+#endif
+
+	TP_MSG(NULL, "add ILITEK touch device driver\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	return spi_register_driver(&ilitek_touch_device_driver);
+#else
+	return i2c_add_driver(&ilitek_touch_device_driver);
+#endif
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "remove touch device driver i2c driver.\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	spi_unregister_driver(&ilitek_touch_device_driver);
+#else
+	i2c_del_driver(&ilitek_touch_device_driver);
+#endif
+}
+#endif
+
+module_init(ilitek_touch_driver_init);
+module_exit(ilitek_touch_driver_exit);
+MODULE_AUTHOR("ILITEK");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.c b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
new file mode 100644
index 000000000000..945bab688405
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
@@ -0,0 +1,3644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_protocol.h"
+
+typedef int (*protocol_func_t)(struct ilitek_ts_device *, void *);
+
+struct protocol_map {
+	u8 cmd;
+	u8 flag;
+	protocol_func_t func;
+	const char *desc;
+};
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} touch_fmts[touch_fmt_max];
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} pen_fmts[pen_fmt_max];
+
+#define X(_cmd, _protocol, _cmd_id, _api) \
+	static int _api(struct ilitek_ts_device *, void *);
+ILITEK_CMD_MAP
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api) {_cmd, _protocol, _api, #_cmd_id},
+struct protocol_map protocol_maps[] = { ILITEK_CMD_MAP };
+#undef X
+
+u16 le16(const u8 *p)
+{
+	return p[0] | p[1] << 8;
+}
+
+u16 be16(const u8 *p)
+{
+	return p[1] | p[0] << 8;
+}
+
+u32 le32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val += (p[bytes] << (8 * bytes));
+
+	return val;
+}
+
+u32 be32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val = (val << 8) | (*p++);
+
+	return val;
+}
+
+static bool is_2501x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "25011") ||
+	    !strcmp(dev->mcu_info.ic_name, "25012"))
+		return true;
+
+	return false;
+}
+
+bool is_29xx(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "2900") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901") ||
+	    !strcmp(dev->mcu_info.ic_name, "2910") ||
+	    !strcmp(dev->mcu_info.ic_name, "2911") ||
+	    !strcmp(dev->mcu_info.ic_name, "2531") ||
+	    !strcmp(dev->mcu_info.ic_name, "2532") ||
+	    !strcmp(dev->mcu_info.ic_name, "2921") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901M") ||
+	    is_2501x(handle))
+		return true;
+
+	return false;
+}
+
+bool _is_231x(char *ic_name)
+{
+	if (!strcmp(ic_name, "2312") || !strcmp(ic_name, "2315"))
+		return true;
+
+	return false;
+}
+
+bool is_231x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	return _is_231x(dev->mcu_info.ic_name);
+}
+
+bool has_hw_key(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle || !dev->tp_info.key_num)
+		return false;
+
+	if (dev->key.info.mode == key_hw ||
+	    dev->key.info.mode == key_hsw)
+		return true;
+
+	return false;
+}
+
+u8 get_protocol_ver_flag(u32 ver)
+{
+	if (((ver >> 16) & 0xFF) == 0x3 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_6 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_7)
+		return PTL_V3;
+
+	if (((ver >> 16) & 0xFF) == 0x6 ||
+	    (ver & 0xFFFF00) == BL_PROTOCOL_V1_8)
+		return PTL_V6;
+
+	return PTL_ANY;
+}
+
+void grid_reset(struct grids *grid)
+{
+	grid->mc.need_update = false;
+	grid->sc_x.need_update = false;
+	grid->sc_y.need_update = false;
+	grid->pen_x.need_update = false;
+	grid->pen_y.need_update = false;
+
+	grid->key_mc.need_update = false;
+	grid->key_x.need_update = false;
+	grid->key_y.need_update = false;
+
+	grid->self.need_update = false;
+
+	if (grid->mc.data)
+		_memset(grid->mc.data, 0,
+		       grid->mc.X * grid->mc.Y * sizeof(s32));
+	if (grid->sc_x.data)
+		_memset(grid->sc_x.data, 0,
+		grid->sc_x.X * grid->sc_x.Y * sizeof(s32));
+	if (grid->sc_y.data)
+		_memset(grid->sc_y.data, 0,
+		grid->sc_y.X * grid->sc_y.Y * sizeof(s32));
+	if (grid->pen_x.data)
+		_memset(grid->pen_x.data, 0,
+		grid->pen_x.X * grid->pen_x.Y * sizeof(s32));
+	if (grid->pen_y.data)
+		_memset(grid->pen_y.data, 0,
+		grid->pen_y.X * grid->pen_y.Y * sizeof(s32));
+
+	if (grid->key_mc.data)
+		_memset(grid->key_mc.data, 0,
+		grid->key_mc.X * grid->key_mc.Y * sizeof(s32));
+	if (grid->key_x.data)
+		_memset(grid->key_x.data, 0,
+		grid->key_x.X * grid->key_x.Y * sizeof(s32));
+	if (grid->key_y.data)
+		_memset(grid->key_y.data, 0,
+		grid->key_y.X * grid->key_y.Y * sizeof(s32));
+
+	if (grid->self.data)
+		_memset(grid->self.data, 0,
+		grid->self.X * grid->self.Y * sizeof(s32));
+
+	grid->dmsg.pen_need_update = false;
+	grid->dmsg.touch_need_update = false;
+	_memset(grid->dmsg.touch, 0, sizeof(grid->dmsg.touch));
+	_memset(grid->dmsg.pen, 0, sizeof(grid->dmsg.pen));
+}
+
+void grid_free(struct grids *grid)
+{
+	if (grid->mc.data)
+		CFREE(grid->mc.data);
+	if (grid->sc_x.data)
+		CFREE(grid->sc_x.data);
+	if (grid->sc_y.data)
+		CFREE(grid->sc_y.data);
+	if (grid->pen_x.data)
+		CFREE(grid->pen_x.data);
+	if (grid->pen_y.data)
+		CFREE(grid->pen_y.data);
+
+	if (grid->key_mc.data)
+		CFREE(grid->key_mc.data);
+	if (grid->key_x.data)
+		CFREE(grid->key_x.data);
+	if (grid->key_y.data)
+		CFREE(grid->key_y.data);
+
+	if (grid->self.data)
+		CFREE(grid->self.data);
+}
+
+int grid_alloc(void *handle, struct grids *grid)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int X, Y, key;
+
+	_memset(grid, 0, sizeof(*grid));
+
+	if (!dev)
+		return -EINVAL;
+
+	X = dev->tp_info.x_ch;
+	Y = dev->tp_info.y_ch;
+	key = dev->tp_info.key_num;
+
+	grid->mc.data = (s32 *)CALLOC(X * Y, sizeof(s32));
+	grid->sc_x.data = (s32 *)CALLOC(X, sizeof(s32));
+	grid->sc_y.data = (s32 *)CALLOC(Y, sizeof(s32));
+	grid->pen_x.data = (s32 *)CALLOC(X * 8, sizeof(s32));
+	grid->pen_y.data = (s32 *)CALLOC(Y * 8, sizeof(s32));
+	grid->key_mc.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_x.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_y.data = (s32 *)CALLOC(1, sizeof(s32));
+	grid->self.data = (s32 *)CALLOC(4, sizeof(s32));
+	if (!grid->mc.data || !grid->sc_x.data || !grid->sc_y.data ||
+	    !grid->pen_x.data || !grid->pen_y.data || !grid->key_mc.data ||
+	    !grid->key_x.data || !grid->key_y.data || !grid->self.data)
+		goto err_free;
+
+	grid->mc.X = X; grid->mc.Y = Y;
+	grid->sc_x.X = X; grid->sc_x.Y = 1;
+	grid->sc_y.X = 1; grid->sc_y.Y = Y;
+	grid->pen_x.X = X; grid->pen_x.Y = 8;
+	grid->pen_y.X = 8; grid->pen_y.Y = Y;
+
+	grid->key_mc.X = key; grid->key_mc.Y = 1;
+	grid->key_x.X = key; grid->key_x.Y = 1;
+	grid->key_y.X = 1; grid->key_y.Y = 1;
+
+	grid->self.X = 4, grid->self.Y = 1;
+
+	grid_reset(grid);
+
+	return 0;
+
+err_free:
+	grid_free(grid);
+
+	return -ENOMEM;
+}
+
+static u16 update_crc(u16 crc, u8 newbyte)
+{
+	char i;
+		const u16 crc_poly = 0x8408;
+
+	crc ^= newbyte;
+
+	for (i = 0; i < 8; i++) {
+		if (crc & 0x01)
+			crc = (crc >> 1) ^ crc_poly;
+		else
+			crc = crc >> 1;
+	}
+
+	return crc;
+}
+
+u16 get_crc(u32 start, u32 end,
+	    u8 *buf, u32 buf_size)
+{
+	u16 crc = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		crc = update_crc(crc, buf[i]);
+
+	return crc;
+}
+
+u32 get_checksum(u32 start, u32 end,
+		 u8 *buf, u32 buf_size)
+{
+	u32 sum = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		sum += buf[i];
+
+	return sum;
+}
+
+bool is_checksum_matched(u8 checksum, int start, int end,
+			 u8 *buf, int buf_size)
+{
+	u8 check;
+
+	check = ~(get_checksum(start, end, buf, buf_size)) + 1;
+	if (check != checksum) {
+		TP_ERR_ARR(NULL, "[data]", TYPE_U8, end - start, buf + start);
+		TP_ERR(NULL, "checksum : 0x%02x/0x%02x not matched\n",
+			check, checksum);
+		return false;
+	}
+
+	return true;
+}
+
+bool support_mcu_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060009))
+		return false;
+
+	return true;
+}
+
+bool support_sensor_id(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060004))
+		return false;
+
+	return true;
+}
+
+bool support_production_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_fwid(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010802) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_power_status(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x06000a))
+		return false;
+
+	return true;
+}
+
+int bridge_set_int_monitor(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf3;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int bridge_set_test_mode(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf2;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int reset_helper(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool need_re_enum = true;
+
+	if (dev->_interface == interface_i2c) {
+		/* sw reset if no reset-gpio found */
+		if (!dev->cb.hw_reset ||
+		    dev->cb.hw_reset(dev->reset_time, dev->_private) < 0)
+			return api_protocol_set_cmd(dev, SET_SW_RST,
+						    &need_re_enum);
+
+		return 0;
+	}
+
+	return api_protocol_set_cmd(dev, SET_SW_RST, &need_re_enum);
+}
+
+static int re_enum_helper(struct ilitek_ts_device *dev, u8 enum_type)
+{
+	int error;
+	int retry = 5;
+
+	if (!dev->cb.re_enum)
+		return -EINVAL;
+
+	do {
+		error = dev->cb.re_enum(enum_type, dev->_private);
+		if (!error)
+			return 0;
+
+		TP_WARN(dev->id, "re-enum failed, error: %d, retry: %d\n", error, retry);
+		dev->cb.delay_ms(500);
+	} while (!dev->setting.no_retry && retry--);
+
+	TP_ERR(dev->id, "re-enum retry failed\n");
+
+	return -ENODEV;
+}
+
+int read_interrupt_in(void *handle, u8 *buf, int rlen,
+		      unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_interrupt_in)
+		return -EINVAL;
+
+	error = dev->cb.read_interrupt_in(buf, rlen, timeout_ms,
+					   dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[int-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		 unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_ctrl_in)
+		return -EINVAL;
+
+	if (dev->quirks & QUIRK_BRIDGE) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA4;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = 0;
+		dev->wbuf[4] = (rlen + 6) & 0xFF;
+		dev->wbuf[5] = ((rlen + 6) >> 8) & 0xFF;
+		dev->wbuf[6] = cmd;
+
+		error = dev->cb.write_then_read_direct(dev->wbuf, 64,
+							dev->rbuf, 64, dev->_private);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.read_ctrl_in(buf, rlen, timeout_ms, dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[ctrl-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int write_then_read(void *handle, u8 *cmd, int wlen,
+		    u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[wbuf]:", TYPE_U8, wlen, cmd);
+
+	if (!wlen && (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+	    dev->quirks & QUIRK_BRIDGE)) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA3;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = rlen;
+
+		error = write_then_read_direct(dev, dev->wbuf, 64, NULL, 0);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.write_then_read(cmd, wlen, buf, rlen, dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[rbuf]:", TYPE_U8, rlen, buf);
+
+	return (error < 0) ? error : 0;
+}
+
+int write_then_read_direct(void *handle, u8 *cmd, int wlen,
+			   u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read_direct)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-wbuf]:", TYPE_U8, wlen, cmd);
+
+	error = dev->cb.write_then_read_direct(cmd, wlen, buf, rlen,
+					       dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-rbuf]:", TYPE_U8, rlen, buf);
+
+	return error;
+}
+
+int write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct ilitek_ts_callback *cb = &dev->cb;
+
+	u8 ack_cmd;
+
+	TP_DBG(dev->id, "cmd: 0x" PFMT_X8 ", tout_ms: %d\n",
+		cmd[0], timeout_ms);
+
+	if (dev->quirks & QUIRK_WAIT_ACK_DELAY) {
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		cb->delay_ms(dev->setting.wait_ack_delay);
+		return 0;
+	}
+
+	if (dev->setting.no_INT_ack) {
+		/* prevent bridge int handling flow affecting the following read */
+		if (dev->quirks & QUIRK_BRIDGE)
+			bridge_set_int_monitor(dev, false);
+
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		/*
+		* for no-INT-ack flow, add delay to prevent
+		* interrupting FW flow too soon, while FW should
+		* be handling previous write command. ex. 0xcd/ 0xc3
+		*/
+		cb->delay_ms(5);
+
+		goto check_busy;
+	}
+
+	if (!cb->init_ack || !cb->wait_ack)
+		return -EINVAL;
+
+	cb->init_ack(timeout_ms, dev->_private);
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, true);
+
+	error = write_then_read(dev, cmd, wlen, NULL, 0);
+	if (error < 0)
+		return error;
+
+	ack_cmd = (cmd[0] == CMD_ACCESS_SLAVE) ? cmd[2] : cmd[0];
+	error = cb->wait_ack(ack_cmd, timeout_ms, dev->_private);
+
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, false);
+
+	/* cmd[0] should be ILITEK cmd code */
+	if (error < 0) {
+		TP_WARN(dev->id, "wait 0x" PFMT_X8 " ack %d ms timeout, err: %d\n",
+			cmd[0], timeout_ms, error);
+
+		if (dev->setting.no_retry)
+			return -EILITIME;
+
+		goto check_busy;
+	}
+
+	return 0;
+
+check_busy:
+	return api_check_busy(dev, timeout_ms, 10);
+}
+
+/* Common APIs */
+static int api_protocol_get_scrn_res(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_screen_info *screen_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 28);
+	if (error < 0)
+		return error;
+
+	screen_info = (struct ilitek_screen_info *)dev->rbuf;
+
+	dev->screen_info.x_min = screen_info->x_min;
+	dev->screen_info.y_min = screen_info->y_min;
+	dev->screen_info.x_max = screen_info->x_max;
+	dev->screen_info.y_max = screen_info->y_max;
+
+	TP_DBG(dev->id, "screen x: " PFMT_U16 "~" PFMT_U16 ", screen y: " PFMT_U16 "~" PFMT_U16 "\n",
+		dev->screen_info.x_min, dev->screen_info.x_max,
+		dev->screen_info.y_min, dev->screen_info.y_max);
+
+	dev->screen_info.pressure_min = 0;
+	dev->screen_info.pressure_max = 0;
+	dev->screen_info.x_tilt_min = 0;
+	dev->screen_info.x_tilt_max = 0;
+	dev->screen_info.y_tilt_min = 0;
+	dev->screen_info.y_tilt_max = 0;
+	if (dev->protocol.ver > 0x60006) {
+		dev->screen_info.pressure_min = screen_info->pressure_min;
+		dev->screen_info.pressure_max = screen_info->pressure_max;
+		dev->screen_info.x_tilt_min = screen_info->x_tilt_min;
+		dev->screen_info.x_tilt_max = screen_info->x_tilt_max;
+		dev->screen_info.y_tilt_min = screen_info->y_tilt_min;
+		dev->screen_info.y_tilt_max = screen_info->y_tilt_max;
+
+		dev->screen_info.pen_x_min = screen_info->pen_x_min;
+		dev->screen_info.pen_y_min = screen_info->pen_y_min;
+		dev->screen_info.pen_x_max = screen_info->pen_x_max;
+		dev->screen_info.pen_y_max = screen_info->pen_y_max;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v3 *tp_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 15);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v3 *)dev->rbuf;
+
+	dev->tp_info.block_num = 2;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+
+	dev->tp_info.support_modes = tp_info->support_modes;
+	if (dev->tp_info.support_modes > 3 || !dev->tp_info.support_modes)
+		dev->tp_info.support_modes = 1;
+
+	TP_DBG(dev->id, "touch ch.(start/end) x: " PFMT_U8 "/" PFMT_U8 ", y: " PFMT_U8 "/" PFMT_U8 "\n",
+		tp_info->touch_start_y, tp_info->touch_end_y,
+		tp_info->touch_start_x, tp_info->touch_end_x);
+
+	if (dev->tp_info.key_num) {
+		/* check v3 key is virtual or hw keys */
+		dev->key.info.mode =
+			(tp_info->touch_start_y == 0xff &&
+			 tp_info->touch_end_y == 0xff &&
+			 tp_info->touch_start_x == 0xff &&
+			 tp_info->touch_end_x == 0xff) ?
+			key_hw : key_vitual;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v6 *tp_info;
+	u8 i;
+
+#define X(_enum, _code, _name) {_code, _name},
+	const struct {
+		const int code;
+		const char *str;
+	} pen_modes[] = { STYLUS_MODES };
+#undef X
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 21);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v6 *)dev->rbuf;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+	dev->tp_info.ic_num = tp_info->ic_num;
+	dev->tp_info.format = tp_info->format;
+	dev->tp_info.support_modes = tp_info->support_modes;
+	dev->tp_info.die_num = tp_info->die_num;
+
+	if (dev->tp_info.format == 5)
+		api_protocol_set_cmd(dev, GET_CRYPTO_INFO, NULL);
+
+	if (dev->tp_info.ic_num > ARRAY_SIZE(dev->ic)) {
+		TP_ERR(dev->id, "invalid ic_num: " PFMT_U8 "\n", dev->tp_info.ic_num);
+		return -EINVAL;
+	}
+	TP_MSG(dev->id, "[Panel Information] Chip count: %u\n",
+		dev->tp_info.ic_num);
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max tp: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	if (dev->protocol.ver < 0x60003)
+		return 0;
+
+	dev->tp_info.block_num = tp_info->block_num;
+	TP_MSG(dev->id, "[Panel Information] Block Number: " PFMT_U8 "\n",
+		dev->tp_info.block_num);
+
+	if (dev->protocol.ver < 0x60007)
+		return 0;
+
+	dev->tp_info.pen_modes = tp_info->pen_modes;
+
+	_memset(dev->pen_mode, 0, sizeof(dev->pen_mode));
+	if (!dev->tp_info.pen_modes)
+		_strcpy(dev->pen_mode, "Disable",
+			sizeof(dev->pen_mode));
+	for (i = 0; i < ARRAY_SIZE(pen_modes); i++) {
+		if (!(tp_info->pen_modes & pen_modes[i].code))
+			continue;
+
+		if (_strlen(dev->pen_mode))
+			_strcat(dev->pen_mode, ",", sizeof(dev->pen_mode));
+
+		_strcat(dev->pen_mode, pen_modes[i].str,
+			sizeof(dev->pen_mode));
+	}
+
+	TP_DBG(dev->id, "pen_modes: " PFMT_U8 "\n", dev->tp_info.pen_modes);
+	TP_MSG(dev->id, "[Panel Information] Pen Mode: " PFMT_C8 "\n",
+		dev->pen_mode);
+
+	dev->tp_info.pen_format = tp_info->pen_format;
+	dev->tp_info.pen_x_resolution = tp_info->pen_x_resolution;
+	dev->tp_info.pen_y_resolution = tp_info->pen_y_resolution;
+	TP_MSG(dev->id, "[Panel Information] Pen Format: 0x" PFMT_X8 "\n",
+		dev->tp_info.pen_format);
+	TP_MSG(dev->id, "[Panel Information] Pen X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.pen_x_resolution,
+		dev->tp_info.pen_y_resolution);
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+#define X(_enum, _id, _size, _cnt)	\
+	pen_fmts[_id].size = _size;	\
+	pen_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_PEN_REPORT_FORMAT;
+#undef X
+
+	if (dev->protocol.flag == PTL_V3)
+		error = api_protocol_get_tp_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		error = api_protocol_get_tp_info_v6(dev, data);
+	else
+		return -EINVAL;
+
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max fingers: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	switch (dev->tp_info.format) {
+	case touch_fmt_0x1:
+	case touch_fmt_0x2:
+	case touch_fmt_0x3:
+	case touch_fmt_0x4:
+	case touch_fmt_0x10:
+		if (dev->setting.default_format_enabled)
+			goto default_fmt_enabled;
+
+		dev->fmt.touch_size = touch_fmts[dev->tp_info.format].size;
+		dev->fmt.touch_max_cnt = touch_fmts[dev->tp_info.format].max_cnt;
+		break;
+
+default_fmt_enabled:
+	default:
+	case touch_fmt_0x11:
+	case touch_fmt_0x0:
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+		break;
+	}
+
+	switch (dev->tp_info.pen_format) {
+	case pen_fmt_0x1:
+	case pen_fmt_0x2:
+		dev->fmt.pen_size = pen_fmts[dev->tp_info.pen_format].size;
+		dev->fmt.pen_max_cnt = pen_fmts[dev->tp_info.pen_format].max_cnt;
+		break;
+	default:
+	case pen_fmt_0x0:
+		dev->fmt.pen_size = pen_fmts[pen_fmt_0x0].size;
+		dev->fmt.pen_max_cnt = pen_fmts[pen_fmt_0x0].max_cnt;
+		break;
+	}
+
+	TP_MSG(dev->id, "[Panel Information] X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_resolution, dev->tp_info.y_resolution);
+	TP_MSG(dev->id, "[Panel Information] X/Y channel: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_ch, dev->tp_info.y_ch);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Fingers\n",
+		dev->tp_info.max_fingers);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Keys\n",
+		dev->tp_info.key_num);
+
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " modes\n",
+		dev->tp_info.support_modes);
+
+	TP_DBG(dev->id, "touch format: 0x" PFMT_X8 ", size: %u bytes, max cnt: %u per packet\n",
+		dev->tp_info.format, dev->fmt.touch_size,
+		dev->fmt.touch_max_cnt);
+
+	if (dev->tp_info.key_num > 0) {
+		error = api_protocol_set_cmd(dev, GET_KEY_INFO, NULL);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v3 *key_info;
+	unsigned int i;
+
+	UNUSED(data);
+
+	/* Only i2c interface has key for V3 */
+	if (dev->_interface != interface_i2c)
+		return 0;
+
+	if (dev->tp_info.key_num > 20) {
+		TP_ERR(dev->id, "key count: " PFMT_U8 " invalid\n", dev->tp_info.key_num);
+		return -EINVAL;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 29);
+	if (error < 0)
+		return error;
+
+	for (i = 0; dev->tp_info.key_num > 5U &&
+	     i < DIV_ROUND_UP(dev->tp_info.key_num, 5U) - 1U; i++) {
+		TP_MSG(dev->id, "read keyinfo again, i: %u\n", i);
+		error = write_then_read(dev, NULL, 0,
+					dev->rbuf + 29 + 5 * i, 25);
+		if (error < 0)
+			return error;
+	}
+
+	key_info = (struct ilitek_key_info_v3 *)dev->rbuf;
+	dev->key.info.x_len = be16(key_info->x_len);
+	dev->key.info.y_len = be16(key_info->y_len);
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = be16(key_info->keys[i].x);
+		dev->key.info.keys[i].y = be16(key_info->keys[i].y);
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v6 *key_info;
+	unsigned int i, offset;
+
+	UNUSED(data);
+
+	if (dev->tp_info.key_num > ARRAY_SIZE(dev->key.info.keys)) {
+		TP_ERR(dev->id, "exception keycount " PFMT_U8 " > %d\n", dev->tp_info.key_num,
+			(int)ARRAY_SIZE(dev->key.info.keys));
+		return -EINVAL;
+	}
+
+	switch (dev->_interface) {
+	case interface_i2c:
+		if (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+		    dev->quirks & QUIRK_BRIDGE) {
+			error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+			if (error < 0)
+				return error;
+			error = read_ctrl_in(dev, CMD_GET_KEY_INFO,
+					     dev->rbuf,
+					     5 + dev->tp_info.key_num * 5, 2000);
+			if (error < 0)
+				return error;
+			offset = (dev->quirks & QUIRK_BRIDGE) ? 1 : 0;
+		} else {
+			error = write_then_read(dev, dev->wbuf, 1,
+						dev->rbuf, 5 + dev->tp_info.key_num * 5);
+			if (error < 0)
+				return error;
+			offset = 0;
+		}
+		break;
+
+	case interface_usb:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 6;
+		break;
+	case interface_hid_over_i2c:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 4;
+		break;
+	default: return -EINVAL;
+	};
+
+	key_info = (struct ilitek_key_info_v6 *)(dev->rbuf + offset);
+	dev->key.info.mode = key_info->mode;
+	TP_MSG(dev->id, "[Panel Information] key mode: " PFMT_U8 "\n", dev->key.info.mode);
+
+	dev->key.info.x_len = key_info->x_len;
+	dev->key.info.y_len = key_info->y_len;
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = key_info->keys[i].x;
+		dev->key.info.keys[i].y = key_info->keys[i].y;
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_key_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_key_info_v6(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_get_ptl_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->protocol.flag = PTL_V6;
+	dev->reset_time = 1000;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->protocol.ver = (dev->rbuf[0] << 16) + (dev->rbuf[1] << 8) +
+			     dev->rbuf[2];
+	TP_MSG(dev->id, "[Protocol Version]: %x.%x.%x\n",
+		(dev->protocol.ver >> 16) & 0xFF,
+		(dev->protocol.ver >> 8) & 0xFF,
+		dev->protocol.ver & 0xFF);
+
+	dev->protocol.flag = get_protocol_ver_flag(dev->protocol.ver);
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->reset_time = 200;
+		break;
+	case PTL_V6:
+		dev->reset_time = 600;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol ver.: 0x%x\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_fw_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->fw_ver, dev->rbuf, 8);
+
+	if (dev->ic[0].mode == BL_MODE) {
+		TP_MSG_ARR(dev->id, "[BL Firmware Version]", TYPE_U8,
+			   8, dev->fw_ver);
+	} else {
+		TP_MSG_ARR(dev->id, "[FW Version]", TYPE_U8, 4, dev->fw_ver);
+		TP_MSG_ARR(dev->id, "[Customer Version]", TYPE_U8,
+			   4, dev->fw_ver + 4);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < ic_num; i++) {
+		dev->ic[i].mode = dev->rbuf[i * 2];
+
+		if (dev->ic[i].mode == AP_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "AP");
+		else if (dev->ic[i].mode == BL_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "BL");
+		else
+			_sprintf(dev->ic[i].mode_str, 0, "UNKNOWN");
+	}
+
+	TP_MSG(dev->id, "[Current Mode] Master: 0x" PFMT_X8 " " PFMT_C8 "\n",
+		dev->ic[0].mode, dev->ic[0].mode_str);
+	for (i = 1; i < ic_num; i++)
+		TP_MSG(dev->id, "[Current Mode] Slave[" PFMT_U8 "]: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, dev->ic[i].mode, dev->ic[i].mode_str);
+
+	return 0;
+}
+
+static int api_protocol_power_status(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 func, lvd_level_sel;
+
+	if (!data)
+		return -EFAULT;
+
+	if (!support_power_status(dev)) {
+		_memset(&dev->pwr, 0, sizeof(dev->pwr));
+		return 0;
+	}
+
+	func = ((*(u16 *)data) >> 8) & 0xff;
+	lvd_level_sel = (*(u16 *)data) & 0xff;
+
+	dev->wbuf[1] = func;
+	switch (func) {
+	/* get level select */
+	case 0x02:
+		dev->wbuf[2] = lvd_level_sel;
+		return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+
+	/* clear flag */
+	case 0x00:
+		return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+
+	/* get flag */
+	case 0x01:
+		error = write_then_read(dev, dev->wbuf, 2,
+					dev->rbuf, 4);
+		if (error < 0)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev->pwr.header = be16(dev->rbuf);
+	dev->pwr.vdd33_lvd_flag = dev->rbuf[2];
+	dev->pwr.vdd33_lvd_level_sel = dev->rbuf[3];
+
+	TP_DBG(dev->id, "[Power-Status] header: 0x" PFMT_X16 ", flag: 0x" PFMT_X8 ", level_sel: 0x" PFMT_X8 "\n",
+		dev->pwr.header, dev->pwr.vdd33_lvd_flag,
+		dev->pwr.vdd33_lvd_level_sel);
+
+	return 0;
+}
+
+static int api_protocol_get_sensor_id(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_sensor_id(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->sensor.header = be16(dev->rbuf);
+	dev->sensor.id = dev->rbuf[2];
+
+	TP_MSG(dev->id, "[Sensor ID] header: 0x" PFMT_X16 ", id: 0x" PFMT_X8 "\n",
+		dev->sensor.header,
+		(u8)(dev->sensor.id & dev->setting.sensor_id_mask));
+
+	return 0;
+}
+
+static int api_protocol_get_product_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_production_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->product_info, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[Production Info]", TYPE_U8, 8, dev->product_info);
+
+	return 0;
+}
+
+static int api_protocol_get_fwid(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_fwid(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->customer_id = le16(dev->rbuf);
+	dev->fwid = le16(dev->rbuf + 2);
+
+	TP_MSG(dev->id, "[Customer ID] 0x%04x\n", dev->customer_id);
+	TP_MSG(dev->id, "[FWID] 0x%04x\n", dev->fwid);
+
+	return 0;
+}
+
+static int api_protocol_get_crypto_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	u16 __MAYBE_UNUSED crypto_ver;
+	u32 crypto_opt;
+
+	UNUSED(data);
+
+	/*
+	 * encrypted report format should be supported after AP v6.0.8
+	 * set report format to 0 if protocol version not matched or
+	 * crypto info say it's not supported.
+	 */
+	if (dev->protocol.ver < 0x060008 ||
+	    write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6) < 0) {
+		dev->tp_info.format = 0;
+		return 0;
+	}
+
+	crypto_ver = le16(dev->rbuf);
+	crypto_opt = le32(dev->rbuf + 2, 4);
+
+	TP_MSG(dev->id, "[Encrypt Ver.] 0x%x\n", crypto_ver);
+	TP_MSG(dev->id, "[Encrypt Options] 0x%x\n", crypto_opt);
+
+	if (!(crypto_opt & 1))
+		dev->tp_info.format = 0;
+
+	return 0;
+}
+
+static int api_protocol_get_hid_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060009)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6);
+	if (error < 0)
+		return error;
+
+	_memcpy(&dev->hid_info, dev->rbuf, sizeof(dev->hid_info));
+
+	TP_MSG(dev->id, "vid/pid/rev: 0x%x/0x%x/0x%x\n",
+		dev->hid_info.vid, dev->hid_info.pid, dev->hid_info.rev);
+
+	return 0;
+}
+
+static bool is_special_char(char c)
+{
+	return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+		(c >= '0' && c <= '9')) ? false : true;
+}
+
+static int api_protocol_get_mcu_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_ver {
+		u16 ic_name;
+		u8 df_start_addr[3];
+		u8 df_size;
+
+		char module_name[26];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, use GET_MCU_VER (0x61) cmd
+	 */
+	if (dev->protocol.flag == PTL_V6 && support_mcu_info(dev)) {
+		error = api_protocol_set_cmd(dev, GET_MCU_INFO, NULL);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1,
+					dev->rbuf, 32);
+		if (error < 0)
+			return error;
+
+		parser = (struct mcu_ver *)dev->rbuf;
+
+		_memset(dev->mcu_info.ic_name, 0,
+			sizeof(dev->mcu_info.ic_name));
+		_sprintf(dev->mcu_info.ic_name, 0, "%04x", parser->ic_name);
+
+		_memset(dev->mcu_info.module_name, 0,
+			sizeof(dev->mcu_info.module_name));
+		_memcpy(dev->mcu_info.module_name, parser->module_name,
+			sizeof(parser->module_name));
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		if (is_29xx(dev)) {
+			/* modify reset time to 100ms for 29xx ICs */
+			dev->reset_time = 100;
+
+			/* set mm_addr for bin file update */
+			dev->mcu_info.mm_addr =
+				is_2501x(dev) ? MM_ADDR_2501X : MM_ADDR_29XX;
+			dev->mcu_info.min_addr = START_ADDR_29XX;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		} else {
+			dev->mcu_info.mm_addr = MM_ADDR_LEGO;
+			dev->mcu_info.min_addr = START_ADDR_LEGO;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		}
+	}
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+	if (!strcmp(dev->mcu_info.ic_name, "2133"))
+		_sprintf(dev->mcu_info.ic_name, 0, "2132S");
+
+	_memset(dev->mcu_info.ic_full_name, 0,
+		sizeof(dev->mcu_info.ic_full_name));
+	_sprintf(dev->mcu_info.ic_full_name, 0,
+		"ILI" PFMT_C8, dev->mcu_info.ic_name);
+
+	TP_MSG(dev->id, "[MCU Kernel Version] " PFMT_C8 "\n",
+		dev->mcu_info.ic_full_name);
+	TP_MSG(dev->id, "[Module Name]: [" PFMT_C8 "]\n",
+		dev->mcu_info.module_name);
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_info {
+		char ic_name[5];
+		char mask_ver[2];
+		u8 mm_addr[3];
+		char module_name[18];
+		u8 reserve[4];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd only support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, return 0 to skip this command.
+	 */
+	if (dev->protocol.flag != PTL_V6 || !support_mcu_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	if (error < 0)
+		return error;
+
+	parser = (struct mcu_info *)dev->rbuf;
+
+	_memset(dev->mcu_info.ic_name, 0, sizeof(dev->mcu_info.ic_name));
+	_memcpy(dev->mcu_info.ic_name, parser->ic_name,
+		sizeof(parser->ic_name));
+
+	_memcpy(dev->mcu_info.module_name, parser->module_name,
+		sizeof(parser->module_name));
+	dev->mcu_info.mm_addr = le32(parser->mm_addr, 3);
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_fs_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->sine.start & 0xFF;
+	dev->wbuf[2] = freq->sine.start >> 8;
+	dev->wbuf[3] = freq->sine.end & 0xFF;
+	dev->wbuf[4] = freq->sine.end >> 8;
+	dev->wbuf[5] = freq->sine.step;
+	dev->wbuf[6] = freq->mc_swcap.start & 0xFF;
+	dev->wbuf[7] = freq->mc_swcap.start >> 8;
+	dev->wbuf[8] = freq->mc_swcap.end & 0xFF;
+	dev->wbuf[9] = freq->mc_swcap.end >> 8;
+	dev->wbuf[10] = freq->mc_swcap.step;
+	dev->wbuf[11] = freq->sc_swcap.start & 0xFF;
+	dev->wbuf[12] = freq->sc_swcap.start >> 8;
+	dev->wbuf[13] = freq->sc_swcap.end & 0xFF;
+	dev->wbuf[14] = freq->sc_swcap.end >> 8;
+	dev->wbuf[15] = freq->sc_swcap.step;
+	dev->wbuf[16] = freq->frame_cnt & 0xFF;
+	dev->wbuf[17] = freq->frame_cnt >> 8;
+	dev->wbuf[18] = freq->scan_type;
+
+	if (dev->protocol.ver < 0x60005)
+		return write_then_read(dev, dev->wbuf, 19, NULL, 0);
+
+	do {
+		if (dev->protocol.ver < 0x60009) {
+			error = write_then_read(dev, dev->wbuf, 19,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[16] = freq->dump1.start & 0xFF;
+		dev->wbuf[17] = freq->dump1.start >> 8;
+		dev->wbuf[18] = freq->dump1.end & 0xFF;
+		dev->wbuf[19] = freq->dump1.end >> 8;
+		dev->wbuf[20] = freq->dump1.step;
+		dev->wbuf[21] = freq->dump1_val;
+		dev->wbuf[22] = freq->dump2.start & 0xFF;
+		dev->wbuf[23] = freq->dump2.start >> 8;
+		dev->wbuf[24] = freq->dump2.end & 0xFF;
+		dev->wbuf[25] = freq->dump2.end >> 8;
+		dev->wbuf[26] = freq->dump2.step;
+		dev->wbuf[27] = freq->dump2_val;
+		dev->wbuf[28] = freq->frame_cnt & 0xFF;
+		dev->wbuf[29] = freq->frame_cnt >> 8;
+		dev->wbuf[30] = freq->scan_type;
+
+		if (dev->protocol.ver < 0x6000a) {
+			error = write_then_read(dev, dev->wbuf, 31,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[31] = freq->mc_frame_cnt & 0xFF;
+		dev->wbuf[32] = freq->mc_frame_cnt >> 8;
+		dev->wbuf[33] = freq->dump_frame_cnt & 0xFF;
+		dev->wbuf[34] = freq->dump_frame_cnt >> 8;
+
+		error = write_then_read(dev, dev->wbuf, 35, dev->rbuf, 5);
+
+	} while (0);
+
+	if (error < 0)
+		return error;
+
+	freq->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != 0x5a || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			freq->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_short_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct short_settings *_short = (struct short_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[short info] dump1: 0x" PFMT_X8 ", dump2: 0x" PFMT_X8 ", vref: 0x" PFMT_X8 ", postidle: 0x" PFMT_X16 "\n",
+		_short->dump_1, _short->dump_2,
+		_short->v_ref_L, _short->post_idle);
+
+	dev->wbuf[1] = _short->dump_1;
+	dev->wbuf[2] = _short->dump_2;
+	dev->wbuf[3] = _short->v_ref_L;
+	dev->wbuf[4] = _short->post_idle & 0xFF;
+	dev->wbuf[5] = (_short->post_idle >> 8) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 6, NULL, 0);
+}
+
+static int api_protocol_set_open_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct open_settings *open = (struct open_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id,
+		"[open info] freq.: " PFMT_U16 ", gain: 0x" PFMT_X8 ", gain_rfb: 0x" PFMT_X8
+		", afe_res_sel: 0x" PFMT_X8 ", mc_fsel: 0x" PFMT_X8 "\n",
+		open->freq, open->gain, open->gain_rfb,
+		open->afe_res_sel, open->mc_fsel);
+
+	dev->wbuf[wlen++] = open->freq & 0xFF;
+	dev->wbuf[wlen++] = (open->freq >> 8) & 0xFF;
+	dev->wbuf[wlen++] = open->gain;
+	dev->wbuf[wlen++] = open->gain_rfb;
+	dev->wbuf[wlen++] = open->afe_res_sel;
+	dev->wbuf[wlen++] = open->mc_fsel;
+
+	if (dev->protocol.ver > 0x060009) {
+		TP_DBG(dev->id, "[open info] frame: " PFMT_U16 "\n",
+			open->frame);
+
+		dev->wbuf[wlen++] = open->frame & 0xFF;
+		dev->wbuf[wlen++] = (open->frame >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_charge_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error, i;
+	struct charge_curve_settings *curve =
+		(struct charge_curve_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "charge-curve info. scan mode: 0x" PFMT_U8 "\n",
+		curve->scan_mode);
+
+	dev->wbuf[1] = curve->scan_mode;
+	dev->wbuf[2] = curve->dump.start & 0xFF;
+	dev->wbuf[3] = (curve->dump.start >> 8) & 0xFF;
+	dev->wbuf[4] = curve->dump.end & 0xFF;
+	dev->wbuf[5] = (curve->dump.end >> 8) & 0xFF;
+	dev->wbuf[6] = curve->dump.step;
+	dev->wbuf[7] = curve->dump.post_idle & 0xFF;
+	dev->wbuf[8] = (curve->dump.post_idle >> 8) & 0xFF;
+	dev->wbuf[9] = curve->dump.fix_val & 0xFF;
+	dev->wbuf[10] = (curve->dump.fix_val >> 8) & 0xFF;
+	dev->wbuf[11] = curve->charge.start & 0xFF;
+	dev->wbuf[12] = (curve->charge.start >> 8) & 0xFF;
+	dev->wbuf[13] = curve->charge.end & 0xFF;
+	dev->wbuf[14] = (curve->charge.end >> 8) & 0xFF;
+	dev->wbuf[15] = curve->charge.step;
+	dev->wbuf[16] = curve->charge.post_idle & 0xFF;
+	dev->wbuf[17] = (curve->charge.post_idle >> 8) & 0xFF;
+	dev->wbuf[18] = curve->charge.fix_val & 0xFF;
+	dev->wbuf[19] = (curve->charge.fix_val >> 8) & 0xFF;
+	dev->wbuf[20] = curve->c_sub & 0xFF;
+	dev->wbuf[21] = (curve->c_sub >> 8) & 0xFF;
+	dev->wbuf[22] = curve->frame_cnt & 0xFF;
+	dev->wbuf[23] = (curve->frame_cnt >> 8) & 0xFF;
+
+	for (i = 0; i < (int)ARRAY_SIZE(curve->pt); i++) {
+		dev->wbuf[24 + i * 4] = curve->pt[i].x & 0xFF;
+		dev->wbuf[24 + i * 4 + 1] = (curve->pt[i].x >> 8) & 0xFF;
+		dev->wbuf[24 + i * 4 + 2] = curve->pt[i].y & 0xFF;
+		dev->wbuf[24 + i * 4 + 3] = (curve->pt[i].y >> 8) & 0xFF;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 60, dev->rbuf, 5);
+	if (error < 0)
+		return error;
+
+	curve->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != AP_MODE || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			curve->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_p2p_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct p2p_settings *p2p = (struct p2p_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[p2p info] frame_cnt.: " PFMT_U16 ", type: 0x" PFMT_X8 "\n",
+		p2p->frame_cnt, p2p->type);
+
+	dev->wbuf[wlen++] = p2p->frame_cnt & 0xFF;
+	dev->wbuf[wlen++] = (p2p->frame_cnt >> 8) & 0xFF;
+
+	if (dev->protocol.ver > 0x060009) {
+		dev->wbuf[wlen++] = p2p->type & 0xFF;
+		dev->wbuf[wlen++] = p2p->freq & 0xFF;
+		dev->wbuf[wlen++] = (p2p->freq >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_pen_fs_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->pen.mode;
+	dev->wbuf[2] = freq->pen.start & 0xFF;
+	dev->wbuf[3] = freq->pen.start >> 8;
+	dev->wbuf[4] = freq->pen.end & 0xFF;
+	dev->wbuf[5] = freq->pen.end >> 8;
+	dev->wbuf[6] = freq->frame_cnt & 0xFF;
+	dev->wbuf[7] = freq->frame_cnt >> 8;
+
+	error = write_then_read(dev, dev->wbuf, 8, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int api_protocol_get_core_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->core_ver, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[CoreVersion]", TYPE_U8, 4, dev->core_ver);
+
+	return 0;
+}
+
+static int api_protocol_get_tuning_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->tuning_ver, dev->rbuf, 4);
+
+	TP_MSG_ARR(dev->id, "[TurningVersion]", TYPE_U8, 4, dev->tuning_ver);
+
+	return 0;
+}
+
+static int api_protocol_set_sw_reset(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	int wlen = 1;
+	bool need_re_enum = (data) ? *(bool *)data : false;
+	bool force_reset = (!data) ? true : false;
+
+	/* make sure touch report in default I2C-HID mode after force reset */
+	if (dev->_interface == interface_hid_over_i2c && !force_reset)
+		return 0;
+
+	dev->wbuf[1] = 0;
+	error = write_then_read(dev, dev->wbuf, wlen, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(dev->reset_time);
+
+	if (dev->_interface == interface_usb && need_re_enum)
+		return re_enum_helper(dev, enum_sw_reset);
+
+	return 0;
+}
+
+static int api_protocol_get_sys_busy(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	if (data)
+		*(u8 *)data = 0;
+
+	_memset(dev->rbuf, 0, 64);
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 1);
+	if (error < 0)
+		return error;
+
+	if (data)
+		*(u8 *)data = dev->rbuf[0];
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	/*
+	 * No need to get/print AP CRC by 0xC7 in BL mode,
+	 * and 2501x ICs would get wrong crc in BL.
+	 */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1,
+				dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	TP_MSG(dev->id, "[FW CRC] Master: 0x%x\n", dev->ic[0].crc[0]);
+
+	for (i = 1; i < ic_num; i++) {
+		dev->ic[i].crc[0] = le16(dev->rbuf + 2 * i);
+		TP_MSG(dev->id, "[FW CRC] Slave[" PFMT_U8 "]: 0x%x\n",
+			i, dev->ic[i].crc[0]);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error, rlen;
+
+	UNUSED(data);
+
+	rlen = (is_231x(dev)) ? 4 : 2;
+
+	if (dev->_interface == interface_i2c) {
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		dev->cb.delay_ms(600);
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	}
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	if (is_231x(dev))
+		dev->ic[0].crc[0] |= (le16(dev->rbuf + 2) << 16);
+
+	TP_MSG(dev->id, "[Check Code] AP: 0x%x\n", dev->ic[0].crc[0]);
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_ap_crc_v6(dev, data);
+	else if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_ap_crc_v3(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_set_mode_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 mode = dev->wbuf[1];
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * Bridge with V3 IC need to set bridge into/out test mode additionally.
+	 */
+	if (dev->quirks & QUIRK_BRIDGE)
+		return bridge_set_test_mode(dev, (mode) ? true : false);
+
+	return 0;
+}
+
+static int api_protocol_write_enable(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool in_ap = (data) ? *(bool *)data : true;
+
+	error = write_then_read(dev, dev->wbuf,
+				(in_ap) ? 3 : 10, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * V3 need AP/BL mode switch delay
+	 */
+	if (in_ap)
+		dev->cb.delay_ms(is_231x(dev) ? 1000 : 100);
+	else
+		dev->cb.delay_ms(10);
+
+	return 0;
+}
+
+static int api_protocol_write_data_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 33, NULL, 0);
+}
+
+static int api_protocol_get_df_crc(struct ilitek_ts_device *dev,  void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->ic[0].crc[1] = 0;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[1] = le16(dev->rbuf + 2) << 16 | le16(dev->rbuf);
+	TP_MSG(dev->id, "[Check Code] Data: 0x%x\n", dev->ic[0].crc[1]);
+
+	return 0;
+}
+
+static int api_protocol_set_mode_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_get_crc_by_addr(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 start, end, t_ms;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		start = le32(dev->wbuf + 2, 3);
+		end = le32(dev->wbuf + 5, 3);
+		t_ms = ((end - start) / 4096 + 1) * TOUT_CD * TOUT_CD_RATIO;
+
+		error = write_then_wait_ack(dev, dev->wbuf, 8, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_get_crc_by_num(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 t_ms = (dev->wbuf[2] == 0) ? TOUT_CF_BLOCK_0 : TOUT_CF_BLOCK_N;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		error = write_then_wait_ack(dev, dev->wbuf, 3, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_read_flash(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 code = *(u32 *)data;
+	bool prepare;
+	int rlen;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		if ((dev->protocol.ver & 0xFFFF00) == BL_PROTOCOL_V1_7 &&
+		     dev->fw_ver[3] < 3) {
+			TP_ERR(dev->id, "BL: 0x%x, FW: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 " not support cmd: 0x" PFMT_X8 "\n",
+				dev->protocol.ver, dev->fw_ver[0],
+				dev->fw_ver[1], dev->fw_ver[2], dev->fw_ver[3],
+				dev->wbuf[0]);
+			return -EINVAL;
+		}
+
+		return write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	}
+
+	if (!data)
+		return -EINVAL;
+
+	prepare = (code >> 16) ? true : false;
+	rlen = code & 0xFFFF;
+
+	if (prepare) {
+		error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+		dev->cb.delay_ms(100);
+
+		return error;
+	}
+
+	if (dev->_interface == interface_i2c)
+		error = write_then_read(dev, dev->wbuf, 2, dev->rbuf, rlen);
+	else
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+
+	return error;
+}
+
+static int api_protocol_set_flash_addr(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 addr = *(u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		dev->wbuf[3] = addr & 0xFF;
+		dev->wbuf[2] = (addr >> 8) & 0xFF;
+		dev->wbuf[1] = (addr >> 16) & 0xFF;
+
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		return 0;
+	}
+
+	dev->wbuf[1] = addr & 0xFF;
+	dev->wbuf[2] = (addr >> 8) & 0xFF;
+	dev->wbuf[3] = (addr >> 16) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 4, NULL, 0);
+}
+
+static int api_protocol_set_data_len(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_set_flash_enable(struct ilitek_ts_device *dev,
+					 void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	int wlen, rlen;
+	bool in_ap = ((type & 0x1) != 0) ? true : false;
+	bool is_slave = ((type & 0x2) != 0) ? true : false;
+
+	u32 set_start, set_end, get_start, get_end;
+
+	if (!is_slave) {
+		wlen = (in_ap) ? 3 : 9;
+		rlen = (in_ap || dev->protocol.ver < 0x010803) ? 0 : 6;
+
+		set_start = le32(dev->wbuf + 3, 3);
+		set_end = le32(dev->wbuf + 6, 3);
+
+		error = write_then_read(dev, dev->wbuf, wlen,
+					dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+
+		if (in_ap || dev->protocol.ver < 0x010803)
+			return 0;
+
+		get_start = le32(dev->rbuf, 3);
+		get_end = le32(dev->rbuf + 3, 3);
+
+		if (set_start != get_start || set_end != get_end) {
+			TP_ERR(dev->id, "start/end addr.: 0x%x/0x%x vs. 0x%x/0x%x not match\n",
+				set_start, set_end, get_start, get_end);
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	error = write_then_wait_ack(dev, dev->wbuf, 9,
+				    TOUT_CC_SLAVE * TOUT_CC_SLAVE_RATIO);
+	if (error < 0)
+		return error;
+	dev->cb.delay_ms(2000);
+
+	return (dev->_interface == interface_usb) ?
+		re_enum_helper(dev, enum_sw_reset) : 0;
+}
+
+static int api_protocol_write_data_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, TOUT_C3 * TOUT_C3_RATIO);
+}
+
+static int api_protocol_write_data_m2v(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, 30000);
+}
+
+static int api_protocol_access_slave(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_slave_access *access;
+
+	if (!data)
+		return -EINVAL;
+
+	access = (struct ilitek_slave_access *)data;
+
+	dev->wbuf[1] = access->slave_id;
+	dev->wbuf[2] = access->func;
+	_memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (access->func) {
+	case CMD_GET_AP_CRC:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 4);
+		*((u32 *)access->data) = le32(dev->rbuf, 4);
+		break;
+
+	case CMD_GET_MCU_MOD:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 1);
+		*((u8 *)access->data) = dev->rbuf[0];
+		break;
+
+	case CMD_GET_FW_VER:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 8);
+		_memcpy((u8 *)access->data, dev->rbuf, 8);
+
+		break;
+
+	case CMD_WRITE_ENABLE:
+		dev->wbuf[3] = ((u8 *)access->data)[0];
+		dev->wbuf[4] = ((u8 *)access->data)[1];
+		dev->wbuf[5] = ((u8 *)access->data)[2];
+		dev->wbuf[6] = ((u8 *)access->data)[3];
+		dev->wbuf[7] = ((u8 *)access->data)[4];
+		dev->wbuf[8] = ((u8 *)access->data)[5];
+
+		error = write_then_wait_ack(dev, dev->wbuf, 9, 5000);
+		break;
+
+	default:
+		error = write_then_wait_ack(dev, dev->wbuf, 3, 5000);
+		break;
+	};
+
+	return error;
+}
+
+static int api_protocol_set_ap_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, false, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_bl_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, true, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_idle(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_set_sleep(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_wakeup(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_func_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool get = (data) ? *(bool *)data : true;
+
+	if (!data)
+		return -EINVAL;
+
+	if (get) {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+		if (error < 0)
+			return error;
+
+		dev->func.header = be16(dev->rbuf);
+		dev->func.mode = dev->rbuf[2];
+		TP_MSG(dev->id, "[FW Mode] 0x" PFMT_X8 "\n", dev->func.mode);
+
+		return 0;
+	}
+
+	if (dev->protocol.flag == PTL_V3) {
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+		return 0;
+	} else if (dev->protocol.flag == PTL_V6) {
+		error = write_then_wait_ack(dev, dev->wbuf, 4,
+					    TOUT_68 * TOUT_68_RATIO);
+		if (error < 0)
+			return error;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int api_protocol_c_model_info(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060008)
+		return write_then_read(dev, dev->wbuf, 12, NULL, 0);
+
+	return write_then_read(dev, dev->wbuf, 18, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct tuning_para_settings tuning =
+		*(struct tuning_para_settings *)data;
+	u32 wlen;
+
+	int header;
+	int tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = tuning.func;
+	dev->wbuf[2] = tuning.ctrl;
+	dev->wbuf[3] = tuning.type;
+
+	if (tuning.func == 0x0) {
+		wlen = 4;
+		tout_ms = TOUT_65_READ * TOUT_65_READ_RATIO;
+
+		switch (tuning.ctrl) {
+		case 0x3: case 0x5: case 0x10:
+			wlen += tuning.len;
+			tout_ms = TOUT_65_WRITE * TOUT_65_WRITE_RATIO;
+
+			//TODO: add memory range check
+			_memcpy(dev->wbuf + 4, tuning.buf, tuning.len);
+			break;
+		}
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	switch (tuning.ctrl) {
+	case 0x2: case 0x4:
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = read_ctrl_in(dev, CMD_TUNING_PARA_V6,
+				     dev->rbuf, 1024, 5000);
+		if (error < 0)
+			return error;
+
+		header = (dev->_interface == interface_i2c) ? 5 : 6;
+		header = (dev->quirks & QUIRK_BRIDGE) ? 6 : header;
+
+		_memcpy(tuning.buf, dev->rbuf + header, tuning.len);
+
+		break;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_cdc_init_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	int wlen;
+	struct cdc_settings *set = (struct cdc_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (set->is_freq) {
+		dev->wbuf[1] = 0x0F;
+		dev->wbuf[2] = set->freq.sine.start;
+		dev->wbuf[3] = set->freq.sine.end;
+		dev->wbuf[4] = set->freq.sine.step;
+
+		error = write_then_read(dev, dev->wbuf, 5, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(200);
+	} else {
+		dev->wbuf[1] = set->cmd;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = set->config & 0xFF;
+		wlen = 4;
+
+		if (set->config & 0xFF00) {
+			dev->wbuf[3] = (set->config >> 8) & 0xFF;
+			dev->wbuf[4] = set->config & 0xFF;
+			wlen = 5;
+		}
+
+		error = write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(10);
+	}
+
+	return api_check_busy(dev, 15000, 10);
+}
+
+static int api_protocol_get_cdc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_wait_ack(dev, dev->wbuf, 1, TOUT_F2 * TOUT_F2_RATIO);
+}
+
+static int api_protocol_set_cdc_init_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	struct cdc_settings *set = (struct cdc_settings *)data;
+	int wlen = 1, tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[wlen++] = set->cmd;
+
+	if (set->is_freq) {
+		tout_ms = set->freq.sine.steps * TOUT_F1_FREQ_MC;
+		tout_ms += (set->freq.mc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.sc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump1.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump2.steps * TOUT_F1_FREQ_SC);
+
+		tout_ms += (set->freq.pen.steps * TOUT_F1_FREQ_MC);
+
+		tout_ms *= MAX(set->freq.frame_cnt, 1);
+		tout_ms *= TOUT_F1_FREQ_RATIO;
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (set->is_p2p) {
+		tout_ms = MAX(set->p2p.frame_cnt, 1) *
+			TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (dev->protocol.ver > 0x60008)
+		dev->wbuf[wlen++] = set->config & 0xFF;
+
+	tout_ms = TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+	if (set->is_curve) {
+		tout_ms = set->curve.dump.steps * TOUT_F1_CURVE;
+		tout_ms += (set->curve.charge.steps * TOUT_F1_CURVE);
+		tout_ms *= MAX(set->curve.frame_cnt, 1);
+		tout_ms *= TOUT_F1_CURVE_RATIO;
+	} else if (set->is_short) {
+		tout_ms = TOUT_F1_SHORT * TOUT_F1_SHORT_RATIO;
+	} else if (set->is_open) {
+		tout_ms = MAX(set->open.frame, 1) *
+			TOUT_F1_OPEN * TOUT_F1_OPEN_RATIO;
+	} else if (set->is_key && dev->protocol.ver < 0x6000a) {
+		tout_ms = TOUT_F1_KEY;
+	}
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+}
+
+static int api_protocol_get_cdc_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u32 *cdc_info = (u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	*cdc_info = le32(dev->rbuf, 4);
+
+	return 0;
+}
+
+int api_protocol_set_cmd(void *handle, u8 idx, void *data)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev || idx >= ARRAY_SIZE(protocol_maps))
+		return -EINVAL;
+
+	if (!(dev->protocol.flag & protocol_maps[idx].flag) &&
+	    protocol_maps[idx].flag != PTL_ANY) {
+		TP_ERR(dev->id, "Unexpected cmd: " PFMT_C8 " for 0x" PFMT_X8 " only, now is 0x" PFMT_X8 "\n",
+			protocol_maps[idx].desc, protocol_maps[idx].flag,
+			dev->protocol.flag);
+		return -EINVAL;
+	}
+
+	dev->wbuf[0] = protocol_maps[idx].cmd;
+	error = protocol_maps[idx].func(dev, data);
+	if (error < 0) {
+		TP_ERR(dev->id, "failed to execute cmd: 0x" PFMT_X8 " " PFMT_C8 ", err: %d\n",
+			protocol_maps[idx].cmd, protocol_maps[idx].desc, error);
+		return error;
+	}
+
+	return 0;
+}
+
+int api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	u8 cmd = 0;
+
+	if (!force && dev->fw_mode == mode)
+		return 0;
+
+	_memset(dev->wbuf, 0, sizeof(dev->wbuf));
+
+	if (dev->protocol.flag == PTL_V3) {
+		/* V3 only support suspend and normal mode */
+		if (mode != mode_normal &&
+		    mode != mode_suspend &&
+		    mode != mode_test)
+			return -EPROTONOSUPPORT;
+		dev->wbuf[1] = (mode == mode_normal) ? 0x00 : 0x01;
+		cmd = SET_TEST_MOD;
+	} else if (dev->protocol.flag == PTL_V6) {
+		dev->wbuf[1] = mode;
+		dev->wbuf[2] = (eng) ? 0x01 : 0x00;
+		cmd = SET_MOD_CTRL;
+	}
+
+	error = api_protocol_set_cmd(dev, cmd, NULL);
+	if (error < 0)
+		return error;
+
+	/* switch from test to normal mode should wait 1 sec. delay */
+	if (dev->protocol.flag == PTL_V6 &&
+	    dev->fw_mode == mode_test && mode == mode_normal)
+		dev->cb.delay_ms(1000);
+	else
+		dev->cb.delay_ms(100);
+
+	dev->fw_mode = mode;
+
+	return 0;
+}
+
+u16 api_get_block_crc_by_addr(void *handle, u8 type,
+			      u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = start;
+	dev->wbuf[3] = (start >> 8) & 0xFF;
+	dev->wbuf[4] = (start >> 16) & 0xFF;
+	dev->wbuf[5] = end & 0xFF;
+	dev->wbuf[6] = (end >> 8) & 0xFF;
+	dev->wbuf[7] = (end >> 16) & 0xFF;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+u16 api_get_block_crc_by_num(void *handle, u8 type,
+			     u8 block_num)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = block_num;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+int api_set_data_len(void *handle, u16 data_len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[1] = data_len & 0xFF;
+	dev->wbuf[2] = (data_len >> 8) & 0xFF;
+
+	return api_protocol_set_cmd(dev, SET_DATA_LEN, NULL);
+}
+
+int api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 type;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = start & 0xFF;
+	dev->wbuf[4] = (start >> 8) & 0xFF;
+	dev->wbuf[5] = start >> 16;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (end >> 8) & 0xFF;
+	dev->wbuf[8] = end >> 16;
+
+	type = (in_ap) ? 0x1 : 0x0;
+	type |= (is_slave) ? 0x2 : 0x0;
+
+	return api_protocol_set_cmd(dev, SET_FLASH_EN, &type);
+}
+
+int api_write_data_v6(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V6, &wlen);
+}
+
+int api_access_slave(void *handle, u8 id, u8 func, void *data)
+{
+	struct ilitek_slave_access access;
+
+	access.slave_id = id;
+	access.func = func;
+	access.data = data;
+
+	return api_protocol_set_cmd(handle, ACCESS_SLAVE, &access);
+}
+
+int api_write_enable_v3(void *handle, bool in_ap, bool write_ap,
+			u32 end, u32 checksum)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = (write_ap) ? 0x0 : 0x1;
+	dev->wbuf[4] = (end >> 16) & 0xFF;
+	dev->wbuf[5] = (end >> 8) & 0xFF;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (checksum >> 16) & 0xFF;
+	dev->wbuf[8] = (checksum >> 8) & 0xFF;
+	dev->wbuf[9] = checksum & 0xFF;
+
+	return api_protocol_set_cmd(dev, WRITE_ENABLE, &in_ap);
+}
+
+int api_write_data_v3(void *handle)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V3, NULL);
+}
+
+int api_check_busy(void *handle, int timeout_ms, int delay_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 busy;
+
+	/* retry 2 times at least */
+	int i = MAX(DIV_ROUND_UP(timeout_ms, delay_ms), 2);
+
+	_memset(dev->wbuf, 0, 64);
+
+	while (i--) {
+		api_protocol_set_cmd(dev, GET_SYS_BUSY, &busy);
+		if (busy == ILITEK_TP_SYSTEM_READY)
+			return 0;
+
+		/* delay ms for each check busy */
+		dev->cb.delay_ms(delay_ms);
+
+		/* if caller set no_retry then skip check busy retry */
+		if (dev->setting.no_retry)
+			break;
+	}
+
+	TP_WARN(dev->id, "check busy timeout: %d ms, state: 0x" PFMT_X8 "\n",
+		timeout_ms, busy);
+
+	return -EILIBUSY;
+}
+
+int api_to_bl_mode(void *handle, bool to_bl,
+		   u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+
+	do {
+		if (api_protocol_set_cmd(dev, GET_MCU_MOD, NULL) < 0)
+			continue;
+
+		if (dev->ic[0].mode == target_mode)
+			goto success_change_mode;
+
+		if (to_bl) {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, true, false,
+						     0, 0) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_BL_MODE, NULL);
+		} else {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, false, false,
+						     start, end) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_AP_MODE, NULL);
+		}
+
+		switch (dev->_interface) {
+		case interface_hid_over_i2c:
+		case interface_i2c:
+			dev->cb.delay_ms(1000 + 100 * cnt);
+			break;
+		case interface_usb:
+			re_enum_helper(dev, enum_ap_bl);
+			break;
+		}
+	} while (!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+
+	/* update fw ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+
+	/* update protocol ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	return 0;
+}
+
+int api_set_idle(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = (enable) ? 1 : 0;
+	return api_protocol_set_cmd(dev, SET_MCU_IDLE, NULL);
+}
+
+int api_set_func_mode(void *handle, u8 mode)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	bool get = false;
+
+	_memset(dev->wbuf, 0, 64);
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->wbuf[1] = 0x55;
+		dev->wbuf[2] = 0xAA;
+		break;
+	case PTL_V6:
+		dev->wbuf[1] = 0x5A;
+		dev->wbuf[2] = 0xA5;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol: %x, flag: " PFMT_U8 "",
+			dev->protocol.ver, dev->protocol.flag);
+		return -EINVAL;
+	}
+	dev->wbuf[3] = mode;
+
+	if (dev->protocol.ver < 0x30400) {
+		TP_ERR(dev->id, "protocol: 0x%x not support\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	error = api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+	if (error < 0)
+		return error;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		return error;
+
+	return (dev->func.mode == mode) ? 0 : -EFAULT;
+}
+
+int api_get_func_mode(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool get = true;
+
+	return api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+}
+
+int api_erase_data_v3(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+
+	TP_INFO(dev->id, "erase data flash for " PFMT_C8 ", mode: " PFMT_X8 "\n",
+		dev->mcu_info.ic_name, dev->ic[0].mode);
+
+	if (is_231x(dev)) {
+		/* V3 231x only support erase data flash in AP mode */
+		if (dev->ic[0].mode != AP_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, true, false, 0, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(100);
+
+		dev->wbuf[1] = 0x02;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V3, NULL);
+		if (error < 0)
+			return error;
+
+		switch (dev->_interface) {
+		case interface_usb:
+			return re_enum_helper(dev, enum_ap_bl);
+		default:
+			dev->cb.delay_ms(1500);
+			break;
+		}
+	} else {
+		/* V3 251x only support erase data flash in BL mode */
+		if (dev->ic[0].mode != BL_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, false, false, 0xf01f, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		_memset(dev->wbuf + 1, 0xFF, 32);
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(500);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v3(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 addr, end = start + len, copied;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += 32, copied += 32) {
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, READ_FLASH, NULL);
+		if (error < 0)
+			return error;
+
+		_memcpy(buf + copied, dev->rbuf, 32);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v6(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 code, addr, end = start + len, copied;
+	u16 data_len;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += data_len, copied += data_len) {
+		if (end - addr > 1024)
+			data_len = 2048;
+		else if (end - addr > 256)
+			data_len = 1024;
+		else if (end - addr > 64)
+			data_len = 256;
+		else
+			data_len = 64;
+
+		error = api_set_data_len(dev, data_len);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x1; code = 1 << 16;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x0; code = data_len & 0xFFFF;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		if (dev->_interface == interface_hid_over_i2c)
+			_memcpy(buf + copied, dev->rbuf + 5, data_len);
+		else
+			_memcpy(buf + copied, dev->rbuf, data_len);
+	}
+
+	return 0;
+}
+
+int api_read_flash(void *handle, u8 *buf,
+		   u32 start, u32 len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (dev->protocol.flag == PTL_V3)
+		return api_read_flash_v3(dev, buf, start, len);
+
+	return api_read_flash_v6(dev, buf, start, len);
+}
+
+int _api_read_mp_result(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)(&dev->mp);
+	u32 addr = (is_29xx(dev)) ? 0x2e000 : 0x3e000;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return api_read_flash_v6(dev, buf, addr, 1000);
+
+	/* 1000 bytes data/ 2 bytes crc/ 1 bytes checksum */
+	tuning.len = 1003;
+	tuning.buf = buf;
+
+	tuning.func = 0x0; tuning.ctrl = 0x4; tuning.type = 0x10;
+	error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+	if (error < 0)
+		return error;
+
+	tuning.func = 0x1; tuning.ctrl = 0x4; tuning.type = 0x10;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_read_mp_result(void *handle)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_read_mp_result(handle);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+int _api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)mp;
+	u16 crc;
+
+	crc = get_crc(0, 1000, buf, sizeof(struct mp_station));
+	mp->crc = crc;
+
+	tuning.func = 0x0;
+	tuning.ctrl = 0x5;
+	tuning.type = 0x10;
+	tuning.buf = buf;
+	tuning.len = 1002;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_write_mp_result(handle, mp);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+static void mp_result(const char *item, u8 data)
+{
+	if (data == 1)
+		TP_INFO(NULL, PFMT_C8 " Result: PASS\n", item);
+	else if (data == 2)
+		TP_INFO(NULL, PFMT_C8 " Result: NG\n", item);
+	else
+		TP_INFO(NULL, PFMT_C8 " Result: N/A\n", item);
+}
+
+void api_decode_mp_result(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct mp_station *mp = &dev->mp;
+	int i, j;
+	struct mp_station_old *_mp;
+	char module_name[32];
+	char bar_code[256];
+
+	TP_DBG_ARR(NULL, "[MpResult Raw]:", TYPE_U8, 1002, (u8 *)mp);
+
+	TP_MSG(NULL, "mp result ver: 0x%08x\n", mp->info.mp_result_ver);
+
+	/* For Old Mp Result Format */
+	if (mp->info.mp_result_ver == 0xFFFFFFFF ||
+	    mp->info.mp_result_ver < 0x01000000) {
+		_mp = (struct mp_station_old *)mp;
+
+		for (i = 0; i < 9; i++) {
+			TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+			if (!_mp->station[i].week ||
+			    _mp->station[i].week == 0xFF) {
+				TP_INFO(NULL, "No Test\n");
+				continue;
+			}
+
+			TP_INFO(NULL, "Week of year : " PFMT_U8 "\n",
+				_mp->station[i].week);
+			TP_INFO(NULL, "Year : 20%02u\n",
+				_mp->station[i].year);
+			TP_INFO_ARR(NULL, "Firmware Version : ",
+				   TYPE_U8, 8, _mp->station[i].fw_ver);
+
+			_memset(module_name, 0, sizeof(module_name));
+			_memcpy(module_name, _mp->station[i].module,
+				sizeof(_mp->station[i].module));
+			module_name[sizeof(_mp->station[i].module) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].module);
+			     j++) {
+				if ((u8)module_name[j] == 0xFF) {
+					module_name[j] = '\0';
+					break;
+				}
+			}
+			TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n",
+				module_name);
+
+			mp_result("Short Test", _mp->station[i].short_test);
+			mp_result("Open Test", _mp->station[i].open_test);
+			mp_result("Self Cap Test", _mp->station[i].self_test);
+			mp_result("Uniformity Test",
+				_mp->station[i].uniform_test);
+			mp_result("DAC Test", _mp->station[i].dac_test);
+			mp_result("Key Raw Test", _mp->station[i].key_test);;
+			mp_result("Painting Test", _mp->station[i].paint_test);
+			mp_result("MicroOpen Test", _mp->station[i].mopen_test);
+			mp_result("GPIO Test", _mp->station[i].gpio_test);
+			mp_result("Final", _mp->station[i].final_result);
+
+			_memset(bar_code, 0, sizeof(bar_code));
+			_memcpy(bar_code, _mp->station[i].bar_code,
+				sizeof(_mp->station[i].bar_code));
+			bar_code[sizeof(_mp->station[i].bar_code) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].bar_code);
+			     j++) {
+				if ((u8)bar_code[j] == 0xFF) {
+					bar_code[j] = '\0';
+					break;
+				}
+			}
+
+			TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+			TP_INFO(NULL, "Customer ID : 0x%04x\n",
+				_mp->station[i].custom_id);
+			TP_INFO(NULL, "FWID : 0x%04x\n",
+				_mp->station[i].fwid);
+		}
+
+		return;
+	}
+
+	TP_INFO(NULL, "[MP Result]\n");
+	TP_INFO(NULL, "***Customer Info***\n");
+
+	TP_INFO(NULL, "MPResult Version:%02X.%02X.%02X.%02X\n",
+		(mp->info.mp_result_ver >> 24) & 0xFF,
+		(mp->info.mp_result_ver >> 16) & 0xFF,
+		(mp->info.mp_result_ver >> 8) & 0xFF,
+		mp->info.mp_result_ver & 0xFF);
+	TP_INFO(NULL, "Customer ID : 0x%04X\n", mp->info.customer_id);
+	TP_INFO(NULL, "FW ID : 0x%04X\n", mp->info.fwid);
+
+	for (i = 0; i < (int)ARRAY_SIZE(mp->station); i++) {
+		TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+		if (!mp->station[i].week || mp->station[i].week == 0xFF) {
+			TP_INFO(NULL, "No Test\n");
+			continue;
+		}
+
+		TP_INFO(NULL, "Week of year : %d\n", mp->station[i].week);
+		TP_INFO(NULL, "Year : 20%02d\n", mp->station[i].year);
+		TP_INFO(NULL, "Firmware Version : 0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+			mp->station[i].fw_ver[7], mp->station[i].fw_ver[6],
+			mp->station[i].fw_ver[5], mp->station[i].fw_ver[4],
+			mp->station[i].fw_ver[3], mp->station[i].fw_ver[2],
+			mp->station[i].fw_ver[1], mp->station[i].fw_ver[0]);
+
+		_memset(module_name, 0, sizeof(module_name));
+		_memcpy(module_name, mp->station[i].module,
+			sizeof(mp->station[i].module));
+		module_name[sizeof(mp->station[i].module) + 1] = '\0';
+		for (j = 0; j < (int)sizeof(mp->station[i].module); j++) {
+			if ((u8)module_name[j] == 0xFF) {
+				module_name[j] = '\0';
+				break;
+			}
+		}
+		TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n", module_name);
+
+		mp_result("Short Test", mp->station[i].short_test);
+		mp_result("Open Test", mp->station[i].open_test);
+		mp_result("Self Cap Test", mp->station[i].self_test);
+		mp_result("Uniformity Test", mp->station[i].uniform_test);
+		mp_result("DAC Test", mp->station[i].dac_test);
+		mp_result("Key Raw Test", mp->station[i].key_test);;
+		mp_result("Painting Test", mp->station[i].paint_test);
+		mp_result("MicroOpen Test", mp->station[i].mopen_test);
+		mp_result("GPIO Test", mp->station[i].gpio_test);
+		mp_result("Final", mp->station[i].final_result);
+
+		TP_INFO(NULL, "Tool Version : " PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "\n",
+			mp->station[i].tool_ver[7], mp->station[i].tool_ver[6],
+			mp->station[i].tool_ver[5], mp->station[i].tool_ver[4],
+			mp->station[i].tool_ver[3], mp->station[i].tool_ver[2],
+			mp->station[i].tool_ver[1], mp->station[i].tool_ver[0]);
+
+		_memset(bar_code, 0, sizeof(bar_code));
+		_memcpy(bar_code, mp->station[i].bar_code,
+			sizeof(mp->station[i].bar_code));
+		bar_code[sizeof(mp->station[i].bar_code) + 1] = '\0';
+		TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+		TP_INFO(NULL, "Customer ID : 0x%04x\n",
+			mp->station[i].custom_id);
+		TP_INFO(NULL, "FWID : 0x%04x\n", mp->station[i].fwid);
+	}
+}
+
+int api_read_tuning(void *handle, u8 *buf, int rlen)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	int got, need;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return -EINVAL;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	for (got = 0, need = rlen; need > 0; got += 1024, need -= 1024) {
+		tuning.len = MIN(need, 1024);
+		tuning.buf = buf + got;
+
+		tuning.func = 0x0; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+
+		tuning.func = 0x1; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+	}
+
+	TP_DBG_ARR(dev->id, "[tuning]:", TYPE_U8, rlen, buf);
+
+	return api_set_ctrl_mode(dev, mode_normal, false, true);
+}
+
+int api_write_data_m2v(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_M2V, &wlen);
+}
+
+int api_to_bl_mode_m2v(void *handle, bool to_bl)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+	u8 mode;
+
+	if (dev->_interface != interface_usb)
+		return -EINVAL;
+
+	do {
+		dev->cb.delay_ms(100);//Reed Add : 20230927
+
+		if (api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) < 0)
+			continue;
+
+		if (mode == target_mode)
+			goto success_change_mode;
+
+		dev->cb.delay_ms(300);//Reed Add : 20230927
+
+		if (to_bl && api_access_slave(dev, 0x80, CMD_SET_BL_MODE,
+					      NULL) < 0)
+			continue;
+		else if (!to_bl && api_access_slave(dev, 0x80, CMD_SET_AP_MODE,
+						    NULL) < 0)
+			continue;
+
+		do {
+			dev->cb.delay_ms(100);//Reed Add : 20230927
+			if (!api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) &&
+			    mode == target_mode)
+				goto success_change_mode;
+			dev->cb.delay_ms(5000);
+		} while (!dev->setting.no_retry && cnt++ < retry);
+		break;
+	} while (!!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "M2V current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "M2V current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		mode, (to_bl) ? "BL" : "AP");
+
+	return 0;
+}
+
+int api_get_ic_crc(void *handle, u8 final_fw_mode)
+{
+	int error;
+	u8 i;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		break;
+	case PTL_V6:
+		for (i = 1; i < dev->tp_info.block_num; i++) {
+			dev->ic[0].crc[i] = api_get_block_crc_by_num(dev,
+				CRC_CALCULATE, i);
+		}
+		break;
+	default:
+		error = -EINVAL; break;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, final_fw_mode, false, true);
+
+	return error;
+}
+
+void api_print_ts_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 i;
+
+	TP_INFO(dev->id, "[Protocol Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X\n",
+		(dev->protocol.ver >> 16) & 0xff,
+		(dev->protocol.ver >> 8) & 0xff,
+		dev->protocol.ver & 0xff);
+
+	TP_INFO(dev->id, "[Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[0], dev->fw_ver[1],
+		dev->fw_ver[2], dev->fw_ver[3]);
+	TP_INFO(dev->id, "[Customer Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[4], dev->fw_ver[5],
+		dev->fw_ver[6], dev->fw_ver[7]);
+
+	TP_INFO(dev->id, "[Kernel Version]\n");
+	if (dev->protocol.flag == PTL_V6 &&
+	    support_mcu_info(dev)) {
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X (" PFMT_C8 ")\n",
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1],
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[4], dev->mcu_info.ic_name);
+	} else {
+		TP_INFO(dev->id, "0x%c%c.0x%c%c\n",
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1]);
+	}
+
+	TP_INFO(dev->id, "[Current Mode]\n");
+	TP_INFO(dev->id, "Master : " PFMT_C8 "\n", dev->ic[0].mode_str);
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	for (i = 1; i < dev->tp_info.ic_num; i++)
+		TP_INFO(dev->id, "Slave : " PFMT_C8 "\n", dev->ic[i].mode_str);
+
+	TP_INFO(dev->id, "[Module Name]\n");
+	TP_INFO(dev->id, PFMT_C8 "\n", dev->mcu_info.module_name);
+
+	TP_INFO(dev->id, "[Core Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->core_ver[0], dev->core_ver[1],
+		dev->core_ver[2], dev->core_ver[3]);
+
+	if (is_231x(dev)) {
+		switch (dev->core_ver[1]) {
+		case 0x00:
+			TP_INFO(dev->id, "(Dual Interface)\n"); break;
+		case 0x01:
+			TP_INFO(dev->id, "(USB Interface)\n"); break;
+		case 0x02:
+			TP_INFO(dev->id, "(I2C Interface)\n"); break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[Tuning Version]\n");
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+			dev->tuning_ver[0], dev->tuning_ver[1],
+			dev->tuning_ver[2], dev->tuning_ver[3]);
+	}
+
+	TP_INFO(dev->id, "[Panel Information]\n");
+	TP_INFO(dev->id, "X resolution : " PFMT_U16 "\n", dev->tp_info.x_resolution);
+	TP_INFO(dev->id, "Y resolution : " PFMT_U16 "\n", dev->tp_info.y_resolution);
+	TP_INFO(dev->id, "AA X Channel : " PFMT_U16 "\n", dev->tp_info.x_ch);
+	TP_INFO(dev->id, "AA Y Channel : " PFMT_U16 "\n", dev->tp_info.y_ch);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Fingers\n", dev->tp_info.max_fingers);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Touch Keys\n", dev->tp_info.key_num);
+
+	if (dev->tp_info.key_num) {
+		switch (dev->key.info.mode) {
+		case key_disable:
+			TP_INFO(dev->id, "Key Mode : NO_Key\n");
+			break;
+		case key_hw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_1\n");
+			break;
+		case key_hsw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_2\n");
+			break;
+		case key_vitual:
+			TP_INFO(dev->id, "Key Mode : Virtual_Key\n");
+			break;
+		case key_fw_disable:
+			TP_INFO(dev->id, "Key Mode : FW_disable\n");
+			break;
+		default:
+			TP_INFO(dev->id, "Key Mode : Unknown(0x" PFMT_X8 ")\n",
+				dev->key.info.mode);
+			break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "Support Pen Type : " PFMT_C8 "\n",
+			dev->pen_mode);
+		TP_INFO(dev->id, "Chip Counts : " PFMT_U8 "\n", dev->tp_info.ic_num);
+		TP_INFO(dev->id, "Report Format : " PFMT_U8 "\n", dev->tp_info.format);
+		TP_INFO(dev->id, "Block Number : " PFMT_U8 "\n", dev->tp_info.block_num);
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[FW CRC]\n");
+		TP_INFO(dev->id, "Master : 0x%04X\n", dev->ic[0].crc[0]);
+		for (i = 1; i < dev->tp_info.ic_num; i++)
+			TP_INFO(dev->id, "Slave : 0x%04X\n", dev->ic[i].crc[0]);
+	} else {
+		TP_INFO(dev->id, "[Check Code]\n");
+		TP_INFO(dev->id, "AP : 0x%08X\n", dev->ic[0].crc[0]);
+		TP_INFO(dev->id, "DATA : 0x%08X\n", dev->ic[0].crc[1]);
+	}
+}
+
+void api_read_then_print_m2v_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 m2v_mode;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	api_set_ctrl_mode(dev, mode_suspend, false, true);
+	api_access_slave(dev, 0x80, CMD_GET_FW_VER, m2v_fw_ver);
+	api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &m2v_mode);
+	api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &m2v_checksum);
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	TP_INFO(dev->id, "[M2V Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+		m2v_fw_ver[0], m2v_fw_ver[1], m2v_fw_ver[2], m2v_fw_ver[3],
+		m2v_fw_ver[4], m2v_fw_ver[5], m2v_fw_ver[6], m2v_fw_ver[7]);
+
+	TP_INFO(dev->id, "[M2V Current Mode]\n");
+	TP_INFO(dev->id, "Mode : " PFMT_C8 " Mode\n",
+		(m2v_mode == AP_MODE) ? "AP" : "BL");
+
+	TP_INFO(dev->id, "[M2V FW CheckSum]\n");
+	TP_INFO(dev->id, "FW CheckSum : 0x%08X\n", m2v_checksum);
+}
+
+int api_update_ts_info(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	/*
+	 * previous set suspend mode is in V6 format.
+	 * set suspend mode again if device protocol is checked as V3.
+	 */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_PRODUCT_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_FWID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_SENSOR_ID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_HID_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	/* BL mode should perform FW upgrade afterward */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	/* V3 need to get DF CRC */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_TUNING_VER, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_CORE_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_SCRN_RES, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->tp_info.ic_num > 1) {
+		error = api_protocol_set_cmd(dev, GET_AP_CRC,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_MCU_MOD,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	return error;
+}
+
+void __ilitek_get_info(void *handle, struct ilitek_common_info *info)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!info || !dev)
+		return;
+
+	_memcpy(info, &dev->quirks, sizeof(struct ilitek_common_info));
+}
+
+void ilitek_dev_set_quirks(void *handle, u32 quirks)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	dev->quirks = quirks;
+}
+
+void ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->sys, sys, sizeof(struct ilitek_sys_info));
+}
+
+void ilitek_dev_setting(void *handle, struct ilitek_ts_settings *setting)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->setting, setting, sizeof(struct ilitek_ts_settings));
+
+	if (dev->setting.default_format_enabled) {
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+	}
+
+	TP_MSG(dev->id, "no-retry: %d, no-INT-ack: %d\n",
+		dev->setting.no_retry, dev->setting.no_INT_ack);
+}
+
+void ilitek_dev_bind_callback(void *handle, struct ilitek_ts_callback *callback)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (callback) {
+		_memcpy(&dev->cb, callback, sizeof(struct ilitek_ts_callback));
+		if (dev->cb.msg)
+			g_msg = dev->cb.msg;
+	}
+}
+
+void *ilitek_dev_init(u8 _interface, const char *id,
+		      bool need_update_ts_info,
+		      struct ilitek_ts_callback *callback, void *_private)
+{
+	struct ilitek_ts_device *dev;
+
+	dev = (struct ilitek_ts_device *)MALLOC(sizeof(*dev));
+	if (!dev)
+		return NULL;
+
+	TP_MSG(NULL, "commonflow code version: 0x%x\n",
+		COMMONFLOW_CODE_VERSION);
+
+	TP_DBG(NULL, "sizeof(ilitek_ts_device): %u\n",
+		(unsigned int)sizeof(struct ilitek_ts_device));
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(dev, 0, sizeof(*dev));
+
+	_strcpy(dev->id, id, sizeof(dev->id));
+	ilitek_dev_bind_callback(dev, callback);
+
+	dev->_interface = _interface;
+	dev->_private = _private;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	dev->fw_mode = mode_unknown;
+
+	if (need_update_ts_info && api_update_ts_info(dev) < 0) {
+		ilitek_dev_exit(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+void ilitek_dev_exit(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/*
+	 * LIBUSB would kill /dev/hidraw* and make system stop handling
+	 * device's usb. sw reset is required to re-enum usb then /dev/hidraw*
+	 * would be created and system would start to handle touch event.
+	 */
+	if (dev->quirks & QUIRK_LIBUSB || dev->setting.sw_reset_at_last)
+		api_protocol_set_cmd(dev, SET_SW_RST, NULL);
+
+	if (dev)
+		FREE(dev);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.h b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
new file mode 100644
index 000000000000..ad9c5c18cd36
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
@@ -0,0 +1,916 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_PROTOCOL_H__
+#define __ILITEK_PROTOCOL_H__
+
+#include "ilitek_def.h"
+
+/* quirks definition */
+#define QUIRK_WAIT_ACK_DELAY		0x1
+#define QUIRK_BRIDGE			0x2
+#define QUIRK_DAEMON_I2C		0x4
+#define QUIRK_WIFI_ITS_I2C		0x8
+#define QUIRK_LIBUSB			0x10
+
+#define START_ADDR_LEGO			0x3000
+#define START_ADDR_29XX			0x4000
+#define END_ADDR_LEGO			0x40000
+
+#define MM_ADDR_LEGO			0x3020
+#define MM_ADDR_29XX			0x4020
+#define MM_ADDR_2501X			0x4038
+
+#define DF_START_ADDR_LEGO		0x3C000
+#define DF_START_ADDR_29XX		0x2C000
+
+#define ILITEK_TP_SYSTEM_READY		0x50
+
+#define CRC_CALCULATE			0
+#define CRC_GET				1
+
+#define ILTIEK_MAX_BLOCK_NUM		20
+
+#define PTL_ANY				0x00
+#define PTL_V3				0x03
+#define PTL_V6				0x06
+
+#define BL_PROTOCOL_V1_8		0x10800
+#define BL_PROTOCOL_V1_7		0x10700
+#define BL_PROTOCOL_V1_6		0x10600
+
+#define TOUT_CF_BLOCK_0			2500
+#define TOUT_CF_BLOCK_N			500
+#define TOUT_F1_SHORT			1600
+#define TOUT_F1_OPEN			12
+#define TOUT_F1_FREQ_MC			2
+#define TOUT_F1_FREQ_SC			1
+#define TOUT_F1_CURVE			13
+#define TOUT_F1_KEY			400
+#define TOUT_F1_OTHER			27
+#define TOUT_F2				7
+#define TOUT_CD				27
+#define TOUT_C3				100
+#define TOUT_65_WRITE			135
+#define TOUT_65_READ			3
+#define TOUT_68				24
+#define TOUT_CC_SLAVE			16000
+
+#define TOUT_F1_SHORT_RATIO		2
+#define TOUT_F1_OPEN_RATIO		3
+#define TOUT_F1_FREQ_RATIO		3
+#define TOUT_F1_CURVE_RATIO		3
+#define TOUT_F1_OTHER_RATIO		3
+#define TOUT_F2_RATIO			3
+#define TOUT_CD_RATIO			3
+#define TOUT_C3_RATIO			3
+#define TOUT_65_WRITE_RATIO		3
+#define TOUT_65_READ_RATIO		3
+#define TOUT_68_RATIO			3
+#define TOUT_CC_SLAVE_RATIO		2
+
+#define AP_MODE		0x5A
+#define BL_MODE		0x55
+
+#define STYLUS_MODES			\
+	X(STYLUS_WGP,	0x1,	"WGP")	\
+	X(STYLUS_USI,	0x2,	"USI")	\
+	X(STYLUS_MPP,	0x4,	"MPP")
+
+#define ILITEK_CMD_MAP							\
+	X(0x20, PTL_ANY, GET_TP_INFO, api_protocol_get_tp_info)		\
+	X(0x21, PTL_ANY, GET_SCRN_RES, api_protocol_get_scrn_res)	\
+	X(0x22, PTL_ANY, GET_KEY_INFO, api_protocol_get_key_info)	\
+	X(0x30, PTL_ANY, SET_IC_SLEEP, api_protocol_set_sleep)		\
+	X(0x31, PTL_ANY, SET_IC_WAKE, api_protocol_set_wakeup)		\
+	X(0x34, PTL_ANY, SET_MCU_IDLE, api_protocol_set_idle)		\
+	X(0x40, PTL_ANY, GET_FW_VER, api_protocol_get_fw_ver)		\
+	X(0x42, PTL_ANY, GET_PTL_VER, api_protocol_get_ptl_ver)		\
+	X(0x43, PTL_ANY, GET_CORE_VER, api_protocol_get_core_ver)	\
+	X(0x60, PTL_ANY, SET_SW_RST, api_protocol_set_sw_reset)		\
+	X(0x61, PTL_ANY, GET_MCU_VER, api_protocol_get_mcu_ver)		\
+	X(0x68, PTL_ANY, SET_FUNC_MOD, api_protocol_set_func_mode)	\
+	X(0x80, PTL_ANY, GET_SYS_BUSY, api_protocol_get_sys_busy)	\
+	X(0xC0, PTL_ANY, GET_MCU_MOD, api_protocol_get_mcu_mode)	\
+	X(0xC1, PTL_ANY, SET_AP_MODE, api_protocol_set_ap_mode)		\
+	X(0xC2, PTL_ANY, SET_BL_MODE, api_protocol_set_bl_mode)		\
+	X(0xC5, PTL_ANY, READ_FLASH, api_protocol_read_flash)		\
+	X(0xC7, PTL_ANY, GET_AP_CRC, api_protocol_get_ap_crc)		\
+	X(0xC8, PTL_ANY, SET_ADDR, api_protocol_set_flash_addr)		\
+									\
+	/* v3 only cmds */						\
+	X(0x25, PTL_V3, GET_CDC_INFO_V3, api_protocol_get_cdc_info_v3)	\
+	X(0x63, PTL_V3, TUNING_PARA_V3, api_protocol_tuning_para_v3)	\
+	X(0xC3, PTL_V3, WRITE_DATA_V3, api_protocol_write_data_v3)	\
+	X(0xC4, PTL_V3, WRITE_ENABLE, api_protocol_write_enable)	\
+	X(0xCA, PTL_V3, GET_DF_CRC, api_protocol_get_df_crc)		\
+	X(0xF2, PTL_V3, SET_TEST_MOD, api_protocol_set_mode_v3)		\
+	X(0xF3, PTL_V3, INIT_CDC_V3, api_protocol_set_cdc_init_v3)	\
+									\
+	/* v6 only cmds */						\
+	X(0x24, PTL_V6, POWER_STATUS, api_protocol_power_status)	\
+	X(0x27, PTL_V6, GET_SENSOR_ID, api_protocol_get_sensor_id)	\
+	X(0x44, PTL_V6, GET_TUNING_VER, api_protocol_get_tuning_ver)	\
+	X(0x45, PTL_V6, GET_PRODUCT_INFO, api_protocol_get_product_info)\
+	X(0x46, PTL_V6, GET_FWID, api_protocol_get_fwid)		\
+	X(0x47, PTL_V6, GET_CRYPTO_INFO, api_protocol_get_crypto_info)	\
+	X(0x48, PTL_V6, GET_HID_INFO, api_protocol_get_hid_info)	\
+	X(0x62, PTL_V6, GET_MCU_INFO, api_protocol_get_mcu_info)	\
+	X(0x65, PTL_V6, TUNING_PARA_V6, api_protocol_tuning_para_v6)	\
+	X(0x69, PTL_V6, SET_FS_INFO, api_protocol_set_fs_info)		\
+	X(0x6A, PTL_V6, SET_SHORT_INFO, api_protocol_set_short_info)	\
+	X(0x6B, PTL_V6, C_MODEL_INFO, api_protocol_c_model_info)	\
+	X(0x6C, PTL_V6, SET_P2P_INFO, api_protocol_set_p2p_info)	\
+	X(0x6D, PTL_V6, SET_OPEN_INFO, api_protocol_set_open_info)	\
+	X(0x6E, PTL_V6, SET_CHARGE_INFO, api_protocol_set_charge_info)	\
+	X(0x6F, PTL_V6, SET_PEN_FS_INFO, api_protocol_set_pen_fs_info)	\
+	X(0xB0, PTL_V6, WRITE_DATA_M2V, api_protocol_write_data_m2v)	\
+	X(0xC3, PTL_V6, WRITE_DATA_V6, api_protocol_write_data_v6)	\
+	X(0xC9, PTL_V6, SET_DATA_LEN, api_protocol_set_data_len)	\
+	X(0xCB, PTL_V6, ACCESS_SLAVE, api_protocol_access_slave)	\
+	X(0xCC, PTL_V6, SET_FLASH_EN, api_protocol_set_flash_enable)	\
+	X(0xCD, PTL_V6, GET_BLK_CRC_ADDR, api_protocol_get_crc_by_addr)	\
+	X(0xCF, PTL_V6, GET_BLK_CRC_NUM, api_protocol_get_crc_by_num)	\
+	X(0xF0, PTL_V6, SET_MOD_CTRL, api_protocol_set_mode_v6)		\
+	X(0xF1, PTL_V6, INIT_CDC_V6, api_protocol_set_cdc_init_v6)	\
+	X(0xF2, PTL_V6, GET_CDC_V6, api_protocol_get_cdc_v6)
+
+
+#define X(_cmd, _protocol, _cmd_id, _api)	_cmd_id,
+enum ilitek_cmd_ids {
+	ILITEK_CMD_MAP
+	/* ALWAYS keep at the end */
+	MAX_CMD_CNT
+};
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api)	CMD_##_cmd_id = _cmd,
+enum ilitek_cmds { ILITEK_CMD_MAP };
+#undef X
+
+enum ilitek_hw_interfaces {
+	interface_i2c = 0,
+	interface_hid_over_i2c,
+	interface_usb,
+};
+
+enum ilitek_fw_modes {
+	mode_unknown = -1,
+	mode_normal = 0,
+	mode_test,
+	mode_debug,
+	mode_suspend,
+};
+
+enum ilitek_key_modes {
+	key_disable = 0,
+	key_hw = 1,
+	key_hsw = 2,
+	key_vitual = 3,
+	key_fw_disable = 0xff,
+};
+
+#define ILITEK_TOUCH_REPORT_FORMAT			\
+	X(touch_fmt_0x0,	0x0, 5, 10)	\
+	X(touch_fmt_0x1,	0x1, 6, 10)	\
+	X(touch_fmt_0x2,	0x2, 10, 5)	\
+	X(touch_fmt_0x3,	0x3, 10, 5)	\
+	X(touch_fmt_0x4,	0x4, 10, 5)	\
+	X(touch_fmt_0x10,	0x10, 10, 6)	\
+	X(touch_fmt_0x11,	0x11, 5, 10)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_touch_fmts {
+	ILITEK_TOUCH_REPORT_FORMAT
+	touch_fmt_max = 0x100,
+};
+#undef X
+
+#define ILITEK_PEN_REPORT_FORMAT		\
+	X(pen_fmt_0x0, 0x0, 12, 1)	\
+	X(pen_fmt_0x1, 0x1, 18, 1)	\
+	X(pen_fmt_0x2, 0x2, 22, 1)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_pen_fmts {
+	ILITEK_PEN_REPORT_FORMAT
+	pen_fmt_max = 0x100,
+};
+#undef X
+
+struct ilitek_slave_access {
+	u8 slave_id;
+	u8 func;
+	void *data;
+};
+
+struct tuning_para_settings {
+	u8 func;
+	u8 ctrl;
+	u8 type;
+
+	u8 *buf;
+	u32 len;
+};
+
+struct reports {
+	bool touch_need_update;
+	bool pen_need_update;
+
+	u8 touch[64];
+	u8 pen[64];
+};
+
+struct grid_data {
+	bool need_update;
+	unsigned int X, Y;
+
+	s32 *data;
+};
+
+struct grids {
+	struct grid_data mc;
+	struct grid_data sc_x;
+	struct grid_data sc_y;
+	struct grid_data pen_x;
+	struct grid_data pen_y;
+
+	struct grid_data key_mc;
+	struct grid_data key_x;
+	struct grid_data key_y;
+
+	struct grid_data self;
+
+	/* touch/pen debug message along with frame update */
+	struct reports dmsg;
+};
+
+enum ilitek_enum_type {
+	enum_ap_bl = 0,
+	enum_sw_reset,
+};
+
+typedef void (*update_grid_t)(u32, u32, struct grids *, void *);
+typedef void (*update_report_rate_t)(unsigned int);
+
+typedef int (*write_then_read_t)(u8 *, int, u8 *, int, void *);
+typedef int (*read_ctrl_in_t)(u8 *, int, unsigned int, void *);
+typedef int (*read_interrupt_in_t)(u8 *, int, unsigned int, void *);
+typedef void (*init_ack_t)(unsigned int, void *);
+typedef int (*wait_ack_t)(u8, unsigned int, void *);
+typedef int (*hw_reset_t)(unsigned int, void *);
+typedef int (*re_enum_t)(u8, void *);
+typedef void (*delay_ms_t)(unsigned int);
+
+
+typedef int (*write_then_read_direct_t)(u8 *, int, u8 *, int, void *);
+typedef void (*mode_switch_notify_t)(bool, bool, void *);
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_fmt {
+	u8 id : 6;
+	u8 status : 1;
+	u8 reserve : 1;
+	u16 x;
+	u16 y;
+	u8 pressure;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ touch_iwb_fmt {
+	u8 status : 3;
+	u8 reserve : 5;
+	u8 id : 6;
+	u8 reserve_1 : 2;
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ pen_fmt {
+	union __PACKED__ {
+		u8 modes;
+		struct __PACKED__ {
+			u8 tip_sw : 1;
+			u8 barrel_sw : 1;
+			u8 eraser : 1;
+			u8 invert : 1;
+			u8 in_range : 1;
+			u8 reserve : 3;
+		};
+	};
+	u16 x;
+	u16 y;
+	u16 pressure;
+	s16 x_tilt;
+	s16 y_tilt;
+
+	u8 battery;
+
+	union __PACKED__ {
+		/* usi v1.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 width;
+			u8 style;
+		} usi_1;
+
+		/* usi v2.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 color_24[3];
+			u8 no_color;
+			u8 width;
+			u8 style;
+		} usi_2;
+	};
+};
+
+struct __PACKED__ ilitek_report_fmt_info {
+	u32 touch_size;
+	u32 touch_max_cnt;
+
+	u32 pen_size;
+	u32 pen_max_cnt;
+};
+
+struct __PACKED__ ilitek_screen_info {
+	u16 x_min;
+	u16 y_min;
+	u16 x_max;
+	u16 y_max;
+	u16 pressure_min;
+	u16 pressure_max;
+	s16 x_tilt_min;
+	s16 x_tilt_max;
+	s16 y_tilt_min;
+	s16 y_tilt_max;
+	u16 pen_x_min;
+	u16 pen_y_min;
+	u16 pen_x_max;
+	u16 pen_y_max;
+};
+
+struct __PACKED__ ilitek_tp_info_v6 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u16 x_ch;
+	u16 y_ch;
+	u8 max_fingers;
+	u8 key_num;
+	u8 ic_num;
+	u8 support_modes;
+	u8 format;
+	u8 die_num;
+	u8 block_num;
+	u8 pen_modes;
+	u8 pen_format;
+	u16 pen_x_resolution;
+	u16 pen_y_resolution;
+};
+
+struct __PACKED__ ilitek_tp_info_v3 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u8 x_ch;
+	u8 y_ch;
+	u8 max_fingers;
+	u8 reserve;
+	u8 key_num;
+	u8 reserve_1;
+	u8 touch_start_y;
+	u8 touch_end_y;
+	u8 touch_start_x;
+	u8 touch_end_x;
+	u8 support_modes;
+};
+
+struct __PACKED__ ilitek_key_info_v6 {
+	u8 mode;
+	u16 x_len;
+	u16 y_len;
+
+	struct __PACKED__ _ilitek_key_info_v6 {
+		u8 id;
+		u16 x;
+		u16 y;
+	} keys[50];
+};
+
+struct __PACKED__ ilitek_key_info_v3 {
+	u8 x_len[2];
+	u8 y_len[2];
+
+	struct __PACKED__ _ilitek_key_info_v3 {
+		u8 id;
+		u8 x[2];
+		u8 y[2];
+	} keys[20];
+};
+
+struct __PACKED__ ilitek_ts_kernel_info {
+	char ic_name[6];
+	char mask_ver[2];
+	u32 mm_addr;
+	u32 min_addr;
+	u32 max_addr;
+	char module_name[32];
+
+	char ic_full_name[16];
+};
+
+struct __PACKED__ ilitek_key_info {
+	struct ilitek_key_info_v6 info;
+	bool clicked[50];
+};
+
+struct __PACKED__ ilitek_power_status {
+	u16 header;
+	u8 vdd33_lvd_flag;
+	u8 vdd33_lvd_level_sel;
+};
+
+struct __PACKED__ ilitek_sensor_id {
+	u16 header;
+	u8 id;
+};
+
+struct __PACKED__ ilitek_func_mode {
+	u16 header;
+	u8 mode;
+};
+
+struct __PACKED__ ilitek_ts_protocol {
+	u32 ver;
+	u8 flag;
+};
+
+struct __PACKED__ ilitek_ts_ic {
+	u8 mode;
+	u32 crc[ILTIEK_MAX_BLOCK_NUM];
+
+	char mode_str[32];
+};
+
+struct __PACKED__ ilitek_hid_info {
+	u16 pid;
+	u16 vid;
+	u16 rev;
+};
+
+struct __PACKED__ freq_category {
+	u32 start;
+	u32 end;
+	u32 step;
+	u32 steps;
+
+		u32 size;
+	char limit[1024];
+
+	u8 mode;
+
+	s32 *data;
+};
+
+struct __PACKED__ freq_settings {
+	bool prepared;
+
+	unsigned int frame_cnt;
+
+	/* add from v6.0.A */
+	unsigned int mc_frame_cnt;
+	unsigned int dump_frame_cnt;
+
+	unsigned int scan_type;
+
+	struct freq_category sine;
+	struct freq_category mc_swcap;
+	struct freq_category sc_swcap;
+	struct freq_category pen;
+
+	struct freq_category dump1;
+	struct freq_category dump2;
+	u8 dump1_val;
+	u8 dump2_val;
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ short_settings {
+	bool prepared;
+
+	u8 dump_1;
+	u8 dump_2;
+	u8 v_ref_L;
+	u16 post_idle;
+};
+
+struct __PACKED__ open_settings {
+	bool prepared;
+
+	u16 freq;
+	u8 gain;
+	u8 gain_rfb;
+	u8 afe_res_sel;
+	u8 mc_fsel;
+	u16 frame;
+};
+
+struct __PACKED__ p2p_settings {
+	bool prepared;
+
+	u16 frame_cnt;
+	u8 type;
+
+	/* add from v6.0.A */
+	u16 freq;
+};
+
+struct __PACKED__ charge_curve_sweep {
+	u16 start;
+	u16 end;
+	u8 step;
+	u16 post_idle;
+	u16 fix_val;
+
+	u16 steps;
+};
+
+struct __PACKED__ charge_curve_settings {
+	bool prepared;
+
+	u8 scan_mode;
+
+	struct charge_curve_sweep dump;
+	struct charge_curve_sweep charge;
+
+	u16 c_sub;
+	u16 frame_cnt;
+
+	struct __PACKED__ charge_curve_point {
+		u16 x;
+		u16 y;
+		u16 *dump_max;
+		u16 *dump_avg;
+		u16 *charge_max;
+		u16 *charge_avg;
+	} pt[9];
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ cdc_settings {
+	u8 cmd;
+	u16 config;
+
+	bool skip_checksum;
+
+	/* freq. */
+	struct freq_settings freq;
+	/* short */
+	struct short_settings _short;
+	/* open */
+	struct open_settings open;
+	/* p2p */
+	struct p2p_settings p2p;
+	/* charge curve */
+	struct charge_curve_settings curve;
+
+	/* status only writable by CDC commonflow */
+	bool is_key;
+	bool is_p2p;
+	bool is_freq;
+	bool is_curve;
+	bool is_short;
+	bool is_open;
+	bool is_16bit;
+	bool is_sign;
+	bool is_fast_mode;
+	unsigned int total_bytes;
+
+	/* error code during cdc data collection */
+	s32 error;
+};
+
+struct __PACKED__ mp_station_old {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test:2;
+		u8 open_test:2;
+		u8 self_test:2;
+		u8 uniform_test:2;
+
+		u8 dac_test:2;
+		u8 key_test:2;
+		u8 final_result:2;
+		u8 paint_test:2;
+
+		u8 mopen_test:2;
+		u8 gpio_test:2;
+		u8 reserve_1:4;
+
+		char bar_code[28];
+		u8 reserve_2[35];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[10];
+};
+
+struct __PACKED__ mp_station {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test : 2;
+		u8 open_test : 2;
+		u8 self_test : 2;
+		u8 uniform_test : 2;
+
+		u8 dac_test : 2;
+		u8 key_test : 2;
+		u8 final_result : 2;
+		u8 paint_test : 2;
+
+		u8 mopen_test : 2;
+		u8 gpio_test : 2;
+		u8 reserve : 4;
+
+		u8 tool_ver[8];
+		char bar_code[135];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[5];
+
+	struct __PACKED__ {
+		u8 reserve_1[91];
+		u32 mp_result_ver;
+		u16 customer_id;
+		u16 fwid;
+		u8 reserve_2;
+	} info;
+
+	u16 crc;
+};
+
+struct __PACKED__ ilitek_ts_settings {
+	bool no_retry;
+	bool no_INT_ack;
+
+	bool sw_reset_at_last;
+
+	u8 sensor_id_mask;
+
+	/* only used for QUIRK_WAIT_ACK_DELAY */
+	u32 wait_ack_delay;
+
+	/*
+	 * engineer mode would likely report default format
+	 * ex. IWB-format
+	 */
+	bool default_format_enabled;
+};
+
+struct __PACKED__ ilitek_sys_info {
+	u16 pid;
+};
+
+struct __PACKED__ ilitek_ts_callback {
+	/* Please don't use "repeated start" for I2C interface */
+	write_then_read_t write_then_read;
+	read_ctrl_in_t read_ctrl_in;
+	read_interrupt_in_t read_interrupt_in;
+	init_ack_t init_ack;
+	wait_ack_t wait_ack;
+	hw_reset_t hw_reset;
+	re_enum_t re_enum;
+	delay_ms_t delay_ms;
+	msg_t msg;
+
+	/* write cmd without adding any hid header */
+	write_then_read_direct_t write_then_read_direct;
+	/* notify caller after AP/BL mode switch command */
+	mode_switch_notify_t mode_switch_notify;
+};
+
+struct __PACKED__ ilitek_common_info {
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen;
+	struct ilitek_tp_info_v6 tp;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu;
+	struct ilitek_hid_info hid;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+};
+
+struct __PACKED__ ilitek_ts_device {
+	void *_private;
+	char id[64];
+	u32 reset_time;
+
+	struct ilitek_ts_settings setting;
+
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen_info;
+	struct ilitek_tp_info_v6 tp_info;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu_info;
+	struct ilitek_hid_info hid_info;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+
+	u8 fw_mode;
+	struct mp_station mp;
+
+	u8 wbuf[4096];
+	u8 rbuf[4096];
+	struct ilitek_ts_callback cb;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16 __DLL le16(const u8 *p);
+u16 __DLL be16(const u8 *p);
+u32 __DLL le32(const u8 *p, int bytes);
+u32 __DLL be32(const u8 *p, int bytes);
+
+bool __DLL is_29xx(void *handle);
+
+bool __DLL _is_231x(char *ic_name);
+bool __DLL is_231x(void *handle);
+
+bool __DLL has_hw_key(void *handle);
+
+u8 __DLL get_protocol_ver_flag(u32 ver);
+
+int __DLL grid_alloc(void *handle, struct grids *grid);
+void __DLL grid_free(struct grids *grid);
+void __DLL grid_reset(struct grids *grid);
+
+u16 __DLL get_crc(u32 start, u32 end,
+		  u8 *buf, u32 buf_size);
+
+u32 __DLL get_checksum(u32 start, u32 end,
+		       u8 *buf, u32 buf_size);
+
+bool __DLL is_checksum_matched(u8 checksum, int start, int end,
+			       u8 *buf, int buf_size);
+
+bool __DLL support_sensor_id(void *handle);
+bool __DLL support_production_info(void *handle);
+bool __DLL support_fwid(void *handle);
+bool __DLL support_mcu_info(void *handle);
+bool __DLL support_power_status(void *handle);
+int __DLL write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms);
+
+int __DLL bridge_set_int_monitor(void *handle, bool enable);
+int __DLL bridge_set_test_mode(void *handle, bool enable);
+
+int __DLL reset_helper(void *handle);
+
+int __DLL write_then_read(void *handle, u8 *cmd, int wlen,
+			  u8 *buf, int rlen);
+int __DLL write_then_read_direct(void *handle, u8 *cmd, int wlen,
+				 u8 *buf, int rlen);
+int __DLL read_interrupt_in(void *handle, u8 *buf, int rlen,
+			    unsigned int timeout_ms);
+int __DLL read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		       unsigned int timeout_ms);
+
+void __DLL __ilitek_get_info(void *handle,
+			     struct ilitek_common_info *info);
+
+void __DLL ilitek_dev_set_quirks(void *handle, u32 quirks);
+void __DLL ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys);
+void __DLL ilitek_dev_setting(void *handle,
+			      struct ilitek_ts_settings *setting);
+
+void __DLL ilitek_dev_bind_callback(void *handle,
+				    struct ilitek_ts_callback *callback);
+
+void __DLL *ilitek_dev_init(u8 _interface, const char *id,
+			    bool need_update_ts_info,
+			    struct ilitek_ts_callback *callback,
+			    void *_private);
+void __DLL ilitek_dev_exit(void *handle);
+
+void __DLL api_print_ts_info(void *handle);
+void __DLL api_read_then_print_m2v_info(void *handle);
+
+int __DLL api_update_ts_info(void *handle);
+
+int __DLL api_protocol_set_cmd(void *handle, u8 idx, void *data);
+int __DLL api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force);
+
+u16 __DLL api_get_block_crc_by_addr(void *handle, u8 type,
+				    u32 start, u32 end);
+u16 __DLL api_get_block_crc_by_num(void *handle, u8 type,
+				   u8 block_num);
+
+int __DLL api_set_data_len(void *handle, u16 data_len);
+int __DLL api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			      u32 start, u32 end);
+int __DLL api_write_data_v6(void *handle, int wlen);
+int __DLL api_access_slave(void *handle, u8 id, u8 func, void *data);
+int __DLL api_check_busy(void *handle, int timeout_ms, int delay_ms);
+int __DLL api_write_enable_v3(void *handle, bool in_ap,
+			      bool write_ap, u32 end, u32 checksum);
+int __DLL api_write_data_v3(void *handle);
+
+int __DLL api_to_bl_mode(void *handle, bool bl, u32 start, u32 end);
+
+int __DLL api_write_data_m2v(void *handle, int wlen);
+int __DLL api_to_bl_mode_m2v(void *handle, bool to_bl);
+
+int __DLL api_set_idle(void *handle, bool enable);
+int __DLL api_set_func_mode(void *handle, u8 mode);
+int __DLL api_get_func_mode(void *handle);
+
+int __DLL api_erase_data_v3(void *handle);
+
+int __DLL api_read_flash(void *handle, u8 *buf,
+			 u32 start_addr, u32 len);
+
+int __DLL _api_read_mp_result(void *handle);
+int __DLL api_read_mp_result(void *handle);
+int __DLL _api_write_mp_result(void *handle, struct mp_station *mp);
+int __DLL api_write_mp_result(void *handle, struct mp_station *mp);
+void __DLL api_decode_mp_result(void *handle);
+
+int __DLL api_read_tuning(void *handle, u8 *buf, int rlen);
+
+int __DLL api_get_ic_crc(void *handle, u8 final_fw_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.c b/drivers/input/touchscreen/ilitek/ilitek_report.c
new file mode 100644
index 000000000000..dbe8abfd64ef
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_report.h"
+#include "ilitek_crypto.h"
+
+static bool is_debug_packet_id(u8 id, u8 _interface)
+{
+	return ((_interface == interface_i2c && id == 0xdb) ||
+		(_interface == interface_usb && id == 0xaa));
+}
+
+static bool is_pen_packet_id(u8 id)
+{
+	return (id == 0x0c || id == 0x0d);
+}
+
+static void touch_decode(struct ilitek_ts_device *dev,
+			 struct ilitek_report *report,
+			 u8 *buf, u8 cnt)
+{
+	struct touch_fmt *parser, *finger;
+	struct touch_iwb_fmt *parser_iwb;
+	u8 i, j, offset;
+	u32 size = dev->fmt.touch_size;
+
+	offset = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	for (i = 0; i < cnt && i < dev->tp_info.max_fingers; i++) {
+		finger = &report->touch.finger[i];
+
+		do {
+			if (dev->protocol.flag == PTL_V3 &&
+			    dev->_interface == interface_i2c) {
+				finger->id = i;
+				finger->status = buf[1 + offset + i * 5] >> 7;
+				finger->x = ((buf[1 + offset + i * 5] & 0x3F) << 8) +
+					buf[2 + offset + i * 5];
+				finger->y = ((buf[3 + offset + i * 5] & 0x3F) << 8) +
+					buf[4 + offset + i * 5];
+				finger->pressure = buf[5 + offset + i * 5];
+				finger->height = 128;
+				finger->width = 1;
+
+				break;
+			}
+
+			parser = (struct touch_fmt *)
+				(buf + 1 + i * size);
+			parser_iwb = (struct touch_iwb_fmt *)
+				(buf + 1 + i * size);
+
+			finger->id = parser->id;
+			finger->status = parser->status;
+			finger->x = parser->x;
+			finger->y = parser->y;
+
+			if (dev->setting.default_format_enabled)
+				break;
+
+			switch (dev->tp_info.format) {
+			case touch_fmt_0x1:
+				finger->pressure = parser->pressure;
+				break;
+			case touch_fmt_0x2:
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x3:
+				finger->pressure = parser->pressure;
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x4:
+				finger->id = parser_iwb->id;
+				finger->status =
+					(parser_iwb->status == 0x7) ? 1 : 0;
+				finger->x = parser_iwb->x;
+				finger->y = parser_iwb->y;
+				finger->width = parser_iwb->width;
+				finger->height = parser_iwb->height;
+				break;
+			case touch_fmt_0x10:
+				finger->width = le16(buf + i * size + 6);
+				finger->height = le16(buf + i * size + 8);
+				finger->algo = buf[i * size + 10];
+				report->touch.algo = 0;
+				break;
+			case touch_fmt_0x11:
+				finger->id =
+					(!finger->id) ? 0 : finger->id - 1;
+				break;
+			}
+		} while (false);
+
+		TP_DBG(dev->id,
+		       "[touch-report] id:%hhu, status:%hhu, x:%hu, y:%hu, p:%hhu, w:%hu, h:%hu, algo: 0x%hhx\n",
+			finger->id, finger->status, finger->x, finger->y,
+			finger->pressure, finger->width, finger->height,
+			finger->algo);
+
+		if (finger->id >= dev->tp_info.max_fingers) {
+			TP_ERR(dev->id, "invalid touch id: %hhu >= %hhu\n",
+				finger->id, dev->tp_info.max_fingers);
+			return;
+		}
+
+		/*
+		 * if x/y within key's range, skip touch range check.
+		 */
+		for (j = 0; j < dev->tp_info.key_num; j++) {
+			if ((finger->x < dev->key.info.keys[j].x &&
+			     finger->x > dev->key.info.keys[j].x +
+				     dev->key.info.x_len) &&
+			    (finger->y < dev->key.info.keys[j].y &&
+			     finger->y > dev->key.info.keys[j].y +
+				     dev->key.info.y_len))
+				continue;
+
+			goto skip_touch_range_check;
+		}
+
+		if (finger->status &&
+		    (finger->x - 1 > dev->screen_info.x_max ||
+		     finger->y - 1 > dev->screen_info.y_max ||
+		     finger->x < dev->screen_info.x_min ||
+		     finger->y < dev->screen_info.y_min)) {
+			TP_ERR(dev->id, "Point[%d]: (%d, %d), Limit: (%d:%d, %d:%d) OOB\n",
+				finger->id, finger->x, finger->y,
+				dev->screen_info.x_min, dev->screen_info.x_max,
+				dev->screen_info.y_min, dev->screen_info.y_max);
+			return;
+		}
+
+skip_touch_range_check:
+		continue;
+	}
+
+	report->touch.cnt = i;
+
+	/*
+	 * report touch event callback,
+	 * which includes actual count of finger report just parsed above.
+	 */
+	if (report->cb.report_touch_event)
+		report->cb.report_touch_event(&report->touch, report->_private);
+}
+
+static void pen_decode(struct ilitek_ts_device *dev,
+		       struct ilitek_report *report,
+		       u8 *buf)
+{
+	struct pen_fmt *parser = (struct pen_fmt *)(buf + 1);
+	struct pen_fmt *pen = &report->pen.pen;
+
+	report->pen.cnt = buf[61];
+	report->pen.algo = buf[62];
+
+	memcpy(pen, parser, sizeof(struct pen_fmt));
+
+	switch (dev->tp_info.pen_format) {
+	default:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery);
+		break;
+	case 1:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_1.barrel_pressure,
+			pen->usi_1.idx, pen->usi_1.color, pen->usi_1.width,
+			pen->usi_1.style);
+		break;
+	case 2:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color24: %u, no_color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_2.barrel_pressure,
+			pen->usi_2.idx, le32(pen->usi_2.color_24, 3),
+			pen->usi_2.no_color, pen->usi_2.width,
+			pen->usi_2.style);
+		break;
+	}
+
+	/* report pen event callback */
+	if (report->cb.report_pen_event)
+		report->cb.report_pen_event(&report->pen, report->_private);
+}
+
+static void dmsg_decode(struct ilitek_ts_device *dev,
+			struct ilitek_report *report,
+			u8 *buf, int buf_size)
+{
+	if ((int)buf[1] >= buf_size)
+		return;
+
+	buf[buf[1]] = '\0';
+	TP_DBG(dev->id, "%s\n", (char *)(buf + 2));
+
+	if (report->cb.report_dmsg)
+		report->cb.report_dmsg((char *)(buf + 2),
+					buf_size - 2, report->_private);
+}
+
+static void report_buf(struct ilitek_report *report, u8 *buf,
+		       bool is_last)
+{
+	if (!report->cb.report_buf)
+		return;
+
+	report->cb.report_buf(buf, 64, is_last, report->_private);
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v3(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+
+	u8 cnt;
+	int idx = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	UNUSED(buf_size);
+
+	if (dev->_interface == interface_i2c) {
+		if (dev->quirks & QUIRK_BRIDGE) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+
+			if (buf[0] != 0x03 || buf[1] != 0xa3 || buf[2] != 0x10)
+				return -EAGAIN;
+
+			/* move algo byte to index 62 */
+			buf[62] = buf[35];
+		} else if (dev->quirks & QUIRK_DAEMON_I2C ||
+			   dev->quirks & QUIRK_BRIDGE ||
+			   dev->quirks & QUIRK_WIFI_ITS_I2C) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+		} else {
+			dev->wbuf[0] = 0x10;
+			error = write_then_read(dev, dev->wbuf, 1, buf, 32);
+			if (error < 0)
+				return error;
+
+			report->touch.algo = buf[31];
+			buf[31] = 0;
+
+			if (buf[0] == 2) {
+				error = write_then_read(dev, NULL, 0,
+							 buf + 31, 20);
+				if (error < 0)
+					return error;
+			}
+
+			/* move algo byte to index 62 */
+			buf[62] = report->touch.algo;
+		}
+
+		switch (dev->rbuf[idx]) {
+		default:
+		case 1:
+			cnt = 6;
+			break;
+		case 0:
+			cnt = 0;
+			break;
+		case 2:
+			cnt = 10;
+			break;
+		}
+	} else {
+		error = read_interrupt_in(dev, buf, 64, 1000);
+		if (error < 0)
+			return error;
+
+		cnt = buf[55];
+
+		/* move algo byte to index 62 */
+		buf[62] = buf[56];
+	}
+
+	report->touch.algo = buf[62];
+
+	report->touch.dbg_size = 64;
+	memcpy(report->touch.dbg, buf, 64);
+
+	report_buf(report, buf, true);
+
+	return cnt;
+}
+
+static bool is_iwb_fmt(struct ilitek_ts_device *dev)
+{
+	return dev->tp_info.format == touch_fmt_0x4 &&
+		!dev->setting.default_format_enabled;
+}
+
+static bool need_skip_checksum(struct ilitek_ts_device *dev,
+			       struct ilitek_report *report,
+			       u8 packet_id, bool is_first)
+{
+	/*
+	 * don't check checksum for below situation:
+	 *   1. debug packet
+	 *   2. normal mode pen packet w/ HID.
+	 *   3. IWB format packet
+	 */
+
+	if (report->skip_checksum)
+		return true;
+
+	if (is_first) {
+		if (is_debug_packet_id(packet_id, dev->_interface))
+			return true;
+
+		if ((dev->_interface == interface_usb ||
+		     dev->_interface == interface_hid_over_i2c) &&
+		    packet_id == 0x0d)
+			return true;
+	}
+
+	if (is_iwb_fmt(dev))
+		return true;
+
+	return false;
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v6(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+	u8 i, cnt;
+	u8 size = dev->fmt.touch_size, max_cnt = dev->fmt.touch_max_cnt;
+	u8 tmp[64];
+
+	UNUSED(buf_size);
+
+	error = read_interrupt_in(dev, tmp, 64, 1000);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.format == 5)
+		ilitek_decrypt(tmp + 1, 48);
+
+	/* set packet id to 0x48 forcely for using BRIDGE finger report */
+	if (dev->quirks & QUIRK_BRIDGE && !is_pen_packet_id(tmp[0]))
+		tmp[0] = 0x48;
+
+	if (!need_skip_checksum(dev, report, tmp[0], true) &&
+	    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+		return -EILIPROTO;
+
+	report->pen.algo = tmp[62];
+	report->touch.algo = tmp[62];
+	report->pen.dbg_size = 64;
+	report->touch.dbg_size = 64;
+	memcpy(report->pen.dbg, tmp, 64);
+	memcpy(report->touch.dbg, tmp, 64);
+	memcpy(buf, tmp, 64);
+
+	/*
+	 * no need to check contact count byte for debug packet and pen packet.
+	 */
+	if (is_pen_packet_id(tmp[0]) ||
+	    is_debug_packet_id(tmp[0], dev->_interface)) {
+		report_buf(report, tmp, true);
+		return 0;
+	}
+
+	cnt = is_iwb_fmt(dev) ? tmp[55] : tmp[61];
+	for (i = 1; i < DIV_ROUND_UP(cnt, max_cnt); i++) {
+		report_buf(report, tmp, false);
+
+		error = read_interrupt_in(dev, tmp, 64, 1000);
+		if (error < 0)
+			return error;
+
+		if (dev->tp_info.format == 5)
+			ilitek_decrypt(tmp + 1, 48);
+
+		if (!need_skip_checksum(dev, report, tmp[0], false) &&
+		    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+			return -EILIPROTO;
+
+		/* copy and skip the first rid byte */
+		memcpy(buf + i * size * max_cnt + 1, tmp + 1, 63);
+	}
+	report_buf(report, tmp, true);
+
+	return cnt;
+}
+
+int ilitek_report_update(void *handle, struct ilitek_report *report)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt;
+
+	if (!dev)
+		return -EINVAL;
+
+	/* initial report event */
+	memset(&report->touch, 0, sizeof(report->touch));
+	memset(&report->pen, 0, sizeof(report->pen));
+
+	memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (dev->protocol.flag) {
+	default: return -EPERM;
+	case PTL_V3:
+		cnt = report_get_raw_v3(dev, report, dev->rbuf,
+					 sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		break;
+
+	case PTL_V6:
+		cnt = report_get_raw_v6(dev, report, dev->rbuf,
+					sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		/* pen packet (V6 only) */
+		if (is_pen_packet_id(dev->rbuf[0])) {
+			pen_decode(dev, report, dev->rbuf);
+			return 0;
+		}
+
+		break;
+	}
+
+	/* debug message packet */
+	if (is_debug_packet_id(dev->rbuf[0], dev->_interface)) {
+		dmsg_decode(dev, report, dev->rbuf, sizeof(dev->rbuf));
+		return 0;
+	}
+
+	touch_decode(dev, report, dev->rbuf, cnt);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.h b/drivers/input/touchscreen/ilitek/ilitek_report.h
new file mode 100644
index 000000000000..e49333f9618b
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_REPORT_H__
+#define __ILITEK_REPORT_H__
+
+#include "ilitek_def.h"
+#include "ilitek_protocol.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_data {
+	struct touch_fmt finger[40];
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+struct __PACKED__ pen_data {
+	struct pen_fmt pen;
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return touch event */
+typedef void(*report_touch_event_t)(struct touch_data *, void *);
+/* return pen event */
+typedef void(*report_pen_event_t)(struct pen_data *, void *);
+/* return debug msg */
+typedef void(*report_dmsg_t)(char *, int, void *);
+/* return raw data buf */
+typedef void(*report_buf_t)(u8 *, int, bool, void *);
+
+struct ilitek_report_callback {
+	report_touch_event_t report_touch_event;
+	report_pen_event_t report_pen_event;
+	report_dmsg_t report_dmsg;
+	report_buf_t report_buf;
+};
+
+struct ilitek_report {
+	struct touch_data touch;
+	struct pen_data pen;
+	struct ilitek_report_callback cb;
+
+	bool skip_checksum;
+
+	void *_private;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	int __DLL ilitek_report_update(void *handle, struct ilitek_report *report);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_tool.c b/drivers/input/touchscreen/ilitek/ilitek_tool.c
new file mode 100644
index 000000000000..689c1c09d397
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_tool.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+#include <linux/firmware.h>
+
+#include <linux/miscdevice.h>
+
+static struct proc_dir_entry *ilitek_proc;
+static struct proc_dir_entry *ilitek_proc_entry;
+
+#define ILITEK_IOCTL_BASE                       100
+#define ILITEK_IOCTL_I2C_WRITE_DATA             _IOWR(ILITEK_IOCTL_BASE, 0, uint64_t)
+#define ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT      _IOWR(ILITEK_IOCTL_BASE, 0, uint32_t)
+#define ILITEK_IOCTL_I2C_WRITE_LENGTH           _IOWR(ILITEK_IOCTL_BASE, 1, int32_t)
+#define ILITEK_IOCTL_I2C_READ_DATA              _IOWR(ILITEK_IOCTL_BASE, 2, uint64_t)
+#define ILITEK_IOCTL_I2C_READ_DATA_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 2, uint32_t)
+#define ILITEK_IOCTL_I2C_READ_LENGTH            _IOWR(ILITEK_IOCTL_BASE, 3, int32_t)
+#define ILITEK_IOCTL_DRIVER_INFORMATION		_IOWR(ILITEK_IOCTL_BASE, 8, int32_t)
+#define ILITEK_IOCTL_USB_UPDATE_RESOLUTION      _IOWR(ILITEK_IOCTL_BASE, 9, int32_t)
+#define ILITEK_IOCTL_I2C_INT_FLAG	        _IOWR(ILITEK_IOCTL_BASE, 10, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE                 _IOWR(ILITEK_IOCTL_BASE, 11, int32_t)
+#define ILITEK_IOCTL_STOP_READ_DATA             _IOWR(ILITEK_IOCTL_BASE, 12, int32_t)
+#define ILITEK_IOCTL_START_READ_DATA            _IOWR(ILITEK_IOCTL_BASE, 13, int32_t)
+#define ILITEK_IOCTL_I2C_SWITCH_IRQ		_IOWR(ILITEK_IOCTL_BASE, 15, int32_t)
+#define ILITEK_IOCTL_UPDATE_FLAG		_IOWR(ILITEK_IOCTL_BASE, 16, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE_FW		_IOWR(ILITEK_IOCTL_BASE, 18, int32_t)
+#define ILITEK_IOCTL_RESET			_IOWR(ILITEK_IOCTL_BASE, 19, int32_t)
+#define ILITEK_IOCTL_INT_STATUS			_IOWR(ILITEK_IOCTL_BASE, 20, int32_t)
+
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#define ILITEK_IOCTL_DEBUG_SWITCH		_IOWR(ILITEK_IOCTL_BASE, 21, int32_t)
+#endif
+
+#define ILITEK_IOCTL_I2C_INT_CLR		_IOWR(ILITEK_IOCTL_BASE, 22, int32_t)
+#define ILITEK_IOCTL_I2C_INT_POLL		_IOWR(ILITEK_IOCTL_BASE, 23, uint64_t)
+#define ILITEK_IOCTL_I2C_INT_POLL_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 23, uint32_t)
+#define ILITEK_IOCTL_I2C_ISR_TYPE		_IOWR(ILITEK_IOCTL_BASE, 24, uint32_t)
+#define ILITEK_IOCTL_I2C_NETLINK		_IOWR(ILITEK_IOCTL_BASE, 25, uint16_t)
+
+
+#define ILITEK_DEVICE_NODE_PERMISSON			0666
+
+static s32 ilitek_file_open(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = true;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static s32 ilitek_file_close(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = false;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static ssize_t ilitek_file_write(struct file *filp,
+				 const char *buf, size_t size, loff_t *f_pos)
+{
+	s32 ret = 0, count = 0;
+	u8 buffer[512];
+	u32 *data;
+	char *token = NULL, *cur = NULL;
+
+	if (size > sizeof(buffer)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(buffer));
+		return -EINVAL;
+	}
+
+	ret = copy_from_user(buffer, buf, size);
+	if (ret < 0) {
+		TP_ERR(NULL, "copy data from user space, failed");
+		return -1;
+	}
+
+	token = cur = buffer;
+
+	data = kcalloc(size, sizeof(u32), GFP_KERNEL);
+
+	while ((token = strsep(&cur, ",")) != NULL) {
+		//data[count] = str2hex(token);
+		sscanf(token, "%x", &data[count]);
+		TP_MSG(NULL, "data[%d] = %x\n", count, data[count]);
+		count++;
+	}
+
+	if (buffer[size - 2] == 'I' && (size == 20 || size == 52) && buffer[0] == 0x77 && buffer[1] == 0x77) {
+
+		TP_MSG(NULL, "IOCTL_WRITE CMD = %d\n", buffer[2]);
+		switch (buffer[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+#ifdef ILITEK_TUNING_MESSAGE
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", buffer[3]);
+			if (buffer[3] == 0) {
+				ilitek_debug_flag = false;
+			} else if (buffer[3] == 1) {
+				ilitek_debug_flag = true;
+			}
+			break;
+#endif
+		case 15:
+			if (buffer[3] == 0)
+				ilitek_irq_disable();
+			else
+				ilitek_irq_enable();
+
+			break;
+		case 16:
+			ts->operation_protection = buffer[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 18:
+			ilitek_irq_disable();
+			mutex_lock(&ts->ilitek_mutex);
+			ret = ilitek_write(&buffer[3], 33);
+			mutex_unlock(&ts->ilitek_mutex);
+			ilitek_irq_enable();
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+
+			return ret;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	if (buffer[size - 2] == 'W') {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write(buffer, size - 2);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			return ret;
+		}
+	} else if (!strncmp(buffer, "unhandle_irq", strlen("unhandle_irq"))) {
+		ts->unhandle_irq = !ts->unhandle_irq;
+		TP_MSG(NULL, "ts->unhandle_irq = %d.\n", ts->unhandle_irq);
+	} else if (!strncmp(buffer, "dbg_pkt", strlen("dbg_pkt"))) {
+		set_log_level(log_level_pkt);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_pkt);
+	} else if (!strncmp(buffer, "dbg_debug", strlen("dbg_debug"))) {
+		set_log_level(log_level_dbg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_dbg);
+	} else if (!strncmp(buffer, "dbg_info", strlen("dbg_info"))) {
+		set_log_level(log_level_msg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_msg);
+	} else if (!strncmp(buffer, "dbg_err", strlen("dbg_err"))) {
+		set_log_level(log_level_err);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_err);
+	} else if (!strncmp(buffer, "dbg_num", strlen("dbg_num"))) {
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", tp_log_level);
+	}
+#ifdef ILITEK_TUNING_MESSAGE
+	else if (!strncmp(buffer, "truning_dbg_flag", strlen("truning_dbg_flag"))) {
+		ilitek_debug_flag = !ilitek_debug_flag;
+		TP_MSG(NULL, " %s debug_flag message(%X).\n", ilitek_debug_flag ? "Enabled" : "Disabled", ilitek_debug_flag);
+	}
+#endif
+	else if (!strncmp(buffer, "irq_status", strlen("irq_status"))) {
+		TP_MSG(NULL, "gpio_get_value(i2c.irq_gpio) = %d.\n", gpio_get_value(ts->irq_gpio));
+	} else if (!strncmp(buffer, "enable", strlen("enable"))) {
+		ilitek_irq_enable();
+		TP_MSG(NULL, "irq enable\n");
+	} else if (!strncmp(buffer, "disable", strlen("disable"))) {
+		ilitek_irq_disable();
+		TP_MSG(NULL, "irq disable\n");
+	} else if (!strncmp(buffer, "info", strlen("info"))) {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		api_update_ts_info(ts->dev);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+	} else if (!strncmp(buffer, "reset", strlen("reset"))) {
+		ilitek_reset(ts->dev->reset_time);
+	}
+
+	TP_DBG(NULL, "ilitek return count = %zu\n", size);
+	kfree(data);
+	return size;
+}
+
+static FOPS_IOCTL_FUNC(ilitek_file_ioctl, uint32_t cmd, unsigned long arg)
+{
+	static u8 *buffer;
+	static unsigned long len;
+	s32 ret = 0;
+	int tmp;
+
+	buffer = kmalloc(ILITEK_IOCTL_MAX_TRANSFER, GFP_KERNEL);
+	memset(buffer, 0, ILITEK_IOCTL_MAX_TRANSFER);
+
+	switch (cmd) {
+	case ILITEK_IOCTL_I2C_WRITE_DATA:
+	case ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid write len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(buffer, (u8 *)arg, len)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, len, 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write failed, cmd: %x\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_I2C_READ_DATA:
+	case ILITEK_IOCTL_I2C_READ_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid read len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(NULL, 0, 0, buffer, len);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c read failed, buf: %x\n", buffer[0]);
+			break;
+		}
+
+		if (copy_to_user((u8 *)arg, buffer, len)) {
+			ret = -EFAULT;
+			TP_ERR(NULL, "copy data to user space, failed\n");
+		}
+		break;
+	case ILITEK_IOCTL_I2C_WRITE_LENGTH:
+	case ILITEK_IOCTL_I2C_READ_LENGTH:
+		len = arg;
+		break;
+	case ILITEK_IOCTL_DRIVER_INFORMATION:
+		memcpy(buffer, driver_ver, 7);
+		if (copy_to_user((u8 *)arg, buffer, 7))
+			ret = -EFAULT;
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE:
+		break;
+	case ILITEK_IOCTL_I2C_INT_FLAG:
+		buffer[0] = !(gpio_get_value(ts->irq_gpio));
+		if (copy_to_user((u8 *)arg, buffer, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_FLAG = %d.\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_START_READ_DATA:
+		ilitek_irq_enable();
+		ts->unhandle_irq = false;
+		TP_MSG(NULL, "enable_irq and ts->unhandle_irq = false.\n");
+		break;
+	case ILITEK_IOCTL_STOP_READ_DATA:
+		ilitek_irq_disable();
+		ts->unhandle_irq = true;
+		TP_MSG(NULL, "disable_irq and ts->unhandle_irq = true.\n");
+		break;
+	case ILITEK_IOCTL_RESET:
+		ilitek_reset(ts->dev->reset_time);
+		break;
+	case ILITEK_IOCTL_INT_STATUS:
+		if (put_user(gpio_get_value(ts->irq_gpio), (s32 *)arg))
+			ret = -EFAULT;
+		break;
+#ifdef ILITEK_TUNING_MESSAGE
+	case ILITEK_IOCTL_DEBUG_SWITCH:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+		TP_MSG(NULL, "ilitek The debug_flag = %d.\n", buffer[0]);
+		if (buffer[0] == 0)
+			ilitek_debug_flag = false;
+		else if (buffer[0] == 1)
+			ilitek_debug_flag = true;
+		break;
+#endif
+	case ILITEK_IOCTL_I2C_SWITCH_IRQ:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		if (buffer[0] == 0)
+			ilitek_irq_disable();
+		else
+			ilitek_irq_enable();
+
+		break;
+	case ILITEK_IOCTL_UPDATE_FLAG:
+		ts->operation_protection = arg;
+		TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE_FW:
+		if (copy_from_user(buffer, (u8 *)arg, 35)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, buffer[34], 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write, failed\n");
+
+		break;
+	case ILITEK_IOCTL_I2C_INT_CLR:
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_CLR, set get_INT false\n");
+		atomic_set(&ts->get_INT, 0);
+		break;
+	case ILITEK_IOCTL_I2C_INT_POLL:
+	case ILITEK_IOCTL_I2C_INT_POLL_COMPAT:
+		tmp = atomic_read(&ts->get_INT);
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_POLL, get_INT: %d\n", tmp);
+
+		if (copy_to_user((u8 *)arg, &tmp, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+		}
+		break;
+	case ILITEK_IOCTL_I2C_ISR_TYPE:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_ISR_TYPE, set ISR type: %lu\n", arg);
+		ts->irq_handle_type = (arg >> 16);
+		ts->irq_read_len = arg & 0xFFFF;
+		break;
+	case ILITEK_IOCTL_I2C_NETLINK:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_NETLINK, set netlink: %s with ETH: %hhu\n",
+			(arg >> 8) ? "ON" : "OFF", (u8)(arg & 0xFF));
+
+		if (arg >> 8)
+			ret = ilitek_netlink_init(arg & 0xFF);
+		else
+			ilitek_netlink_exit();
+
+		break;
+	default:
+		TP_ERR(NULL, "unrecognized ioctl cmd: 0x%04x\n", cmd);
+		ret = -EINVAL;
+		break;
+	}
+
+	kfree(buffer);
+	return (ret < 0) ? ret : 0;
+}
+
+static ssize_t ilitek_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	u8 *tmp;
+	s32 ret;
+
+	if (count > 8192)
+		count = 8192;
+
+	tmp = kmalloc(count, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = ilitek_read(tmp, count);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+	if (ret < 0) {
+		TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		goto err_free;
+	}
+
+	if (copy_to_user(buf, tmp, count)) {
+		ret = -EFAULT;
+		goto err_free;
+	}
+
+err_free:
+	kfree(tmp);
+
+	return ret > 0 ? count : ret;
+}
+
+/* compat ioctl for 32/64 bit user program compatibility */
+#ifdef CONFIG_COMPAT
+static long ilitek_file_compat_ioctl(struct file *fp, uint32_t cmd,
+				     unsigned long arg)
+{
+	return ilitek_file_ioctl(fp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+
+static struct file_operations ilitek_fops = {
+	.FOPS_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = ilitek_file_compat_ioctl,
+#endif
+
+	.read = ilitek_file_read,
+	.write = ilitek_file_write,
+	.open = ilitek_file_open,
+	.release = ilitek_file_close,
+};
+
+static struct PROC_FOPS_T ilitek_proc_fops = {
+	.PROC_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.PROC_COMPAT_IOCTL = ilitek_file_compat_ioctl,
+#endif
+
+	.PROC_READ = ilitek_file_read,
+	.PROC_WRITE = ilitek_file_write,
+	.PROC_OPEN = ilitek_file_open,
+	.PROC_RELEASE = ilitek_file_close,
+};
+
+static ssize_t ilitek_driver_version_read(struct file *fp, char __user *buf,
+					  size_t size, loff_t *off)
+{
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	cnt = scnprintf(str, sizeof(str), "driver-version-tag: [%*phD]\n",
+			7, driver_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_drv_version = {
+	.PROC_READ = ilitek_driver_version_read,
+};
+
+static ssize_t ilitek_update_fw_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int error;
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.bin");
+		if (error < 0)
+			cnt += scnprintf(str, sizeof(str),
+					 "upgrade failed, err: %d\n", error);
+	} else {
+		cnt += scnprintf(str, sizeof(str),
+				 "upgrade success, fw version: %*phD\n",
+				 8, ts->dev->fw_ver);
+	}
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwupdate = {
+	.PROC_READ = ilitek_update_fw_read,
+};
+
+static ssize_t ilitek_firmware_version_read(struct file *fp, char __user *buf,
+					    size_t size, loff_t *off)
+{
+	int error;
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (error < 0)
+		cnt = scnprintf(str, sizeof(str), "read failed, err: %d\n",
+				error);
+	else
+		cnt = scnprintf(str, sizeof(str), "fw-version-tag: [%*phD]\n",
+				8, ts->dev->fw_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwversion = {
+	.PROC_READ = ilitek_firmware_version_read,
+};
+
+static ssize_t ilitek_console_write(struct file *filp, const char *buf,
+				    size_t size, loff_t *f_pos)
+{
+	int error;
+	char tmp[256], str[256];
+	char *ptr, *cur = str;
+	size_t wlen, rlen, _wlen = 0;
+	u8 cmd[64], data[64];
+
+	if (size > sizeof(tmp)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(tmp));
+		return -EINVAL;
+	}
+
+	error = copy_from_user(tmp, buf, size);
+	if (error < 0) {
+		TP_ERR(NULL, "copy_from_user failed, err: %d", error);
+		return error;
+	}
+
+	memset(str, 0, sizeof(str));
+	sscanf(tmp, "%zu:%zu:%s", &wlen, &rlen, str);
+	TP_MSG(NULL, "wlen: %zu, rlen: %zu, command: %s\n", wlen, rlen, str);
+
+	while ((ptr = strsep(&cur, "-")) && _wlen < sizeof(cmd))
+		sscanf(ptr, "%hhx", &cmd[_wlen++]);
+
+	if (wlen > 0 && wlen != _wlen) {
+		TP_ERR(NULL, "write cmd length: %zu not match with %s\n",
+			wlen, str);
+		return -EINVAL;
+	} else if (rlen > sizeof(data)) {
+		TP_ERR(NULL, "invalid read cmd length: %zu > %zu too large\n",
+			rlen, sizeof(data));
+		return -EINVAL;
+	}
+
+	if (wlen > 0)
+		TP_MSG(NULL, "[write]: %*phD, len: %zu\n",
+			(int)wlen, cmd, wlen);
+
+	error = ilitek_write_and_read(cmd, wlen, 1, data, rlen);
+	if (error < 0)
+		return error;
+
+	if (rlen > 0)
+		TP_MSG(NULL, "[read]: %*phD, len: %zu\n",
+			(int)rlen, data, rlen);
+
+	return size;
+}
+
+static struct PROC_FOPS_T ilitek_fops_console = {
+	.PROC_WRITE = ilitek_console_write,
+};
+
+static ssize_t ilitek_func_mode_write(struct file *fp, const char *buf,
+				      size_t size, loff_t *off)
+{
+	int error, cnt;
+	char str[64];
+	u8 func_mode;
+
+	cnt = MIN(size, sizeof(str));
+	memset(str, 0, sizeof(str));
+	if (copy_from_user(str, buf, cnt))
+		return -EFAULT;
+
+	sscanf(str, "%hhu", &func_mode);
+	TP_MSG(NULL, "set func mode: %hhu, support max: %hhu modes\n",
+		func_mode, ts->dev->tp_info.support_modes);
+
+	if (func_mode >= ts->dev->tp_info.support_modes)
+		return -EINVAL;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	error |= api_set_func_mode(ts->dev, func_mode);
+	error |= api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return (error < 0) ? error : cnt;
+}
+
+static ssize_t ilitek_func_mode_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 i;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_protocol_set_cmd(ts->dev, GET_TP_INFO, NULL);
+	api_get_func_mode(ts->dev);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "function mode: ");
+	for (i = 0; i < ts->dev->tp_info.support_modes; i++) {
+		if (i == ts->dev->func.mode)
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "[%hhu] ", i);
+		else
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "%hhu ", i);
+	}
+	cnt += scnprintf(str + strlen(str), sizeof(str) - cnt, "\n");
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_func_mode = {
+	.PROC_READ = ilitek_func_mode_read,
+	.PROC_WRITE = ilitek_func_mode_write,
+};
+
+static ssize_t ilitek_crypto_key_write(struct file *fp, const char *buf,
+				       size_t size, loff_t *off)
+{
+	u8 crypto[AES_KEY_LEN * 2];
+
+	if (size != sizeof(crypto) ||
+	    copy_from_user(crypto, buf, sizeof(crypto)))
+		return -EFAULT;
+
+	TP_MSG(NULL, "set crypto_key: %*phD\n", AES_KEY_LEN, crypto);
+	TP_MSG(NULL, "set crypto_iv: %*phD\n", AES_KEY_LEN,
+		crypto + AES_KEY_LEN);
+
+	memcpy(crypto_key, crypto, AES_KEY_LEN);
+	memcpy(crypto_iv, crypto + AES_KEY_LEN, AES_KEY_LEN);
+
+	return sizeof(crypto);
+}
+
+static ssize_t ilitek_crypto_key_read(struct file *fp, char __user *buf,
+				      size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "key: %*phD\n",
+			 AES_KEY_LEN, crypto_key);
+	cnt += scnprintf(str + strlen(str), sizeof(str), "iv: %*phD\n",
+			 AES_KEY_LEN, crypto_iv);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_crypto_key = {
+	.PROC_READ = ilitek_crypto_key_read,
+	.PROC_WRITE = ilitek_crypto_key_write,
+};
+
+static ssize_t ilitek_setmode_0_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 0);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_0 = {
+	.PROC_READ = ilitek_setmode_0_read,
+};
+
+static ssize_t ilitek_setmode_1_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 1);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_1 = {
+	.PROC_READ = ilitek_setmode_1_read,
+};
+
+static ssize_t ilitek_setmode_2_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 2);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_2 = {
+	.PROC_READ = ilitek_setmode_2_read,
+};
+
+static ssize_t ilitek_driver_version_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "driver-version-tag: [%*phD]\n",
+			 7, driver_ver);
+}
+
+static DEVICE_ATTR(driver_version, 0664, ilitek_driver_version_show, NULL);
+
+static ssize_t ilitek_eds_check_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	if (ts->esd_check)
+		return scnprintf(buf, PAGE_SIZE, "[enable] disable\n");
+
+	return scnprintf(buf, PAGE_SIZE, "enable [disable]\n");
+}
+
+static ssize_t ilitek_esd_check_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	TP_MSG(NULL, "set esd check: %hhx to %s\n", ts->esd_check, buf);
+
+	if (!strncmp(buf, "enable", 6) && !ts->esd_check) {
+		ilitek_create_esd_check_workqueue();
+		ts->esd_check = true;
+	} else if (!strncmp(buf, "disable", 7) && ts->esd_check) {
+		ilitek_remove_esd_check_workqueue();
+		ts->esd_check = false;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(esd_check, 0664,
+		   ilitek_eds_check_show,
+		   ilitek_esd_check_store);
+
+
+static ssize_t ilitek_low_power_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->low_power_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_low_power_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	int i;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set low power: %hhx to %s\n", ts->low_power_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		ts->low_power_status = modes[i].id;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(low_power, 0664,
+		   ilitek_low_power_show,
+		   ilitek_low_power_store);
+
+static ssize_t ilitek_gesture_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->gesture_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_gesture_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int i;
+	u8 type = ts->gesture_status;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set gesture: %hhx to %s\n", ts->gesture_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		type = modes[i].id;
+	}
+
+	if (!ts->gesture_status && type)
+		ilitek_register_gesture(ts, true);
+	else if (ts->gesture_status && !type)
+		ilitek_register_gesture(ts, false);
+
+	ts->gesture_status = type;
+
+	return size;
+}
+static DEVICE_ATTR(gesture, 0664, ilitek_gesture_show, ilitek_gesture_store);
+
+static ssize_t ilitek_firmware_version_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	int ret;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (ret < 0) {
+		TP_ERR(NULL, "read tp info failed, err: %d\n", ret);
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n", ret);
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "fw-version-tag: [%*phD]\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(firmware_version, 0664, ilitek_firmware_version_show, NULL);
+
+static ssize_t ilitek_module_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_protocol_set_cmd(ts->dev, GET_MCU_VER, NULL);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	if (error < 0)
+		TP_ERR(NULL, "read mcu ver. failed, err: %d\n", error);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+out:
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE, "module-name-tag: [%s]\n",
+			 ts->dev->mcu_info.module_name);
+}
+
+static DEVICE_ATTR(product_id, 0664, ilitek_module_name_show, NULL);
+
+static ssize_t ilitek_update_fw_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0)
+		error = ilitek_upgrade_firmware("ilitek.bin");
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "upgrade failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE,
+			 "upgrade success, current fw version: %*phD\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(update_fw, 0664, ilitek_update_fw_show, NULL);
+
+static struct attribute *ilitek_sysfs_attrs_ctrl[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_gesture.attr,
+	&dev_attr_low_power.attr,
+	&dev_attr_esd_check.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static struct attribute_group ilitek_attribute_group[] = {
+	{ .attrs = ilitek_sysfs_attrs_ctrl },
+};
+
+int ilitek_create_sysfsnode(void)
+{
+	int error;
+
+	error = sysfs_create_group(&ts->device->kobj, ilitek_attribute_group);
+	if (error < 0) {
+		TP_ERR(NULL, "sysfs_create_group failed, err: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+void ilitek_remove_sys_node(void)
+{
+	sysfs_remove_group(&ts->device->kobj, ilitek_attribute_group);
+}
+
+static struct miscdevice ilitek_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ilitek_ctrl",
+	.fops = &ilitek_fops,
+	.mode = 0666,
+};
+
+int ilitek_create_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+	misc_register(&ilitek_misc);
+
+	ilitek_proc = proc_create("ilitek_ctrl", ILITEK_DEVICE_NODE_PERMISSON,
+				  NULL, &ilitek_proc_fops);
+
+	if (!ilitek_proc)
+		TP_ERR(NULL, "proc_create(ilitek_ctrl, ILITEK_DEVICE_NODE_PERMISSON, NULL, &ilitek_fops) fail\n");
+
+	ilitek_proc_entry = proc_mkdir("ilitek", NULL);
+	if (!ilitek_proc_entry) {
+		TP_ERR(NULL, "Error, failed to creat procfs.\n");
+		return -EINVAL;
+	}
+
+	if (!proc_create("driver_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_drv_version))
+		TP_ERR(NULL, "failed to create procfs driver_version.\n");
+
+	if (!proc_create("firmware_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwversion))
+		TP_ERR(NULL, "failed to create procfs firmware_version.\n");
+
+	if (!proc_create("console", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_console))
+		TP_ERR(NULL, "failed to create procfs console.\n");
+
+	if (!proc_create("update_fw", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwupdate))
+		TP_ERR(NULL, "failed to create procfs update_fw.\n");
+
+	if (!proc_create("func_mode", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_func_mode))
+		TP_ERR(NULL, "failed to create procfs func_mode.\n");
+
+	if (!proc_create("crypto_key", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_crypto_key))
+		TP_ERR(NULL, "failed to create procfs crypto_key.\n");
+
+	/*
+	 * below setmode_X is historical setting for some customer need
+	 * to set function mode by cat procfs node.
+	 * please make sure it's not risky to modifiy below.
+	 */
+	if (!proc_create("setmode_0", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_0))
+		TP_ERR(NULL, "failed to create procfs setmode_0.\n");
+	if (!proc_create("setmode_1", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_1))
+		TP_ERR(NULL, "failed to create procfs setmode_1.\n");
+	if (!proc_create("setmode_2", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_2))
+		TP_ERR(NULL, "failed to create procfs setmode_2.\n");
+#endif
+
+	return 0;
+}
+
+int ilitek_remove_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+
+	misc_deregister(&ilitek_misc);
+
+	if (ilitek_proc) {
+		TP_MSG(NULL, "remove procfs ilitek_ctrl.\n");
+		remove_proc_entry("ilitek_ctrl", NULL);
+		ilitek_proc = NULL;
+	}
+
+	if (ilitek_proc_entry) {
+		TP_MSG(NULL, "remove procfs inode\n");
+		remove_proc_entry("driver_version", ilitek_proc_entry);
+		remove_proc_entry("firmware_version", ilitek_proc_entry);
+		remove_proc_entry("console", ilitek_proc_entry);
+		remove_proc_entry("update_fw", ilitek_proc_entry);
+		remove_proc_entry("func_mode", ilitek_proc_entry);
+		remove_proc_entry("crypto_key", ilitek_proc_entry);
+
+		remove_proc_entry("setmode_0", ilitek_proc_entry);
+		remove_proc_entry("setmode_1", ilitek_proc_entry);
+		remove_proc_entry("setmode_2", ilitek_proc_entry);
+
+		remove_proc_entry("ilitek", NULL);
+		ilitek_proc_entry = NULL;
+	}
+#endif
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_ts.h b/drivers/input/touchscreen/ilitek/ilitek_ts.h
new file mode 100644
index 000000000000..9ee0b13e08cd
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_ts.h
@@ -0,0 +1,268 @@
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ *
+ */
+
+#ifndef __ILITEK_TS_H__
+#define __ILITEK_TS_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include <linux/init.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+//#if defined(CONFIG_FB)
+#if 0
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#define ILITEK_BLANK_POWERDOWN			FB_BLANK_POWERDOWN
+#define	ILITEK_BLANK_UNBLANK			FB_BLANK_UNBLANK
+#define	ILITEK_EVENT_BLANK			FB_EVENT_BLANK
+#define	ILITEK_BLANK_NORMAL			FB_BLANK_NORMAL
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#define ILITEK_BL_ADDR				0x41
+#define ILITEK_SENSOR_ID_MASK			0xff
+
+#define ILITEK_PLAT_QCOM			1
+#define ILITEK_PLAT_MTK				2
+#define ILITEK_PLAT_ROCKCHIP			3
+#define ILITEK_PLAT_ALLWIN			4
+#define ILITEK_PLAT_AMLOGIC			5
+#define ILITEK_PLAT				ILITEK_PLAT_ROCKCHIP
+//#define CONFIG_QCOM_DRM
+#if ILITEK_PLAT == ILITEK_PLAT_QCOM
+#ifdef CONFIG_QCOM_DRM
+#include <linux/msm_drm_notify.h>
+#define ILITEK_BLANK_POWERDOWN			MSM_DRM_BLANK_POWERDOWN
+#define ILITEK_BLANK_UNBLANK			MSM_DRM_BLANK_UNBLANK
+#define ILITEK_EVENT_BLANK			MSM_DRM_EVENT_BLANK
+#define ILITEK_BLANK_NORMAL			MSM_DRM_BLANK_UNBLANK
+#endif
+#endif
+
+#define ILITEK_GESTURE_TYPES			\
+	X(Disable, 0, "disable")		\
+	X(Single_Click, 1, "single-click")	\
+	X(Double_Click, 2, "double-click")
+#define ILITEK_GESTURE_DEFAULT			Gesture_Disable
+
+#define X(_type, _id, _str)	Gesture_##_type = _id,
+enum Gesture_Type {
+	ILITEK_GESTURE_TYPES
+};
+#undef X
+
+#define ILITEK_LOW_POWER_TYPES		\
+	X(Sleep,	0, "sleep")	\
+	X(Idle,		1, "idle")	\
+	X(PowerOff,	2, "poweroff")
+#define ILITEK_LOW_POWER_DEFAULT		Low_Power_Sleep
+
+#define X(_type, _id, _str)	Low_Power_##_type = _id,
+enum Low_Power_Type {
+	ILITEK_LOW_POWER_TYPES
+};
+#undef X
+
+#define ILITEK_CHECKSUM_FAILED_RELEASE		true
+
+#define ILITEK_TOUCH_PROTOCOL_B
+//#define ILITEK_REPORT_PRESSURE
+//#define ILITEK_USE_LCM_RESOLUTION
+//#define ILITEK_ISR_PROTECT
+
+#define ILITEK_TUNING_MESSAGE
+#define ILITEK_REGISTER_SUSPEND_RESUME
+#define ILITEK_ESD_CHECK_ENABLE			0
+
+#define ILITEK_TOOL
+
+#define ILITEK_ROTATE_FLAG			0
+#define ILITEK_REVERT_X				0
+#define ILITEK_REVERT_Y				0
+#define TOUCH_SCREEN_X_MAX			1080	//LCD_WIDTH
+#define TOUCH_SCREEN_Y_MAX			1920	//LCD_HEIGHT
+#define ILITEK_RESOLUTION_MAX			16384
+//#define ILITEK_ENABLE_REGULATOR_POWER_ON
+#define ILITEK_GET_GPIO_NUM
+
+#define ILITEK_GET_TIME_FUNC_WITH_TIME		0
+#define ILITEK_GET_TIME_FUNC_WITH_JIFFIES	1
+#define ILITEK_GET_TIME_FUNC			ILITEK_GET_TIME_FUNC_WITH_JIFFIES
+
+#define DOUBLE_CLICK_DISTANCE			1000
+#define DOUBLE_CLICK_ONE_CLICK_USED_TIME	800
+#define DOUBLE_CLICK_NO_TOUCH_TIME		1000
+#define DOUBLE_CLICK_TOTAL_USED_TIME		(DOUBLE_CLICK_NO_TOUCH_TIME + (DOUBLE_CLICK_ONE_CLICK_USED_TIME * 2))
+
+//#define ILITEK_WAKELOCK_SUPPORT
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+#include <linux/wakelock.h>
+#endif
+
+#define ILITEK_TS_NAME				"ilitek_ts"
+
+#define ABSSUB(a, b) ((a > b) ? (a - b) : (b - a))
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+//#define MTK_UNDTS //no use dts and for mtk old version
+
+#define ILITEK_ENABLE_DMA
+#define ILITEK_DMA_SIZE				4096
+#define ILITEK_USE_MTK_INPUT_DEV
+
+#if define(ILITEK_USE_MTK_INPUT_DEV) && !defined(ILITEK_USE_LCM_RESOLUTION)
+#define ILITEK_USE_LCM_RESOLUTION
+#endif
+
+#ifdef ILITEK_GET_GPIO_NUM
+#undef ILITEK_GET_GPIO_NUM
+#endif
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include <linux/namei.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#ifdef MTK_UNDTS
+
+#define TPD_KEY_COUNT   4
+#define key_1           {60, 17000}	//auto define
+#define key_2           {180, 17000}
+#define key_3           {300, 17000}
+#define key_4           {420, 17000}
+
+#define TPD_KEYS        {KEY_MENU, KEY_HOMEPAGE, KEY_BACK, KEY_SEARCH}	//change for you panel key info
+#define TPD_KEYS_DIM    {{key_1, 50, 30 }, {key_2, 50, 30 }, {key_3, 50, 30 }, {key_4, 50, 30 } }
+
+struct touch_vitual_key_map_t {
+	int point_x;
+	int point_y;
+};
+
+extern struct touch_vitual_key_map_t touch_key_point_maping_array[];
+
+#include <mach/mt_pm_ldo.h>
+#include <cust_eint.h>
+#include "cust_gpio_usage.h"
+#include <mach/mt_gpio.h>
+#include <mach/mt_typedefs.h>
+#include <pmic_drv.h>
+#include <mach/mt_boot.h>
+#include <linux/dma-mapping.h>
+
+#else
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_MTK_BOOT
+#include "mt_boot_common.h"
+#endif
+
+#endif /* MTK_UNDTS */
+
+#include "tpd.h"
+extern struct tpd_device *tpd;
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#include <linux/irq.h>
+#include <linux/init-input.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+extern struct ctp_config_info config_info;
+#endif
+
+
+#ifndef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef MTK_UNDTS
+#define ILITEK_IRQ_GPIO				GPIO_CTP_EINT_PIN
+#define ILITEK_RESET_GPIO			GPIO_CTP_RST_PIN
+#else
+#define ILITEK_IRQ_GPIO				GTP_INT_PORT
+#define ILITEK_RESET_GPIO			GTP_RST_PORT
+#endif
+#elif ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#define ILITEK_IRQ_GPIO				config_info.int_number
+#define ILITEK_RESET_GPIO			config_info.wakeup_gpio.gpio
+#else
+#define ILITEK_IRQ_GPIO				9
+#define ILITEK_RESET_GPIO			10
+#endif
+#endif
+
+#define ILITEK_I2C_RETRY_COUNT			3
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+extern int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg);
+extern int ilitek_resume_allwin(struct i2c_client *client);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.c b/drivers/input/touchscreen/ilitek/ilitek_update.c
new file mode 100644
index 000000000000..d7e873a3c204
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_update.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifndef __KERNEL__
+static int hex_to_bin(u8 ch)
+{
+	u8 cu = ch & 0xdf;
+	return -1 +
+		((ch - '0' +  1) & (unsigned)((ch - '9' - 1) &
+		('0' - 1 - ch)) >> 8) +
+		((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) &
+		('A' - 1 - cu)) >> 8);
+}
+
+static int hex2bin(u8 *dst, const u8 *src, size_t count)
+{
+	int hi = 0, lo = 0;
+
+	while (count--) {
+		hi = hex_to_bin(*src++);
+		lo = hex_to_bin(*src++);
+		if (hi < 0 || lo < 0) {
+			TP_ERR(NULL, "hex_to_bin failed, hi: %d, lo: %d\n",
+				hi, lo);
+			return -EINVAL;
+		}
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
+#endif
+
+static u32 get_tag_addr(u32 start, u32 end,
+			const u8 *buf, unsigned int buf_size,
+			const u8 *tag, unsigned int tag_size)
+{
+	unsigned int i;
+
+	for (i = start; i <= end - tag_size && i < buf_size - tag_size; i++) {
+		if (!memcmp(buf + i, tag, tag_size))
+			return i + tag_size + 1;
+	}
+
+	return end;
+}
+
+static u32 get_endaddr(u32 start, u32 end, const u8 *buf,
+		       unsigned int buf_size, bool is_AP)
+{
+	u32 addr;
+	u8 tag[32];
+	const u8 ap_tag[] = "ILITek AP CRC   ";
+	const u8 blk_tag[] = "ILITek END TAG  ";
+
+	_memset(tag, 0xFF, sizeof(tag));
+	_memcpy(tag + 16, (is_AP) ? ap_tag : blk_tag, 16);
+
+	addr = get_tag_addr(start, end, buf, buf_size, tag, sizeof(tag));
+	TP_DBG(NULL, "find tag in start/end: 0x%x/0x%x, tag addr: 0x%x\n",
+		start, end, addr);
+
+	return addr;
+}
+
+static int decode_mm(struct ilitek_fw_handle *fw, u32 addr,
+		     u8 *buf, u32 buf_size)
+{
+	u8 i;
+	union mapping_info *mapping;
+
+	TP_INFO(NULL, "------------Memory Mapping information------------\n");
+	TP_INFO(NULL, "memory-mapping-info addr: 0x%x\n", addr);
+
+	mapping = (union mapping_info *)(buf + addr);
+	_memset(fw->file.ic_name, 0, sizeof(fw->file.ic_name));
+
+	switch (mapping->mapping_ver[2]) {
+	case 0x2:
+		_memcpy(fw->file.ic_name, mapping->ic_name,
+			sizeof(mapping->ic_name));
+		break;
+	default:
+	case 0x1:
+		_sprintf(fw->file.ic_name, 0, "%02x%02x",
+			mapping->ic_name[1], mapping->ic_name[0]);
+		break;
+	}
+
+	if (!strcmp(fw->file.ic_name, "2133"))
+		_sprintf(fw->file.ic_name, 0, "2132S");
+
+	if (fw->dev && strcmp(fw->dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " not matched\n",
+			fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EINVAL;
+	}
+
+	TP_MSG(NULL, "Hex Mapping Ver.: 0x%x\n",
+		le32(mapping->mapping_ver, 3));
+	TP_MSG(NULL, "Hex Protocol: 0x%x\n",
+		le32(mapping->protocol_ver, 3));
+	TP_MSG(NULL, "Hex MCU Ver.: " PFMT_C8 "\n", fw->file.ic_name);
+
+	_memset(fw->file.fw_ver, 0, sizeof(fw->file.fw_ver));
+	fw->file.fwid = 0xffff;
+
+	fw->file.mm_addr = addr;
+	switch (addr) {
+	case 0x4038:
+	case 0x4020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x2C007];
+		fw->file.fw_ver[5] = buf[0x2C006];
+		fw->file.fw_ver[6] = buf[0x2C005];
+		fw->file.fw_ver[7] = buf[0x2C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x3020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x3C007];
+		fw->file.fw_ver[5] = buf[0x3C006];
+		fw->file.fw_ver[6] = buf[0x3C005];
+		fw->file.fw_ver[7] = buf[0x3C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x2020:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x2033];
+		fw->file.fw_ver[1] = buf[0x2032];
+		fw->file.fw_ver[2] = buf[0x2031];
+		fw->file.fw_ver[3] = buf[0x2030];
+		fw->file.fw_ver[4] = buf[0xF004];
+		fw->file.fw_ver[5] = buf[0xF005];
+		fw->file.fw_ver[6] = buf[0xF006];
+		fw->file.fw_ver[7] = buf[0xF007];
+
+		/* for V3 251x IC, get AP crc and DF checksum */
+		fw->file.blocks[0].check = get_crc(fw->file.blocks[0].start,
+			fw->file.blocks[0].end - 1, buf, buf_size);
+
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+
+	case 0x500:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x52D];
+		fw->file.fw_ver[1] = buf[0x52C];
+		fw->file.fw_ver[2] = buf[0x52B];
+		fw->file.fw_ver[3] = buf[0x52A];
+		fw->file.fw_ver[4] = buf[0x1F404];
+		fw->file.fw_ver[5] = buf[0x1F405];
+		fw->file.fw_ver[6] = buf[0x1F406];
+		fw->file.fw_ver[7] = buf[0x1F407];
+
+		/* for V3 231x IC, get AP checksum and DF checksum */
+		fw->file.blocks[0].check = get_checksum(
+			fw->file.blocks[0].start,
+			fw->file.blocks[0].end + 1, buf, buf_size);
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+	default:
+		fw->file.mm_size = 0;
+		break;
+	}
+
+	TP_MSG(NULL, "file fwid: 0x%04x\n", fw->file.fwid);
+
+	TP_INFO(NULL, "File FW Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[0], fw->file.fw_ver[1],
+		fw->file.fw_ver[2], fw->file.fw_ver[3]);
+	TP_INFO(NULL, "File Customer Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[4], fw->file.fw_ver[5],
+		fw->file.fw_ver[6], fw->file.fw_ver[7]);
+
+	if (le32(mapping->mapping_ver, 3) < 0x10000)
+		goto memory_mapping_end;
+
+	TP_INFO(NULL, "File Tuning Version: %02x-%02x-%02x-%02x\n",
+		mapping->_lego.tuning_ver[3], mapping->_lego.tuning_ver[2],
+		mapping->_lego.tuning_ver[1], mapping->_lego.tuning_ver[0]);
+
+	if (mapping->_lego.block_num > ARRAY_SIZE(fw->file.blocks)) {
+		TP_ERR(NULL, "Unexpected block num: " PFMT_U8 " > %u\n",
+			mapping->_lego.block_num,
+			(unsigned int)ARRAY_SIZE(fw->file.blocks));
+		goto memory_mapping_end;
+	}
+
+	fw->file.block_num = mapping->_lego.block_num;
+
+	TP_MSG(NULL, "Total " PFMT_U8 " blocks\n", fw->file.block_num);
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].start =
+			le32(mapping->_lego.blocks[i].addr, 3);
+		fw->file.blocks[i].end = (i == fw->file.block_num - 1) ?
+			le32(mapping->_lego.end_addr, 3) :
+			le32(mapping->_lego.blocks[i + 1].addr, 3);
+
+		/*
+		 * get end addr. of block,
+		 * i.e. address of block's final byte of crc.
+		 */
+		fw->file.blocks[i].end = get_endaddr(
+			fw->file.blocks[i].start, fw->file.blocks[i].end,
+			buf, buf_size, i == 0);
+
+		fw->file.blocks[i].check = get_crc(fw->file.blocks[i].start,
+			fw->file.blocks[i].end - 1,
+			buf, buf_size);
+
+		TP_MSG(NULL, "Block[%u], start:0x%x end:0x%x, crc:0x%x\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			fw->file.blocks[i].check);
+	}
+
+memory_mapping_end:
+	TP_INFO(NULL, "--------------------------------------------------\n");
+
+	return 0;
+}
+
+static int decode_hex(struct ilitek_fw_handle *fw, u8 *hex,
+		      u32 start, u32 end,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	u8 info[4], data[16];
+	unsigned int i, len, addr, type, exaddr = 0;
+	u32 mapping_info_addr = 0;
+
+	/* m2v hex has another block at the end of hex file */
+	u8 j = (fw->m2v) ? fw->file.block_num : 0;
+
+	fw->file.blocks[j].start = (~0U);
+	fw->file.blocks[j].end = 0x0;
+	fw->file.blocks[j].check = 0x0;
+	fw->file.blocks[j + 1].start = (~0U);
+	fw->file.blocks[j + 1].end = 0x0;
+	fw->file.blocks[j + 1].check = 0x0;
+
+	for (i = start; i < end; i++) {
+		/* filter out non-hexadecimal characters */
+		if (hex_to_bin(hex[i]) < 0)
+			continue;
+
+		error = hex2bin(info, hex + i, sizeof(info));
+		if (error < 0)
+			return error;
+
+		len = info[0];
+		addr = be32(info + 1, 2);
+		type = info[3];
+
+		error = hex2bin(data, hex + i + 8, len);
+		if (error < 0)
+			return error;
+
+		switch (type) {
+		case 0xAC:
+			mapping_info_addr = be32(data, len);
+			break;
+
+		case 0xAD:
+			fw->file.blocks[1].start = be32(data, len);
+			_memset(buf + fw->file.blocks[1].start, 0, 0x1000);
+			break;
+
+		case 0xBA:
+			if (be32(data, len) != 2U)
+				break;
+
+			TP_MSG(NULL, "start to decode M2V part of hex file\n");
+			fw->m2v = true;
+			////Reed Add : 20230721��������2326��������Decode_mm,�ټ�������M2V���֡���
+			if (mapping_info_addr)
+				decode_mm(fw, mapping_info_addr, buf, buf_size);
+			return decode_hex(fw, hex, i + 10 + len * 2 + 1, end,
+					  fw->m2v_buf, ILITEK_FW_BUF_SIZE);
+
+		case 0x01:
+			goto success_return;
+
+		case 0x02:
+			exaddr = be32(data, len) << 4;
+			break;
+
+		case 0x04:
+			exaddr = be32(data, len) << 16;
+			break;
+
+		case 0x05:
+			TP_MSG(NULL, "hex data type: 0x%x, start linear address: 0x%x\n",
+				type, be32(data, len));
+			break;
+
+		case 0x00:
+			addr += exaddr;
+
+			if (addr + len > buf_size) {
+				TP_ERR(NULL, "hex addr: 0x%x, buf size: 0x%x OOB\n",
+					addr + len, buf_size);
+				return -ENOBUFS;
+			}
+			_memcpy(buf + addr, data, len);
+
+			fw->file.blocks[j].start =
+				MIN(fw->file.blocks[j].start, addr);
+
+			if (addr + len < fw->file.blocks[j + 1].start) {
+				fw->file.blocks[j].end =
+					MAX(fw->file.blocks[j].end,
+					    addr + len - 1);
+				fw->file.blocks[j].check += get_checksum(
+					0, len, data, sizeof(data));
+			} else {
+				fw->file.blocks[j + 1].end =
+					MAX(fw->file.blocks[j + 1].end,
+					    addr + len - 1);
+				fw->file.blocks[j + 1].check += get_checksum(
+					0, len, data, sizeof(data));
+			}
+
+			break;
+		default:
+			TP_ERR(NULL, "unexpected type:0x%x in hex, len:%u, addr:0x%x\n",
+				type, len, addr);
+			return -EINVAL;
+		}
+
+		i = i + 10 + len * 2;
+	}
+
+success_return:
+	if (fw->m2v)
+		fw->m2v_checksum = fw->file.blocks[fw->file.block_num].check;
+	if (mapping_info_addr)
+		return decode_mm(fw, mapping_info_addr, fw->file.buf, buf_size);
+
+	return 0;
+}
+
+static int decode_bin(struct ilitek_fw_handle *fw,
+		      u8 *bin, u32 bin_size,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 mapping_info_addr;
+
+	if (!dev) {
+		TP_ERR(NULL, "offline decode bin file is not supported\n");
+		return -EINVAL;
+	}
+
+	if (bin_size > buf_size) {
+		TP_ERR(dev->id, "bin file size: 0x%x, buf size: 0x%x OOB\n",
+			bin_size, buf_size);
+		return -ENOBUFS;
+	}
+	_memcpy(buf, bin, bin_size);
+
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		return error;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		return error;
+
+	switch (dev->protocol.flag) {
+	case PTL_V6:
+		mapping_info_addr = dev->mcu_info.mm_addr;
+		break;
+
+	case PTL_V3:
+		/*
+		 * For 231x: AP checksum and DF checksum, DF start addr: 0x1f000
+		 * For 251x: AP crc and DF checksum, DF start addr: 0xf000
+		 */
+		if (is_231x(dev)) {
+			mapping_info_addr = 0x500;
+
+			fw->file.blocks[1].start = 0x1f000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x0;
+
+			/* +2 to get end addr. of last byte of 4 bytes checksum */
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true) + 2;
+
+		} else {
+			mapping_info_addr = 0x2020;
+			fw->file.blocks[1].start = 0xf000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x2000;
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true);
+		}
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * take the whole "buf" into decode_mm, "buf" should be
+	 * properly initialized, and the size should be
+	 * larger than "bin", which reduce OOB issue.
+	 */
+	return decode_mm(fw, mapping_info_addr, buf, buf_size);
+}
+
+#ifdef ILITEK_BOOT_UPDATE
+#include "ilitek_fw.h"
+
+static int decode_ili(struct ilitek_fw_handle *fw,
+		      u8 *buf, u32 buf_size)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 *ili_buf = CTPM_FW;
+	int size = sizeof(CTPM_FW);
+	u8 id;
+
+#if defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		if (!support_sensor_id(dev)) {
+			TP_WARN(dev->id,
+				"protocol: 0x%x, mode: 0x" PFMT_X8 ", sensor-id not supported, take default fw(id: 0x%x)\n",
+				dev->protocol.ver, dev->ic[0].mode,
+				ILITEK_BOOT_UPDATE_DEF_ID);
+			fw->file.id = ILITEK_BOOT_UPDATE_DEF_ID;
+			break;
+		}
+
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id >= ARRAY_SIZE(ili_arr)) {
+			TP_ERR(dev->id, "invalid sensor id: " PFMT_U8 " >= %d\n",
+				dev->sensor.id, (int)ARRAY_SIZE(ili_arr));
+			return -EINVAL;
+		}
+
+		fw->file.id = id;
+		ili_buf = ili_arr[id].buf;
+		size = ili_arr[id].size;
+		break;
+	case ili_default:
+		break;
+	default:
+		TP_ERR(dev->id, "unexpected ili-select-type: %u\n",
+			__ili_select_type__);
+		return -EINVAL;
+	}
+#endif
+
+	if (!ili_buf || size < 32)
+		return -EINVAL;
+
+	fw->setting.fw_ver_check = true;
+	fw->setting.fw_ver_policy = 0;
+	_memcpy(fw->setting.fw_ver, ili_buf + 18, 8);
+
+	TP_MSG_ARR(dev->id, "IC  fw ver:", TYPE_U8, 8, dev->fw_ver);
+	TP_MSG_ARR(dev->id, "Hex fw ver:", TYPE_U8, 8, fw->setting.fw_ver);
+
+	return decode_bin(fw, ili_buf + 32, size - 32, buf, buf_size);
+}
+#endif
+
+static bool need_retry(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 id;
+
+#if defined(ILITEK_BOOT_UPDATE) && defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	if (fw->file.type != fw_ili)
+		return false;
+
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id == fw->file.id)
+			break;
+
+		/* reload correct fw if sensor-id not matched with default fw */
+		TP_MSG(dev->id, "sensor id: 0x%x, file: 0x%x not matched, "
+			"reload fw again\n",
+			id, fw->file.id);
+
+		if (ilitek_update_load_fw(fw, "ilitek.ili") < 0) {
+			TP_ERR(dev->id, "reload ilitek.ili failed\n");
+			return false;
+		}
+
+		/* MUST DISABLE fw ver check for the second try */
+		fw->setting.fw_ver_check = false;
+
+		return true;
+	default:
+		break;
+	}
+#endif
+
+	UNUSED(dev);
+	UNUSED(id);
+
+	return false;
+}
+
+static int decode_firmware(struct ilitek_fw_handle *fw, WCHAR *filename)
+{
+	int error;
+	int size = 0;
+	u8 *buf;
+	WCHAR *file_ext;
+
+	/* initialization */
+	_memset(fw->file.buf, 0xFF, fw->file.buf_size);
+	_memset(fw->m2v_buf, 0xFF, ILITEK_FW_BUF_SIZE);
+	fw->m2v = false;
+	/*
+	 * set block num 2 for V3 AP and Data Flash as default,
+	 * for V6, block num would be updated after decoding memory mapping.
+	 */
+	fw->file.block_num = 2;
+	fw->file.blocks[0].start = (~0U);
+	fw->file.blocks[0].end = 0x0;
+	fw->file.blocks[0].check = 0x0;
+	fw->file.blocks[1].start = (~0U);
+	fw->file.blocks[1].end = 0x0;
+	fw->file.blocks[1].check = 0x0;
+
+	TP_MSG(NULL, "start to read fw file: " PFMT_C16 "\n", filename);
+
+	buf = (u8 *)CALLOC(ILITEK_FW_FILE_SIZE, 1);
+	if (!buf)
+		return -ENOMEM;
+
+	file_ext = WCSRCHR(filename, '.');
+	if (!file_ext)
+		return -ENOENT;
+
+	/* no need to read .ili file */
+	if (WCSCASECMP(file_ext, ".ili")) {
+		if (!fw->cb.read_fw) {
+			error = -EFAULT;
+			TP_ERR(NULL, "read fw callback not registered\n");
+			goto err_free;
+		}
+
+		size = fw->cb.read_fw(filename, buf, ILITEK_FW_FILE_SIZE,
+				      fw->_private);
+
+		error = size;
+		if (error < 0) {
+			TP_ERR(NULL, "read fw file: " PFMT_C16 " failed, err: %d\n",
+				filename, error);
+			goto err_free;
+		}
+	}
+
+	if (!WCSCASECMP(file_ext, ".hex")) {
+		fw->file.type = fw_hex;
+		error = decode_hex(fw, buf, 0, size, fw->file.buf,
+				   fw->file.buf_size);
+	} else if (!WCSCASECMP(file_ext, ".bin")) {
+		fw->file.type = fw_bin;
+		error = decode_bin(fw, buf, size, fw->file.buf,
+				   fw->file.buf_size);
+	}
+#ifdef ILITEK_BOOT_UPDATE
+	else if (!WCSCASECMP(file_ext, ".ili")) {
+		fw->file.type = fw_ili;
+		error = decode_ili(fw, fw->file.buf, fw->file.buf_size);
+	}
+#endif
+	else {
+		error = -EINVAL;
+	}
+
+err_free:
+	CFREE(buf);
+
+	return error;
+}
+
+static bool need_fw_update_v3(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool fw_file_has_data_flash =
+		(fw->file.blocks[1].start < fw->file.blocks[1].end);
+	bool need = false;
+
+	TP_INFO(dev->id, "------------V3 AP/DF Info.------------\n");
+
+	fw->file.blocks[0].check_match = (fw->setting.force_update) ?
+		false : dev->ic[0].crc[0] == fw->file.blocks[0].check;
+
+	TP_INFO(dev->id, "AP block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+		fw->file.blocks[0].start, fw->file.blocks[0].end,
+		dev->ic[0].crc[0], fw->file.blocks[0].check,
+		(fw->file.blocks[0].check_match) ? "matched" : "not matched");
+
+	fw->file.blocks[1].check_match = true;
+	if (fw_file_has_data_flash) {
+		fw->file.blocks[1].check_match = (fw->setting.force_update) ?
+			false : dev->ic[0].crc[1] == fw->file.blocks[1].check;
+
+		TP_INFO(dev->id, "DF block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(fw->file.blocks[1].check_match) ?
+			"matched" : "not matched");
+	} else if (!is_231x(dev)) {
+		/*
+		 * for 251x ICs, if no data flash in fw file,
+		 * need to switch to BL mode then erase data flash forcely.
+		 */
+		need = true;
+	}
+
+	TP_INFO(dev->id, "--------------------------------------\n");
+
+	need |= (!fw->file.blocks[0].check_match ||
+		 !fw->file.blocks[1].check_match);
+
+	if (dev->ic[0].mode == BL_MODE)
+		return true;
+
+	return need;
+}
+
+static bool need_fw_update_v6(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	bool need = false;
+
+	TP_INFO(dev->id, "------------Lego Block Info.------------\n");
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].check_match = (fw->setting.force_update) ?
+			false : (dev->ic[0].crc[i] == fw->file.blocks[i].check);
+
+		need = (!fw->file.blocks[i].check_match) ? true : need;
+
+		TP_INFO(dev->id, "Block[" PFMT_U8 "]: Start/End Addr.: 0x%x/0x%x, IC/File CRC: 0x%x/0x%x " PFMT_C8 "\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			dev->ic[0].crc[i], fw->file.blocks[i].check,
+			(fw->file.blocks[i].check_match) ?
+			"matched" : "not matched");
+	}
+
+	/* check BL mode firstly before AP-cmd related varaible, ex: ic_num */
+	if (dev->ic[0].mode == BL_MODE) {
+		need = true;
+		goto force_return;
+	}
+
+	for (i = 1; i < dev->tp_info.ic_num; i++) {
+		TP_INFO(dev->id, "Master/Slave[" PFMT_U8 "] CRC: 0x%x/0x%x " PFMT_C8 ", Slave Mode: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, fw->file.blocks[0].check, dev->ic[i].crc[0],
+			(fw->file.blocks[0].check == dev->ic[i].crc[0]) ?
+			"matched" : "not matched",
+			dev->ic[i].mode, dev->ic[i].mode_str);
+
+		if (dev->ic[i].crc[0] == fw->file.blocks[0].check &&
+			dev->ic[i].mode == AP_MODE)
+			continue;
+		need = true;
+	}
+
+	if (fw->m2v) {
+		api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &fw->m2v_checksum);
+
+		fw->file.blocks[fw->file.block_num].check_match =
+			fw->file.blocks[fw->file.block_num].check ==
+			fw->m2v_checksum;
+		TP_INFO(dev->id, "M2V IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->m2v_checksum,
+			fw->file.blocks[fw->file.block_num].check,
+			(fw->file.blocks[fw->file.block_num].check_match) ?
+			"matched" : "not matched");
+
+		fw->m2v_need_update =
+			!fw->file.blocks[fw->file.block_num].check_match ||
+			fw->setting.force_update;
+		need |= fw->m2v_need_update;
+	}
+
+force_return:
+	TP_INFO(dev->id, "----------------------------------------\n");
+
+	return need;
+}
+
+static bool need_fw_update(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool need = false;
+
+	struct ilitek_fw_settings *set = &fw->setting;
+	u64 dev_fw_ver, file_fw_ver;
+
+	if (dev->protocol.flag == PTL_V3)
+		need = need_fw_update_v3(fw);
+	else if (dev->protocol.flag == PTL_V6)
+		need = need_fw_update_v6(fw);
+
+	if (fw->cb.update_fw_ic_info)
+		fw->cb.update_fw_ic_info(false,
+					 dev->fw_ver, dev->ic[0].crc,
+					 fw->file.block_num, fw->_private);
+
+	if (set->force_update)
+		return true;
+	else if (set->fw_check_only)
+		return false;
+
+	if (set->fw_ver_check && dev->ic[0].mode == AP_MODE) {
+		TP_INFO(dev->id, "IC FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			dev->fw_ver[0], dev->fw_ver[1], dev->fw_ver[2],
+			dev->fw_ver[3], dev->fw_ver[4], dev->fw_ver[5],
+			dev->fw_ver[6], dev->fw_ver[7]);
+		TP_INFO(dev->id, "File FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			set->fw_ver[0], set->fw_ver[1],
+			set->fw_ver[2], set->fw_ver[3],
+			set->fw_ver[4], set->fw_ver[5],
+			set->fw_ver[6], set->fw_ver[7]);
+
+		dev_fw_ver =
+			U82U64(dev->fw_ver[0], 7) + U82U64(dev->fw_ver[1], 6) +
+			U82U64(dev->fw_ver[2], 5) + U82U64(dev->fw_ver[3], 4) +
+			U82U64(dev->fw_ver[4], 3) + U82U64(dev->fw_ver[5], 2) +
+			U82U64(dev->fw_ver[6], 1) + U82U64(dev->fw_ver[7], 0);
+		file_fw_ver =
+			U82U64(set->fw_ver[0], 7) + U82U64(set->fw_ver[1], 6) +
+			U82U64(set->fw_ver[2], 5) + U82U64(set->fw_ver[3], 4) +
+			U82U64(set->fw_ver[4], 3) + U82U64(set->fw_ver[5], 2) +
+			U82U64(set->fw_ver[6], 1) + U82U64(set->fw_ver[7], 0);
+
+		TP_MSG(dev->id, "IC fw ver: 0x" PFMT_X64 ", File fw ver: 0x" PFMT_X64 "\n",
+			(long long unsigned int)dev_fw_ver,
+			(long long unsigned int)file_fw_ver);
+
+		if (file_fw_ver > dev_fw_ver) {
+			TP_INFO(dev->id, "IC FW version is older than File FW version\n");
+			return true;
+		} else if (file_fw_ver == dev_fw_ver) {
+			TP_INFO(dev->id, "File FW version is the same, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_same) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_same) ?
+				need : false;
+		} else {
+			TP_INFO(dev->id, "File FW version is older, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				need : false;
+		}
+	}
+
+	return need;
+}
+
+static int update_master(struct ilitek_fw_handle *fw, int idx, u32 len)
+{
+	int error = 0;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+	u16 file_crc;
+	int retry = 3;
+
+	TP_MSG(dev->id, "updating block[%d], data len: %u, start/end addr: 0x%x/0x%x\n",
+		idx, len, fw->file.blocks[idx].start, fw->file.blocks[idx].end);
+
+err_retry:
+	if ((dev->setting.no_retry && error < 0) || retry-- < 0)
+		return (error < 0) ? error : -EINVAL;
+
+	error = api_write_enable_v6(dev, false, false,
+				     fw->file.blocks[idx].start,
+				     fw->file.blocks[idx].end);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[idx].start;
+	     i < fw->file.blocks[idx].end; i += len) {
+		/*
+		 * check end addr. of data write buffer is within valid range.
+		 */
+		if (i + len > END_ADDR_LEGO) {
+			TP_ERR(dev->id, "block[%d] write addr. 0x%x + 0x%x > 0x%x OOB\n",
+				idx, i, len, END_ADDR_LEGO);
+			return -EINVAL;
+		}
+
+		_memcpy(dev->wbuf + 1, fw->file.buf + i, len);
+		error = api_write_data_v6(dev, len + 1);
+
+		if (error < 0)
+			goto err_retry;
+
+		fw->progress_curr = MIN(i + len - fw->file.blocks[idx].offset,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "block[%d] update progress: " PFMT_U8 "%%\n",
+			idx, fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	file_crc = get_crc(fw->file.blocks[idx].start,
+			   fw->file.blocks[idx].end - 1,
+			   fw->file.buf, fw->file.buf_size);
+	dev->ic[0].crc[idx] =
+		api_get_block_crc_by_addr(dev, CRC_GET,
+					  fw->file.blocks[idx].start,
+					  fw->file.blocks[idx].end);
+
+	TP_INFO(dev->id, "block[%d]: start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+		idx, fw->file.blocks[idx].start, fw->file.blocks[idx].end,
+		dev->ic[0].crc[idx], file_crc,
+		(file_crc == dev->ic[0].crc[idx]) ?
+		"matched" : "not matched");
+
+	if (file_crc != dev->ic[0].crc[idx]) {
+		error = -EINVAL;
+		goto err_retry;
+	}
+
+	return 0;
+}
+
+static int update_slave(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[0].crc[0] == dev->ic[i].crc[0] &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "updating slave, master/slave[" PFMT_U8 "] crc: 0x%x/0x%x\n",
+			i, dev->ic[0].crc[0], dev->ic[i].crc[0]);
+		error = api_access_slave(dev, 0x3, CMD_WRITE_DATA_V6, NULL);
+		if (error < 0)
+			return error;
+		error = api_write_enable_v6(dev, false, true,
+					    fw->file.blocks[0].start,
+					    fw->file.blocks[0].end);
+		if (error < 0)
+			return error;
+
+		goto success_return;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[i].mode == AP_MODE &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "changing slave[" PFMT_U8 "]: 0x" PFMT_X8 " to AP mode\n",
+			i, dev->ic[i].mode);
+		error = api_access_slave(dev, 0x3, CMD_SET_AP_MODE, NULL);
+		if (error < 0)
+			return error;
+		break;
+	}
+
+success_return:
+	return 0;
+}
+
+static int update_M3_M2V(struct ilitek_fw_handle *fw, u32 len)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 i;
+	u8 buf[6];
+	u32 AdressRange, M2V_Buf_Len, CheckSumRange;
+	u16 AddDataCount;
+
+	u8 j = fw->file.block_num;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	//Reed Add : 20230721
+	AdressRange = fw->file.blocks[j].end - fw->file.blocks[j].start;
+	M2V_Buf_Len = fw->file.blocks[j].end - fw->file.blocks[j].start + 1;
+	CheckSumRange = fw->file.blocks[j].check;
+	AddDataCount = len - M2V_Buf_Len % len;
+	AddDataCount = (AddDataCount >= 8) ? AddDataCount : AddDataCount + len;
+	AdressRange += AddDataCount;
+	CheckSumRange += AddDataCount * 0xFF;
+
+	error = api_to_bl_mode_m2v(dev, true);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_set_data_len(dev, len);
+	if (error < 0)
+		return error;
+
+	TP_INFO(dev->id, "updating M2V, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+		fw->file.blocks[j].start, fw->file.blocks[j].end,
+		fw->file.blocks[j].check);
+
+	//Reed Add : 20230721
+	buf[0] = (AdressRange >> 16) & 0xFF;
+	buf[1] = (AdressRange >> 8) & 0xFF;
+	buf[2] = AdressRange & 0xFF;
+	buf[3] = (CheckSumRange >> 16) & 0xFF;
+	buf[4] = (CheckSumRange >> 8) & 0xFF;
+	buf[5] = CheckSumRange & 0xFF;
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+	error = api_access_slave(dev, 0x80, CMD_WRITE_ENABLE, buf);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[j].start;
+	     i < fw->file.blocks[j].end; i += len) {
+		_memcpy(dev->wbuf + 1, fw->m2v_buf + i, len);
+
+		error = api_write_data_m2v(dev, len + 1);
+		if (error < 0)
+			return error;
+
+		fw->progress_curr = MIN(fw->progress_curr + len,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "m2v update progress: " PFMT_U8 "%%\n", fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	error = api_to_bl_mode_m2v(dev, false);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_access_slave(dev, 0x80, CMD_GET_FW_VER, fw->m2v_fw_ver);
+	if (error < 0)
+		return error;
+
+	TP_MSG_ARR(dev->id, "update M2V success, fw version:", TYPE_U8,
+		   8, fw->m2v_fw_ver);
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_8(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	error = api_set_data_len(dev, fw->update_len);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		if (fw->file.blocks[i].check_match)
+			continue;
+
+		error = update_master(fw, i, fw->update_len);
+		if (error < 0) {
+			TP_ERR(dev->id, "Upgrade Block:" PFMT_U8 " failed, err: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	error = api_to_bl_mode(dev, false, fw->file.blocks[0].start,
+			       fw->file.blocks[0].end);
+	if (error < 0)
+		return error;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	/* get tp info. for updating ic_num */
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.ic_num > 1) {
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(true, fw->_private);
+		error = update_slave(fw);
+
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(false, fw->_private);
+
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade slave failed, err: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	if (fw->m2v && fw->m2v_need_update) {
+		error = update_M3_M2V(fw, 1024);
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade m2v slave failed, err: %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_7(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+
+	/*
+	 * Erase data initially for the case that hex w/o data flash section.
+	 * Due to historical factor, please double confirm before modify here.
+	 */
+	error = api_erase_data_v3(dev);
+	if (error < 0)
+		return error;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false,
+					     fw->file.blocks[1].end + 1,
+					     fw->file.blocks[1].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start;
+		     i <= fw->file.blocks[1].end; i += 32) {
+			_memset(dev->wbuf + 1, 0, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->cb.delay_ms(50);
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[1] = (le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+
+		TP_INFO(dev->id, "DF block, start/end addr.: 0x%x/0x%x, ic/file checkksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(dev->ic[0].crc[1] == fw->file.blocks[1].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[1] != fw->file.blocks[1].check)
+			return -EFAULT;
+	}
+
+	/*
+	 * Update AP code forcely if AP crc/checksum not match or
+	 * Data Flash has been updated.
+	 */
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true,
+					     fw->file.blocks[0].end + 1,
+					     fw->file.blocks[0].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start;
+		     i <= fw->file.blocks[0].end; i += 32) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[0] =
+			(le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+		TP_INFO(dev->id, "AP block, start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			dev->ic[0].crc[0], fw->file.blocks[0].check,
+			(dev->ic[0].crc[0] == fw->file.blocks[0].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[0] != fw->file.blocks[0].check)
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_6(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	struct ilitek_ts_callback *cb = &dev->cb;
+	unsigned int i, j;
+
+	u32 bytes, end, check;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[1].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[1].end + 1) % 32) : 31;
+		end = fw->file.blocks[1].end + bytes;
+		check = fw->file.blocks[1].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) DF start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[1].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start, j = 0;
+		     i <= fw->file.blocks[1].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+	}
+
+	/*
+	* Update AP code forcely if AP crc/checksum not match or
+	* Data Flash has been updated.
+	*/
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[0].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[0].end + 1) % 32) : 31;
+		end = fw->file.blocks[0].end + bytes;
+		check = fw->file.blocks[0].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) AP start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[0].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start, j = 0;
+			i <= fw->file.blocks[0].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[0].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+
+#ifdef ILITEK_BOOT_UPDATE
+		/*
+		 * .ili file fill DF section with default 0xFF, but not 0
+		 * which make flow stuck in BL mode.
+		 * so HW-reset is need before switching to AP mode.
+		 * remove HW-reset after fixing .ili converter bug.
+		 */
+		reset_helper(dev);
+#endif
+	}
+
+	return 0;
+}
+
+static void update_progress(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	unsigned int last_end = 0, last_offset = 0;
+
+	fw->progress = 0;
+	fw->progress_max = 0;
+	fw->progress_curr = 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		fw->progress_max +=
+			(fw->file.blocks[1].end > fw->file.blocks[1].start) ?
+			fw->file.blocks[1].end - fw->file.blocks[1].start : 0;
+		fw->progress_max +=
+			(fw->file.blocks[0].end > fw->file.blocks[0].start) ?
+			fw->file.blocks[0].end - fw->file.blocks[0].start : 0;
+		break;
+
+	case PTL_V6:
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->file.blocks[i].check_match)
+				continue;
+
+			fw->progress_max +=
+				fw->file.blocks[i].end -
+				fw->file.blocks[i].start;
+			last_offset += fw->file.blocks[i].start - last_end;
+			fw->file.blocks[i].offset = last_offset;
+
+			last_end = fw->file.blocks[i].end;
+		}
+
+		if (fw->m2v_need_update) {
+			fw->progress_max +=
+				fw->file.blocks[fw->file.block_num].end -
+				fw->file.blocks[fw->file.block_num].start;
+		}
+
+		break;
+	}
+}
+
+void *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			 struct ilitek_update_callback *cb, void *_private)
+{
+	struct ilitek_fw_handle *fw;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)_dev;
+
+	if (need_update_ts_info && dev && api_update_ts_info(dev) < 0)
+		return NULL;
+
+	fw = (struct ilitek_fw_handle *)MALLOC(sizeof(*fw));
+	if (!fw)
+		return NULL;
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(fw, 0, sizeof(*fw));
+	fw->dev = (dev) ? dev : NULL;
+
+	/* initial update-len to default UPDATE_LEN */
+	fw->update_len = UPDATE_LEN;
+
+	fw->dev = dev;
+	fw->_private = _private;
+	fw->file.buf_size = ILITEK_FW_BUF_SIZE;
+	fw->file.buf = (u8 *)CALLOC(fw->file.buf_size, 1);
+	if (!fw->file.buf)
+		goto err_free_fw;
+
+	fw->m2v_buf = (u8 *)CALLOC(ILITEK_FW_BUF_SIZE, 1);
+	if (!fw->m2v_buf)
+		goto err_free_fw_buf;
+
+	if (cb)
+		_memcpy(&fw->cb, cb, sizeof(*cb));
+
+	return fw;
+
+err_free_fw_buf:
+	CFREE(fw->file.buf);
+err_free_fw:
+	FREE(fw);
+
+	return NULL;
+}
+
+void ilitek_update_exit(void *handle)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	if (fw->file.buf)
+		CFREE(fw->file.buf);
+
+	if (fw->m2v_buf)
+		CFREE(fw->m2v_buf);
+
+	if (fw)
+		FREE(fw);
+}
+
+void ilitek_update_set_data_length(void *handle, u16 len)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!fw)
+		return;
+
+	fw->update_len = len;
+}
+
+int ilitek_update_load_fw(void *handle, WCHAR *fw_name)
+{
+	int error;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	u32 i;
+
+	if (!handle)
+		return -EINVAL;
+
+	error = decode_firmware(fw, fw_name);
+	if (error < 0)
+		return error;
+
+	if (fw->cb.update_fw_file_info)
+		fw->cb.update_fw_file_info(&fw->file, fw->_private);
+
+	if (!fw->dev)
+		return 0;
+
+	/* for Lego and V6 IC, check block's start/end address validity */
+	if (fw->dev->protocol.flag == PTL_V6) {
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->dev->mcu_info.min_addr <=
+				fw->file.blocks[i].start &&
+			    fw->dev->mcu_info.max_addr >
+				fw->file.blocks[i].end)
+				continue;
+
+			if (!(fw->file.blocks[i].start % 0x1000))
+				continue;
+
+			TP_ERR(fw->dev->id, "Block[%u] addr. OOB (0x%x <= 0x%x/0x%x < 0x%x) or invalid start addr\n",
+				i, fw->dev->mcu_info.min_addr,
+				fw->file.blocks[i].start,
+				fw->file.blocks[i].end,
+				fw->dev->mcu_info.max_addr);
+			return -EINVAL;
+		}
+	}
+
+	TP_MSG(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " matched\n",
+		fw->dev->mcu_info.ic_name, fw->file.ic_name);
+
+	return 0;
+}
+
+int ilitek_update_start(void *handle)
+{
+	int error;
+	s8 retry = 0;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+	struct ilitek_ts_device *dev;
+
+	if (!handle)
+		return -EINVAL;
+	dev = fw->dev;
+
+	/*
+	 * Some platform (ITS-Bridge) might change touch controller
+	 * after loading fw file, get panel info. forcely and
+	 * re-check the ic/file are matched.
+	 */
+	error = api_update_ts_info(dev);
+	if (error < 0 || strcmp(dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "get ic info failed, err: %d or ic/file (" PFMT_C8 "/" PFMT_C8 ") not matched\n",
+			error, fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EPERM;
+	}
+
+	TP_INFO(dev->id, "[ilitek_update_start] start\n");
+
+	do {
+		TP_DBG(dev->id, "retry: %hhd, retry_limit: %hhd\n",
+			retry, fw->setting.retry);
+		if (retry)
+			reset_helper(dev);
+
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			continue;
+
+		if (!need_fw_update(fw)) {
+			if (is_231x(dev))
+				goto erase_data_flash_231x;
+			goto success_return;
+		}
+
+		update_progress(fw);
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(0, fw->_private);
+
+		error = api_to_bl_mode(dev, true, 0, 0);
+		if (error < 0)
+			continue;
+
+		TP_INFO_ARR(dev->id, "[BL Firmware Version]",
+			    TYPE_U8, 8, dev->fw_ver);
+		TP_INFO(dev->id, "[ilitek_update_start] start to program\n");
+
+		switch (dev->protocol.ver & 0xFFFF00) {
+		case BL_PROTOCOL_V1_8:
+			error = ilitek_update_BL_v1_8(fw);
+			break;
+		case BL_PROTOCOL_V1_7:
+			error = ilitek_update_BL_v1_7(fw);
+			break;
+		case BL_PROTOCOL_V1_6:
+			error = ilitek_update_BL_v1_6(fw);
+			break;
+		default:
+			TP_ERR(dev->id, "BL protocol ver: 0x%x not supported\n",
+				dev->protocol.ver);
+			continue;
+		}
+		if (error < 0)
+			continue;
+
+		error = api_to_bl_mode(dev, false,
+				       fw->file.blocks[0].start,
+				       fw->file.blocks[0].end);
+		if (error < 0)
+			continue;
+
+erase_data_flash_231x:
+		/*
+		 * If no data flash section in firmware file,
+		 * 231x need to erase data flash after change to AP mode.
+		 */
+		if (fw->file.blocks[1].end < fw->file.blocks[1].start &&
+		    is_231x(dev)) {
+			error = api_erase_data_v3(dev);
+			if (error < 0)
+				continue;
+		}
+
+success_return:
+		error = api_update_ts_info(dev);
+		if (error < 0)
+			continue;
+
+		error = api_set_ctrl_mode(dev, mode_normal, false, true);
+		if (error < 0)
+			continue;
+
+		if (fw->cb.update_fw_ic_info)
+			fw->cb.update_fw_ic_info(true,
+						 dev->fw_ver, dev->ic[0].crc,
+						 fw->file.block_num,
+						 fw->_private);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(100, fw->_private);
+
+		if (need_retry(fw))
+			continue;
+
+		TP_INFO(dev->id, "[ilitek_update_start] success\n");
+
+		return 0;
+	} while (!dev->setting.no_retry && ++retry < fw->setting.retry);
+
+	TP_ERR(dev->id, "[ilitek_update_start] fw update failed, err: %d\n",
+		error);
+
+	return (error < 0) ? error : -EFAULT;
+}
+
+void ilitek_update_setting(void *handle, struct ilitek_fw_settings *setting)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&fw->setting, setting, sizeof(struct ilitek_fw_settings));
+}
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.h b/drivers/input/touchscreen/ilitek/ilitek_update.h
new file mode 100644
index 000000000000..b7987b9fb121
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.h
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_UPDATE_H__
+#define __ILITEK_UPDATE_H__
+
+#include "ilitek_protocol.h"
+
+#define UPDATE_LEN			1024
+
+#define ILITEK_FW_FILE_SIZE		(512 * 1024)
+#define ILITEK_FW_BUF_SIZE		(256 * 1024)
+
+enum fw_file_type {
+	fw_hex = 0,
+	fw_bin,
+	fw_ili,
+};
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ mapping_info_lego {
+	u8 tuning_ver[4];
+	u8 fw_ver[4];
+	u8 core_test;
+	u8 core_day;
+	u8 core_month;
+	u8 core_year;
+	u32 core_ver;
+	u8 vendor_ver[6];
+	u8 _reserve_1[8];
+	u16 customer_id;
+	u16 fwid;
+	u16 i2c_addr;
+	u8 _reserve_2[2];
+	char model_name[16];
+	u8 _reserve_3[2];
+	u8 ic_num;
+	u8 total_tuning_num;
+	u16 sizeof_tuning;
+	u16 sizeof_tp_param;
+	u16 sizeof_sys_info;
+	u16 sizeof_sys_algo;
+	u16 sizeof_key_info;
+	u8 block_num;
+	u8 support_tuning_num;
+	u8 _reserve_4[2];
+
+	struct __PACKED__ {
+		u8 addr[3];
+	} blocks[10];
+
+	u8 _reserve_5[9];
+	u8 end_addr[3];
+	u8 _reserve_6[2];
+};
+
+union __PACKED__ mapping_info {
+	struct __PACKED__ {
+		u8 mapping_ver[3];
+		u8 protocol_ver[3];
+		u8 ic_name[6];
+
+		struct mapping_info_lego _lego;
+	};
+};
+
+/*
+ * for V3, "check" is checksum, block[0] for AP and block[1] for Data Flash.
+ * for V6, "check" is CRC.
+ */
+struct __PACKED__ ilitek_block {
+	bool check_match;
+	u32 start;
+	u32 end;
+	u32 check;
+	u32 offset;
+};
+
+struct __PACKED__ ilitek_fw_file_info {
+	char ic_name[8];
+	u8 fw_ver[8];
+	u16 fwid;
+
+	u8 block_num;
+	struct ilitek_block blocks[ILTIEK_MAX_BLOCK_NUM];
+
+	u32 mm_addr;
+	u32 mm_size;
+
+	u32 buf_size;
+	u8 *buf;
+
+	/* fw file's sensor-id or fwid or other id */
+	u32 id;
+	u8 type;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return file size in # of bytes, or negative error code */
+typedef int (*read_fw_t)(WCHAR *, u8 *, int, void *);
+/* update progress of fw updating */
+typedef void (*update_progress_t)(u8, void *);
+/* update fw info to callers */
+typedef void (*update_fw_file_info_t)(struct ilitek_fw_file_info *, void *);
+
+/* notify caller before/after slave upgrade (for ITS only) */
+typedef void (*slave_update_notify_t)(bool, void *);
+/* update fw version and crc/checksum before/after update (for ITS only) */
+typedef void (*update_fw_ic_info_t)(bool, u8 *, u32 *, int, void *);
+
+struct ilitek_update_callback {
+	read_fw_t read_fw;
+	update_progress_t update_progress;
+	update_fw_file_info_t update_fw_file_info;
+
+	slave_update_notify_t slave_update_notify;
+	update_fw_ic_info_t update_fw_ic_info;
+};
+
+enum fw_ver_check_policy {
+	allow_fw_ver_downgrade = 1,
+	allow_fw_ver_same = 2,
+};
+
+struct ilitek_fw_settings {
+	s8 retry;
+	bool fw_check_only;
+	bool force_update;
+
+	bool fw_ver_check;
+	u8 fw_ver_policy;
+	u8 fw_ver[8];
+};
+
+struct ilitek_fw_handle {
+	struct ilitek_ts_device *dev;
+	void *_private;
+
+	/* upgrade options */
+	struct ilitek_fw_settings setting;
+
+	/* common variable */
+	int update_len;
+
+	struct ilitek_fw_file_info file;
+
+	/* M3 + M2V */
+	bool m2v;
+	bool m2v_need_update;
+	u8 *m2v_buf;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	/* upgrade status */
+	unsigned int progress_curr;
+	unsigned int progress_max;
+	u8 progress;
+
+	/* callbacks */
+	struct ilitek_update_callback cb;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			       struct ilitek_update_callback *callback,
+			       void *_private);
+
+void __DLL ilitek_update_exit(void *handle);
+
+void __DLL ilitek_update_set_data_length(void *handle, u16 len);
+
+void __DLL ilitek_update_setting(void *handle,
+				 struct ilitek_fw_settings *setting);
+
+int __DLL ilitek_update_load_fw(void *handle, WCHAR *fw_name);
+
+int __DLL ilitek_update_start(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git "a/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" "b/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"
new file mode 100644
index 0000000000000000000000000000000000000000..54af506a4b7c4546aaa12ec99f136c9083b62a83
GIT binary patch
literal 162
zcmZQCuPRR|FHUA439vJiGgJX_3J@1FfHc>ZFflPOG_>^}{sH8{XrBE*K_md<!NeuG
f)w8dM-eF8(e4Ne9!oaZdltBfE57ScAbow{|sQnt1

literal 0
HcmV?d00001

-- 
2.25.1


^ permalink raw reply related

* [PATCH] input: touchscreen: Add ilitek touchscreen driver support
From: 2724853925 @ 2025-11-16 13:23 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg
  Cc: linux-input, linux-kernel, linux-gpio, 2724853925, weisicheng

From: weisicheng <7855009+weisicheng@user.noreply.gitee.com>

Add basic support for Ilitek I2C touchscreen controllers (e.g., ILitek2511, ILitek2701).
This patch includes:
1. Kconfig entry for Ilitek driver configuration
2. Makefile entry to compile the ilitek driver module
3. Core driver files under drivers/input/touchscreen/ilitek/ (I2C communication,
   touch event handling, power management)

The driver is compatible with standard input subsystem and supports:
- Multi-touch detection
- Pressure sensing
- Dynamic power saving mode

Signed-off-by: weisicheng <2724853925@qq.com>

Signed-off-by: weisicheng <7855009+weisicheng@user.noreply.gitee.com>
---
 drivers/input/touchscreen/Kconfig             |   13 +
 drivers/input/touchscreen/Makefile            |    1 +
 drivers/input/touchscreen/ilitek/Makefile     |   34 +
 .../input/touchscreen/ilitek/ilitek_common.h  |  264 ++
 .../input/touchscreen/ilitek/ilitek_crypto.c  |  465 +++
 .../input/touchscreen/ilitek/ilitek_crypto.h  |   61 +
 drivers/input/touchscreen/ilitek/ilitek_def.c |  235 ++
 drivers/input/touchscreen/ilitek/ilitek_def.h |  554 +++
 .../input/touchscreen/ilitek/ilitek_main.c    | 2198 ++++++++++
 .../touchscreen/ilitek/ilitek_platform_init.c |  404 ++
 .../touchscreen/ilitek/ilitek_protocol.c      | 3644 +++++++++++++++++
 .../touchscreen/ilitek/ilitek_protocol.h      |  916 +++++
 .../input/touchscreen/ilitek/ilitek_report.c  |  455 ++
 .../input/touchscreen/ilitek/ilitek_report.h  |   78 +
 .../input/touchscreen/ilitek/ilitek_tool.c    | 1156 ++++++
 drivers/input/touchscreen/ilitek/ilitek_ts.h  |  268 ++
 .../input/touchscreen/ilitek/ilitek_update.c  | 1657 ++++++++
 .../input/touchscreen/ilitek/ilitek_update.h  |  199 +
 ...344\272\244\346\265\201\347\250\213 .docx" |  Bin 0 -> 162 bytes
 19 files changed, 12602 insertions(+)
 create mode 100644 drivers/input/touchscreen/ilitek/Makefile
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_common.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_main.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_platform_init.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_tool.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_ts.h
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.c
 create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.h
 create mode 100644 "drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7d5b72ee07fa..3552629c4d4d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -515,6 +515,19 @@ config TOUCHSCREEN_ILITEK
 	  To compile this driver as a module, choose M here: the
 	  module will be called ilitek_ts_i2c.
 
+config TOUCHSCREEN_ILI2511
+	tristate "Ilitek I2C or SPI Touch ICs"
+	depends on I2C
+	depends on SPI
+	help
+	  Say Y here if you have touchscreen with ILITEK touch IC,
+	  it supports 213X/23XX/25XX and other Lego series.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ilitek.
+
 config TOUCHSCREEN_IPROC
 	tristate "IPROC touch panel driver support"
 	depends on ARCH_BCM_IPROC || COMPILE_TEST
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ab9abd151078..3124c4dd8ee1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX)	+= hynitron_cstxxx.o
 obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X)	+= hynitron-cst816x.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_ILITEK)	+= ilitek_ts_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_ILI2511)	+= ilitek/
 obj-$(CONFIG_TOUCHSCREEN_IMAGIS)	+= imagis.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/ilitek/Makefile b/drivers/input/touchscreen/ilitek/Makefile
new file mode 100644
index 000000000000..c912438a3b3e
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/Makefile
@@ -0,0 +1,34 @@
+ccflags-y += -Wall
+
+ifndef __KERNEL__
+ccflags-y += -D__KERNEL__
+endif
+
+#Un-mark below item to enable FW upgrade on boot
+#ccflags-y += -DILITEK_BOOT_UPDATE
+
+ILITEK_INTERFACE = i2c
+#ILITEK_INTERFACE = spi
+
+
+ifeq ($(ILITEK_INTERFACE), spi)
+$(info start to build ilitek SPI driver)
+ccflags-y += -DILITEK_SPI_INTERFACE
+ilitek := ilitek_spi
+else
+$(info start to build ilitek I2C driver)
+ilitek := ilitek_i2c
+endif
+
+$(ilitek)-objs += \
+	ilitek_def.o \
+	ilitek_main.o \
+	ilitek_platform_init.o \
+	ilitek_update.o \
+	ilitek_tool.o \
+	ilitek_protocol.o \
+	ilitek_crypto.o \
+	ilitek_report.o
+
+obj-$(CONFIG_TOUCHSCREEN_ILI2511) += $(ilitek).o
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_common.h b/drivers/input/touchscreen/ilitek/ilitek_common.h
new file mode 100644
index 000000000000..7f058c3e4914
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_common.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _ILITEK_COMMON_H_
+#define _ILITEK_COMMON_H_
+/* Includes of headers ------------------------------------------------------*/
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "ilitek_ts.h"
+#include "ilitek_protocol.h"
+#include "ilitek_update.h"
+
+#include "ilitek_crypto.h"
+#include "ilitek_report.h"
+
+/* Extern define ------------------------------------------------------------*/
+//driver information
+#define DRIVER_VERSION_0				5
+#define DRIVER_VERSION_1				9
+#define DRIVER_VERSION_2				3
+#define DRIVER_VERSION_3				0
+#define CUSTOMER_H_ID					0
+#define CUSTOMER_L_ID					0
+#define TEST_VERSION					0
+
+#define ILITEK_IOCTL_MAX_TRANSFER			5000UL
+
+#define set_arr(arr, idx, val)			\
+	do {					\
+		if ((idx) < ARRAY_SIZE(arr))	\
+			(arr)[(idx)] = (val);	\
+	} while (0)
+
+/* i2c clock rate for rk3288 */
+#if ILITEK_PLAT == ILITEK_PLAT_ROCKCHIP && \
+	KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE
+#define SCL_RATE(rate)	.scl_rate = (rate),
+#else
+#define SCL_RATE(rate)
+#endif
+
+/* netlink */
+#if KERNEL_VERSION(3, 6, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)	\
+	struct netlink_kernel_cfg cfg = {	\
+		.groups = 0,			\
+		.input = func,			\
+	}
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), (cfg_ptr))
+#else
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), THIS_MODULE, (cfg_ptr))
+#endif
+#else
+#define NETLINK_KERNEL_CFG_DECLARE(cfg, func)
+#define NETLINK_KERNEL_CREATE(unit, cfg_ptr, func)	\
+	netlink_kernel_create(&init_net, (unit), 0, (func), NULL, THIS_MODULE)
+#endif
+
+/* input_dev */
+#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+#define INPUT_MT_INIT_SLOTS(dev, num)	\
+		input_mt_init_slots((dev), (num), INPUT_MT_DIRECT)
+#else
+#define INPUT_MT_INIT_SLOTS(dev, num)	input_mt_init_slots((dev), (num))
+#endif
+
+/* file_operations ioctl */
+#if KERNEL_VERSION(2, 6, 36) <= LINUX_VERSION_CODE
+#define FOPS_IOCTL	unlocked_ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		long func(struct file *fp, cmd, arg)
+#else
+#define FOPS_IOCTL	ioctl
+#define FOPS_IOCTL_FUNC(func, cmd, arg) \
+		s32 func(struct inode *np, struct file *fp,	cmd, arg)
+
+#endif
+
+#if KERNEL_VERSION(6, 3, 0) > LINUX_VERSION_CODE
+#define I2C_PROBE_FUNC(func, client_arg)	\
+	int func(client_arg, const struct i2c_device_id *id)
+#else
+#define I2C_PROBE_FUNC(func, client_arg)	int func(client_arg)
+#endif
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+#define REMOVE_FUNC(func, client_arg)	int func(client_arg)
+#define REMOVE_RETURN(val)		({ __typeof__(val) _val = (val); return _val; })
+#else
+#define REMOVE_FUNC(func, client_arg)	void func(client_arg)
+#define REMOVE_RETURN(val)		(val)
+#endif
+
+#if KERNEL_VERSION(6, 4, 0) > LINUX_VERSION_CODE
+#define CLASS_CREATE(name)	class_create(THIS_MODULE, (name))
+#else
+#define CLASS_CREATE(name)	class_create((name))
+#endif
+
+/* procfs */
+#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
+#define PROC_FOPS_T	file_operations
+#define PROC_READ	read
+#define PROC_WRITE	write
+#define PROC_IOCTL		FOPS_IOCTL
+#define PROC_COMPAT_IOCTL	compat_ioctl
+#define PROC_OPEN	open
+#define PROC_RELEASE	release
+#else
+#define PROC_FOPS_T	proc_ops
+#define PROC_READ	proc_read
+#define PROC_WRITE	proc_write
+#define PROC_IOCTL		proc_ioctl
+#define PROC_COMPAT_IOCTL	proc_compat_ioctl
+#define PROC_OPEN	proc_open
+#define PROC_RELEASE	proc_release
+#endif
+
+#ifdef MTK_UNDTS
+#define ISR_FUNC(func)	void func(void)
+#define ISR_RETURN(val)
+#else
+#define ISR_FUNC(func)	irqreturn_t func(int irq, void *dev_id)
+#define ISR_RETURN(val)	({ __typeof__(val) _val = (val); return _val; })
+#endif
+
+enum ilitek_irq_handle_type {
+	irq_type_normal = 0,
+	irq_type_debug,
+	irq_type_c_model,
+};
+
+struct ilitek_ts_data {
+	void *client;
+	struct device *device;
+	struct ilitek_ts_device *dev;
+
+	/* should > 2K for C-Model */
+	u8 buf[4096];
+
+	struct input_dev *input_dev;
+	struct input_dev *pen_input_dev;
+	struct regulator *vdd;
+	struct regulator *vdd_i2c;
+	struct regulator *vcc_io;
+
+	int irq;
+	int irq_gpio;
+	int reset_gpio;
+	int test_gpio;
+
+	bool system_suspend;
+	bool power_key_triggered;
+
+	u8 irq_trigger_type;
+
+	bool is_touched;
+	bool touch_key_hold_press;
+	int touch_flag[40];
+
+#if 0
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+	struct task_struct *update_thread;
+
+	atomic_t firmware_updating;
+	bool operation_protection;
+	bool unhandle_irq;
+	unsigned int irq_handle_type;
+	unsigned int irq_read_len;
+
+	u8 gesture_status;
+	u8 low_power_status;
+
+	bool esd_check;
+	bool esd_skip;
+	struct workqueue_struct *esd_workq;
+	struct delayed_work esd_work;
+	unsigned long esd_delay;
+
+	/* Mutex for protecting concurrent access */
+	struct mutex ilitek_mutex;
+
+	atomic_t irq_enabled;
+	atomic_t get_INT;
+
+	bool wake_irq_enabled;
+
+	bool irq_registered;
+};
+
+/* Extern macro -------------------------------------------------------------*/
+
+#define CEIL(n, d) (((n) % (d)) ? ((n) / (d)) + 1 : ((n) / (d)))
+
+/* Extern variables ---------------------------------------------------------*/
+
+extern u8 driver_ver[];
+
+extern struct ilitek_ts_data *ts;
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#endif
+/* Extern function prototypes -----------------------------------------------*/
+/* Extern functions ---------------------------------------------------------*/
+void ilitek_resume(void);
+void ilitek_suspend(void);
+int ilitek_main_probe(void *client, struct device *dev);
+int ilitek_main_remove(void *client);
+void ilitek_reset(int delay);
+
+int ilitek_write(u8 *cmd, int len);
+int ilitek_read(u8 *buf, int len);
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len);
+
+void ilitek_irq_enable(void);
+void ilitek_irq_disable(void);
+
+int ilitek_upgrade_firmware(char *filename);
+
+int ilitek_create_tool_node(void);
+int ilitek_remove_tool_node(void);
+
+int ilitek_create_sysfsnode(void);
+void ilitek_remove_sys_node(void);
+
+int ilitek_netlink_init(u8 unit);
+void ilitek_netlink_exit(void);
+
+void ilitek_gpio_dbg(void);
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init);
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y);
+
+int ilitek_create_esd_check_workqueue(void);
+void ilitek_remove_esd_check_workqueue(void);
+
+int ilitek_read_data_and_report(void);
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.c b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
new file mode 100644
index 000000000000..2ba6105266d2
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_crypto.h"
+
+/*
+ * The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
+ * The numbers below can be computed dynamically trading ROM for RAM -
+ * This can be useful in (embedded) bootloader applications, where ROM is often limited.
+ */
+static const u8 sbox[256] = {
+	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+	0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+	0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+	0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+	0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+	0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+	0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+	0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+	0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+	0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+	0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+	0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+	0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+	0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+	0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+	0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
+
+static const u8 rsbox[256] = {
+	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+	0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+	0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+	0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+	0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+	0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+	0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+	0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+	0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+	0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+	0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+	0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+	0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+	0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+	0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+	0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
+
+/*
+ * The round constant word array, contains the values given by
+ * x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
+ */
+static const u8 rcon[11] = {
+	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+/* array holding the intermediate results during decryption. */
+typedef u8 state_t[4][4];
+
+struct crypto_aes_ctx {
+	u8 key_dec[240];
+};
+
+static u8 xtime(u8 x)
+{
+	return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
+}
+
+static void inv_shift_rows(state_t *state)
+{
+	u8 tmp;
+
+	/* Rotate first row 1 columns to right */
+	tmp = (*state)[3][1];
+	(*state)[3][1] = (*state)[2][1];
+	(*state)[2][1] = (*state)[1][1];
+	(*state)[1][1] = (*state)[0][1];
+	(*state)[0][1] = tmp;
+
+	/* Rotate second row 2 columns to right */
+	tmp = (*state)[0][2];
+	(*state)[0][2] = (*state)[2][2];
+	(*state)[2][2] = tmp;
+	tmp = (*state)[1][2];
+	(*state)[1][2] = (*state)[3][2];
+	(*state)[3][2] = tmp;
+
+	/* Rotate third row 3 columns to right */
+	tmp = (*state)[0][3];
+	(*state)[0][3] = (*state)[1][3];
+	(*state)[1][3] = (*state)[2][3];
+	(*state)[2][3] = (*state)[3][3];
+	(*state)[3][3] = tmp;
+}
+
+static void inv_sub_bytes(state_t *state)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[j][i] = rsbox[(*state)[j][i]];
+}
+
+/*
+ * multiply is used to multiply numbers in the field GF(2^8)
+ * Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
+ *       The compiler seems to be able to vectorize the operation better this way.
+ *       See https://github.com/kokke/tiny-AES-c/pull/34
+ */
+static u8 multiply(u8 x, u8 y)
+{
+	return (((y & 1) * x) ^
+		(((y >> 1) & 1) * xtime(x)) ^
+		(((y >> 2) & 1) * xtime(xtime(x))) ^
+		(((y >> 3) & 1) * xtime(xtime(xtime(x)))) ^
+		(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
+}
+
+/*
+ * inv_mix_cols function mixes the columns of the state matrix.
+ */
+static void inv_mix_cols(state_t *state)
+{
+	int i;
+	u8 a, b, c, d;
+
+	for (i = 0; i < 4; ++i) {
+		a = (*state)[i][0];
+		b = (*state)[i][1];
+		c = (*state)[i][2];
+		d = (*state)[i][3];
+
+		(*state)[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^
+			multiply(c, 0x0d) ^ multiply(d, 0x09);
+		(*state)[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^
+			multiply(c, 0x0b) ^ multiply(d, 0x0d);
+		(*state)[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^
+			multiply(c, 0x0e) ^ multiply(d, 0x0b);
+		(*state)[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^
+			multiply(c, 0x09) ^ multiply(d, 0x0e);
+	}
+}
+
+/*
+ * This function adds the round key to state.
+ * The round key is added to the state by an XOR function.
+ */
+static void add_round_key(u8 round, state_t *state, const u8 *key)
+{
+	u8 i, j;
+
+	for (i = 0; i < 4; ++i)
+		for (j = 0; j < 4; ++j)
+			(*state)[i][j] ^= key[(round * Nb * 4) + (i * Nb) + j];
+}
+
+static void aes_decrypt(state_t *state, const u8 *key)
+{
+	u8 round;
+
+	/* Add the first round key to the state before starting the rounds. */
+	add_round_key(Nr, state, key);
+
+	/*
+	 * There will be Nr rounds.
+	 * The first Nr-1 rounds are identical.
+	 * These Nr rounds are executed in the loop below.
+	 * Last one without InvMixColumn()
+	 */
+	for (round = Nr - 1; ; --round) {
+		inv_shift_rows(state);
+		inv_sub_bytes(state);
+		add_round_key(round, state, key);
+
+		if (!round)
+			break;
+
+		inv_mix_cols(state);
+	}
+}
+
+/*
+ * Produces Nb(Nr+1) round keys, used in each round to decrypt the states.
+ */
+static void aes_expandkey(u8 *round_key, const u8 *key)
+{
+	unsigned int i, j, k;
+	u8 tmp_a[4];
+	u8 tmp_b;
+
+	/* The first round key is the key itself. */
+	for (i = 0; i < Nk; ++i) {
+		round_key[(i * 4) + 0] = key[(i * 4) + 0];
+		round_key[(i * 4) + 1] = key[(i * 4) + 1];
+		round_key[(i * 4) + 2] = key[(i * 4) + 2];
+		round_key[(i * 4) + 3] = key[(i * 4) + 3];
+	}
+
+	/* All other round keys are found from the previous round keys. */
+	for (i = Nk; i < Nb * (Nr + 1); ++i) {
+		k = (i - 1) * 4;
+		tmp_a[0] = round_key[k + 0];
+		tmp_a[1] = round_key[k + 1];
+		tmp_a[2] = round_key[k + 2];
+		tmp_a[3] = round_key[k + 3];
+
+		if (!(i % Nk)) {
+			/*
+			 * Shifts the 4 bytes in a word to the left once.
+			 * [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
+			 */
+			tmp_b = tmp_a[0];
+			tmp_a[0] = tmp_a[1];
+			tmp_a[1] = tmp_a[2];
+			tmp_a[2] = tmp_a[3];
+			tmp_a[3] = tmp_b;
+
+			tmp_a[0] = sbox[tmp_a[0]];
+			tmp_a[1] = sbox[tmp_a[1]];
+			tmp_a[2] = sbox[tmp_a[2]];
+			tmp_a[3] = sbox[tmp_a[3]];
+
+			tmp_a[0] = tmp_a[0] ^ rcon[i / Nk];
+		}
+
+		j = i * 4;
+		k = (i - Nk) * 4;
+		round_key[j + 0] = round_key[k + 0] ^ tmp_a[0];
+		round_key[j + 1] = round_key[k + 1] ^ tmp_a[1];
+		round_key[j + 2] = round_key[k + 2] ^ tmp_a[2];
+		round_key[j + 3] = round_key[k + 3] ^ tmp_a[3];
+	}
+}
+
+u8 crypto_key[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			       0x4, 0x5, 0x6, 0x7,
+			       0x8, 0x9, 0xa, 0xb,
+			       0xc, 0xd, 0xe, 0xf };
+
+u8 crypto_iv[AES_KEY_LEN] = { 0x0, 0x1, 0x2, 0x3,
+			      0x4, 0x5, 0x6, 0x7,
+			      0x8, 0x9, 0xa, 0xb,
+			      0xc, 0xd, 0xe, 0xf };
+
+void ilitek_decrypt(u8 *buf, u32 len)
+{
+	u8 key[AES_KEY_LEN];
+	u8 iv[AES_KEY_LEN];
+	u8 *tmp = buf;
+	u8 iv_tmp[AES_KEY_LEN];
+	struct crypto_aes_ctx ctx;
+
+	u32 i, j;
+
+	memcpy(key, crypto_key, AES_KEY_LEN);
+	memcpy(iv, crypto_iv, AES_KEY_LEN);
+
+	aes_expandkey(ctx.key_dec, key);
+
+	for (i = 0; i < len; i += AES_KEY_LEN, tmp += AES_KEY_LEN) {
+		memcpy(iv_tmp, tmp, AES_KEY_LEN);
+
+		aes_decrypt((state_t *)tmp, ctx.key_dec);
+		for (j = 0; j < AES_KEY_LEN; j++)
+			tmp[j] ^= iv[j];
+
+		memcpy(iv, iv_tmp, AES_KEY_LEN);
+	}
+}
+
+static void sha256_init(struct sha256_ctx *ctx)
+{
+	ctx->datalen = 0;
+	ctx->bitlen[0] = 0;
+	ctx->bitlen[1] = 0;
+
+	/*
+	 * 8 hash value for sha256
+	 * square root of prime number 2,3,5,7,11,13,17,19
+	 * then take 32 bit numbers after the decimal point
+	 */
+	ctx->state[0] = 0x6a09e667;
+	ctx->state[1] = 0xbb67ae85;
+	ctx->state[2] = 0x3c6ef372;
+	ctx->state[3] = 0xa54ff53a;
+	ctx->state[4] = 0x510e527f;
+	ctx->state[5] = 0x9b05688c;
+	ctx->state[6] = 0x1f83d9ab;
+	ctx->state[7] = 0x5be0cd19;
+}
+
+static void sha256_transform(struct sha256_ctx *ctx, u8 *data)
+{
+	static u32 key[64] = {
+		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	};
+
+	u32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+	for (i = 0, j = 0; i < 16; ++i, j += 4)
+		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+
+	for (; i < 64; ++i)
+		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+
+	a = ctx->state[0];
+	b = ctx->state[1];
+	c = ctx->state[2];
+	d = ctx->state[3];
+	e = ctx->state[4];
+	f = ctx->state[5];
+	g = ctx->state[6];
+	h = ctx->state[7];
+
+	for (i = 0; i < 64; ++i) {
+		t1 = h + EP1(e) + CH(e, f, g) + key[i] + m[i];
+		t2 = EP0(a) + MAJ(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + t1;
+		d = c;
+		c = b;
+		b = a;
+		a = t1 + t2;
+	}
+
+	ctx->state[0] += a;
+	ctx->state[1] += b;
+	ctx->state[2] += c;
+	ctx->state[3] += d;
+	ctx->state[4] += e;
+	ctx->state[5] += f;
+	ctx->state[6] += g;
+	ctx->state[7] += h;
+}
+
+static void sha256_update(struct sha256_ctx *ctx, u8 byte)
+{
+	ctx->data[ctx->datalen] = byte;
+	ctx->datalen++;
+	if (ctx->datalen == 64) {
+		sha256_transform(ctx, ctx->data);
+		DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], 512);
+		ctx->datalen = 0;
+	}
+}
+
+static void sha256_final(struct sha256_ctx *ctx, u8 *hash)
+{
+	u32 i;
+
+	i = ctx->datalen;
+
+	/* Pad whatever data is left in the buffer. */
+	if (ctx->datalen < 56) {
+		ctx->data[i++] = 0x80;
+		while (i < 56)
+			ctx->data[i++] = 0x00;
+	} else {
+		ctx->data[i++] = 0x80;
+		while (i < 64)
+			ctx->data[i++] = 0x00;
+		sha256_transform(ctx, ctx->data);
+		memset(ctx->data, 0, 56);
+	}
+
+	/* Append to the padding the total message's length in bits and transform. */
+	DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], ctx->datalen * 8);
+	ctx->data[63] = ctx->bitlen[0];
+	ctx->data[62] = ctx->bitlen[0] >> 8;
+	ctx->data[61] = ctx->bitlen[0] >> 16;
+	ctx->data[60] = ctx->bitlen[0] >> 24;
+	ctx->data[59] = ctx->bitlen[1];
+	ctx->data[58] = ctx->bitlen[1] >> 8;
+	ctx->data[57] = ctx->bitlen[1] >> 16;
+	ctx->data[56] = ctx->bitlen[1] >> 24;
+	sha256_transform(ctx, ctx->data);
+
+	/*
+	 * Since this implementation uses little endian byte ordering and SHA uses big endian,
+	 * reverse all the bytes when copying the final state to the output hash.
+	 */
+	for (i = 0; i < 4; ++i) {
+		hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+		hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+	}
+}
+
+void get_sha256(u32 start, u32 end, u8 *buf, u32 buf_size, u8 sha256[32])
+{
+	struct sha256_ctx ctx;
+	u32 i;
+
+	sha256_init(&ctx);
+
+	if (end >= buf_size || start > buf_size || end < start) {
+		TP_ERR(NULL, "start/end addr: %#x/%#x buf size: %#x OOB\n", start, end, buf_size);
+		return;
+	}
+
+	for (i = start; i <= end && i < buf_size; i++)
+		sha256_update(&ctx, buf[i]);
+
+	sha256_final(&ctx, sha256);
+
+	TP_MSG_ARR(NULL, "sha256:", TYPE_U8, 32, sha256);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_crypto.h b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
new file mode 100644
index 000000000000..ef01ca434776
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_CRYPTO_H__
+#define __ILITEK_CRYPTO_H__
+
+#include "ilitek_def.h"
+
+#define Nb	(4)
+#define Nk	(4)       /* The number of 32 bit words in a key. */
+#define Nr	(10)      /* The number of rounds in AES Cipher. */
+
+#define AES_KEY_LEN      (16) /* Key length in bytes */
+
+extern u8 crypto_key[AES_KEY_LEN];
+extern u8 crypto_iv[AES_KEY_LEN];
+
+#define DBL_INT_ADD(a, b, c)			\
+	do {					\
+		if ((a) > 0xffffffff - (c))	\
+			++(b);			\
+		(a) += (c);			\
+	} while (0)
+
+#define ROTLEFT(a, b)	(((a) << (b)) | ((a) >> (32 - (b))))
+#define ROTRIGHT(a, b)	(((a) >> (b)) | ((a) << (32 - (b))))
+
+#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
+#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
+#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
+
+struct sha256_ctx {
+	u8 data[64];
+	u32 datalen;
+	u32 bitlen[2];
+	u32 state[8];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL ilitek_decrypt(u8 *buf, u32 len);
+
+void __DLL get_sha256(u32 start, u32 end,
+		      u8 *buf, u32 buf_size, u8 sha256[32]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.c b/drivers/input/touchscreen/ilitek/ilitek_def.c
new file mode 100644
index 000000000000..e9934cac240d
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_def.h"
+
+int tp_log_level = log_level_msg;
+bool tp_print_en = true;
+FILE *tp_fp;
+
+char g_str[4096];
+msg_t g_msg;
+
+#if defined(__KERNEL__) || defined(__UEFI_DXE__)
+
+int get_time_ms(u32 *t_ms)
+{
+	*t_ms = 0;
+
+	return -EINVAL;
+}
+
+#else
+#ifdef _WIN32
+
+static int gettimeofday(struct timeval *tp, void *tzp)
+{
+	time_t clock;
+	struct tm tm;
+	SYSTEMTIME wtm;
+
+	GetLocalTime(&wtm);
+	tm.tm_year = wtm.wYear - 1900;
+	tm.tm_mon = wtm.wMonth - 1;
+	tm.tm_mday = wtm.wDay;
+	tm.tm_hour = wtm.wHour;
+	tm.tm_min = wtm.wMinute;
+	tm.tm_sec = wtm.wSecond;
+	tm.tm_isdst = -1;
+	clock = mktime(&tm);
+	tp->tv_sec = (long)clock;
+	tp->tv_usec = wtm.wMilliseconds * 1000;
+	return 0;
+}
+
+#endif
+
+int get_time_ms(u32 *t_ms)
+{
+	static u32 time_ms_init;
+	struct timeval t;
+	u32 time_ms;
+
+	gettimeofday(&t, NULL);
+	time_ms = t.tv_sec * 1000 + t.tv_usec / 1000;
+	time_ms_init = (!time_ms_init) ? time_ms : time_ms_init;
+
+	*t_ms = time_ms - time_ms_init;
+
+	return 0;
+}
+
+#endif
+
+void tp_log_arr(char *id, int level, const char *header, const char *tag,
+		int type, int len, void *buf)
+{
+	const int num = 64;
+	int i, idx = 0;
+	u32 time_ms;
+	int error;
+
+	if (level > tp_log_level || !buf)
+		return;
+
+	error = get_time_ms(&time_ms);
+
+	do {
+		_memset(g_str, 0, sizeof(g_str));
+
+		if (!error)
+			_sprintf(g_str, 0, "[%7u.%03u]",
+				time_ms / 1000, time_ms % 1000);
+
+		if (id)
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 "[" PFMT_C8 "] " PFMT_C8 " ",
+				 header, id, tag);
+		else
+			_sprintf(g_str, _strlen(g_str),
+				 PFMT_C8 " " PFMT_C8 " ",
+				 header, tag);
+
+		for (i = 0; i < num && idx < len; i++, idx++) {
+			switch (type) {
+			default:
+			case TYPE_U8:
+				_sprintf(g_str, _strlen(g_str), "%02x-",
+					((u8 *)buf)[idx]);
+				break;
+			case TYPE_INT:
+				_sprintf(g_str, _strlen(g_str), "%d-",
+					((int *)buf)[idx]);
+				break;
+			}
+		}
+		_sprintf(g_str, _strlen(g_str) - 1, ", len: [%d/%d]\n",
+			idx, len);
+
+		if (tp_print_en)
+			TP_PRINTF(PFMT_C8, g_str);
+		if (g_msg)
+			g_msg(level, g_str);
+		TP_LOG(tp_fp, g_str);
+	} while (idx < len);
+}
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items)
+{
+	int error = 0;
+
+	MUTEX_INIT(q->mutex);
+
+	MUTEX_LOCK(q->mutex);
+
+	do {
+		q->item_size = item_size;
+		q->curr_size = 0;
+		q->max_size = max_items;
+
+		q->buf = (u8 *)CALLOC(max_items, item_size);
+		if (!q->buf) {
+			error = -ENOMEM;
+			break;
+		}
+
+		q->push_ptr = q->buf;
+		q->pop_ptr = q->buf;
+		q->end_ptr = q->buf + (max_items - 1) * item_size;
+	} while (false);
+
+	MUTEX_UNLOCK(q->mutex);
+
+	return error;
+}
+
+void queue_exit(struct queue *q)
+{
+	if (q->buf)
+		CFREE(q->buf);
+	MUTEX_EXIT(q->mutex);
+}
+
+void queue_push(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	/* Stop push data when queue is full */
+	if (q->curr_size >= q->max_size)
+		goto release_push_lock;
+
+	q->curr_size++;
+	if (q->push_ptr == q->end_ptr)
+		q->push_ptr = q->buf;
+	else
+		q->push_ptr += q->item_size;
+
+	if (q->push_ptr == q->pop_ptr)
+		TP_ERR(NULL, "[Warn]Queue overload, queue size: %u\n", q->curr_size);
+
+release_push_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void queue_pop(struct queue *q)
+{
+	MUTEX_LOCK(q->mutex);
+
+	if (!q->curr_size)
+		goto release_pop_lock;
+
+	q->curr_size--;
+	if (q->pop_ptr == q->end_ptr)
+		q->pop_ptr = q->buf;
+	else
+		q->pop_ptr += q->item_size;
+
+release_pop_lock:
+	MUTEX_UNLOCK(q->mutex);
+}
+
+void set_print_en(bool enable)
+{
+	tp_print_en = enable;
+}
+
+void set_log_level(int level)
+{
+	tp_log_level = level;
+}
+
+int set_log_fopen(WCHAR *filename)
+{
+	int error;
+
+	if (tp_fp)
+		return -EINVAL;
+
+	error = WFOPEN(&tp_fp, filename, "w+");
+	if (error < 0) {
+		tp_fp = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+void set_log_fclose(void)
+{
+	if (!tp_fp)
+		return;
+
+	_fclose(tp_fp);
+	tp_fp = NULL;
+}
+
+void set_log_fwrite(char *str)
+{
+	TP_LOG(tp_fp, str);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.h b/drivers/input/touchscreen/ilitek/ilitek_def.h
new file mode 100644
index 000000000000..d6f2077d7760
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_def.h
@@ -0,0 +1,554 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_DEF_H__
+#define __ILITEK_DEF_H__
+
+#define COMMONFLOW_CODE_VERSION		0x00000301
+
+/*
+ * Windows
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+#define __MAYBE_UNUSED
+
+#ifdef _WINDLL
+#define __DLL __declspec(dllexport)
+#else
+#define __DLL __declspec(dllimport)
+#endif
+
+#define __PACKED__
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	sprintf_s((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size)	strncpy_s((dst), (dst_size), (src), (n))
+#define _strcasecmp(l, r)		_stricmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat_s((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)		sscanf_s(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy_s((dst), (dst_size), (src))
+#define _strtok(str, del, next_token)	strtok_s((str), (del), (next_token))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				wchar_t
+#define WSTRING				wstring
+#define WCSCPY(dst, src, dst_size)	wcscpy_s((dst), (dst_size), (src))
+#define WCSCASECMP(str, tag)		_wcsicmp((str), (L##tag))
+#define WCSRCHR(str, ch)		wcsrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	swprintf_s((buf), (size), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)					\
+	((!((*(pfp)) = _wfsopen((filename), (const wchar_t *)(L##mode), \
+				_SH_DENYWR))) ? -EFAULT : 0)
+#define WACCESS(filename, mode)		_waccess((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)					\
+	do {							\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_U8TEXT);		\
+		fwprintf((fp), (fmt), ##__VA_ARGS__);		\
+		fflush((fp));					\
+		_setmode(_fileno((fp)), _O_TEXT);		\
+	} while (false)
+
+#define PFMT_C16			"%ls"
+#define PFMT_C8				"%hs"
+
+#include <codecvt>
+#define TO_WCHAR(x)	\
+	std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t > ().from_bytes(x).c_str()
+
+#define _localtime(ptm, ptime)		localtime_s((ptm), (ptime))
+
+#define _fopen(pfp, filename, mode)	\
+	(fopen_s((pfp), (filename), (const char *)(mode)))
+#define _fclose(pfp)			fclose((pfp))
+
+#define MUTEX_T				HANDLE
+#define MUTEX_INIT(x)			((x) = CreateMutex(NULL, false, NULL))
+#define MUTEX_LOCK(x)			(WaitForSingleObject((x), INFINITE))
+#define MUTEX_UNLOCK(x)			(ReleaseMutex((x)))
+#define MUTEX_EXIT(x)			(CloseHandle((x)))
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+/*
+ * UEFI Driver
+ */
+#elif defined(__UEFI_DXE__)
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+
+#define UEFI_MAX_STR_SIZE	0x1000
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+
+#define _sprintf(buf, idx, fmt, ...)	\
+	AsciiSPrint((buf) + (idx), sizeof((buf)) - (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) \
+	AsciiStrnCpyS(dst, dst_size, src, (UINTN)n)
+#define _strcasecmp(l, r)		AsciiStriCmp((l), (r))
+#define _strcat(dst, src, dst_size)	\
+	AsciiStrCatS((dst), (dst_size), (src))
+#define _sscanf(str, fmt, ...)
+#define _strlen(str)			AsciiStrnLenS((str), UEFI_MAX_STR_SIZE)
+#define _strcpy(dst, src, dst_size)	\
+	_strncpy((dst), (src), _strlen(src), (dst_size))
+#define _strtok(str, del, next_token)
+
+static __MAYBE_UNUSED char *_strrchr(const char *s, char c)
+{
+	char *found = NULL;
+
+	do {
+		if (*s == c)
+			found = (char *)s;
+	} while (*s++ != '\0');
+
+	return found;
+}
+
+#define _memset(ptr, ch, size)		\
+	SetMem((ptr), (UINTN)(size), (UINT8)(ch))
+#define _memcpy(dst, src, size)		\
+	CopyMem((dst), (src), (UINTN)(size))
+
+#define _WTEXT(str)			L ## str
+#define WTEXT(str)			_WTEXT(str)
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		_strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	(-EINVAL)
+#define WACCESS(filename, mode)		(-EINVAL)
+#define WFPRINTF(fp, fmt, ...)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%a"
+#define PFMT_C8				"%a"
+#define PFMT_U8				"%u"
+#define PFMT_U16			"%u"
+#define PFMT_X8				"%02x"
+#define PFMT_X16			"%04x"
+#define PFMT_X64			"%016llx"
+
+#define _localtime(ptm, ptime)
+#define _fclose(pfp)
+
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define MALLOC(size)		AllocatePool(size)
+#define CALLOC(num, size)	AllocateZeroPool(num * size)
+#define FREE(ptr)			\
+	do {				\
+		FreePool((ptr));	\
+		(ptr) = NULL;		\
+	} while (0)
+
+#define CFREE(ptr)		FREE(ptr)
+#define TP_PRINTF(fmt, ...)	DebugPrint(DEBUG_INFO, fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+/*
+ * Linux-Based System
+ */
+#else
+
+#define __MAYBE_UNUSED	__maybe_unused
+#define __DLL
+#define __PACKED__	__packed
+#define _sprintf(buf, idx, fmt, ...)	sprintf((buf) + (idx), (fmt), ##__VA_ARGS__)
+#define _strncpy(dst, src, n, dst_size) strncpy((dst), (src), (n))
+#define _strcasecmp(l, r)		strcasecmp((l), (r))
+#define _strcat(dst, src, dst_size)	strcat((dst), (src))
+#define _sscanf(str, fmt, ...)		sscanf(str, fmt, ##__VA_ARGS__)
+#define _strlen(str)			strlen((str))
+#define _strcpy(dst, src, dst_size)	strcpy((dst), (src))
+
+#define _memset(ptr, ch, size)		memset((ptr), (ch), (size))
+#define _memcpy(dst, src, size)		memcpy((dst), (src), (size))
+
+#define WTEXT(str)			str
+#define WCHAR				char
+#define WSTRING				string
+#define WCSCPY(dst, src, dst_size)	_strcpy((dst), (src), (dst_size))
+#define WCSCASECMP(str, tag)		_strcasecmp((str), (tag))
+#define WCSRCHR(str, ch)		strrchr((str), (ch))
+#define SWPRINTF(buf, size, fmt, ...)	\
+	sprintf((buf), (fmt), ##__VA_ARGS__)
+#define WFOPEN(pfp, filename, mode)	_fopen(pfp, filename, mode)
+#define WACCESS(filename, mode)		_access((filename), (mode))
+#define WFPRINTF(fp, fmt, ...)		fprintf((fp), (fmt), ##__VA_ARGS__)
+#define TO_WCHAR(x)			(x)
+
+#define PFMT_C16			"%s"
+#define PFMT_C8				"%s"
+
+#define _localtime(ptm, ptime)						\
+		do {							\
+			struct tm *__tm__;				\
+									\
+			__tm__ = localtime((ptime));			\
+			_memcpy((ptm), __tm__, sizeof(struct tm));	\
+		} while (false)
+
+#ifdef __KERNEL__
+#define FILE				void
+#define _fopen(pfp, filename, mode)	(-EINVAL)
+#define _fclose(pfp)
+#define _access(filename, mode)		(-EINVAL)
+
+#define _strtok(str, del, next_token)
+
+#include <linux/spinlock.h>
+#define MUTEX_T				spinlock_t
+#define MUTEX_INIT(x)			spin_lock_init(&(x))
+#define MUTEX_LOCK(x)			spin_lock(&(x))
+#define MUTEX_UNLOCK(x)			spin_unlock(&(x))
+#define MUTEX_EXIT(x)
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#define MALLOC(size)		kmalloc(size, GFP_KERNEL)
+#define CALLOC(num, size)	vmalloc(num * size)
+#define FREE(ptr)		\
+	do {			\
+		kfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		\
+	do {			\
+		vfree((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+
+#define TP_PRINTF(fmt, ...)	printk(fmt, ##__VA_ARGS__)
+#define TP_LOG(fp, str)
+
+#else
+#include <sys/time.h>
+#include <unistd.h>
+
+#define _fopen(pfp, filename, mode)	\
+	((!((*(pfp)) = fopen((filename), (mode)))) ? -EFAULT : 0)
+#define _fclose(pfp)			fclose((pfp))
+#define _access(filename, mode)		access((filename), (mode))
+
+#define _strtok(str, del, next_token)	strtok((str), (del))
+
+#ifdef __UEFI_APP__
+#define MUTEX_T				void *
+#define MUTEX_INIT(x)
+#define MUTEX_LOCK(x)
+#define MUTEX_UNLOCK(x)
+#define MUTEX_EXIT(x)
+#else
+#include <pthread.h>
+#define MUTEX_T				pthread_mutex_t
+#define MUTEX_INIT(x)			(pthread_mutex_init(&(x), NULL))
+#define MUTEX_LOCK(x)			(pthread_mutex_lock(&(x)))
+#define MUTEX_UNLOCK(x)			(pthread_mutex_unlock(&(x)))
+#define MUTEX_EXIT(x)			(pthread_mutex_destroy(&(x)))
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MALLOC(size)		malloc(size)
+#define CALLOC(num, size)	calloc(num, size)
+#define FREE(ptr)		\
+	do {			\
+		free((ptr));	\
+		(ptr) = NULL;	\
+	} while (0)
+#define CFREE(ptr)		FREE(ptr)
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#define TP_PRINTF(fmt, ...)							\
+	__android_log_print(ANDROID_LOG_INFO, "ILITEK COMMON", "[%s][%d]" fmt,	\
+			    __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _WIN32 */
+
+#define U82U64(byte, order)	\
+	((u64)((u64)(byte) << ((order) * 8)))
+
+#ifndef PFMT_C16
+#define PFMT_C16			"%ls"
+#endif
+#ifndef PFMT_C8
+#define PFMT_C8				"%hs"
+#endif
+#ifndef PFMT_U8
+#define PFMT_U8				"%hhu"
+#endif
+#ifndef PFMT_U16
+#define PFMT_U16			"%hu"
+#endif
+#ifndef PFMT_X8
+#define PFMT_X8				"%hhx"
+#endif
+#ifndef PFMT_X16
+#define PFMT_X16			"%hx"
+#endif
+
+#ifndef PFMT_X64
+#define PFMT_X64			"%llx"
+#endif
+
+#ifndef TP_PRINTF
+#define TP_PRINTF(fmt, ...)				\
+	do {						\
+		printf(fmt, ##__VA_ARGS__);		\
+		fflush(stdout);				\
+	} while (0)
+#endif
+
+#ifndef TP_LOG
+#define TP_LOG(fp, str)				\
+	do {					\
+		if (!fp)			\
+			break;			\
+		fprintf((fp), PFMT_C8, (str));	\
+		fflush((fp));			\
+	} while (0)
+#endif
+
+#ifndef TP_PRINT
+#define TP_PRINT(_id, level, need_tag, tag, fmt, ...)			\
+	do {								\
+		char *__id__ = (_id);					\
+		u32 __time_ms__;					\
+									\
+		if (level > tp_log_level)				\
+			break;						\
+									\
+		g_str[0] = '\0';					\
+									\
+		if (need_tag) {						\
+			if (!get_time_ms(&__time_ms__))			\
+				_sprintf(g_str, _strlen(g_str),		\
+					"[%7u.%03u]",			\
+					__time_ms__ / 1000,		\
+					__time_ms__ % 1000);		\
+			_sprintf(g_str, _strlen(g_str), PFMT_C8, tag);	\
+		}							\
+									\
+		if (__id__) {						\
+			_sprintf(g_str, _strlen(g_str),			\
+				 "[" PFMT_C8 "] " fmt,			\
+				 __id__, ##__VA_ARGS__);		\
+		} else {						\
+			_sprintf(g_str, _strlen(g_str), " " fmt,	\
+				 ##__VA_ARGS__);			\
+		}							\
+									\
+		if (tp_print_en)					\
+			TP_PRINTF(PFMT_C8, g_str);			\
+		if (g_msg)						\
+			g_msg(level, g_str);				\
+		TP_LOG(tp_fp, g_str);					\
+	} while (0)
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d)	(((n) + (d) - 1) / (d))
+#endif
+
+#ifndef UNUSED
+#define UNUSED(x)		((void)(x))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a)		((sizeof(a) / sizeof(*(a))))
+#endif
+
+#ifndef MIN
+#define MIN(l, r)		(((l) > (r)) ? (r) : (l))
+#endif
+
+#ifndef MAX
+#define MAX(l, r)		(((l) > (r)) ? (l) : (r))
+#endif
+
+#define EILICOMM		200
+#define EILIBUSY		201
+#define EILITIME		202
+#define EILIPROTO		203
+
+enum ilitek_log_level {
+	log_level_none = -1,	/* no log displayed */
+	log_level_err = 0,	/* critical errors */
+	log_level_warn,		/* warnings */
+	log_level_tag,		/* special-required tags */
+	log_level_info,		/* important/UI messages */
+	log_level_msg,		/* non-important messages */
+	log_level_dbg,		/* debugging messages */
+	log_level_pkt,		/* tx/rx packets */
+
+	log_level_max,		/* sentinel */
+};
+
+#define _TP_ERR(fmt, ...)	\
+	TP_PRINT(NULL, log_level_err, false, "", fmt, ##__VA_ARGS__)
+#define _TP_WARN(fmt, ...)	\
+	TP_PRINT(NULL, log_level_warn, false, "", fmt, ##__VA_ARGS__)
+#define _TP_TAG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_tag, false, "", fmt, ##__VA_ARGS__)
+#define _TP_INFO(fmt, ...)	\
+	TP_PRINT(NULL, log_level_info, false, "", fmt, ##__VA_ARGS__)
+#define _TP_MSG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_msg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_DBG(fmt, ...)	\
+	TP_PRINT(NULL, log_level_dbg, false, "", fmt, ##__VA_ARGS__)
+#define _TP_PKT(fmt, ...)	\
+	TP_PRINT(NULL, log_level_pkt, false, "", fmt, ##__VA_ARGS__)
+
+
+#define TP_ERR(id, fmt, ...)	\
+	TP_PRINT(id, log_level_err, true, "[ILITEK][ERR]", fmt, ##__VA_ARGS__)
+#define TP_WARN(id, fmt, ...)	\
+	TP_PRINT(id, log_level_warn, true, "[ILITEK][WARN]", fmt, ##__VA_ARGS__)
+#define TP_TAG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_tag, true, "[ILITEK][TAG]", fmt, ##__VA_ARGS__)
+#define TP_INFO(id, fmt, ...)	\
+	TP_PRINT(id, log_level_info, true, "[ILITEK][INFO]", fmt, ##__VA_ARGS__)
+#define TP_MSG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_msg, true, "[ILITEK][MSG]", fmt, ##__VA_ARGS__)
+#define TP_DBG(id, fmt, ...)	\
+	TP_PRINT(id, log_level_dbg, true, "[ILITEK][DBG]", fmt, ##__VA_ARGS__)
+#define TP_PKT(id, fmt, ...)	\
+	TP_PRINT(id, log_level_pkt, true, "[ILITEK][PKT]", fmt, ##__VA_ARGS__)
+
+enum ilitek_array_type {
+	TYPE_U8 = 0,
+	TYPE_INT,
+	TYPE_U32,
+};
+
+#define TP_ERR_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_err, "[ILITEK][ERR]", tag, type, len, buf)
+#define TP_WARN_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_warn, "[ILITEK][WARN]", tag, type, len, buf)
+#define TP_TAG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_tag, "[ILITEK][TAG]", tag, type, len, buf)
+#define TP_INFO_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_info, "[ILITEK][INFO]", tag, type, len, buf)
+#define TP_MSG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_msg, "[ILITEK][MSG]", tag, type, len, buf)
+#define TP_DBG_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_dbg, "[ILITEK][DBG]", tag, type, len, buf)
+#define TP_PKT_ARR(id, tag, type, len, buf) \
+	tp_log_arr(id, log_level_pkt, "[ILITEK][PKT]", tag, type, len, buf)
+
+extern int tp_log_level;
+extern bool tp_print_en;
+extern char g_str[4096];
+extern FILE *tp_fp;
+
+typedef void (*msg_t)(int, char *);
+extern msg_t g_msg;
+
+struct queue {
+	u32 curr_size;
+	u32 max_size;
+	u8 *buf;
+	u8 *push_ptr;
+	u8 *pop_ptr;
+	u8 *end_ptr;
+	u32 item_size;
+
+	MUTEX_T mutex;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL tp_log_arr(char *id, int level, const char *header,
+		      const char *tag, int type, int len, void *buf);
+
+int __DLL get_time_ms(u32 *t_ms);
+void __DLL set_print_en(bool enable);
+void __DLL set_log_level(int level);
+int __DLL set_log_fopen(WCHAR *filename);
+void __DLL set_log_fclose(void);
+void __DLL set_log_fwrite(char *str);
+
+int queue_init(struct queue *q, u32 item_size, u32 max_items);
+void queue_exit(struct queue *q);
+void queue_push(struct queue *q);
+void queue_pop(struct queue *q);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_main.c b/drivers/input/touchscreen/ilitek/ilitek_main.c
new file mode 100644
index 000000000000..9bb1fe6aa4b7
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_main.c
@@ -0,0 +1,2198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+struct ilitek_ts_data *ts;
+
+u8 driver_ver[] = {
+	DRIVER_VERSION_0, DRIVER_VERSION_1, DRIVER_VERSION_2, DRIVER_VERSION_3,
+	CUSTOMER_H_ID, CUSTOMER_L_ID, TEST_VERSION,
+};
+
+static bool checksum_failed_release = ILITEK_CHECKSUM_FAILED_RELEASE;
+module_param(checksum_failed_release, bool, 0664);
+MODULE_PARM_DESC(checksum_failed_release,
+		 "When packet's checksum is wrong, (default)release all touch point or ignore the packet");
+
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+extern struct tpd_device *tpd;
+#ifdef ILITEK_ENABLE_DMA
+static u8 *I2CDMABuf_va;
+static dma_addr_t I2CDMABuf_pa;
+#endif
+#endif
+
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+struct wake_lock ilitek_wake_lock;
+#endif
+
+#ifdef ILITEK_TUNING_MESSAGE
+static struct sock *ilitek_netlink_sock;
+bool ilitek_debug_flag;
+static u_int ilitek_pid = 100, ilitek_seq = 23;
+#endif
+
+static void __maybe_unused ilitek_udp_reply(void *payload, int size)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int len = NLMSG_SPACE(size);
+	int ret;
+	int pid = ilitek_pid, seq = ilitek_seq;
+
+	TP_DBG(NULL, "[%s] ilitek_debug_flag: %d\n", __func__, ilitek_debug_flag);
+	if (!ilitek_debug_flag)
+		return;
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		TP_ERR(NULL, "alloc skb error\n");
+		return;
+	}
+
+	nlh = nlmsg_put(skb, pid, seq, 0, size, 0);
+	if (!nlh)
+		goto nlmsg_failure;
+
+	nlh->nlmsg_flags = 0;
+	memcpy(NLMSG_DATA(nlh), payload, size);
+
+	NETLINK_CB(skb).portid = 0;	/* from kernel */
+	NETLINK_CB(skb).dst_group = 0;	/* unicast */
+
+	ret = netlink_unicast(ilitek_netlink_sock, skb, pid, MSG_DONTWAIT);
+	if (ret < 0)
+		TP_ERR(NULL, "ilitek send failed, ret: %d\n", ret);
+	return;
+
+nlmsg_failure:
+	kfree_skb(skb);
+
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void __maybe_unused udp_receive(struct sk_buff *skb)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	int count = 0, ret = 0, i = 0;
+	u8 *data;
+	struct nlmsghdr *nlh;
+
+	nlh = (struct nlmsghdr *)skb->data;
+	ilitek_pid = NETLINK_CREDS(skb)->pid;
+	ilitek_seq = nlh->nlmsg_seq;
+
+	TP_DBG(NULL, "netlink received, pid: %d, seq: %d\n",
+		     ilitek_pid, ilitek_seq);
+
+	data = (u8 *) NLMSG_DATA(nlh);
+	count = nlmsg_len(nlh);
+	if (!strcmp(data, "Open!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = true;
+		ilitek_udp_reply(data, sizeof("Open!"));
+	} else if (!strcmp(data, "Close!")) {
+		TP_MSG(NULL, "data is :%s\n", (char *)data);
+		ts->operation_protection = false;
+	} else if (!strcmp(data, "Wifi_Paint_Start") ||
+		   !strcmp(data, "Daemon_Debug_Start")) {
+		ilitek_debug_flag = true;
+	} else if (!strcmp(data, "Wifi_Paint_End") ||
+		   !strcmp(data, "Daemon_Debug_End")) {
+		ilitek_debug_flag = false;
+	}
+
+
+	TP_DBG(NULL, "count = %d  data[count -3] = %d data[count -2] = %c\n",
+	       count, data[count - 3], data[count - 2]);
+	for (i = 0; i < count; i++)
+		//TP_MSG(NULL, "data[%d] = 0x%x\n", i, data[i]);
+	if (data[count - 2] == 'I' && (count == 20 || count == 52) &&
+	    data[0] == 0x77 && data[1] == 0x77) {
+		TP_DBG(NULL, "IOCTL_WRITE CMD = %d\n", data[2]);
+		switch (data[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", data[3]);
+			if (data[3] == 0)
+				ilitek_debug_flag = false;
+			else if (data[3] == 1)
+				ilitek_debug_flag = true;
+			break;
+		case 15:
+			if (data[3] == 0) {
+				ilitek_irq_disable();
+				TP_DBG(NULL, "ilitek_irq_disable.\n");
+			} else {
+				ilitek_irq_enable();
+				TP_DBG(NULL, "ilitek_irq_enable.\n");
+			}
+			break;
+		case 16:
+			ts->operation_protection = data[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 8:
+			TP_MSG(NULL, "get driver version\n");
+			ilitek_udp_reply(driver_ver, 7);
+			break;
+		case 18:
+			TP_DBG(NULL, "firmware update write 33 bytes data\n");
+			ret = ilitek_write(&data[3], 33);
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			if (ret < 0)
+				data[0] = 1;
+			else
+				data[0] = 0;
+			ilitek_udp_reply(data, 1);
+			return;
+		default:
+			return;
+		}
+	} else if (data[count - 2] == 'W') {
+		ret = ilitek_write(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+		if (ret < 0)
+			data[0] = 1;
+		else
+			data[0] = 0;
+		ilitek_udp_reply(data, 1);
+	} else if (data[count - 2] == 'R') {
+		ret = ilitek_read(data, count - 2);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		if (ret < 0)
+			data[count - 2] = 1;
+		else
+			data[count - 2] = 0;
+		ilitek_udp_reply(data, count - 1);
+	}
+#endif /* ILITEK_TUNING_MESSAGE */
+}
+
+static void ilitek_esd_check(struct work_struct *work)
+{
+	int retry = 3;
+	static bool is_first_run = true;
+	static u32 protocol_ver;
+
+	/*
+	 * update protocol version at the first run
+	 */
+	if (is_first_run) {
+		is_first_run = false;
+		protocol_ver = ts->dev->protocol.ver;
+		TP_MSG(NULL, "[ESD] firstly loading protocol ver: %x as ref.\n",
+			protocol_ver);
+	}
+
+	if (ts->operation_protection || ts->esd_skip) {
+		TP_MSG(NULL, "[ESD] operation_protection: %hhu, esd_skip: %hhu\n",
+			ts->operation_protection, ts->esd_skip);
+		goto skip_return;
+	}
+
+	mutex_lock(&ts->ilitek_mutex);
+
+	for (; retry-- > 0;) {
+		if (api_protocol_set_cmd(ts->dev, GET_PTL_VER, NULL) < 0) {
+			TP_ERR(NULL, "[ESD] i2c comm. failed\n");
+			continue;
+		}
+
+		if (protocol_ver != ts->dev->protocol.ver) {
+			TP_ERR(NULL, "unexpected ptl ver (referance)%x vs. %x\n",
+				protocol_ver, ts->dev->protocol.ver);
+			continue;
+		}
+
+		goto pass_return;
+	}
+
+	ilitek_reset(ts->dev->reset_time);
+
+pass_return:
+	mutex_unlock(&ts->ilitek_mutex);
+
+skip_return:
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+}
+
+void ilitek_irq_enable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	enable_irq(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 1);
+}
+
+void ilitek_irq_disable(void)
+{
+	if (!ts->irq_registered)
+		return;
+
+	if (!atomic_read(&ts->irq_enabled))
+		return;
+
+#ifdef MTK_UNDTS
+	mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+	disable_irq_nosync(ts->irq);
+#endif
+
+	atomic_set(&ts->irq_enabled, 0);
+}
+
+#ifdef ILITEK_ENABLE_DMA
+static int ilitek_dma_i2c_read(u8 *buf, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int err;
+
+
+	if (len < 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_recv(client, buf, len);
+	}
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+	err = i2c_master_recv(client, (u8 *)I2CDMABuf_pa, len);
+	if (err < 0)
+		return err;
+
+	memcpy(buf, I2CDMABuf_va, len);
+
+	return 0;
+}
+
+static int ilitek_dma_i2c_write(u8 *cmd, int len)
+{
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+
+	if (len <= 8) {
+		client->ext_flag = client->ext_flag & (~I2C_DMA_FLAG);
+		return i2c_master_send(client, cmd, len);
+	}
+
+	memcpy(I2CDMABuf_va, cmd, len);
+
+	client->ext_flag = client->ext_flag | I2C_DMA_FLAG;
+
+	return i2c_master_send(client, (u8 *)I2CDMABuf_pa, len);
+}
+#endif
+
+static int ilitek_i2c_transfer(struct i2c_msg *msgs, int cnt)
+{
+	int err = 0;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	int count = ILITEK_I2C_RETRY_COUNT;
+
+#ifdef ILITEK_ENABLE_DMA
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		count = ILITEK_I2C_RETRY_COUNT;
+		while (count-- >= 0) {
+			msgs[i].ext_flag = 0;
+			if (msgs[i].flags == I2C_M_RD)
+				err = ilitek_dma_i2c_read(msgs[i].buf, msgs[i].len);
+			else if (!msgs[i].flags)
+				err = ilitek_dma_i2c_write(msgs[i].buf, msgs[i].len);
+
+			if (err < 0) {
+				TP_ERR(NULL, "i2c[0x%hx] dma tx/rx failed, err: %d\n",
+					msgs[i].addr, err);
+				mdelay(20);
+				continue;
+			}
+
+			break;
+		}
+	}
+#else
+	while (count-- >= 0) {
+		err = i2c_transfer(client->adapter, msgs, cnt);
+		if (err < 0) {
+			TP_ERR(NULL, "i2c[0x%hx] tx/rx failed, err: %d\n",
+				msgs[0].addr, err);
+			mdelay(20);
+			continue;
+		}
+		break;
+	}
+#endif
+
+	return err;
+}
+
+static int __maybe_unused ilitek_i2c_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error;
+
+	/*
+	 * Default ILITEK_BL_ADDR. is firstly used.
+	 * if communication failed, change between BL addr. and
+	 * other addr. defined by DTS, then retry.
+	 */
+	static unsigned short addr = ILITEK_BL_ADDR;
+	struct i2c_client *client = (struct i2c_client *)ts->client;
+	struct i2c_msg msgs[2] = {
+		{.addr = addr, .flags = 0, .len = w_len,
+		 .buf = cmd, SCL_RATE(400000)},
+		{.addr = addr, .flags = I2C_M_RD, .len = r_len,
+		 .buf = buf, SCL_RATE(400000)}
+	};
+
+	/*
+	 * IMPORTANT: If I2C repeat start is required, please check with ILITEK.
+	 */
+	if (w_len > 0 && r_len > 0 && !delay_ms) {
+		if (ilitek_i2c_transfer(msgs, 2) < 0) {
+			/* try another i2c addr. (default: 0x41) */
+			addr = (addr == ILITEK_BL_ADDR) ?
+				client->addr : ILITEK_BL_ADDR;
+			msgs[0].addr = msgs[1].addr = addr;
+
+			return ilitek_i2c_transfer(msgs, 2);
+		}
+
+		return 0;
+	}
+
+	if (w_len > 0 && ilitek_i2c_transfer(msgs, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		error = ilitek_i2c_transfer(msgs, 1);
+		if (error < 0)
+			return error;
+	}
+
+	if (delay_ms > 0)
+		mdelay(delay_ms);
+
+	if (r_len > 0 && ilitek_i2c_transfer(msgs + 1, 1) < 0) {
+		/* try another i2c addr. (default: 0x41) */
+		addr = (addr == ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR;
+		msgs[0].addr = msgs[1].addr = addr;
+
+		return ilitek_i2c_transfer(msgs + 1, 1);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_i2c_write(u8 *cmd, int len)
+{
+	return ilitek_i2c_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_i2c_read(u8 *buf, int len)
+{
+	return ilitek_i2c_write_and_read(NULL, 0, 0, buf, len);
+}
+
+static int __maybe_unused ilitek_spi_write_and_read(u8 *cmd, int w_len,
+						    int delay_ms, u8 *buf,
+						    int r_len)
+{
+	int error = 0;
+	u8 *wbuf, *rbuf;
+	struct spi_device *spi = (struct spi_device *)ts->client;
+	struct spi_transfer xfer = {
+		.len = r_len + 4,
+		.speed_hz = ((struct spi_device *)ts->client)->max_speed_hz,
+	};
+	struct spi_message msg;
+
+	if (w_len > 0 && r_len > 0) {
+		error = ilitek_spi_write_and_read(cmd, w_len, delay_ms,
+						  NULL, 0);
+		if (error < 0)
+			return error;
+
+		return ilitek_spi_write_and_read(NULL, 0, 0, buf, r_len);
+	}
+
+	wbuf = CALLOC(4096, sizeof(u8));
+	rbuf = CALLOC(4096, sizeof(u8));
+
+	if (!wbuf || !rbuf) {
+		error = -ENOMEM;
+		goto exit;
+	}
+
+	xfer.tx_buf = wbuf;
+	xfer.rx_buf = rbuf;
+
+	wbuf[1] = 0xAA;
+
+	/* wbuf[0] set as 0x83 for spi data read */
+	if (r_len > 0) {
+		wbuf[0] = 0x83;
+		memset(wbuf + 2, 0, xfer.len - 2);
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfer, &msg);
+		error = spi_sync(spi, &msg);
+		if (error < 0)
+			goto exit;
+
+		TP_DBG(NULL, "[rbuf]: %*phD, len: %d\n", xfer.len, rbuf, xfer.len);
+
+		memcpy(buf, rbuf + 4, r_len);
+	} else if (w_len > 0) {
+		wbuf[0] = 0x82;
+		wbuf[2] = cmd[0];
+		wbuf[3] = 0;
+		memcpy(wbuf + 4, cmd + 1, w_len - 1);
+
+		TP_DBG(NULL, "[wbuf]: %*phD, len: %d\n", 3 + w_len, wbuf, 3 + w_len);
+
+		error = spi_write(spi, wbuf, 3 + w_len);
+		if (error < 0)
+			goto exit;
+
+		if (delay_ms > 0)
+			mdelay(delay_ms);
+	}
+
+exit:
+	CFREE(wbuf);
+	CFREE(rbuf);
+
+	return (error < 0) ? error : 0;
+}
+
+static int __maybe_unused ilitek_spi_write(u8 *cmd, int len)
+{
+	return ilitek_spi_write_and_read(cmd, len, 0, NULL, 0);
+}
+
+static int __maybe_unused ilitek_spi_read(u8 *buf, int len)
+{
+	return ilitek_spi_write_and_read(NULL, 0, 0, buf, len);
+}
+
+int ilitek_write(u8 *cmd, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write(cmd, len);
+#else
+	error = ilitek_i2c_write(cmd, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_read(u8 *buf, int len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_read(buf, len);
+#else
+	error = ilitek_i2c_read(buf, len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+int ilitek_write_and_read(u8 *cmd, int w_len, int delay_ms,
+			  u8 *buf, int r_len)
+{
+	int error;
+
+#ifdef ILITEK_SPI_INTERFACE
+	error = ilitek_spi_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#else
+	error = ilitek_i2c_write_and_read(cmd, w_len, delay_ms, buf, r_len);
+#endif
+
+	return (error < 0) ? error : 0;
+}
+
+void __maybe_unused ilitek_gpio_dbg(void)
+{
+#if defined(ILITEK_GPIO_DEBUG)
+	gpio_direction_output(ts->test_gpio, 0);
+	mdelay(1);
+	gpio_direction_output(ts->test_gpio, 1);
+#endif
+}
+
+void ilitek_reset(int delay)
+{
+	TP_MSG(NULL, "reset_gpio: %d, delay: %d\n", ts->reset_gpio, delay);
+
+	ilitek_irq_disable();
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK && defined(MTK_UNDTS)
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ZERO);
+	mdelay(10);
+
+	mt_set_gpio_mode(ts->reset_gpio, GPIO_CTP_RST_PIN_M_GPIO);
+	mt_set_gpio_dir(ts->reset_gpio, GPIO_DIR_OUT);
+	mt_set_gpio_out(ts->reset_gpio, GPIO_OUT_ONE);
+	mdelay(delay);
+#elif ILITEK_PLAT == ILITEK_PLAT_MTK
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 0);
+	mdelay(10);
+	tpd_gpio_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#else
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 0);
+	mdelay(10);
+	gpio_direction_output(ts->reset_gpio, 1);
+	mdelay(delay);
+#endif
+
+	ilitek_irq_enable();
+}
+
+static int ilitek_free_gpio(void)
+{
+
+#ifndef MTK_UNDTS
+	if (gpio_is_valid(ts->reset_gpio)) {
+		TP_MSG(NULL, "reset_gpio is valid so free\n");
+		gpio_free(ts->reset_gpio);
+	}
+	if (gpio_is_valid(ts->irq_gpio)) {
+		TP_MSG(NULL, "irq_gpio is valid so free\n");
+		gpio_free(ts->irq_gpio);
+	}
+#endif
+
+#if defined(ILITEK_GPIO_DEBUG)
+	if (gpio_is_valid(ts->test_gpio)) {
+		TP_MSG(NULL, "test_gpio is valid so free\n");
+		gpio_free(ts->test_gpio);
+	}
+#endif
+
+	return 0;
+}
+
+static int ilitek_request_pen_input_dev(void)
+{
+	int error;
+	struct input_dev *input;
+
+	int x_min = ts->dev->screen_info.pen_x_min;
+	int y_min = ts->dev->screen_info.pen_y_min;
+	int x_max = ts->dev->screen_info.pen_x_max;
+	int y_max = ts->dev->screen_info.pen_y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	TP_DBG(NULL, "registering pen input device\n");
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+	__set_bit(BTN_TOOL_PEN, input->keybit);		/* In Range */
+	__set_bit(BTN_TOOL_RUBBER, input->keybit);	/* Invert */
+	__set_bit(BTN_STYLUS, input->keybit);		/* Barrel Switch */
+	__set_bit(BTN_TOUCH, input->keybit);		/* Tip Switch */
+
+	input->name = "ILITEK STYLUS";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
+
+	input_set_abs_params(input, ABS_PRESSURE,
+			     ts->dev->screen_info.pressure_min,
+			     ts->dev->screen_info.pressure_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_X,
+			     ts->dev->screen_info.x_tilt_min,
+			     ts->dev->screen_info.x_tilt_max, 0, 0);
+	input_set_abs_params(input, ABS_TILT_Y,
+			     ts->dev->screen_info.y_tilt_min,
+			     ts->dev->screen_info.y_tilt_max, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "register pen device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+
+	ts->pen_input_dev = input;
+
+	return 0;
+}
+
+static int ilitek_request_input_dev(void)
+{
+	int error;
+	int i;
+	struct input_dev *input;
+
+#ifdef ILITEK_USE_MTK_INPUT_DEV
+	input = tpd->dev;
+	if (!input)
+		return -ENOMEM;
+#ifdef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
+			input_set_capability(input, EV_KEY,
+					     tpd_dts_data.tpd_key_local[i]);
+	}
+#endif
+#else
+	int x_min = ts->dev->screen_info.x_min;
+	int y_min = ts->dev->screen_info.y_min;
+	int x_max = ts->dev->screen_info.x_max;
+	int y_max = ts->dev->screen_info.y_max;
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+#endif
+
+	TP_DBG(NULL, "registering touch input device\n");
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	INPUT_MT_INIT_SLOTS(input, MAX(2, ts->dev->tp_info.max_fingers));
+#else
+	input_set_abs_params(input, ABS_MT_TRACKING_ID, 0,
+			     ts->dev->tp_info.max_fingers, 0, 0);
+#endif
+
+#ifdef ILITEK_REPORT_PRESSURE
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+#endif
+
+	for (i = 0; i < ts->dev->tp_info.key_num; i++)
+		set_bit(ts->dev->key.info.keys[i].id & KEY_MAX, input->keybit);
+
+	input_set_capability(input, EV_KEY, KEY_POWER);
+
+#ifndef ILITEK_USE_MTK_INPUT_DEV
+	input->name = ILITEK_TS_NAME;
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = ts->device;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x_min = 0; y_min = 0;
+	x_max = TOUCH_SCREEN_X_MAX; y_max = TOUCH_SCREEN_Y_MAX;
+#endif
+
+#if ILITEK_ROTATE_FLAG
+	swap(x_min, y_min);
+	swap(x_max, y_max);
+#endif
+
+	input_set_abs_params(input, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 32767, 0, 0);
+	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 32767, 0, 0);
+
+	error = input_register_device(input);
+	if (error) {
+		TP_ERR(NULL, "input_register_device failed, err: %d\n", error);
+		input_free_device(input);
+		return error;
+	}
+#endif
+
+	ts->input_dev = input;
+
+	if (ts->dev->tp_info.pen_modes)
+		ilitek_request_pen_input_dev();
+
+	return 0;
+}
+
+static int ilitek_touch_down(int id, int x, int y, int p, int h, int w)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_USE_LCM_RESOLUTION
+	x = (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX /
+		(ts->dev->screen_info.x_max - ts->dev->screen_info.x_min);
+	y = (y - ts->dev->screen_info.y_min) * TOUCH_SCREEN_Y_MAX /
+		(ts->dev->screen_info.y_max - ts->dev->screen_info.y_min);
+#endif
+
+	input_report_key(input, BTN_TOUCH, 1);
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_mt_slot(input, id);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+#endif
+	input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
+	input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
+	input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, h);
+	input_event(input, EV_ABS, ABS_MT_WIDTH_MAJOR, w);
+#ifdef ILITEK_REPORT_PRESSURE
+	input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
+#endif
+#ifndef ILITEK_TOUCH_PROTOCOL_B
+	input_event(input, EV_ABS, ABS_MT_TRACKING_ID, id);
+	input_mt_sync(input);
+#endif
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(x, y, 1);
+			TP_DBG(NULL, "tpd_button(x, y, 1) = tpd_button(%d, %d, 1)\n", x, y);
+		}
+	}
+#endif
+#endif
+#endif
+	return 0;
+}
+
+static int ilitek_touch_release(int id)
+{
+	struct input_dev *input = ts->input_dev;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	if (ts->touch_flag[id] == 1) {
+		TP_DBG(NULL, "release point id = %d\n", id);
+		input_mt_slot(input, id);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+#else
+	input_report_key(input, BTN_TOUCH, 0);
+	input_mt_sync(input);
+#endif
+	set_arr(ts->touch_flag, id, 0);
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef CONFIG_MTK_BOOT
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button) {
+		if (FACTORY_BOOT == get_boot_mode() ||
+		    RECOVERY_BOOT == get_boot_mode()) {
+			tpd_button(0, 0, 0);
+			TP_DBG(NULL, "tpd_button(x, y, 0) = tpd_button(0, 0, 0)\n");
+		}
+	}
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int ilitek_touch_release_all_point(void)
+{
+	struct input_dev *input = ts->input_dev;
+	int i = 0;
+
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+	input_report_key(input, BTN_TOUCH, 0);
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		ilitek_touch_release(i);
+#else
+	for (i = 0; i < ts->dev->tp_info.max_fingers; i++)
+		set_arr(ts->touch_flag, i, 0);
+	ilitek_touch_release(0);
+#endif
+	ts->is_touched = false;
+	input_sync(input);
+	return 0;
+}
+
+static int ilitek_check_key_down(int x, int y)
+{
+	int j;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if ((x >= ts->dev->key.info.keys[j].x &&
+		     x <= ts->dev->key.info.keys[j].x +
+			ts->dev->key.info.x_len) &&
+		    (y >= ts->dev->key.info.keys[j].y &&
+		     y <= ts->dev->key.info.keys[j].y +
+			ts->dev->key.info.y_len)) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 1);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				x = tpd_dts_data.tpd_key_dim_local[j].key_x;
+				y = tpd_dts_data.tpd_key_dim_local[j].key_y;
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key down\n",
+					j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_down(0, x, y, 10, 128, 1);
+			}
+#else
+			x = touch_key_point_maping_array[j].point_x;
+			y = touch_key_point_maping_array[j].point_y;
+			ilitek_touch_down(0, x, y, 10, 128, 1);
+#endif
+#endif
+			ts->dev->key.clicked[j] = true;
+			ts->touch_key_hold_press = true;
+			ts->is_touched = true;
+			TP_DBG(NULL, "Key, Keydown ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int ilitek_check_key_release(int x, int y, int check_point)
+{
+	int j = 0;
+
+	for (j = 0; j < ts->dev->tp_info.key_num; j++) {
+		if (!ts->dev->key.clicked[j])
+			continue;
+
+		if (check_point) {
+			if (x < ts->dev->key.info.keys[j].x ||
+			    x > ts->dev->key.info.keys[j].x + ts->dev->key.info.x_len ||
+			    y < ts->dev->key.info.keys[j].y ||
+			    y > ts->dev->key.info.keys[j].y + ts->dev->key.info.y_len) {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+				input_report_key(ts->input_dev,
+						 ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+				if (tpd_dts_data.use_tpd_button) {
+					TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+					ilitek_touch_release(0);
+				}
+#else
+				ilitek_touch_release(0);
+#endif
+#endif
+				ts->dev->key.clicked[j] = false;
+				ts->touch_key_hold_press = false;
+				TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+					ts->dev->key.info.keys[j].id, x, y,
+					ts->dev->key.clicked[j]);
+				break;
+			}
+		} else {
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+			input_report_key(ts->input_dev, ts->dev->key.info.keys[j].id, 0);
+#else
+#ifndef MTK_UNDTS
+			if (tpd_dts_data.use_tpd_button) {
+				TP_DBG(NULL, "key index=%x, tpd_dts_data.tpd_key_local[%d]=%d key up\n", j, j, tpd_dts_data.tpd_key_local[j]);
+				ilitek_touch_release(0);
+			}
+#else
+			ilitek_touch_release(0);
+#endif
+#endif
+			ts->dev->key.clicked[j] = false;
+			ts->touch_key_hold_press = false;
+			TP_DBG(NULL, "Key, Keyout ID=%d, X=%d, Y=%d, key_status=%d\n",
+				ts->dev->key.info.keys[j].id, x, y,
+				ts->dev->key.clicked[j]);
+			break;
+		}
+	}
+	return 0;
+}
+
+int event_spacing;
+static u8 finger_state;
+static int start_x;
+static int start_y;
+static int current_x;
+static int current_y;
+
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+static struct timeval start_event_time;
+#else
+unsigned long start_event_time_jiffies;
+#endif
+
+static int ilitek_get_time_diff(void)
+{
+	int diff_milliseconds = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+	struct timeval time_now;
+
+	do_gettimeofday(&time_now);
+	diff_milliseconds += (time_now.tv_sec - start_event_time.tv_sec) * 1000;
+
+	if (time_now.tv_usec < start_event_time.tv_usec) {
+		diff_milliseconds -= 1000;
+		diff_milliseconds += (1000 * 1000 + time_now.tv_usec - start_event_time.tv_usec) / 1000;
+	} else
+		diff_milliseconds += (time_now.tv_usec - start_event_time.tv_usec) / 1000;
+
+	if (diff_milliseconds < (-10000))
+		diff_milliseconds = 10000;
+	TP_MSG(NULL, "time_now.tv_sec = %d start_event_time.tv_sec = %d time_now.tv_usec = %d start_event_time.tv_usec = %d diff_milliseconds = %d\n",
+			(int)time_now.tv_sec, (int)start_event_time.tv_sec, (int)time_now.tv_usec, (int)start_event_time.tv_usec, diff_milliseconds);
+#else
+	diff_milliseconds = jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_event_time_jiffies);
+	TP_MSG(NULL, "jiffies_to_msecs(jiffies) = %u jiffies_to_msecs(start_event_time_jiffies) = %u diff_milliseconds = %d\n", jiffies_to_msecs(jiffies),
+			jiffies_to_msecs(start_event_time_jiffies), diff_milliseconds);
+#endif
+	return diff_milliseconds;
+}
+
+static u8 ilitek_double_click_touch(int finger_id, int x, int y,
+					 u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_id > 0) {
+		finger_state = 0;
+		goto out;
+	}
+	if (finger_state == 0 || finger_state == 5) {
+
+		finger_state = 1;
+		start_x = x;
+		start_y = y;
+		current_x = 0;
+		current_y = 0;
+		event_spacing = 0;
+#if ILITEK_GET_TIME_FUNC == ILITEK_GET_TIME_FUNC_WITH_TIME
+		do_gettimeofday(&start_event_time);
+#else
+		start_event_time_jiffies = jiffies;
+#endif
+	} else if (finger_state == 1) {
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 4;
+	} else if (finger_state == 2) {
+		finger_state = 3;
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > (DOUBLE_CLICK_ONE_CLICK_USED_TIME + DOUBLE_CLICK_NO_TOUCH_TIME))
+			finger_state = 0;
+	} else if (finger_state == 3) {
+		current_x = x;
+		current_y = y;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_TOTAL_USED_TIME) {
+			start_x = current_x;
+			start_y = current_y;
+			finger_state = 4;
+		}
+	}
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+static u8 ilitek_double_click_release(u8 finger_state)
+{
+	TP_MSG(NULL, "start finger_state = %d\n", finger_state);
+	if (finger_state == 1) {
+		finger_state = 2;
+		event_spacing = ilitek_get_time_diff();
+		if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME)
+			finger_state = 0;
+	}
+	if (finger_state == 3) {
+		event_spacing = ilitek_get_time_diff();
+		if ((event_spacing < DOUBLE_CLICK_TOTAL_USED_TIME && event_spacing > 50) && (ABSSUB(current_x, start_x) < DOUBLE_CLICK_DISTANCE)
+				&& ((ABSSUB(current_y, start_y) < DOUBLE_CLICK_DISTANCE))) {
+			finger_state = 5;
+			goto out;
+		} else
+			finger_state = 0;
+	} else if (finger_state == 4)
+		finger_state = 0;
+out:
+	TP_MSG(NULL, "finger_state = %d event_spacing = %d\n", finger_state, event_spacing);
+	return finger_state;
+}
+
+void __maybe_unused ilitek_gesture_handle(bool touch, int idx, int x, int y)
+{
+	struct input_dev *input = ts->input_dev;
+
+	if (ts->gesture_status == Gesture_Double_Click) {
+		if (touch) {
+			finger_state = ilitek_double_click_touch(idx, x, y, finger_state);
+			return;
+		}
+		finger_state = ilitek_double_click_release(finger_state);
+
+		if (finger_state != 5)
+			return;
+	}
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_timeout(&ilitek_wake_lock, 5 * HZ);
+#endif
+
+	/* prevent power key being triggered multiple times */
+	if (ts->power_key_triggered)
+		return;
+
+	input_report_key(input, KEY_POWER, 1);
+	input_sync(input);
+	input_report_key(input, KEY_POWER, 0);
+	input_sync(input);
+
+	ts->power_key_triggered = true;
+}
+
+static void ilitek_report_touch_event(struct touch_data *touch, void *_private)
+{
+	struct input_dev *input = ts->input_dev;
+	u8 i, released_cnt = 0;
+
+	u16 x_max = ts->dev->screen_info.x_max;
+	u16 x_min = ts->dev->screen_info.x_min;
+	u16 y_max = ts->dev->screen_info.y_max;
+	u16 y_min = ts->dev->screen_info.y_min;
+
+	/*
+	 * ISR may be activated after registering irq and
+	 * before creating input_dev
+	 */
+	if (!input) {
+		TP_ERR(NULL, "input_dev is not registerred\n");
+		return;
+	}
+
+	for (i = 0; i < touch->cnt; i++) {
+		if (!touch->finger[i].status) {
+			released_cnt++;
+#ifdef ILITEK_TOUCH_PROTOCOL_B
+			ilitek_touch_release(touch->finger[i].id);
+#endif
+			continue;
+		}
+
+		set_arr(ts->touch_flag, touch->finger[i].id, 1);
+
+		touch->finger[i].x = (ILITEK_REVERT_X) ?
+			x_max - touch->finger[i].x + x_min : touch->finger[i].x;
+		touch->finger[i].y = (ILITEK_REVERT_Y) ?
+			y_max - touch->finger[i].y + y_min : touch->finger[i].y;
+
+#if ILITEK_ROTATE_FLAG
+		swap(touch->finger[i].x, touch->finger[i].y);
+#endif
+
+		if (ts->system_suspend) {
+			TP_MSG(NULL, "system is suspend not report point\n");
+			ilitek_gesture_handle(true, i, touch->finger[i].x,
+					      touch->finger[i].y);
+			continue;
+		}
+
+		if (!ts->is_touched)
+			ilitek_check_key_down(touch->finger[i].x,
+					      touch->finger[i].y);
+
+		if (!ts->touch_key_hold_press) {
+			ts->is_touched = true;
+			ilitek_touch_down(touch->finger[i].id,
+					  touch->finger[i].x,
+					  touch->finger[i].y,
+					  touch->finger[i].pressure,
+					  touch->finger[i].height,
+					  touch->finger[i].width);
+		} else {
+			ilitek_check_key_release(touch->finger[i].x,
+						 touch->finger[i].y, 1);
+		}
+	}
+
+	if (touch->cnt == released_cnt) {
+		if (ts->is_touched)
+			ilitek_touch_release_all_point();
+
+		ilitek_check_key_release(0, 0, 0);
+		ts->is_touched = false;
+
+		if (ts->system_suspend)
+			ilitek_gesture_handle(false, 0, 0, 0);
+	}
+
+	input_sync(input);
+}
+
+static void ilitek_report_pen_event(struct pen_data *pen, void *_private)
+{
+	static int curr_tool = BTN_TOOL_PEN;
+	struct input_dev *pen_input = ts->pen_input_dev;
+	int tool;
+
+	u16 x_max = ts->dev->screen_info.pen_x_max;
+	u16 x_min = ts->dev->screen_info.pen_x_min;
+	u16 y_max = ts->dev->screen_info.pen_y_max;
+	u16 y_min = ts->dev->screen_info.pen_y_min;
+
+	if (!pen_input)
+		return;
+
+	tool = (pen->pen.in_range && pen->pen.invert) ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	if (curr_tool != tool) {
+		input_report_key(pen_input, curr_tool, 0);
+		input_sync(pen_input);
+		curr_tool = tool;
+	}
+
+	pen->pen.x = (ILITEK_REVERT_X) ?
+		x_max - pen->pen.x + x_min : pen->pen.x;
+	pen->pen.y = (ILITEK_REVERT_Y) ?
+		y_max - pen->pen.y + y_min : pen->pen.y;
+
+#if ILITEK_ROTATE_FLAG
+	swap(pen->pen.x, pen->pen.y);
+#endif
+
+	input_report_key(pen_input, BTN_TOUCH,
+			 pen->pen.tip_sw || pen->pen.eraser);
+	input_report_key(pen_input, curr_tool, pen->pen.in_range);
+	input_report_key(pen_input, BTN_STYLUS, pen->pen.barrel_sw);
+	input_event(pen_input, EV_ABS, ABS_X, pen->pen.x);
+	input_event(pen_input, EV_ABS, ABS_Y, pen->pen.y);
+	input_event(pen_input, EV_ABS, ABS_PRESSURE, pen->pen.pressure);
+	input_event(pen_input, EV_ABS, ABS_TILT_X, pen->pen.x_tilt);
+	input_event(pen_input, EV_ABS, ABS_TILT_Y, pen->pen.y_tilt);
+
+	input_sync(pen_input);
+}
+
+
+static void ilitek_report_buf(u8 *buf, int size,
+			      bool is_last, void *_private)
+{
+	UNUSED(is_last);
+
+	ilitek_udp_reply(buf, size);
+}
+
+int ilitek_read_data_and_report(void)
+{
+	int error;
+	u8 i, count;
+	struct ilitek_report report;
+
+	memset(&report, 0, sizeof(report));
+	report.cb.report_touch_event = ilitek_report_touch_event;
+	report.cb.report_pen_event = ilitek_report_pen_event;
+	report.cb.report_buf = ilitek_report_buf;
+
+	switch (ts->irq_handle_type) {
+	case irq_type_c_model:
+		memset(ts->buf, 0, sizeof(ts->buf));
+
+		for (i = 0, count = 1; i < count; i++) {
+			ilitek_read(ts->buf, ts->irq_read_len);
+			ilitek_udp_reply(ts->buf, ts->irq_read_len);
+			count = ts->buf[ts->irq_read_len - 1];
+		}
+
+		break;
+
+	case irq_type_debug:
+	case irq_type_normal:
+	default:
+		error = ilitek_report_update(ts->dev, &report);
+		if (error < 0) {
+			if (error == -EILIPROTO && !checksum_failed_release)
+				break;
+
+			if (ts->is_touched) {
+				ilitek_touch_release_all_point();
+				ilitek_check_key_release(0, 0, 0);
+			}
+
+			return error;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+static ISR_FUNC(ilitek_i2c_isr)
+{
+	int error;
+
+	TP_DBG(NULL, "%s\n", __func__);
+
+	atomic_set(&ts->get_INT, 1);
+	ilitek_gpio_dbg();
+
+	ts->esd_skip = true;
+
+	if (atomic_read(&ts->firmware_updating)) {
+		TP_DBG(NULL, "firmware_updating return\n");
+		goto exit;
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_disable();
+#endif
+
+	if (!ts->unhandle_irq) {
+		mutex_lock(&ts->ilitek_mutex);
+		error = ilitek_read_data_and_report();
+		if (error < 0)
+			TP_ERR(NULL, "process error\n");
+		mutex_unlock(&ts->ilitek_mutex);
+	}
+
+#ifdef ILITEK_ISR_PROTECT
+	ilitek_irq_enable();
+#endif
+
+exit:
+	ts->esd_skip = false;
+
+	ISR_RETURN(IRQ_HANDLED);
+}
+
+static int ilitek_request_irq(void)
+{
+	int error;
+
+#ifdef MTK_UNDTS
+	mt_set_gpio_mode(ILITEK_IRQ_GPIO, GPIO_CTP_EINT_PIN_M_EINT);
+	mt_set_gpio_dir(ILITEK_IRQ_GPIO, GPIO_DIR_IN);
+	mt_set_gpio_pull_enable(ILITEK_IRQ_GPIO, GPIO_PULL_ENABLE);
+	mt_set_gpio_pull_select(ILITEK_IRQ_GPIO, GPIO_PULL_UP);
+
+	mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
+	mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, ilitek_i2c_isr, 1);
+	mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#else
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	struct device_node *node;
+
+	node = of_find_matching_node(NULL, touch_of_match);
+	if (node)
+		ts->irq = irq_of_parse_and_map(node, 0);
+#else
+	ts->irq = gpio_to_irq(ts->irq_gpio);
+#endif
+
+	TP_MSG(NULL, "ts->irq: %d\n", ts->irq);
+	if (ts->irq <= 0)
+		return -EINVAL;
+
+	error = request_threaded_irq(ts->irq, NULL, ilitek_i2c_isr,
+				     ts->irq_trigger_type | IRQF_ONESHOT,
+				     "ilitek_touch_irq", ts);
+	if (error) {
+		TP_ERR(NULL, "request threaded irq failed, err: %d\n", error);
+		return error;
+	}
+#endif
+
+	ts->irq_registered = true;
+	atomic_set(&ts->irq_enabled, 1);
+
+	return 0;
+}
+
+static int ilitek_read_fw(char *filename, unsigned char *buf, int size, void *data)
+{
+	int error, fw_size;
+	const struct firmware *fw;
+	struct device *device = (struct device *)data;
+
+	error = request_firmware(&fw, filename, device);
+	if (error) {
+		TP_ERR(NULL, "request fw: %s failed, err:%d\n", filename, error);
+		return error;
+	}
+
+	if (size < fw->size) {
+		fw_size = -EFBIG;
+		goto release_fw;
+	}
+
+	fw_size = fw->size;
+	memcpy(buf, fw->data, fw->size);
+
+release_fw:
+	release_firmware(fw);
+
+	return fw_size;
+}
+
+struct ilitek_update_callback update_cb = {
+	.read_fw = ilitek_read_fw,
+	.update_progress = NULL,
+	.update_fw_file_info = NULL,
+
+	.slave_update_notify = NULL,
+	.update_fw_ic_info = NULL,
+};
+
+int ilitek_upgrade_firmware(char *filename)
+{
+	int error;
+	struct ilitek_fw_handle *handle;
+	struct ilitek_fw_settings setting;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	atomic_set(&ts->firmware_updating, 1);
+	ts->operation_protection = true;
+
+	handle = ilitek_update_init(ts->dev, false, &update_cb, ts->device);
+
+	setting.force_update = false;
+	setting.fw_check_only = false;
+	setting.fw_ver_check = false;
+	setting.retry = 3;
+	ilitek_update_setting(handle, &setting);
+
+	error = ilitek_update_load_fw(handle, filename);
+	if (error < 0)
+		goto err_return;
+	error = ilitek_update_start(handle);
+	if (error < 0)
+		goto err_return;
+
+err_return:
+	ilitek_update_exit(handle);
+
+	ts->operation_protection = false;
+	atomic_set(&ts->firmware_updating, 0);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return error;
+}
+
+
+static int __maybe_unused ilitek_update_thread(void *arg)
+{
+#ifdef ILITEK_BOOT_UPDATE
+	int error;
+
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (kthread_should_stop()) {
+		TP_MSG(NULL, "ilitek_update_thread, stop\n");
+		return -1;
+	}
+
+	mdelay(100);
+
+	error = ilitek_upgrade_firmware("ilitek.ili");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.hex");
+		if (error < 0) {
+			error = ilitek_upgrade_firmware("ilitek.bin");
+			if (error < 0)
+				return error;
+		}
+	}
+
+	error = ilitek_request_input_dev();
+	if (error)
+		return (error < 0) ? error : -EFAULT;
+#endif
+
+	return 0;
+}
+
+void ilitek_suspend(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	ts->esd_skip = true;
+	if (ts->esd_check && ts->esd_workq)
+		cancel_delayed_work_sync(&ts->esd_work);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ts->wake_irq_enabled = (enable_irq_wake(ts->irq) == 0);
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_set_idle(ts->dev, true) < 0)
+				TP_ERR(NULL, "enable Idle mode failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	} else {
+		/*
+		 * Must disable irq before sleep cmd,
+		 * Avoid getting into ISR handling (and do i2c read),
+		 * after sending sleep cmd.
+		 */
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			if (api_protocol_set_cmd(ts->dev, SET_IC_SLEEP,
+						 NULL) < 0)
+				TP_ERR(NULL, "set tp sleep failed\n");
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->power_key_triggered = false;
+	ts->system_suspend = true;
+}
+
+void ilitek_resume(void)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (ts->operation_protection || atomic_read(&ts->firmware_updating)) {
+		TP_MSG(NULL, "operation_protection or firmware_updating return\n");
+		return;
+	}
+
+	if (ts->gesture_status) {
+		ilitek_irq_disable();
+
+		if (ts->low_power_status == Low_Power_Idle) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_set_idle(ts->dev, false);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+
+		if (ts->gesture_status == Gesture_Double_Click)
+			finger_state = 0;
+
+		if (ts->wake_irq_enabled) {
+			disable_irq_wake(ts->irq);
+			ts->wake_irq_enabled = false;
+		}
+	} else {
+		if (ts->dev->protocol.flag == PTL_V3) {
+			/*
+			 * If ILITEK_SLEEP is defined and FW support wakeup cmd,
+			 * the hw reset can be mark.
+			 */
+			ilitek_reset(ts->dev->reset_time);
+		}
+
+		if (ts->low_power_status == Low_Power_Sleep) {
+			mutex_lock(&ts->ilitek_mutex);
+			api_protocol_set_cmd(ts->dev, SET_IC_WAKE, NULL);
+			mutex_unlock(&ts->ilitek_mutex);
+		}
+	}
+
+	ts->esd_skip = false;
+	if (ts->esd_check && ts->esd_workq)
+		queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	ilitek_touch_release_all_point();
+	ilitek_check_key_release(0, 0, 0);
+
+	ts->system_suspend = false;
+
+	ilitek_irq_enable();
+}
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+int ilitek_resume_allwin(struct i2c_client *client)
+{
+	ilitek_resume();
+	return 0;
+}
+#endif
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+static int __maybe_unused ilitek_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+#ifdef CONFIG_QCOM_DRM
+	struct msm_drm_notifier *ev_data = data;
+#else
+	struct fb_event *ev_data = data;
+#endif
+	int *blank;
+
+	TP_MSG(NULL, "FB EVENT event: %lu\n", event);
+
+#ifdef CONFIG_QCOM_DRM
+	if (!ev_data || (ev_data->id != 0))
+		return 0;
+#endif
+	if (ev_data && ev_data->data && event == ILITEK_EVENT_BLANK) {
+		blank = ev_data->data;
+		TP_MSG(NULL, "blank: %d\n", *blank);
+		if (*blank == ILITEK_BLANK_POWERDOWN) {
+			ilitek_suspend();
+		} else if (*blank == ILITEK_BLANK_UNBLANK ||
+			   *blank == ILITEK_BLANK_NORMAL) {
+			ilitek_resume();
+		}
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void __maybe_unused ilitek_early_suspend(struct early_suspend *h)
+{
+	ilitek_suspend();
+}
+
+static void __maybe_unused ilitek_late_resume(struct early_suspend *h)
+{
+	ilitek_resume();
+}
+#endif
+#endif
+
+static void ilitek_get_gpio_num(void)
+{
+#ifdef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	TP_MSG(NULL, "(config_info.wakeup_gpio.gpio) = %d (config_info.int_number) = %d\n", (config_info.wakeup_gpio.gpio), (config_info.int_number));
+	ts->reset_gpio = (config_info.wakeup_gpio.gpio);
+	ts->irq_gpio = (config_info.int_number);
+#else
+#ifdef CONFIG_OF
+	ts->reset_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,reset-gpio", 0);
+	if (ts->reset_gpio < 0)
+		TP_ERR(NULL, "reset_gpio = %d\n", ts->reset_gpio);
+	ts->irq_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,irq-gpio", 0);
+	if (ts->irq_gpio < 0)
+		TP_ERR(NULL, "irq_gpio = %d\n", ts->irq_gpio);
+#endif
+#endif
+#else
+	ts->reset_gpio = ILITEK_RESET_GPIO;
+	ts->irq_gpio = ILITEK_IRQ_GPIO;
+#endif
+
+	TP_MSG(NULL, "reset_gpio = %d irq_gpio = %d\n", ts->reset_gpio, ts->irq_gpio);
+
+
+#if defined(ILITEK_GPIO_DEBUG)
+	do {
+		ts->test_gpio = of_get_named_gpio(ts->device->of_node, "ilitek,test-gpio", 0);
+		if (ts->test_gpio < 0) {
+			TP_ERR(NULL, "test_gpio: %d\n", ts->test_gpio);
+			break;
+		}
+
+		TP_MSG(NULL, "test_gpio: %d\n", ts->test_gpio);
+
+		if (gpio_request(ts->test_gpio, "ilitek-test-gpio")) {
+			TP_ERR(NULL, "request test_gpio failed\n");
+			break;
+		}
+
+		gpio_direction_output(ts->test_gpio, 1);
+
+	} while (0);
+#endif
+}
+
+static int ilitek_request_gpio(void)
+{
+	int ret = 0;
+
+	ts->irq_gpio = -ENODEV;
+	ts->reset_gpio = -ENODEV;
+
+	ilitek_get_gpio_num();
+
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+	if (ts->reset_gpio > 0) {
+		ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio so free retry\n");
+			gpio_free(ts->reset_gpio);
+			ret = gpio_request(ts->reset_gpio, "ilitek-reset-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request reset_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request reset_gpio\n");
+		} else {
+			ret = gpio_direction_output(ts->reset_gpio, 1);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction output rest gpio err\n");
+		}
+	}
+	if (ts->irq_gpio > 0) {
+		ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio so free retry\n");
+			gpio_free(ts->irq_gpio);
+			ret = gpio_request(ts->irq_gpio, "ilitek-irq-gpio");
+			if (ret)
+				TP_ERR(NULL, "Failed to request irq_gpio\n");
+		}
+		if (ret) {
+			TP_ERR(NULL, "Failed to request irq_gpio\n");
+		} else {
+			ret = gpio_direction_input(ts->irq_gpio);
+			if (ret)
+				TP_ERR(NULL, "Failed to direction input irq gpio err\n");
+		}
+	}
+#endif
+	return ret;
+}
+
+int ilitek_create_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to create esd workqueue\n");
+
+	INIT_DELAYED_WORK(&ts->esd_work, ilitek_esd_check);
+	ts->esd_workq = create_singlethread_workqueue("ilitek_esd_wq");
+	if (!ts->esd_workq)
+		return -ENOMEM;
+
+	ts->esd_skip = false;
+	ts->esd_delay = 2 * HZ;
+	queue_delayed_work(ts->esd_workq, &ts->esd_work, ts->esd_delay);
+
+	return 0;
+}
+
+void ilitek_remove_esd_check_workqueue(void)
+{
+	TP_MSG(NULL, "start to remove esd workqueue\n");
+
+	if (ts->esd_workq) {
+		cancel_delayed_work_sync(&ts->esd_work);
+		destroy_workqueue(ts->esd_workq);
+		ts->esd_workq = NULL;
+	}
+}
+
+static int ilitek_register_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+#if ILITEK_PLAT != ILITEK_PLAT_MTK
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+	int error;
+
+	ts->fb_notif.notifier_call = ilitek_notifier_callback;
+
+#ifdef CONFIG_QCOM_DRM
+	error = msm_drm_register_client(&ts->fb_notif);
+#else
+	error = fb_register_client(&ts->fb_notif);
+#endif
+	if (error)
+		TP_ERR(NULL, "register fb_notifier failed, err: %d\n", error);
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	ts->early_suspend.suspend = ilitek_early_suspend;
+	ts->early_suspend.resume = ilitek_late_resume;
+	register_early_suspend(&ts->early_suspend);
+#endif
+#endif /* ILITEK_PLAT != ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	device_enable_async_suspend(ts->device);
+	pm_runtime_set_active(ts->device);
+	pm_runtime_get(ts->device);
+	pm_runtime_enable(ts->device);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_resume_suspend(void)
+{
+#ifdef ILITEK_REGISTER_SUSPEND_RESUME
+
+//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM)
+#if defined(CONFIG_QCOM_DRM)
+#ifdef CONFIG_QCOM_DRM
+	msm_drm_unregister_client(&ts->fb_notif);
+#else
+	fb_unregister_client(&ts->fb_notif);
+#endif
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts->early_suspend);
+#endif
+
+#endif /* ILITEK_REGISTER_SUSPEND_RESUME */
+}
+
+int ilitek_netlink_init(u8 unit)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	NETLINK_KERNEL_CFG_DECLARE(cfg, udp_receive);
+
+	if (ilitek_netlink_sock)
+		ilitek_netlink_exit();
+
+	ilitek_netlink_sock = NETLINK_KERNEL_CREATE(unit, &cfg, udp_receive);
+
+	if (!ilitek_netlink_sock) {
+		TP_ERR(NULL, "netlink_kernel_create failed\n");
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+#endif
+	return -EPERM;
+}
+
+void ilitek_netlink_exit(void)
+{
+#ifdef ILITEK_TUNING_MESSAGE
+	if (ilitek_netlink_sock)
+		netlink_kernel_release(ilitek_netlink_sock);
+
+	ilitek_netlink_sock = NULL;
+#endif
+}
+
+static int __maybe_unused ilitek_alloc_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	I2CDMABuf_va = (u8 *) dma_alloc_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+	if (!I2CDMABuf_va) {
+		TP_ERR(NULL, "ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n");
+		I2CDMABuf_va = (u8 *) dma_alloc_coherent(NULL, ILITEK_DMA_SIZE, &I2CDMABuf_pa, GFP_KERNEL);
+		if (!I2CDMABuf_va) {
+			TP_ERR(NULL, "ilitek [TPD] NULL dma_alloc_coherent error\n");
+			return -ENOMEM;
+		}
+	}
+	memset(I2CDMABuf_va, 0, ILITEK_DMA_SIZE);
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_free_dma(void)
+{
+#ifdef ILITEK_ENABLE_DMA
+	if (I2CDMABuf_va) {
+		dma_free_coherent(&tpd->dev->dev, ILITEK_DMA_SIZE,
+				  I2CDMABuf_va, I2CDMABuf_pa);
+
+		I2CDMABuf_va = NULL;
+		I2CDMABuf_pa = 0;
+
+	}
+#endif
+	return 0;
+}
+
+static int __maybe_unused ilitek_power_on(bool status)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int error;
+
+	TP_MSG(NULL, "%s\n", status ? "POWER ON" : "POWER OFF");
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	input_set_power_enable(&(config_info.input_type), status);
+#else
+
+	if (status) {
+		if (ts->vdd) {
+			error = regulator_enable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_enable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	} else {
+		if (ts->vdd) {
+			error = regulator_disable(ts->vdd);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd fail\n");
+				return error;
+			}
+		}
+		if (ts->vdd_i2c) {
+			error = regulator_disable(ts->vdd_i2c);
+			if (error < 0) {
+				TP_ERR(NULL, "regulator_enable vdd_i2c fail\n");
+				return error;
+			}
+		}
+	}
+
+#ifdef MTK_UNDTS
+	if (status)
+		hwPowerOn(PMIC_APP_CAP_TOUCH_VDD, VOL_3300, "TP");
+#endif
+#endif
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused ilitek_request_regulator(struct ilitek_ts_data *ts)
+{
+#ifdef ILITEK_ENABLE_REGULATOR_POWER_ON
+	int ret = 0;
+	char *vdd_name = "vdd";
+	char *vcc_i2c_name = "vcc_i2c";
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+	vdd_name = "vtouch";
+	ts->vdd = regulator_get(tpd->tpd_dev, vdd_name);
+	tpd->reg = ts->vdd;
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+	}
+#elif ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	ts->vdd = regulator_get(ts->device, vdd_name);
+	if (IS_ERR(ts->vdd)) {
+		TP_ERR(NULL, "regulator_get vdd fail\n");
+		ts->vdd = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n");
+
+	}
+
+	ts->vdd_i2c = regulator_get(ts->device, vcc_i2c_name);
+	if (IS_ERR(ts->vdd_i2c)) {
+		TP_ERR(NULL, "regulator_get vdd_i2c fail\n");
+		ts->vdd_i2c = NULL;
+	} else {
+		ret = regulator_set_voltage(ts->vdd_i2c, 3000000, 3300000);
+		if (ret)
+			TP_ERR(NULL, "Could not set i2c to 3000~3300mv.\n");
+	}
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+#endif /* ILITEK_ENABLE_REGULATOR_POWER_ON */
+
+	return 0;
+}
+
+static void __maybe_unused ilitek_release_regulator(void)
+{
+#if defined(ILITEK_ENABLE_REGULATOR_POWER_ON) && ILITEK_PLAT != ILITEK_PLAT_ALLWIN
+	if (ts->vdd)
+		regulator_put(ts->vdd);
+	if (ts->vdd_i2c)
+		regulator_put(ts->vdd_i2c);
+#endif
+}
+
+void ilitek_register_gesture(struct ilitek_ts_data *ts, bool init)
+{
+	if (init) {
+		device_init_wakeup(ts->device, 1);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+		wake_lock_init(&ilitek_wake_lock, WAKE_LOCK_SUSPEND, "ilitek wakelock");
+#endif
+		return;
+	}
+
+	device_init_wakeup(ts->device, 0);
+
+#ifdef ILITEK_WAKELOCK_SUPPORT
+	wake_lock_destroy(&ilitek_wake_lock);
+#endif
+}
+
+static int _ilitek_write_then_read(unsigned char *wbuf, int wlen,
+				   unsigned char *rbuf, int rlen, void *data)
+{
+	return ilitek_write_and_read(wbuf, wlen, 1, rbuf, rlen);
+}
+
+static int ilitek_read_interrupt_in(unsigned char *rbuf, int rlen,
+				    unsigned int timeout_ms, void *data)
+{
+	return ilitek_write_and_read(NULL, 0, 0, rbuf, rlen);
+}
+
+static void _ilitek_init_ack(unsigned int tout_ms, void *data)
+{
+	UNUSED(tout_ms);
+
+	ilitek_irq_enable();
+	ts->unhandle_irq = true;
+	atomic_set(&ts->get_INT, 0);
+}
+
+static int _ilitek_wait_ack(u8 cmd, unsigned int tout_ms, void *data)
+{
+	unsigned int t_ms = 0;
+	int tmp, error = -ETIME;
+
+	UNUSED(cmd);
+
+	do {
+		tmp = atomic_read(&ts->get_INT);
+		if (tmp) {
+			error = 0;
+			break;
+		}
+
+		udelay(1000);
+		t_ms++;
+	} while (t_ms < tout_ms);
+
+	ts->unhandle_irq = false;
+	ilitek_irq_disable();
+
+	return error;
+}
+
+static void _ilitek_delay(unsigned int delay_ms)
+{
+	mdelay(delay_ms);
+}
+
+static int _ilitek_reset(unsigned int delay_ms, void *data)
+{
+	/* return error if no reset gpio found */
+	if (ts->reset_gpio < 0)
+		return -ENODEV;
+
+	ilitek_reset(delay_ms);
+	return 0;
+}
+
+struct ilitek_ts_callback dev_cb = {
+	.write_then_read = _ilitek_write_then_read,
+	.read_interrupt_in = ilitek_read_interrupt_in,
+	.init_ack = _ilitek_init_ack,
+	.wait_ack = _ilitek_wait_ack,
+	.hw_reset = _ilitek_reset,
+	.re_enum = NULL,
+	.delay_ms = _ilitek_delay,
+	.msg = NULL,
+
+	.write_then_read_direct = NULL,
+	.mode_switch_notify = NULL,
+};
+
+int ilitek_main_probe(void *client, struct device *device)
+{
+	struct ilitek_ts_settings setting;
+
+	TP_MSG(NULL, "driver version: %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu\n",
+		driver_ver[0], driver_ver[1], driver_ver[2], driver_ver[3],
+		driver_ver[4], driver_ver[5], driver_ver[6]);
+
+	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+	if (!ts) {
+		TP_ERR(NULL, "allocate ts failed\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	ts->device = device;
+
+	mutex_init(&ts->ilitek_mutex);
+	ts->unhandle_irq = false;
+
+	ilitek_alloc_dma();
+	ilitek_request_regulator(ts);
+	ilitek_power_on(true);
+	ilitek_request_gpio();
+
+	ilitek_reset(600);
+
+	ts->dev = ilitek_dev_init(interface_i2c, "0", false, &dev_cb, ts);
+	if (!ts->dev)
+		goto err_free_gpio;
+
+	memset(&setting, 0, sizeof(setting));
+	setting.sensor_id_mask = ILITEK_SENSOR_ID_MASK;
+	ilitek_dev_setting(ts->dev, &setting);
+
+	if (api_update_ts_info(ts->dev) < 0)
+		goto err_dev_exit;
+
+	ts->irq_trigger_type = (ts->dev->protocol.flag == PTL_V6) ?
+		IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
+	if (ilitek_request_irq())
+		goto err_dev_exit;
+
+#ifdef ILITEK_BOOT_UPDATE
+	ts->update_thread = kthread_run(ilitek_update_thread, NULL,
+					"ilitek_update_thread");
+	if (IS_ERR(ts->update_thread))
+		goto err_free_irq;
+#else
+	if (ilitek_request_input_dev())
+		goto err_free_irq;
+#endif
+
+	ilitek_register_resume_suspend();
+	ilitek_create_sysfsnode();
+	ilitek_create_tool_node();
+	ilitek_netlink_init(NETLINK_USERSOCK);
+
+	ts->esd_check = ILITEK_ESD_CHECK_ENABLE;
+	if (ts->esd_check)
+		ilitek_create_esd_check_workqueue();
+
+	ts->gesture_status = ILITEK_GESTURE_DEFAULT;
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, true);
+
+	ts->low_power_status = ILITEK_LOW_POWER_DEFAULT;
+
+	return 0;
+
+err_free_irq:
+	free_irq(ts->irq, ts);
+
+err_dev_exit:
+	ilitek_dev_exit(ts->dev);
+
+err_free_gpio:
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+	kfree(ts);
+
+	return -ENODEV;
+}
+
+int ilitek_main_remove(void *client)
+{
+	TP_MSG(NULL, "%s\n", __func__);
+
+	if (!ts)
+		return 0;
+
+	ilitek_netlink_exit();
+
+	if (ts->gesture_status)
+		ilitek_register_gesture(ts, false);
+
+	ilitek_remove_esd_check_workqueue();
+	ilitek_remove_tool_node();
+	ilitek_remove_sys_node();
+	ilitek_release_resume_suspend();
+
+	if (ts->pen_input_dev)
+		input_unregister_device(ts->pen_input_dev);
+
+	if (ts->input_dev)
+		input_unregister_device(ts->input_dev);
+
+#ifndef MTK_UNDTS
+	free_irq(ts->irq, ts);
+#endif
+
+	ilitek_dev_exit(ts->dev);
+
+	ilitek_free_gpio();
+	ilitek_power_on(false);
+	ilitek_release_regulator();
+	ilitek_free_dma();
+
+	kfree(ts);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_platform_init.c b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
new file mode 100644
index 000000000000..0b7f97ab7746
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_platform_init.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+
+#define TPD_OK (0)
+
+#ifdef MTK_UNDTS
+static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
+static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
+struct touch_vitual_key_map_t touch_key_point_maping_array[] = { {key_1}, {key_2}, {key_3}, {key_4} };
+
+static struct i2c_board_info __initdata ilitek_i2c_tpd = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41)
+};
+#endif
+
+/* probe function is used for matching and initializing input device */
+static int /*__devinit*/ tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	TP_MSG(NULL, "TPD probe\n");
+
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -1;
+	}
+
+	ret = ilitek_main_probe(client, &client->dev);
+	if (ret == 0)	// If probe is success, then enable the below flag.
+		tpd_load_status = 1;
+
+	TP_MSG(NULL, "TPD probe done\n");
+	return TPD_OK;
+}
+
+static int tpd_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	strcpy(info->type, TPD_DEVICE);
+	return TPD_OK;
+}
+
+static int /*__devexit*/ tpd_remove(struct i2c_client *client)
+{
+	TP_MSG(NULL, "TPD removed\n");
+	return ilitek_main_remove(client);
+}
+
+/* The I2C device list is used for matching I2C device and I2C device driver. */
+static const struct i2c_device_id tpd_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+
+MODULE_DEVICE_TABLE(i2c, tpd_device_id);
+
+const struct of_device_id touch_dt_match_table[] = {
+	{ .compatible = "mediatek,cap_touch",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, touch_dt_match_table);
+
+static struct i2c_driver tpd_i2c_driver = {
+
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.of_match_table = of_match_ptr(touch_dt_match_table),
+	},
+	.probe = tpd_probe,
+	.remove = tpd_remove,
+	.id_table = tpd_device_id,
+	.detect = tpd_detect,
+};
+
+static int tpd_local_init(void)
+{
+	TP_MSG(NULL, "TPD init device driver\n");
+
+	if (i2c_add_driver(&tpd_i2c_driver) != 0) {
+		TP_ERR(NULL, "Unable to add i2c driver.\n");
+		return -1;
+	}
+	if (tpd_load_status == 0) {
+		TP_ERR(NULL, "Add error touch panel driver.\n");
+		i2c_del_driver(&tpd_i2c_driver);
+		return -1;
+	}
+#ifndef MTK_UNDTS
+	if (tpd_dts_data.use_tpd_button)
+		tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, tpd_dts_data.tpd_key_dim_local);
+
+#else
+	tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);	// initialize tpd button data
+#endif
+	tpd_type_cap = 1;
+	TP_MSG(NULL, "TPD init done\n");
+	return TPD_OK;
+}
+
+#ifdef MTK_UNDTS
+static void tpd_resume(struct early_suspend *h)
+{
+#else
+static void tpd_resume(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD wake up\n");
+	ilitek_resume();
+	TP_MSG(NULL, "TPD wake up done\n");
+}
+
+#ifdef MTK_UNDTS
+static void tpd_suspend(struct early_suspend *h)
+{
+#else
+static void tpd_suspend(struct device *h)
+{
+#endif
+	TP_MSG(NULL, "TPD enter sleep\n");
+	ilitek_suspend();
+	TP_MSG(NULL, "TPD enter sleep done\n");
+}
+
+static struct tpd_driver_t tpd_device_driver = {
+	.tpd_device_name = ILITEK_TS_NAME,
+	.tpd_local_init = tpd_local_init,
+	.suspend = tpd_suspend,
+	.resume = tpd_resume,
+	.tpd_have_button = 1,
+};
+
+static int __init ilitek_touch_driver_init(void)
+{
+	TP_MSG(NULL, "touch panel driver init\n");
+
+#ifdef MTK_UNDTS
+	i2c_register_board_info(2, &ilitek_i2c_tpd, 1);
+#else
+	tpd_get_dts_info();
+#endif
+	if (tpd_driver_add(&tpd_device_driver) < 0)
+		TP_ERR(NULL, "TPD add TP driver failed\n");
+
+	return 0;
+
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "touch panel driver exit\n");
+	tpd_driver_remove(&tpd_device_driver);
+}
+
+#else
+
+#ifdef ILITEK_SPI_INTERFACE
+static int ilitek_touch_driver_probe(struct spi_device *spi)
+{
+	int error;
+
+	spi->bits_per_word = 8;
+	spi->mode = SPI_MODE_0;
+	error = spi_setup(spi);
+	if (error < 0) {
+		TP_ERR(NULL, "SPI setup failed, err: %d\n", error);
+		return error;
+	}
+
+	TP_MSG(NULL, "SPI start probe, max_speed_hz: %d\n", spi->max_speed_hz);
+
+	return ilitek_main_probe(spi, &spi->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct spi_device *spi)
+{
+	REMOVE_RETURN(ilitek_main_remove(spi));
+}
+#else
+static I2C_PROBE_FUNC(ilitek_touch_driver_probe, struct i2c_client *client)
+{
+	if (!client) {
+		TP_ERR(NULL, "i2c client is NULL\n");
+		return -ENODEV;
+	}
+
+	TP_MSG(NULL, "ILITEK client->addr: 0x%x, client->irq: %d\n",
+		client->addr, client->irq);
+
+	return ilitek_main_probe(client, &client->dev);
+}
+
+static REMOVE_FUNC(ilitek_touch_driver_remove, struct i2c_client *client)
+{
+	REMOVE_RETURN(ilitek_main_remove(client));
+}
+#endif
+
+#ifdef CONFIG_OF
+static struct of_device_id ilitek_touch_match_table[] = {
+	{.compatible = "tchip,ilitek",},
+	{},
+};
+#endif
+
+static const struct i2c_device_id ilitek_touch_device_id[] = {
+	{ILITEK_TS_NAME, 0},
+	{},			/* should not omitted */
+};
+MODULE_DEVICE_TABLE(i2c, ilitek_touch_device_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ilitekts_acpi_id[] = {
+	{"ILIT2901", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
+#endif
+
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+static int __maybe_unused ilitek_ts_suspend(struct device *dev)
+{
+	ilitek_suspend();
+	return 0;
+}
+
+static int __maybe_unused ilitek_ts_resume(struct device *dev)
+{
+	ilitek_resume();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_ts_suspend, ilitek_ts_resume);
+#endif
+
+#ifdef ILITEK_SPI_INTERFACE
+static struct spi_driver ilitek_touch_device_driver = {
+#else
+static struct i2c_driver ilitek_touch_device_driver = {
+#endif
+	.driver = {
+		.name = ILITEK_TS_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = ilitek_touch_match_table,
+#endif
+#ifdef CONFIG_ACPI
+		.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
+#endif
+
+#ifndef ILITEK_REGISTER_SUSPEND_RESUME
+		.pm = &ilitek_pm_ops,
+#endif
+	},
+
+	.probe = ilitek_touch_driver_probe,
+	.remove = ilitek_touch_driver_remove,
+
+#if !defined(ILITEK_SPI_INTERFACE)
+	.id_table = ilitek_touch_device_id,
+#endif
+};
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+static const unsigned short normal_i2c[2] = { 0x41, I2C_CLIENT_END };
+struct ctp_config_info config_info = {
+	.input_type = CTP_TYPE,
+	.name = NULL,
+	.int_number = 0,
+};
+
+static int twi_id;
+static int screen_max_x;
+static int screen_max_y;
+static int revert_x_flag;
+static int revert_y_flag;
+static int exchange_x_y_flag;
+static int ctp_get_system_config(void)
+{
+	twi_id = config_info.twi_id;
+	screen_max_x = config_info.screen_max_x;
+	screen_max_y = config_info.screen_max_y;
+	TP_MSG(NULL, "Ilitek: screen_max_x = %d\n", screen_max_x);
+	revert_x_flag = config_info.revert_x_flag;
+	revert_y_flag = config_info.revert_y_flag;
+	exchange_x_y_flag = config_info.exchange_x_y_flag;
+	if ((screen_max_x == 0) || (screen_max_y == 0)) {
+		TP_ERR(NULL, "%s:read config error!\n", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+int ctp_ts_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+
+	if (twi_id == adapter->nr) {
+		strlcpy(info->type, ILITEK_TS_NAME, I2C_NAME_SIZE);
+		return 0;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static struct i2c_board_info i2c_info_dev = {
+	I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41),
+	.platform_data = NULL,
+};
+
+static int add_ctp_device(void)
+{
+	struct i2c_adapter *adap;
+	//script_parser_fetch("ctp_para", "ctp_twi_id", &twi_id, 1);
+	adap = i2c_get_adapter(twi_id);
+	i2c_new_device(adap, &i2c_info_dev);
+	return 0;
+}
+
+static int ilitek_init_allwin(void)
+{
+
+	int ret = 0;
+
+	if (input_fetch_sysconfig_para(&(config_info.input_type))) {
+		TP_ERR(NULL, "Ilitek:  ctp_fetch_sysconfig_para err.\n");
+		return -1;
+	} else {
+		ret = input_init_platform_resource(&(config_info.input_type));
+		if (0 != ret)
+			TP_ERR(NULL, "Ilitek: ctp_ops.init_platform_resource err.\n");
+	}
+
+	if (config_info.ctp_used == 0) {
+		TP_ERR(NULL, "Ilitek: *** if use ctp,please put the sys_config.fex ctp_used set to 1.\n");
+		return -1;
+	}
+
+	if (ctp_get_system_config() < 0)
+		TP_ERR(NULL, "Ilitek: %s:read config fail!\n", __func__);
+
+	add_ctp_device();
+	ilitek_touch_device_driver.address_list = normal_i2c;
+	ilitek_touch_device_driver.detect = ctp_ts_detect;
+	return 0;
+
+}
+
+#endif
+static int __init ilitek_touch_driver_init(void)
+{
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+	if (ilitek_init_allwin() < 0) {
+		TP_ERR(NULL, "ilitek_init_allwin failed.\n");
+		return -ENODEV;
+	}
+#endif
+
+	TP_MSG(NULL, "add ILITEK touch device driver\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	return spi_register_driver(&ilitek_touch_device_driver);
+#else
+	return i2c_add_driver(&ilitek_touch_device_driver);
+#endif
+}
+
+static void __exit ilitek_touch_driver_exit(void)
+{
+	TP_MSG(NULL, "remove touch device driver i2c driver.\n");
+
+#ifdef ILITEK_SPI_INTERFACE
+	spi_unregister_driver(&ilitek_touch_device_driver);
+#else
+	i2c_del_driver(&ilitek_touch_device_driver);
+#endif
+}
+#endif
+
+module_init(ilitek_touch_driver_init);
+module_exit(ilitek_touch_driver_exit);
+MODULE_AUTHOR("ILITEK");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.c b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
new file mode 100644
index 000000000000..945bab688405
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.c
@@ -0,0 +1,3644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_protocol.h"
+
+typedef int (*protocol_func_t)(struct ilitek_ts_device *, void *);
+
+struct protocol_map {
+	u8 cmd;
+	u8 flag;
+	protocol_func_t func;
+	const char *desc;
+};
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} touch_fmts[touch_fmt_max];
+
+static struct {
+	unsigned int size;
+	unsigned int max_cnt;
+} pen_fmts[pen_fmt_max];
+
+#define X(_cmd, _protocol, _cmd_id, _api) \
+	static int _api(struct ilitek_ts_device *, void *);
+ILITEK_CMD_MAP
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api) {_cmd, _protocol, _api, #_cmd_id},
+struct protocol_map protocol_maps[] = { ILITEK_CMD_MAP };
+#undef X
+
+u16 le16(const u8 *p)
+{
+	return p[0] | p[1] << 8;
+}
+
+u16 be16(const u8 *p)
+{
+	return p[1] | p[0] << 8;
+}
+
+u32 le32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val += (p[bytes] << (8 * bytes));
+
+	return val;
+}
+
+u32 be32(const u8 *p, int bytes)
+{
+	u32 val = 0;
+
+	while (bytes--)
+		val = (val << 8) | (*p++);
+
+	return val;
+}
+
+static bool is_2501x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "25011") ||
+	    !strcmp(dev->mcu_info.ic_name, "25012"))
+		return true;
+
+	return false;
+}
+
+bool is_29xx(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	if (!strcmp(dev->mcu_info.ic_name, "2900") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901") ||
+	    !strcmp(dev->mcu_info.ic_name, "2910") ||
+	    !strcmp(dev->mcu_info.ic_name, "2911") ||
+	    !strcmp(dev->mcu_info.ic_name, "2531") ||
+	    !strcmp(dev->mcu_info.ic_name, "2532") ||
+	    !strcmp(dev->mcu_info.ic_name, "2921") ||
+	    !strcmp(dev->mcu_info.ic_name, "2901M") ||
+	    is_2501x(handle))
+		return true;
+
+	return false;
+}
+
+bool _is_231x(char *ic_name)
+{
+	if (!strcmp(ic_name, "2312") || !strcmp(ic_name, "2315"))
+		return true;
+
+	return false;
+}
+
+bool is_231x(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!dev)
+		return false;
+
+	return _is_231x(dev->mcu_info.ic_name);
+}
+
+bool has_hw_key(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle || !dev->tp_info.key_num)
+		return false;
+
+	if (dev->key.info.mode == key_hw ||
+	    dev->key.info.mode == key_hsw)
+		return true;
+
+	return false;
+}
+
+u8 get_protocol_ver_flag(u32 ver)
+{
+	if (((ver >> 16) & 0xFF) == 0x3 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_6 ||
+	     (ver & 0xFFFF00) == BL_PROTOCOL_V1_7)
+		return PTL_V3;
+
+	if (((ver >> 16) & 0xFF) == 0x6 ||
+	    (ver & 0xFFFF00) == BL_PROTOCOL_V1_8)
+		return PTL_V6;
+
+	return PTL_ANY;
+}
+
+void grid_reset(struct grids *grid)
+{
+	grid->mc.need_update = false;
+	grid->sc_x.need_update = false;
+	grid->sc_y.need_update = false;
+	grid->pen_x.need_update = false;
+	grid->pen_y.need_update = false;
+
+	grid->key_mc.need_update = false;
+	grid->key_x.need_update = false;
+	grid->key_y.need_update = false;
+
+	grid->self.need_update = false;
+
+	if (grid->mc.data)
+		_memset(grid->mc.data, 0,
+		       grid->mc.X * grid->mc.Y * sizeof(s32));
+	if (grid->sc_x.data)
+		_memset(grid->sc_x.data, 0,
+		grid->sc_x.X * grid->sc_x.Y * sizeof(s32));
+	if (grid->sc_y.data)
+		_memset(grid->sc_y.data, 0,
+		grid->sc_y.X * grid->sc_y.Y * sizeof(s32));
+	if (grid->pen_x.data)
+		_memset(grid->pen_x.data, 0,
+		grid->pen_x.X * grid->pen_x.Y * sizeof(s32));
+	if (grid->pen_y.data)
+		_memset(grid->pen_y.data, 0,
+		grid->pen_y.X * grid->pen_y.Y * sizeof(s32));
+
+	if (grid->key_mc.data)
+		_memset(grid->key_mc.data, 0,
+		grid->key_mc.X * grid->key_mc.Y * sizeof(s32));
+	if (grid->key_x.data)
+		_memset(grid->key_x.data, 0,
+		grid->key_x.X * grid->key_x.Y * sizeof(s32));
+	if (grid->key_y.data)
+		_memset(grid->key_y.data, 0,
+		grid->key_y.X * grid->key_y.Y * sizeof(s32));
+
+	if (grid->self.data)
+		_memset(grid->self.data, 0,
+		grid->self.X * grid->self.Y * sizeof(s32));
+
+	grid->dmsg.pen_need_update = false;
+	grid->dmsg.touch_need_update = false;
+	_memset(grid->dmsg.touch, 0, sizeof(grid->dmsg.touch));
+	_memset(grid->dmsg.pen, 0, sizeof(grid->dmsg.pen));
+}
+
+void grid_free(struct grids *grid)
+{
+	if (grid->mc.data)
+		CFREE(grid->mc.data);
+	if (grid->sc_x.data)
+		CFREE(grid->sc_x.data);
+	if (grid->sc_y.data)
+		CFREE(grid->sc_y.data);
+	if (grid->pen_x.data)
+		CFREE(grid->pen_x.data);
+	if (grid->pen_y.data)
+		CFREE(grid->pen_y.data);
+
+	if (grid->key_mc.data)
+		CFREE(grid->key_mc.data);
+	if (grid->key_x.data)
+		CFREE(grid->key_x.data);
+	if (grid->key_y.data)
+		CFREE(grid->key_y.data);
+
+	if (grid->self.data)
+		CFREE(grid->self.data);
+}
+
+int grid_alloc(void *handle, struct grids *grid)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int X, Y, key;
+
+	_memset(grid, 0, sizeof(*grid));
+
+	if (!dev)
+		return -EINVAL;
+
+	X = dev->tp_info.x_ch;
+	Y = dev->tp_info.y_ch;
+	key = dev->tp_info.key_num;
+
+	grid->mc.data = (s32 *)CALLOC(X * Y, sizeof(s32));
+	grid->sc_x.data = (s32 *)CALLOC(X, sizeof(s32));
+	grid->sc_y.data = (s32 *)CALLOC(Y, sizeof(s32));
+	grid->pen_x.data = (s32 *)CALLOC(X * 8, sizeof(s32));
+	grid->pen_y.data = (s32 *)CALLOC(Y * 8, sizeof(s32));
+	grid->key_mc.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_x.data = (s32 *)CALLOC(key, sizeof(s32));
+	grid->key_y.data = (s32 *)CALLOC(1, sizeof(s32));
+	grid->self.data = (s32 *)CALLOC(4, sizeof(s32));
+	if (!grid->mc.data || !grid->sc_x.data || !grid->sc_y.data ||
+	    !grid->pen_x.data || !grid->pen_y.data || !grid->key_mc.data ||
+	    !grid->key_x.data || !grid->key_y.data || !grid->self.data)
+		goto err_free;
+
+	grid->mc.X = X; grid->mc.Y = Y;
+	grid->sc_x.X = X; grid->sc_x.Y = 1;
+	grid->sc_y.X = 1; grid->sc_y.Y = Y;
+	grid->pen_x.X = X; grid->pen_x.Y = 8;
+	grid->pen_y.X = 8; grid->pen_y.Y = Y;
+
+	grid->key_mc.X = key; grid->key_mc.Y = 1;
+	grid->key_x.X = key; grid->key_x.Y = 1;
+	grid->key_y.X = 1; grid->key_y.Y = 1;
+
+	grid->self.X = 4, grid->self.Y = 1;
+
+	grid_reset(grid);
+
+	return 0;
+
+err_free:
+	grid_free(grid);
+
+	return -ENOMEM;
+}
+
+static u16 update_crc(u16 crc, u8 newbyte)
+{
+	char i;
+		const u16 crc_poly = 0x8408;
+
+	crc ^= newbyte;
+
+	for (i = 0; i < 8; i++) {
+		if (crc & 0x01)
+			crc = (crc >> 1) ^ crc_poly;
+		else
+			crc = crc >> 1;
+	}
+
+	return crc;
+}
+
+u16 get_crc(u32 start, u32 end,
+	    u8 *buf, u32 buf_size)
+{
+	u16 crc = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		crc = update_crc(crc, buf[i]);
+
+	return crc;
+}
+
+u32 get_checksum(u32 start, u32 end,
+		 u8 *buf, u32 buf_size)
+{
+	u32 sum = 0;
+	u32 i;
+
+	if (end > buf_size || start > buf_size) {
+		TP_WARN(NULL, "start/end addr: 0x%x/0x%x buf size: 0x%x OOB\n",
+			start, end, buf_size);
+		return 0;
+	}
+
+	for (i = start; i < end && i < buf_size; i++)
+		sum += buf[i];
+
+	return sum;
+}
+
+bool is_checksum_matched(u8 checksum, int start, int end,
+			 u8 *buf, int buf_size)
+{
+	u8 check;
+
+	check = ~(get_checksum(start, end, buf, buf_size)) + 1;
+	if (check != checksum) {
+		TP_ERR_ARR(NULL, "[data]", TYPE_U8, end - start, buf + start);
+		TP_ERR(NULL, "checksum : 0x%02x/0x%02x not matched\n",
+			check, checksum);
+		return false;
+	}
+
+	return true;
+}
+
+bool support_mcu_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060009))
+		return false;
+
+	return true;
+}
+
+bool support_sensor_id(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060004))
+		return false;
+
+	return true;
+}
+
+bool support_production_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010803) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_fwid(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE && dev->protocol.ver < 0x010802) ||
+		(dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x060007))
+		return false;
+
+	return true;
+}
+
+bool support_power_status(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if ((dev->ic[0].mode == BL_MODE) ||
+	    (dev->ic[0].mode == AP_MODE && dev->protocol.ver < 0x06000a))
+		return false;
+
+	return true;
+}
+
+int bridge_set_int_monitor(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf3;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int bridge_set_test_mode(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 wbuf[64];
+
+	_memset(wbuf, 0, sizeof(wbuf));
+	wbuf[0] = 0x03;
+	wbuf[1] = 0xf2;
+	wbuf[2] = (enable) ? 0x01 : 0x00;
+
+	return write_then_read_direct(dev, wbuf, 3, NULL, 0);
+}
+
+int reset_helper(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool need_re_enum = true;
+
+	if (dev->_interface == interface_i2c) {
+		/* sw reset if no reset-gpio found */
+		if (!dev->cb.hw_reset ||
+		    dev->cb.hw_reset(dev->reset_time, dev->_private) < 0)
+			return api_protocol_set_cmd(dev, SET_SW_RST,
+						    &need_re_enum);
+
+		return 0;
+	}
+
+	return api_protocol_set_cmd(dev, SET_SW_RST, &need_re_enum);
+}
+
+static int re_enum_helper(struct ilitek_ts_device *dev, u8 enum_type)
+{
+	int error;
+	int retry = 5;
+
+	if (!dev->cb.re_enum)
+		return -EINVAL;
+
+	do {
+		error = dev->cb.re_enum(enum_type, dev->_private);
+		if (!error)
+			return 0;
+
+		TP_WARN(dev->id, "re-enum failed, error: %d, retry: %d\n", error, retry);
+		dev->cb.delay_ms(500);
+	} while (!dev->setting.no_retry && retry--);
+
+	TP_ERR(dev->id, "re-enum retry failed\n");
+
+	return -ENODEV;
+}
+
+int read_interrupt_in(void *handle, u8 *buf, int rlen,
+		      unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_interrupt_in)
+		return -EINVAL;
+
+	error = dev->cb.read_interrupt_in(buf, rlen, timeout_ms,
+					   dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[int-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		 unsigned int timeout_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.read_ctrl_in)
+		return -EINVAL;
+
+	if (dev->quirks & QUIRK_BRIDGE) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA4;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = 0;
+		dev->wbuf[4] = (rlen + 6) & 0xFF;
+		dev->wbuf[5] = ((rlen + 6) >> 8) & 0xFF;
+		dev->wbuf[6] = cmd;
+
+		error = dev->cb.write_then_read_direct(dev->wbuf, 64,
+							dev->rbuf, 64, dev->_private);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.read_ctrl_in(buf, rlen, timeout_ms, dev->_private);
+	if (error < 0)
+		return error;
+
+	TP_PKT_ARR(dev->id, "[ctrl-in]:", TYPE_U8, rlen, buf);
+
+	return 0;
+}
+
+int write_then_read(void *handle, u8 *cmd, int wlen,
+		    u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[wbuf]:", TYPE_U8, wlen, cmd);
+
+	if (!wlen && (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+	    dev->quirks & QUIRK_BRIDGE)) {
+		_memset(dev->wbuf, 0, 64);
+
+		dev->wbuf[0] = 0x03;
+		dev->wbuf[1] = 0xA3;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = rlen;
+
+		error = write_then_read_direct(dev, dev->wbuf, 64, NULL, 0);
+		if (error < 0)
+			return error;
+	}
+
+	error = dev->cb.write_then_read(cmd, wlen, buf, rlen, dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[rbuf]:", TYPE_U8, rlen, buf);
+
+	return (error < 0) ? error : 0;
+}
+
+int write_then_read_direct(void *handle, u8 *cmd, int wlen,
+			   u8 *buf, int rlen)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev->cb.write_then_read_direct)
+		return -EINVAL;
+
+	if (wlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-wbuf]:", TYPE_U8, wlen, cmd);
+
+	error = dev->cb.write_then_read_direct(cmd, wlen, buf, rlen,
+					       dev->_private);
+
+	if (rlen > 0)
+		TP_PKT_ARR(dev->id, "[direct-rbuf]:", TYPE_U8, rlen, buf);
+
+	return error;
+}
+
+int write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct ilitek_ts_callback *cb = &dev->cb;
+
+	u8 ack_cmd;
+
+	TP_DBG(dev->id, "cmd: 0x" PFMT_X8 ", tout_ms: %d\n",
+		cmd[0], timeout_ms);
+
+	if (dev->quirks & QUIRK_WAIT_ACK_DELAY) {
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		cb->delay_ms(dev->setting.wait_ack_delay);
+		return 0;
+	}
+
+	if (dev->setting.no_INT_ack) {
+		/* prevent bridge int handling flow affecting the following read */
+		if (dev->quirks & QUIRK_BRIDGE)
+			bridge_set_int_monitor(dev, false);
+
+		error = write_then_read(dev, cmd, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		/*
+		* for no-INT-ack flow, add delay to prevent
+		* interrupting FW flow too soon, while FW should
+		* be handling previous write command. ex. 0xcd/ 0xc3
+		*/
+		cb->delay_ms(5);
+
+		goto check_busy;
+	}
+
+	if (!cb->init_ack || !cb->wait_ack)
+		return -EINVAL;
+
+	cb->init_ack(timeout_ms, dev->_private);
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, true);
+
+	error = write_then_read(dev, cmd, wlen, NULL, 0);
+	if (error < 0)
+		return error;
+
+	ack_cmd = (cmd[0] == CMD_ACCESS_SLAVE) ? cmd[2] : cmd[0];
+	error = cb->wait_ack(ack_cmd, timeout_ms, dev->_private);
+
+	if (dev->quirks & QUIRK_BRIDGE)
+		bridge_set_int_monitor(dev, false);
+
+	/* cmd[0] should be ILITEK cmd code */
+	if (error < 0) {
+		TP_WARN(dev->id, "wait 0x" PFMT_X8 " ack %d ms timeout, err: %d\n",
+			cmd[0], timeout_ms, error);
+
+		if (dev->setting.no_retry)
+			return -EILITIME;
+
+		goto check_busy;
+	}
+
+	return 0;
+
+check_busy:
+	return api_check_busy(dev, timeout_ms, 10);
+}
+
+/* Common APIs */
+static int api_protocol_get_scrn_res(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_screen_info *screen_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 28);
+	if (error < 0)
+		return error;
+
+	screen_info = (struct ilitek_screen_info *)dev->rbuf;
+
+	dev->screen_info.x_min = screen_info->x_min;
+	dev->screen_info.y_min = screen_info->y_min;
+	dev->screen_info.x_max = screen_info->x_max;
+	dev->screen_info.y_max = screen_info->y_max;
+
+	TP_DBG(dev->id, "screen x: " PFMT_U16 "~" PFMT_U16 ", screen y: " PFMT_U16 "~" PFMT_U16 "\n",
+		dev->screen_info.x_min, dev->screen_info.x_max,
+		dev->screen_info.y_min, dev->screen_info.y_max);
+
+	dev->screen_info.pressure_min = 0;
+	dev->screen_info.pressure_max = 0;
+	dev->screen_info.x_tilt_min = 0;
+	dev->screen_info.x_tilt_max = 0;
+	dev->screen_info.y_tilt_min = 0;
+	dev->screen_info.y_tilt_max = 0;
+	if (dev->protocol.ver > 0x60006) {
+		dev->screen_info.pressure_min = screen_info->pressure_min;
+		dev->screen_info.pressure_max = screen_info->pressure_max;
+		dev->screen_info.x_tilt_min = screen_info->x_tilt_min;
+		dev->screen_info.x_tilt_max = screen_info->x_tilt_max;
+		dev->screen_info.y_tilt_min = screen_info->y_tilt_min;
+		dev->screen_info.y_tilt_max = screen_info->y_tilt_max;
+
+		dev->screen_info.pen_x_min = screen_info->pen_x_min;
+		dev->screen_info.pen_y_min = screen_info->pen_y_min;
+		dev->screen_info.pen_x_max = screen_info->pen_x_max;
+		dev->screen_info.pen_y_max = screen_info->pen_y_max;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v3 *tp_info;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 15);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v3 *)dev->rbuf;
+
+	dev->tp_info.block_num = 2;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+
+	dev->tp_info.support_modes = tp_info->support_modes;
+	if (dev->tp_info.support_modes > 3 || !dev->tp_info.support_modes)
+		dev->tp_info.support_modes = 1;
+
+	TP_DBG(dev->id, "touch ch.(start/end) x: " PFMT_U8 "/" PFMT_U8 ", y: " PFMT_U8 "/" PFMT_U8 "\n",
+		tp_info->touch_start_y, tp_info->touch_end_y,
+		tp_info->touch_start_x, tp_info->touch_end_x);
+
+	if (dev->tp_info.key_num) {
+		/* check v3 key is virtual or hw keys */
+		dev->key.info.mode =
+			(tp_info->touch_start_y == 0xff &&
+			 tp_info->touch_end_y == 0xff &&
+			 tp_info->touch_start_x == 0xff &&
+			 tp_info->touch_end_x == 0xff) ?
+			key_hw : key_vitual;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_tp_info_v6 *tp_info;
+	u8 i;
+
+#define X(_enum, _code, _name) {_code, _name},
+	const struct {
+		const int code;
+		const char *str;
+	} pen_modes[] = { STYLUS_MODES };
+#undef X
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 21);
+	if (error < 0)
+		return error;
+
+	tp_info = (struct ilitek_tp_info_v6 *)dev->rbuf;
+	dev->tp_info.x_resolution = tp_info->x_resolution;
+	dev->tp_info.y_resolution = tp_info->y_resolution;
+	dev->tp_info.x_ch = tp_info->x_ch;
+	dev->tp_info.y_ch = tp_info->y_ch;
+	dev->tp_info.max_fingers = tp_info->max_fingers;
+	dev->tp_info.key_num = tp_info->key_num;
+	dev->tp_info.ic_num = tp_info->ic_num;
+	dev->tp_info.format = tp_info->format;
+	dev->tp_info.support_modes = tp_info->support_modes;
+	dev->tp_info.die_num = tp_info->die_num;
+
+	if (dev->tp_info.format == 5)
+		api_protocol_set_cmd(dev, GET_CRYPTO_INFO, NULL);
+
+	if (dev->tp_info.ic_num > ARRAY_SIZE(dev->ic)) {
+		TP_ERR(dev->id, "invalid ic_num: " PFMT_U8 "\n", dev->tp_info.ic_num);
+		return -EINVAL;
+	}
+	TP_MSG(dev->id, "[Panel Information] Chip count: %u\n",
+		dev->tp_info.ic_num);
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max tp: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	if (dev->protocol.ver < 0x60003)
+		return 0;
+
+	dev->tp_info.block_num = tp_info->block_num;
+	TP_MSG(dev->id, "[Panel Information] Block Number: " PFMT_U8 "\n",
+		dev->tp_info.block_num);
+
+	if (dev->protocol.ver < 0x60007)
+		return 0;
+
+	dev->tp_info.pen_modes = tp_info->pen_modes;
+
+	_memset(dev->pen_mode, 0, sizeof(dev->pen_mode));
+	if (!dev->tp_info.pen_modes)
+		_strcpy(dev->pen_mode, "Disable",
+			sizeof(dev->pen_mode));
+	for (i = 0; i < ARRAY_SIZE(pen_modes); i++) {
+		if (!(tp_info->pen_modes & pen_modes[i].code))
+			continue;
+
+		if (_strlen(dev->pen_mode))
+			_strcat(dev->pen_mode, ",", sizeof(dev->pen_mode));
+
+		_strcat(dev->pen_mode, pen_modes[i].str,
+			sizeof(dev->pen_mode));
+	}
+
+	TP_DBG(dev->id, "pen_modes: " PFMT_U8 "\n", dev->tp_info.pen_modes);
+	TP_MSG(dev->id, "[Panel Information] Pen Mode: " PFMT_C8 "\n",
+		dev->pen_mode);
+
+	dev->tp_info.pen_format = tp_info->pen_format;
+	dev->tp_info.pen_x_resolution = tp_info->pen_x_resolution;
+	dev->tp_info.pen_y_resolution = tp_info->pen_y_resolution;
+	TP_MSG(dev->id, "[Panel Information] Pen Format: 0x" PFMT_X8 "\n",
+		dev->tp_info.pen_format);
+	TP_MSG(dev->id, "[Panel Information] Pen X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.pen_x_resolution,
+		dev->tp_info.pen_y_resolution);
+
+	return 0;
+}
+
+static int api_protocol_get_tp_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+#define X(_enum, _id, _size, _cnt)	\
+	pen_fmts[_id].size = _size;	\
+	pen_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_PEN_REPORT_FORMAT;
+#undef X
+
+	if (dev->protocol.flag == PTL_V3)
+		error = api_protocol_get_tp_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		error = api_protocol_get_tp_info_v6(dev, data);
+	else
+		return -EINVAL;
+
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.max_fingers > 40) {
+		TP_ERR(dev->id, "invalid max fingers: %d > 40\n",
+			dev->tp_info.max_fingers);
+		return -EINVAL;
+	}
+
+	switch (dev->tp_info.format) {
+	case touch_fmt_0x1:
+	case touch_fmt_0x2:
+	case touch_fmt_0x3:
+	case touch_fmt_0x4:
+	case touch_fmt_0x10:
+		if (dev->setting.default_format_enabled)
+			goto default_fmt_enabled;
+
+		dev->fmt.touch_size = touch_fmts[dev->tp_info.format].size;
+		dev->fmt.touch_max_cnt = touch_fmts[dev->tp_info.format].max_cnt;
+		break;
+
+default_fmt_enabled:
+	default:
+	case touch_fmt_0x11:
+	case touch_fmt_0x0:
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+		break;
+	}
+
+	switch (dev->tp_info.pen_format) {
+	case pen_fmt_0x1:
+	case pen_fmt_0x2:
+		dev->fmt.pen_size = pen_fmts[dev->tp_info.pen_format].size;
+		dev->fmt.pen_max_cnt = pen_fmts[dev->tp_info.pen_format].max_cnt;
+		break;
+	default:
+	case pen_fmt_0x0:
+		dev->fmt.pen_size = pen_fmts[pen_fmt_0x0].size;
+		dev->fmt.pen_max_cnt = pen_fmts[pen_fmt_0x0].max_cnt;
+		break;
+	}
+
+	TP_MSG(dev->id, "[Panel Information] X/Y resolution: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_resolution, dev->tp_info.y_resolution);
+	TP_MSG(dev->id, "[Panel Information] X/Y channel: " PFMT_U16 "/" PFMT_U16 "\n",
+		dev->tp_info.x_ch, dev->tp_info.y_ch);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Fingers\n",
+		dev->tp_info.max_fingers);
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " Keys\n",
+		dev->tp_info.key_num);
+
+	TP_MSG(dev->id, "[Panel Information] Support " PFMT_U8 " modes\n",
+		dev->tp_info.support_modes);
+
+	TP_DBG(dev->id, "touch format: 0x" PFMT_X8 ", size: %u bytes, max cnt: %u per packet\n",
+		dev->tp_info.format, dev->fmt.touch_size,
+		dev->fmt.touch_max_cnt);
+
+	if (dev->tp_info.key_num > 0) {
+		error = api_protocol_set_cmd(dev, GET_KEY_INFO, NULL);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v3 *key_info;
+	unsigned int i;
+
+	UNUSED(data);
+
+	/* Only i2c interface has key for V3 */
+	if (dev->_interface != interface_i2c)
+		return 0;
+
+	if (dev->tp_info.key_num > 20) {
+		TP_ERR(dev->id, "key count: " PFMT_U8 " invalid\n", dev->tp_info.key_num);
+		return -EINVAL;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 29);
+	if (error < 0)
+		return error;
+
+	for (i = 0; dev->tp_info.key_num > 5U &&
+	     i < DIV_ROUND_UP(dev->tp_info.key_num, 5U) - 1U; i++) {
+		TP_MSG(dev->id, "read keyinfo again, i: %u\n", i);
+		error = write_then_read(dev, NULL, 0,
+					dev->rbuf + 29 + 5 * i, 25);
+		if (error < 0)
+			return error;
+	}
+
+	key_info = (struct ilitek_key_info_v3 *)dev->rbuf;
+	dev->key.info.x_len = be16(key_info->x_len);
+	dev->key.info.y_len = be16(key_info->y_len);
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = be16(key_info->keys[i].x);
+		dev->key.info.keys[i].y = be16(key_info->keys[i].y);
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct ilitek_key_info_v6 *key_info;
+	unsigned int i, offset;
+
+	UNUSED(data);
+
+	if (dev->tp_info.key_num > ARRAY_SIZE(dev->key.info.keys)) {
+		TP_ERR(dev->id, "exception keycount " PFMT_U8 " > %d\n", dev->tp_info.key_num,
+			(int)ARRAY_SIZE(dev->key.info.keys));
+		return -EINVAL;
+	}
+
+	switch (dev->_interface) {
+	case interface_i2c:
+		if (dev->quirks & QUIRK_WIFI_ITS_I2C ||
+		    dev->quirks & QUIRK_BRIDGE) {
+			error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+			if (error < 0)
+				return error;
+			error = read_ctrl_in(dev, CMD_GET_KEY_INFO,
+					     dev->rbuf,
+					     5 + dev->tp_info.key_num * 5, 2000);
+			if (error < 0)
+				return error;
+			offset = (dev->quirks & QUIRK_BRIDGE) ? 1 : 0;
+		} else {
+			error = write_then_read(dev, dev->wbuf, 1,
+						dev->rbuf, 5 + dev->tp_info.key_num * 5);
+			if (error < 0)
+				return error;
+			offset = 0;
+		}
+		break;
+
+	case interface_usb:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 6;
+		break;
+	case interface_hid_over_i2c:
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = write_then_read(dev, NULL, 0, dev->rbuf, 256);
+		if (error < 0)
+			return error;
+		offset = 4;
+		break;
+	default: return -EINVAL;
+	};
+
+	key_info = (struct ilitek_key_info_v6 *)(dev->rbuf + offset);
+	dev->key.info.mode = key_info->mode;
+	TP_MSG(dev->id, "[Panel Information] key mode: " PFMT_U8 "\n", dev->key.info.mode);
+
+	dev->key.info.x_len = key_info->x_len;
+	dev->key.info.y_len = key_info->y_len;
+	TP_MSG(dev->id, "key_x_len: " PFMT_U16 ", key_y_len: " PFMT_U16 "\n",
+		dev->key.info.x_len, dev->key.info.y_len);
+
+	for (i = 0; i < dev->tp_info.key_num; i++) {
+		dev->key.info.keys[i].id = key_info->keys[i].id;
+		dev->key.info.keys[i].x = key_info->keys[i].x;
+		dev->key.info.keys[i].y = key_info->keys[i].y;
+		TP_MSG(dev->id, "key[%u] id: " PFMT_U8 ", x: " PFMT_U16 ", y: " PFMT_U16 "\n", i,
+			dev->key.info.keys[i].id, dev->key.info.keys[i].x,
+			dev->key.info.keys[i].y);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_key_info(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_key_info_v3(dev, data);
+	else if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_key_info_v6(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_get_ptl_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->protocol.flag = PTL_V6;
+	dev->reset_time = 1000;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->protocol.ver = (dev->rbuf[0] << 16) + (dev->rbuf[1] << 8) +
+			     dev->rbuf[2];
+	TP_MSG(dev->id, "[Protocol Version]: %x.%x.%x\n",
+		(dev->protocol.ver >> 16) & 0xFF,
+		(dev->protocol.ver >> 8) & 0xFF,
+		dev->protocol.ver & 0xFF);
+
+	dev->protocol.flag = get_protocol_ver_flag(dev->protocol.ver);
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->reset_time = 200;
+		break;
+	case PTL_V6:
+		dev->reset_time = 600;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol ver.: 0x%x\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_fw_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->fw_ver, dev->rbuf, 8);
+
+	if (dev->ic[0].mode == BL_MODE) {
+		TP_MSG_ARR(dev->id, "[BL Firmware Version]", TYPE_U8,
+			   8, dev->fw_ver);
+	} else {
+		TP_MSG_ARR(dev->id, "[FW Version]", TYPE_U8, 4, dev->fw_ver);
+		TP_MSG_ARR(dev->id, "[Customer Version]", TYPE_U8,
+			   4, dev->fw_ver + 4);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < ic_num; i++) {
+		dev->ic[i].mode = dev->rbuf[i * 2];
+
+		if (dev->ic[i].mode == AP_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "AP");
+		else if (dev->ic[i].mode == BL_MODE)
+			_sprintf(dev->ic[i].mode_str, 0, "BL");
+		else
+			_sprintf(dev->ic[i].mode_str, 0, "UNKNOWN");
+	}
+
+	TP_MSG(dev->id, "[Current Mode] Master: 0x" PFMT_X8 " " PFMT_C8 "\n",
+		dev->ic[0].mode, dev->ic[0].mode_str);
+	for (i = 1; i < ic_num; i++)
+		TP_MSG(dev->id, "[Current Mode] Slave[" PFMT_U8 "]: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, dev->ic[i].mode, dev->ic[i].mode_str);
+
+	return 0;
+}
+
+static int api_protocol_power_status(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 func, lvd_level_sel;
+
+	if (!data)
+		return -EFAULT;
+
+	if (!support_power_status(dev)) {
+		_memset(&dev->pwr, 0, sizeof(dev->pwr));
+		return 0;
+	}
+
+	func = ((*(u16 *)data) >> 8) & 0xff;
+	lvd_level_sel = (*(u16 *)data) & 0xff;
+
+	dev->wbuf[1] = func;
+	switch (func) {
+	/* get level select */
+	case 0x02:
+		dev->wbuf[2] = lvd_level_sel;
+		return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+
+	/* clear flag */
+	case 0x00:
+		return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+
+	/* get flag */
+	case 0x01:
+		error = write_then_read(dev, dev->wbuf, 2,
+					dev->rbuf, 4);
+		if (error < 0)
+			return error;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev->pwr.header = be16(dev->rbuf);
+	dev->pwr.vdd33_lvd_flag = dev->rbuf[2];
+	dev->pwr.vdd33_lvd_level_sel = dev->rbuf[3];
+
+	TP_DBG(dev->id, "[Power-Status] header: 0x" PFMT_X16 ", flag: 0x" PFMT_X8 ", level_sel: 0x" PFMT_X8 "\n",
+		dev->pwr.header, dev->pwr.vdd33_lvd_flag,
+		dev->pwr.vdd33_lvd_level_sel);
+
+	return 0;
+}
+
+static int api_protocol_get_sensor_id(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_sensor_id(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+	if (error < 0)
+		return error;
+
+	dev->sensor.header = be16(dev->rbuf);
+	dev->sensor.id = dev->rbuf[2];
+
+	TP_MSG(dev->id, "[Sensor ID] header: 0x" PFMT_X16 ", id: 0x" PFMT_X8 "\n",
+		dev->sensor.header,
+		(u8)(dev->sensor.id & dev->setting.sensor_id_mask));
+
+	return 0;
+}
+
+static int api_protocol_get_product_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_production_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->product_info, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[Production Info]", TYPE_U8, 8, dev->product_info);
+
+	return 0;
+}
+
+static int api_protocol_get_fwid(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	/* return 0 to skip error check */
+	if (!support_fwid(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->customer_id = le16(dev->rbuf);
+	dev->fwid = le16(dev->rbuf + 2);
+
+	TP_MSG(dev->id, "[Customer ID] 0x%04x\n", dev->customer_id);
+	TP_MSG(dev->id, "[FWID] 0x%04x\n", dev->fwid);
+
+	return 0;
+}
+
+static int api_protocol_get_crypto_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	u16 __MAYBE_UNUSED crypto_ver;
+	u32 crypto_opt;
+
+	UNUSED(data);
+
+	/*
+	 * encrypted report format should be supported after AP v6.0.8
+	 * set report format to 0 if protocol version not matched or
+	 * crypto info say it's not supported.
+	 */
+	if (dev->protocol.ver < 0x060008 ||
+	    write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6) < 0) {
+		dev->tp_info.format = 0;
+		return 0;
+	}
+
+	crypto_ver = le16(dev->rbuf);
+	crypto_opt = le32(dev->rbuf + 2, 4);
+
+	TP_MSG(dev->id, "[Encrypt Ver.] 0x%x\n", crypto_ver);
+	TP_MSG(dev->id, "[Encrypt Options] 0x%x\n", crypto_opt);
+
+	if (!(crypto_opt & 1))
+		dev->tp_info.format = 0;
+
+	return 0;
+}
+
+static int api_protocol_get_hid_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060009)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 6);
+	if (error < 0)
+		return error;
+
+	_memcpy(&dev->hid_info, dev->rbuf, sizeof(dev->hid_info));
+
+	TP_MSG(dev->id, "vid/pid/rev: 0x%x/0x%x/0x%x\n",
+		dev->hid_info.vid, dev->hid_info.pid, dev->hid_info.rev);
+
+	return 0;
+}
+
+static bool is_special_char(char c)
+{
+	return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+		(c >= '0' && c <= '9')) ? false : true;
+}
+
+static int api_protocol_get_mcu_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_ver {
+		u16 ic_name;
+		u8 df_start_addr[3];
+		u8 df_size;
+
+		char module_name[26];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, use GET_MCU_VER (0x61) cmd
+	 */
+	if (dev->protocol.flag == PTL_V6 && support_mcu_info(dev)) {
+		error = api_protocol_set_cmd(dev, GET_MCU_INFO, NULL);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1,
+					dev->rbuf, 32);
+		if (error < 0)
+			return error;
+
+		parser = (struct mcu_ver *)dev->rbuf;
+
+		_memset(dev->mcu_info.ic_name, 0,
+			sizeof(dev->mcu_info.ic_name));
+		_sprintf(dev->mcu_info.ic_name, 0, "%04x", parser->ic_name);
+
+		_memset(dev->mcu_info.module_name, 0,
+			sizeof(dev->mcu_info.module_name));
+		_memcpy(dev->mcu_info.module_name, parser->module_name,
+			sizeof(parser->module_name));
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		if (is_29xx(dev)) {
+			/* modify reset time to 100ms for 29xx ICs */
+			dev->reset_time = 100;
+
+			/* set mm_addr for bin file update */
+			dev->mcu_info.mm_addr =
+				is_2501x(dev) ? MM_ADDR_2501X : MM_ADDR_29XX;
+			dev->mcu_info.min_addr = START_ADDR_29XX;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		} else {
+			dev->mcu_info.mm_addr = MM_ADDR_LEGO;
+			dev->mcu_info.min_addr = START_ADDR_LEGO;
+			dev->mcu_info.max_addr = END_ADDR_LEGO;
+		}
+	}
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+	if (!strcmp(dev->mcu_info.ic_name, "2133"))
+		_sprintf(dev->mcu_info.ic_name, 0, "2132S");
+
+	_memset(dev->mcu_info.ic_full_name, 0,
+		sizeof(dev->mcu_info.ic_full_name));
+	_sprintf(dev->mcu_info.ic_full_name, 0,
+		"ILI" PFMT_C8, dev->mcu_info.ic_name);
+
+	TP_MSG(dev->id, "[MCU Kernel Version] " PFMT_C8 "\n",
+		dev->mcu_info.ic_full_name);
+	TP_MSG(dev->id, "[Module Name]: [" PFMT_C8 "]\n",
+		dev->mcu_info.module_name);
+
+	return 0;
+}
+
+static int api_protocol_get_mcu_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	unsigned int i;
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+	struct __PACKED__ mcu_info {
+		char ic_name[5];
+		char mask_ver[2];
+		u8 mm_addr[3];
+		char module_name[18];
+		u8 reserve[4];
+	} *parser;
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+	UNUSED(data);
+
+	/*
+	 * GET_MCU_INFO (0x62) cmd only support V6 and BL > v1.8.2 and AP > v6.0.7
+	 * otherwise, return 0 to skip this command.
+	 */
+	if (dev->protocol.flag != PTL_V6 || !support_mcu_info(dev))
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	if (error < 0)
+		return error;
+
+	parser = (struct mcu_info *)dev->rbuf;
+
+	_memset(dev->mcu_info.ic_name, 0, sizeof(dev->mcu_info.ic_name));
+	_memcpy(dev->mcu_info.ic_name, parser->ic_name,
+		sizeof(parser->ic_name));
+
+	_memcpy(dev->mcu_info.module_name, parser->module_name,
+		sizeof(parser->module_name));
+	dev->mcu_info.mm_addr = le32(parser->mm_addr, 3);
+
+	for (i = 0; i < sizeof(dev->mcu_info.module_name); i++) {
+		if (is_special_char(dev->mcu_info.module_name[i]))
+			dev->mcu_info.module_name[i] = 0;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_fs_info(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->sine.start & 0xFF;
+	dev->wbuf[2] = freq->sine.start >> 8;
+	dev->wbuf[3] = freq->sine.end & 0xFF;
+	dev->wbuf[4] = freq->sine.end >> 8;
+	dev->wbuf[5] = freq->sine.step;
+	dev->wbuf[6] = freq->mc_swcap.start & 0xFF;
+	dev->wbuf[7] = freq->mc_swcap.start >> 8;
+	dev->wbuf[8] = freq->mc_swcap.end & 0xFF;
+	dev->wbuf[9] = freq->mc_swcap.end >> 8;
+	dev->wbuf[10] = freq->mc_swcap.step;
+	dev->wbuf[11] = freq->sc_swcap.start & 0xFF;
+	dev->wbuf[12] = freq->sc_swcap.start >> 8;
+	dev->wbuf[13] = freq->sc_swcap.end & 0xFF;
+	dev->wbuf[14] = freq->sc_swcap.end >> 8;
+	dev->wbuf[15] = freq->sc_swcap.step;
+	dev->wbuf[16] = freq->frame_cnt & 0xFF;
+	dev->wbuf[17] = freq->frame_cnt >> 8;
+	dev->wbuf[18] = freq->scan_type;
+
+	if (dev->protocol.ver < 0x60005)
+		return write_then_read(dev, dev->wbuf, 19, NULL, 0);
+
+	do {
+		if (dev->protocol.ver < 0x60009) {
+			error = write_then_read(dev, dev->wbuf, 19,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[16] = freq->dump1.start & 0xFF;
+		dev->wbuf[17] = freq->dump1.start >> 8;
+		dev->wbuf[18] = freq->dump1.end & 0xFF;
+		dev->wbuf[19] = freq->dump1.end >> 8;
+		dev->wbuf[20] = freq->dump1.step;
+		dev->wbuf[21] = freq->dump1_val;
+		dev->wbuf[22] = freq->dump2.start & 0xFF;
+		dev->wbuf[23] = freq->dump2.start >> 8;
+		dev->wbuf[24] = freq->dump2.end & 0xFF;
+		dev->wbuf[25] = freq->dump2.end >> 8;
+		dev->wbuf[26] = freq->dump2.step;
+		dev->wbuf[27] = freq->dump2_val;
+		dev->wbuf[28] = freq->frame_cnt & 0xFF;
+		dev->wbuf[29] = freq->frame_cnt >> 8;
+		dev->wbuf[30] = freq->scan_type;
+
+		if (dev->protocol.ver < 0x6000a) {
+			error = write_then_read(dev, dev->wbuf, 31,
+						dev->rbuf, 5);
+			break;
+		}
+
+		dev->wbuf[31] = freq->mc_frame_cnt & 0xFF;
+		dev->wbuf[32] = freq->mc_frame_cnt >> 8;
+		dev->wbuf[33] = freq->dump_frame_cnt & 0xFF;
+		dev->wbuf[34] = freq->dump_frame_cnt >> 8;
+
+		error = write_then_read(dev, dev->wbuf, 35, dev->rbuf, 5);
+
+	} while (0);
+
+	if (error < 0)
+		return error;
+
+	freq->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != 0x5a || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			freq->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_short_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct short_settings *_short = (struct short_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[short info] dump1: 0x" PFMT_X8 ", dump2: 0x" PFMT_X8 ", vref: 0x" PFMT_X8 ", postidle: 0x" PFMT_X16 "\n",
+		_short->dump_1, _short->dump_2,
+		_short->v_ref_L, _short->post_idle);
+
+	dev->wbuf[1] = _short->dump_1;
+	dev->wbuf[2] = _short->dump_2;
+	dev->wbuf[3] = _short->v_ref_L;
+	dev->wbuf[4] = _short->post_idle & 0xFF;
+	dev->wbuf[5] = (_short->post_idle >> 8) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 6, NULL, 0);
+}
+
+static int api_protocol_set_open_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct open_settings *open = (struct open_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id,
+		"[open info] freq.: " PFMT_U16 ", gain: 0x" PFMT_X8 ", gain_rfb: 0x" PFMT_X8
+		", afe_res_sel: 0x" PFMT_X8 ", mc_fsel: 0x" PFMT_X8 "\n",
+		open->freq, open->gain, open->gain_rfb,
+		open->afe_res_sel, open->mc_fsel);
+
+	dev->wbuf[wlen++] = open->freq & 0xFF;
+	dev->wbuf[wlen++] = (open->freq >> 8) & 0xFF;
+	dev->wbuf[wlen++] = open->gain;
+	dev->wbuf[wlen++] = open->gain_rfb;
+	dev->wbuf[wlen++] = open->afe_res_sel;
+	dev->wbuf[wlen++] = open->mc_fsel;
+
+	if (dev->protocol.ver > 0x060009) {
+		TP_DBG(dev->id, "[open info] frame: " PFMT_U16 "\n",
+			open->frame);
+
+		dev->wbuf[wlen++] = open->frame & 0xFF;
+		dev->wbuf[wlen++] = (open->frame >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_charge_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error, i;
+	struct charge_curve_settings *curve =
+		(struct charge_curve_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "charge-curve info. scan mode: 0x" PFMT_U8 "\n",
+		curve->scan_mode);
+
+	dev->wbuf[1] = curve->scan_mode;
+	dev->wbuf[2] = curve->dump.start & 0xFF;
+	dev->wbuf[3] = (curve->dump.start >> 8) & 0xFF;
+	dev->wbuf[4] = curve->dump.end & 0xFF;
+	dev->wbuf[5] = (curve->dump.end >> 8) & 0xFF;
+	dev->wbuf[6] = curve->dump.step;
+	dev->wbuf[7] = curve->dump.post_idle & 0xFF;
+	dev->wbuf[8] = (curve->dump.post_idle >> 8) & 0xFF;
+	dev->wbuf[9] = curve->dump.fix_val & 0xFF;
+	dev->wbuf[10] = (curve->dump.fix_val >> 8) & 0xFF;
+	dev->wbuf[11] = curve->charge.start & 0xFF;
+	dev->wbuf[12] = (curve->charge.start >> 8) & 0xFF;
+	dev->wbuf[13] = curve->charge.end & 0xFF;
+	dev->wbuf[14] = (curve->charge.end >> 8) & 0xFF;
+	dev->wbuf[15] = curve->charge.step;
+	dev->wbuf[16] = curve->charge.post_idle & 0xFF;
+	dev->wbuf[17] = (curve->charge.post_idle >> 8) & 0xFF;
+	dev->wbuf[18] = curve->charge.fix_val & 0xFF;
+	dev->wbuf[19] = (curve->charge.fix_val >> 8) & 0xFF;
+	dev->wbuf[20] = curve->c_sub & 0xFF;
+	dev->wbuf[21] = (curve->c_sub >> 8) & 0xFF;
+	dev->wbuf[22] = curve->frame_cnt & 0xFF;
+	dev->wbuf[23] = (curve->frame_cnt >> 8) & 0xFF;
+
+	for (i = 0; i < (int)ARRAY_SIZE(curve->pt); i++) {
+		dev->wbuf[24 + i * 4] = curve->pt[i].x & 0xFF;
+		dev->wbuf[24 + i * 4 + 1] = (curve->pt[i].x >> 8) & 0xFF;
+		dev->wbuf[24 + i * 4 + 2] = curve->pt[i].y & 0xFF;
+		dev->wbuf[24 + i * 4 + 3] = (curve->pt[i].y >> 8) & 0xFF;
+	}
+
+	error = write_then_read(dev, dev->wbuf, 60, dev->rbuf, 5);
+	if (error < 0)
+		return error;
+
+	curve->packet_steps = le16(dev->rbuf + 3);
+
+	if (dev->rbuf[0] != AP_MODE || dev->rbuf[1] != 0xa5 || dev->rbuf[2]) {
+		TP_ERR(dev->id, "invalid header: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 ", total steps: " PFMT_U16 "\n",
+			dev->rbuf[0], dev->rbuf[1], dev->rbuf[2],
+			curve->packet_steps);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_p2p_info(struct ilitek_ts_device *dev, void *data)
+{
+	struct p2p_settings *p2p = (struct p2p_settings *)data;
+	int wlen = 1;
+
+	if (!data)
+		return -EINVAL;
+
+	TP_DBG(dev->id, "[p2p info] frame_cnt.: " PFMT_U16 ", type: 0x" PFMT_X8 "\n",
+		p2p->frame_cnt, p2p->type);
+
+	dev->wbuf[wlen++] = p2p->frame_cnt & 0xFF;
+	dev->wbuf[wlen++] = (p2p->frame_cnt >> 8) & 0xFF;
+
+	if (dev->protocol.ver > 0x060009) {
+		dev->wbuf[wlen++] = p2p->type & 0xFF;
+		dev->wbuf[wlen++] = p2p->freq & 0xFF;
+		dev->wbuf[wlen++] = (p2p->freq >> 8) & 0xFF;
+	}
+
+	return write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+}
+
+static int api_protocol_set_pen_fs_info(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	struct freq_settings *freq = (struct freq_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = freq->pen.mode;
+	dev->wbuf[2] = freq->pen.start & 0xFF;
+	dev->wbuf[3] = freq->pen.start >> 8;
+	dev->wbuf[4] = freq->pen.end & 0xFF;
+	dev->wbuf[5] = freq->pen.end >> 8;
+	dev->wbuf[6] = freq->frame_cnt & 0xFF;
+	dev->wbuf[7] = freq->frame_cnt >> 8;
+
+	error = write_then_read(dev, dev->wbuf, 8, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	return 0;
+}
+
+static int api_protocol_get_core_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->core_ver, dev->rbuf, 8);
+
+	TP_MSG_ARR(dev->id, "[CoreVersion]", TYPE_U8, 4, dev->core_ver);
+
+	return 0;
+}
+
+static int api_protocol_get_tuning_ver(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 8);
+	if (error < 0)
+		return error;
+
+	_memcpy(dev->tuning_ver, dev->rbuf, 4);
+
+	TP_MSG_ARR(dev->id, "[TurningVersion]", TYPE_U8, 4, dev->tuning_ver);
+
+	return 0;
+}
+
+static int api_protocol_set_sw_reset(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	int wlen = 1;
+	bool need_re_enum = (data) ? *(bool *)data : false;
+	bool force_reset = (!data) ? true : false;
+
+	/* make sure touch report in default I2C-HID mode after force reset */
+	if (dev->_interface == interface_hid_over_i2c && !force_reset)
+		return 0;
+
+	dev->wbuf[1] = 0;
+	error = write_then_read(dev, dev->wbuf, wlen, dev->rbuf, 0);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(dev->reset_time);
+
+	if (dev->_interface == interface_usb && need_re_enum)
+		return re_enum_helper(dev, enum_sw_reset);
+
+	return 0;
+}
+
+static int api_protocol_get_sys_busy(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	if (data)
+		*(u8 *)data = 0;
+
+	_memset(dev->rbuf, 0, 64);
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 1);
+	if (error < 0)
+		return error;
+
+	if (data)
+		*(u8 *)data = dev->rbuf[0];
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 i, ic_num = (data) ? *(u8 *)data : 1;
+
+	if (ic_num > ARRAY_SIZE(dev->ic))
+		return -EINVAL;
+
+	/*
+	 * No need to get/print AP CRC by 0xC7 in BL mode,
+	 * and 2501x ICs would get wrong crc in BL.
+	 */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	error = write_then_read(dev, dev->wbuf, 1,
+				dev->rbuf, 2 * ic_num);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	TP_MSG(dev->id, "[FW CRC] Master: 0x%x\n", dev->ic[0].crc[0]);
+
+	for (i = 1; i < ic_num; i++) {
+		dev->ic[i].crc[0] = le16(dev->rbuf + 2 * i);
+		TP_MSG(dev->id, "[FW CRC] Slave[" PFMT_U8 "]: 0x%x\n",
+			i, dev->ic[i].crc[0]);
+	}
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error, rlen;
+
+	UNUSED(data);
+
+	rlen = (is_231x(dev)) ? 4 : 2;
+
+	if (dev->_interface == interface_i2c) {
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		dev->cb.delay_ms(600);
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	} else {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+	}
+
+	dev->ic[0].crc[0] = le16(dev->rbuf);
+	if (is_231x(dev))
+		dev->ic[0].crc[0] |= (le16(dev->rbuf + 2) << 16);
+
+	TP_MSG(dev->id, "[Check Code] AP: 0x%x\n", dev->ic[0].crc[0]);
+
+	return 0;
+}
+
+static int api_protocol_get_ap_crc(struct ilitek_ts_device *dev, void *data)
+{
+	if (dev->protocol.flag == PTL_V6)
+		return api_protocol_get_ap_crc_v6(dev, data);
+	else if (dev->protocol.flag == PTL_V3)
+		return api_protocol_get_ap_crc_v3(dev, data);
+
+	return -EINVAL;
+}
+
+static int api_protocol_set_mode_v3(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u8 mode = dev->wbuf[1];
+
+	UNUSED(data);
+
+	error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * Bridge with V3 IC need to set bridge into/out test mode additionally.
+	 */
+	if (dev->quirks & QUIRK_BRIDGE)
+		return bridge_set_test_mode(dev, (mode) ? true : false);
+
+	return 0;
+}
+
+static int api_protocol_write_enable(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool in_ap = (data) ? *(bool *)data : true;
+
+	error = write_then_read(dev, dev->wbuf,
+				(in_ap) ? 3 : 10, NULL, 0);
+	if (error < 0)
+		return error;
+
+	/*
+	 * V3 need AP/BL mode switch delay
+	 */
+	if (in_ap)
+		dev->cb.delay_ms(is_231x(dev) ? 1000 : 100);
+	else
+		dev->cb.delay_ms(10);
+
+	return 0;
+}
+
+static int api_protocol_write_data_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 33, NULL, 0);
+}
+
+static int api_protocol_get_df_crc(struct ilitek_ts_device *dev,  void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	dev->ic[0].crc[1] = 0;
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	dev->ic[0].crc[1] = le16(dev->rbuf + 2) << 16 | le16(dev->rbuf);
+	TP_MSG(dev->id, "[Check Code] Data: 0x%x\n", dev->ic[0].crc[1]);
+
+	return 0;
+}
+
+static int api_protocol_set_mode_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_get_crc_by_addr(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 start, end, t_ms;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		start = le32(dev->wbuf + 2, 3);
+		end = le32(dev->wbuf + 5, 3);
+		t_ms = ((end - start) / 4096 + 1) * TOUT_CD * TOUT_CD_RATIO;
+
+		error = write_then_wait_ack(dev, dev->wbuf, 8, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_get_crc_by_num(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	u32 t_ms = (dev->wbuf[2] == 0) ? TOUT_CF_BLOCK_0 : TOUT_CF_BLOCK_N;
+
+	dev->wbuf[1] = type;
+
+	if (type == CRC_CALCULATE) {
+		error = write_then_wait_ack(dev, dev->wbuf, 3, t_ms);
+		if (error < 0)
+			return error;
+		type = CRC_GET;
+		return api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type);
+	}
+
+	return write_then_read(dev, dev->wbuf, 2, dev->rbuf, 2);
+}
+
+static int api_protocol_read_flash(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 code = *(u32 *)data;
+	bool prepare;
+	int rlen;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		if ((dev->protocol.ver & 0xFFFF00) == BL_PROTOCOL_V1_7 &&
+		     dev->fw_ver[3] < 3) {
+			TP_ERR(dev->id, "BL: 0x%x, FW: 0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 "-0x" PFMT_X8 " not support cmd: 0x" PFMT_X8 "\n",
+				dev->protocol.ver, dev->fw_ver[0],
+				dev->fw_ver[1], dev->fw_ver[2], dev->fw_ver[3],
+				dev->wbuf[0]);
+			return -EINVAL;
+		}
+
+		return write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32);
+	}
+
+	if (!data)
+		return -EINVAL;
+
+	prepare = (code >> 16) ? true : false;
+	rlen = code & 0xFFFF;
+
+	if (prepare) {
+		error = write_then_read(dev, dev->wbuf, 2, NULL, 0);
+		dev->cb.delay_ms(100);
+
+		return error;
+	}
+
+	if (dev->_interface == interface_i2c)
+		error = write_then_read(dev, dev->wbuf, 2, dev->rbuf, rlen);
+	else
+		error = write_then_read(dev, NULL, 0, dev->rbuf, rlen);
+
+	return error;
+}
+
+static int api_protocol_set_flash_addr(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	u32 addr = *(u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (dev->protocol.flag == PTL_V3) {
+		dev->wbuf[3] = addr & 0xFF;
+		dev->wbuf[2] = (addr >> 8) & 0xFF;
+		dev->wbuf[1] = (addr >> 16) & 0xFF;
+
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		return 0;
+	}
+
+	dev->wbuf[1] = addr & 0xFF;
+	dev->wbuf[2] = (addr >> 8) & 0xFF;
+	dev->wbuf[3] = (addr >> 16) & 0xFF;
+
+	return write_then_read(dev, dev->wbuf, 4, NULL, 0);
+}
+
+static int api_protocol_set_data_len(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 3, NULL, 0);
+}
+
+static int api_protocol_set_flash_enable(struct ilitek_ts_device *dev,
+					 void *data)
+{
+	int error;
+	u8 type = (data) ? *(u8 *)data : 0;
+	int wlen, rlen;
+	bool in_ap = ((type & 0x1) != 0) ? true : false;
+	bool is_slave = ((type & 0x2) != 0) ? true : false;
+
+	u32 set_start, set_end, get_start, get_end;
+
+	if (!is_slave) {
+		wlen = (in_ap) ? 3 : 9;
+		rlen = (in_ap || dev->protocol.ver < 0x010803) ? 0 : 6;
+
+		set_start = le32(dev->wbuf + 3, 3);
+		set_end = le32(dev->wbuf + 6, 3);
+
+		error = write_then_read(dev, dev->wbuf, wlen,
+					dev->rbuf, rlen);
+		if (error < 0)
+			return error;
+
+		if (in_ap || dev->protocol.ver < 0x010803)
+			return 0;
+
+		get_start = le32(dev->rbuf, 3);
+		get_end = le32(dev->rbuf + 3, 3);
+
+		if (set_start != get_start || set_end != get_end) {
+			TP_ERR(dev->id, "start/end addr.: 0x%x/0x%x vs. 0x%x/0x%x not match\n",
+				set_start, set_end, get_start, get_end);
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	error = write_then_wait_ack(dev, dev->wbuf, 9,
+				    TOUT_CC_SLAVE * TOUT_CC_SLAVE_RATIO);
+	if (error < 0)
+		return error;
+	dev->cb.delay_ms(2000);
+
+	return (dev->_interface == interface_usb) ?
+		re_enum_helper(dev, enum_sw_reset) : 0;
+}
+
+static int api_protocol_write_data_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, TOUT_C3 * TOUT_C3_RATIO);
+}
+
+static int api_protocol_write_data_m2v(struct ilitek_ts_device *dev, void *data)
+{
+	int wlen;
+
+	if (!data)
+		return -EINVAL;
+
+	wlen = *(int *)data;
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, 30000);
+}
+
+static int api_protocol_access_slave(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct ilitek_slave_access *access;
+
+	if (!data)
+		return -EINVAL;
+
+	access = (struct ilitek_slave_access *)data;
+
+	dev->wbuf[1] = access->slave_id;
+	dev->wbuf[2] = access->func;
+	_memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (access->func) {
+	case CMD_GET_AP_CRC:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 4);
+		*((u32 *)access->data) = le32(dev->rbuf, 4);
+		break;
+
+	case CMD_GET_MCU_MOD:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 1);
+		*((u8 *)access->data) = dev->rbuf[0];
+		break;
+
+	case CMD_GET_FW_VER:
+		error = write_then_read(dev, dev->wbuf, 3, dev->rbuf, 8);
+		_memcpy((u8 *)access->data, dev->rbuf, 8);
+
+		break;
+
+	case CMD_WRITE_ENABLE:
+		dev->wbuf[3] = ((u8 *)access->data)[0];
+		dev->wbuf[4] = ((u8 *)access->data)[1];
+		dev->wbuf[5] = ((u8 *)access->data)[2];
+		dev->wbuf[6] = ((u8 *)access->data)[3];
+		dev->wbuf[7] = ((u8 *)access->data)[4];
+		dev->wbuf[8] = ((u8 *)access->data)[5];
+
+		error = write_then_wait_ack(dev, dev->wbuf, 9, 5000);
+		break;
+
+	default:
+		error = write_then_wait_ack(dev, dev->wbuf, 3, 5000);
+		break;
+	};
+
+	return error;
+}
+
+static int api_protocol_set_ap_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, false, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_bl_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+
+	UNUSED(data);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(true, false, dev->_private);
+
+	error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+
+	if (dev->cb.mode_switch_notify)
+		dev->cb.mode_switch_notify(false, true, dev->_private);
+
+	return error;
+}
+
+static int api_protocol_set_idle(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_set_sleep(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_wakeup(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 1, NULL, 0);
+}
+
+static int api_protocol_set_func_mode(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	bool get = (data) ? *(bool *)data : true;
+
+	if (!data)
+		return -EINVAL;
+
+	if (get) {
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3);
+		if (error < 0)
+			return error;
+
+		dev->func.header = be16(dev->rbuf);
+		dev->func.mode = dev->rbuf[2];
+		TP_MSG(dev->id, "[FW Mode] 0x" PFMT_X8 "\n", dev->func.mode);
+
+		return 0;
+	}
+
+	if (dev->protocol.flag == PTL_V3) {
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+		return 0;
+	} else if (dev->protocol.flag == PTL_V6) {
+		error = write_then_wait_ack(dev, dev->wbuf, 4,
+					    TOUT_68 * TOUT_68_RATIO);
+		if (error < 0)
+			return error;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int api_protocol_c_model_info(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	if (dev->protocol.ver < 0x060008)
+		return write_then_read(dev, dev->wbuf, 12, NULL, 0);
+
+	return write_then_read(dev, dev->wbuf, 18, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v3(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_read(dev, dev->wbuf, 2, NULL, 0);
+}
+
+static int api_protocol_tuning_para_v6(struct ilitek_ts_device *dev, void *data)
+{
+	int error;
+	struct tuning_para_settings tuning =
+		*(struct tuning_para_settings *)data;
+	u32 wlen;
+
+	int header;
+	int tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[1] = tuning.func;
+	dev->wbuf[2] = tuning.ctrl;
+	dev->wbuf[3] = tuning.type;
+
+	if (tuning.func == 0x0) {
+		wlen = 4;
+		tout_ms = TOUT_65_READ * TOUT_65_READ_RATIO;
+
+		switch (tuning.ctrl) {
+		case 0x3: case 0x5: case 0x10:
+			wlen += tuning.len;
+			tout_ms = TOUT_65_WRITE * TOUT_65_WRITE_RATIO;
+
+			//TODO: add memory range check
+			_memcpy(dev->wbuf + 4, tuning.buf, tuning.len);
+			break;
+		}
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	switch (tuning.ctrl) {
+	case 0x2: case 0x4:
+		error = write_then_read(dev, dev->wbuf, 4, NULL, 0);
+		if (error < 0)
+			return error;
+		error = read_ctrl_in(dev, CMD_TUNING_PARA_V6,
+				     dev->rbuf, 1024, 5000);
+		if (error < 0)
+			return error;
+
+		header = (dev->_interface == interface_i2c) ? 5 : 6;
+		header = (dev->quirks & QUIRK_BRIDGE) ? 6 : header;
+
+		_memcpy(tuning.buf, dev->rbuf + header, tuning.len);
+
+		break;
+	}
+
+	return 0;
+}
+
+static int api_protocol_set_cdc_init_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	int wlen;
+	struct cdc_settings *set = (struct cdc_settings *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	if (set->is_freq) {
+		dev->wbuf[1] = 0x0F;
+		dev->wbuf[2] = set->freq.sine.start;
+		dev->wbuf[3] = set->freq.sine.end;
+		dev->wbuf[4] = set->freq.sine.step;
+
+		error = write_then_read(dev, dev->wbuf, 5, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(200);
+	} else {
+		dev->wbuf[1] = set->cmd;
+		dev->wbuf[2] = 0;
+		dev->wbuf[3] = set->config & 0xFF;
+		wlen = 4;
+
+		if (set->config & 0xFF00) {
+			dev->wbuf[3] = (set->config >> 8) & 0xFF;
+			dev->wbuf[4] = set->config & 0xFF;
+			wlen = 5;
+		}
+
+		error = write_then_read(dev, dev->wbuf, wlen, NULL, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(10);
+	}
+
+	return api_check_busy(dev, 15000, 10);
+}
+
+static int api_protocol_get_cdc_v6(struct ilitek_ts_device *dev, void *data)
+{
+	UNUSED(data);
+
+	return write_then_wait_ack(dev, dev->wbuf, 1, TOUT_F2 * TOUT_F2_RATIO);
+}
+
+static int api_protocol_set_cdc_init_v6(struct ilitek_ts_device *dev,
+					void *data)
+{
+	struct cdc_settings *set = (struct cdc_settings *)data;
+	int wlen = 1, tout_ms;
+
+	if (!data)
+		return -EINVAL;
+
+	dev->wbuf[wlen++] = set->cmd;
+
+	if (set->is_freq) {
+		tout_ms = set->freq.sine.steps * TOUT_F1_FREQ_MC;
+		tout_ms += (set->freq.mc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.sc_swcap.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump1.steps * TOUT_F1_FREQ_SC);
+		tout_ms += (set->freq.dump2.steps * TOUT_F1_FREQ_SC);
+
+		tout_ms += (set->freq.pen.steps * TOUT_F1_FREQ_MC);
+
+		tout_ms *= MAX(set->freq.frame_cnt, 1);
+		tout_ms *= TOUT_F1_FREQ_RATIO;
+
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (set->is_p2p) {
+		tout_ms = MAX(set->p2p.frame_cnt, 1) *
+			TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+		return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+	}
+
+	if (dev->protocol.ver > 0x60008)
+		dev->wbuf[wlen++] = set->config & 0xFF;
+
+	tout_ms = TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO;
+	if (set->is_curve) {
+		tout_ms = set->curve.dump.steps * TOUT_F1_CURVE;
+		tout_ms += (set->curve.charge.steps * TOUT_F1_CURVE);
+		tout_ms *= MAX(set->curve.frame_cnt, 1);
+		tout_ms *= TOUT_F1_CURVE_RATIO;
+	} else if (set->is_short) {
+		tout_ms = TOUT_F1_SHORT * TOUT_F1_SHORT_RATIO;
+	} else if (set->is_open) {
+		tout_ms = MAX(set->open.frame, 1) *
+			TOUT_F1_OPEN * TOUT_F1_OPEN_RATIO;
+	} else if (set->is_key && dev->protocol.ver < 0x6000a) {
+		tout_ms = TOUT_F1_KEY;
+	}
+
+	return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms);
+}
+
+static int api_protocol_get_cdc_info_v3(struct ilitek_ts_device *dev,
+					void *data)
+{
+	int error;
+	u32 *cdc_info = (u32 *)data;
+
+	if (!data)
+		return -EINVAL;
+
+	error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+	if (error < 0)
+		return error;
+
+	*cdc_info = le32(dev->rbuf, 4);
+
+	return 0;
+}
+
+int api_protocol_set_cmd(void *handle, u8 idx, void *data)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	if (!dev || idx >= ARRAY_SIZE(protocol_maps))
+		return -EINVAL;
+
+	if (!(dev->protocol.flag & protocol_maps[idx].flag) &&
+	    protocol_maps[idx].flag != PTL_ANY) {
+		TP_ERR(dev->id, "Unexpected cmd: " PFMT_C8 " for 0x" PFMT_X8 " only, now is 0x" PFMT_X8 "\n",
+			protocol_maps[idx].desc, protocol_maps[idx].flag,
+			dev->protocol.flag);
+		return -EINVAL;
+	}
+
+	dev->wbuf[0] = protocol_maps[idx].cmd;
+	error = protocol_maps[idx].func(dev, data);
+	if (error < 0) {
+		TP_ERR(dev->id, "failed to execute cmd: 0x" PFMT_X8 " " PFMT_C8 ", err: %d\n",
+			protocol_maps[idx].cmd, protocol_maps[idx].desc, error);
+		return error;
+	}
+
+	return 0;
+}
+
+int api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	u8 cmd = 0;
+
+	if (!force && dev->fw_mode == mode)
+		return 0;
+
+	_memset(dev->wbuf, 0, sizeof(dev->wbuf));
+
+	if (dev->protocol.flag == PTL_V3) {
+		/* V3 only support suspend and normal mode */
+		if (mode != mode_normal &&
+		    mode != mode_suspend &&
+		    mode != mode_test)
+			return -EPROTONOSUPPORT;
+		dev->wbuf[1] = (mode == mode_normal) ? 0x00 : 0x01;
+		cmd = SET_TEST_MOD;
+	} else if (dev->protocol.flag == PTL_V6) {
+		dev->wbuf[1] = mode;
+		dev->wbuf[2] = (eng) ? 0x01 : 0x00;
+		cmd = SET_MOD_CTRL;
+	}
+
+	error = api_protocol_set_cmd(dev, cmd, NULL);
+	if (error < 0)
+		return error;
+
+	/* switch from test to normal mode should wait 1 sec. delay */
+	if (dev->protocol.flag == PTL_V6 &&
+	    dev->fw_mode == mode_test && mode == mode_normal)
+		dev->cb.delay_ms(1000);
+	else
+		dev->cb.delay_ms(100);
+
+	dev->fw_mode = mode;
+
+	return 0;
+}
+
+u16 api_get_block_crc_by_addr(void *handle, u8 type,
+			      u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = start;
+	dev->wbuf[3] = (start >> 8) & 0xFF;
+	dev->wbuf[4] = (start >> 16) & 0xFF;
+	dev->wbuf[5] = end & 0xFF;
+	dev->wbuf[6] = (end >> 8) & 0xFF;
+	dev->wbuf[7] = (end >> 16) & 0xFF;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_ADDR, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+u16 api_get_block_crc_by_num(void *handle, u8 type,
+			     u8 block_num)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[2] = block_num;
+	if (api_protocol_set_cmd(dev, GET_BLK_CRC_NUM, &type) < 0)
+		return 0;
+
+	return le16(dev->rbuf);
+}
+
+int api_set_data_len(void *handle, u16 data_len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+
+	dev->wbuf[1] = data_len & 0xFF;
+	dev->wbuf[2] = (data_len >> 8) & 0xFF;
+
+	return api_protocol_set_cmd(dev, SET_DATA_LEN, NULL);
+}
+
+int api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 type;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = start & 0xFF;
+	dev->wbuf[4] = (start >> 8) & 0xFF;
+	dev->wbuf[5] = start >> 16;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (end >> 8) & 0xFF;
+	dev->wbuf[8] = end >> 16;
+
+	type = (in_ap) ? 0x1 : 0x0;
+	type |= (is_slave) ? 0x2 : 0x0;
+
+	return api_protocol_set_cmd(dev, SET_FLASH_EN, &type);
+}
+
+int api_write_data_v6(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V6, &wlen);
+}
+
+int api_access_slave(void *handle, u8 id, u8 func, void *data)
+{
+	struct ilitek_slave_access access;
+
+	access.slave_id = id;
+	access.func = func;
+	access.data = data;
+
+	return api_protocol_set_cmd(handle, ACCESS_SLAVE, &access);
+}
+
+int api_write_enable_v3(void *handle, bool in_ap, bool write_ap,
+			u32 end, u32 checksum)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = 0x5A;
+	dev->wbuf[2] = 0xA5;
+	dev->wbuf[3] = (write_ap) ? 0x0 : 0x1;
+	dev->wbuf[4] = (end >> 16) & 0xFF;
+	dev->wbuf[5] = (end >> 8) & 0xFF;
+	dev->wbuf[6] = end & 0xFF;
+	dev->wbuf[7] = (checksum >> 16) & 0xFF;
+	dev->wbuf[8] = (checksum >> 8) & 0xFF;
+	dev->wbuf[9] = checksum & 0xFF;
+
+	return api_protocol_set_cmd(dev, WRITE_ENABLE, &in_ap);
+}
+
+int api_write_data_v3(void *handle)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_V3, NULL);
+}
+
+int api_check_busy(void *handle, int timeout_ms, int delay_ms)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 busy;
+
+	/* retry 2 times at least */
+	int i = MAX(DIV_ROUND_UP(timeout_ms, delay_ms), 2);
+
+	_memset(dev->wbuf, 0, 64);
+
+	while (i--) {
+		api_protocol_set_cmd(dev, GET_SYS_BUSY, &busy);
+		if (busy == ILITEK_TP_SYSTEM_READY)
+			return 0;
+
+		/* delay ms for each check busy */
+		dev->cb.delay_ms(delay_ms);
+
+		/* if caller set no_retry then skip check busy retry */
+		if (dev->setting.no_retry)
+			break;
+	}
+
+	TP_WARN(dev->id, "check busy timeout: %d ms, state: 0x" PFMT_X8 "\n",
+		timeout_ms, busy);
+
+	return -EILIBUSY;
+}
+
+int api_to_bl_mode(void *handle, bool to_bl,
+		   u32 start, u32 end)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+
+	do {
+		if (api_protocol_set_cmd(dev, GET_MCU_MOD, NULL) < 0)
+			continue;
+
+		if (dev->ic[0].mode == target_mode)
+			goto success_change_mode;
+
+		if (to_bl) {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, true, false,
+						     0, 0) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_BL_MODE, NULL);
+		} else {
+			if (dev->protocol.flag == PTL_V3 &&
+			    api_write_enable_v3(dev, true, false, 0, 0) < 0)
+				continue;
+			else if (dev->protocol.flag == PTL_V6 &&
+				 api_write_enable_v6(dev, false, false,
+						     start, end) < 0)
+				continue;
+
+			api_protocol_set_cmd(dev, SET_AP_MODE, NULL);
+		}
+
+		switch (dev->_interface) {
+		case interface_hid_over_i2c:
+		case interface_i2c:
+			dev->cb.delay_ms(1000 + 100 * cnt);
+			break;
+		case interface_usb:
+			re_enum_helper(dev, enum_ap_bl);
+			break;
+		}
+	} while (!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		dev->ic[0].mode, (to_bl) ? "BL" : "AP");
+
+	/* update fw ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+
+	/* update protocol ver. in AP/BL mode */
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	return 0;
+}
+
+int api_set_idle(void *handle, bool enable)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	_memset(dev->wbuf, 0, 64);
+	dev->wbuf[1] = (enable) ? 1 : 0;
+	return api_protocol_set_cmd(dev, SET_MCU_IDLE, NULL);
+}
+
+int api_set_func_mode(void *handle, u8 mode)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+	bool get = false;
+
+	_memset(dev->wbuf, 0, 64);
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		dev->wbuf[1] = 0x55;
+		dev->wbuf[2] = 0xAA;
+		break;
+	case PTL_V6:
+		dev->wbuf[1] = 0x5A;
+		dev->wbuf[2] = 0xA5;
+		break;
+	default:
+		TP_ERR(dev->id, "unrecognized protocol: %x, flag: " PFMT_U8 "",
+			dev->protocol.ver, dev->protocol.flag);
+		return -EINVAL;
+	}
+	dev->wbuf[3] = mode;
+
+	if (dev->protocol.ver < 0x30400) {
+		TP_ERR(dev->id, "protocol: 0x%x not support\n",
+			dev->protocol.ver);
+		return -EINVAL;
+	}
+
+	error = api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+	if (error < 0)
+		return error;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		return error;
+
+	return (dev->func.mode == mode) ? 0 : -EFAULT;
+}
+
+int api_get_func_mode(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	bool get = true;
+
+	return api_protocol_set_cmd(dev, SET_FUNC_MOD, &get);
+}
+
+int api_erase_data_v3(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+
+	TP_INFO(dev->id, "erase data flash for " PFMT_C8 ", mode: " PFMT_X8 "\n",
+		dev->mcu_info.ic_name, dev->ic[0].mode);
+
+	if (is_231x(dev)) {
+		/* V3 231x only support erase data flash in AP mode */
+		if (dev->ic[0].mode != AP_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, true, false, 0, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(100);
+
+		dev->wbuf[1] = 0x02;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V3, NULL);
+		if (error < 0)
+			return error;
+
+		switch (dev->_interface) {
+		case interface_usb:
+			return re_enum_helper(dev, enum_ap_bl);
+		default:
+			dev->cb.delay_ms(1500);
+			break;
+		}
+	} else {
+		/* V3 251x only support erase data flash in BL mode */
+		if (dev->ic[0].mode != BL_MODE) {
+			TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n",
+				dev->ic[0].mode);
+			return 0;
+		}
+
+		error = api_write_enable_v3(dev, false, false, 0xf01f, 0);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(5);
+
+		_memset(dev->wbuf + 1, 0xFF, 32);
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+
+		dev->cb.delay_ms(500);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v3(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 addr, end = start + len, copied;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += 32, copied += 32) {
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, READ_FLASH, NULL);
+		if (error < 0)
+			return error;
+
+		_memcpy(buf + copied, dev->rbuf, 32);
+	}
+
+	return 0;
+}
+
+static int api_read_flash_v6(struct ilitek_ts_device *dev, u8 *buf,
+			     u32 start, u32 len)
+{
+	int error;
+	u32 code, addr, end = start + len, copied;
+	u16 data_len;
+
+	if (dev->ic[0].mode != BL_MODE)
+		return -EINVAL;
+
+	for (addr = start, copied = 0; addr < end;
+	     addr += data_len, copied += data_len) {
+		if (end - addr > 1024)
+			data_len = 2048;
+		else if (end - addr > 256)
+			data_len = 1024;
+		else if (end - addr > 64)
+			data_len = 256;
+		else
+			data_len = 64;
+
+		error = api_set_data_len(dev, data_len);
+		if (error < 0)
+			return error;
+		error = api_protocol_set_cmd(dev, SET_ADDR, &addr);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x1; code = 1 << 16;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[1] = 0x0; code = data_len & 0xFFFF;
+		error = api_protocol_set_cmd(dev, READ_FLASH, &code);
+		if (error < 0)
+			return error;
+
+		if (dev->_interface == interface_hid_over_i2c)
+			_memcpy(buf + copied, dev->rbuf + 5, data_len);
+		else
+			_memcpy(buf + copied, dev->rbuf, data_len);
+	}
+
+	return 0;
+}
+
+int api_read_flash(void *handle, u8 *buf,
+		   u32 start, u32 len)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (dev->protocol.flag == PTL_V3)
+		return api_read_flash_v3(dev, buf, start, len);
+
+	return api_read_flash_v6(dev, buf, start, len);
+}
+
+int _api_read_mp_result(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)(&dev->mp);
+	u32 addr = (is_29xx(dev)) ? 0x2e000 : 0x3e000;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return api_read_flash_v6(dev, buf, addr, 1000);
+
+	/* 1000 bytes data/ 2 bytes crc/ 1 bytes checksum */
+	tuning.len = 1003;
+	tuning.buf = buf;
+
+	tuning.func = 0x0; tuning.ctrl = 0x4; tuning.type = 0x10;
+	error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+	if (error < 0)
+		return error;
+
+	tuning.func = 0x1; tuning.ctrl = 0x4; tuning.type = 0x10;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_read_mp_result(void *handle)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_read_mp_result(handle);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+int _api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	u8 *buf = (u8 *)mp;
+	u16 crc;
+
+	crc = get_crc(0, 1000, buf, sizeof(struct mp_station));
+	mp->crc = crc;
+
+	tuning.func = 0x0;
+	tuning.ctrl = 0x5;
+	tuning.type = 0x10;
+	tuning.buf = buf;
+	tuning.len = 1002;
+
+	return api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+}
+
+int api_write_mp_result(void *handle, struct mp_station *mp)
+{
+	int error;
+
+	api_set_ctrl_mode(handle, mode_suspend, false, true);
+	error = _api_write_mp_result(handle, mp);
+	api_set_ctrl_mode(handle, mode_normal, false, true);
+
+	return error;
+}
+
+static void mp_result(const char *item, u8 data)
+{
+	if (data == 1)
+		TP_INFO(NULL, PFMT_C8 " Result: PASS\n", item);
+	else if (data == 2)
+		TP_INFO(NULL, PFMT_C8 " Result: NG\n", item);
+	else
+		TP_INFO(NULL, PFMT_C8 " Result: N/A\n", item);
+}
+
+void api_decode_mp_result(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct mp_station *mp = &dev->mp;
+	int i, j;
+	struct mp_station_old *_mp;
+	char module_name[32];
+	char bar_code[256];
+
+	TP_DBG_ARR(NULL, "[MpResult Raw]:", TYPE_U8, 1002, (u8 *)mp);
+
+	TP_MSG(NULL, "mp result ver: 0x%08x\n", mp->info.mp_result_ver);
+
+	/* For Old Mp Result Format */
+	if (mp->info.mp_result_ver == 0xFFFFFFFF ||
+	    mp->info.mp_result_ver < 0x01000000) {
+		_mp = (struct mp_station_old *)mp;
+
+		for (i = 0; i < 9; i++) {
+			TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+			if (!_mp->station[i].week ||
+			    _mp->station[i].week == 0xFF) {
+				TP_INFO(NULL, "No Test\n");
+				continue;
+			}
+
+			TP_INFO(NULL, "Week of year : " PFMT_U8 "\n",
+				_mp->station[i].week);
+			TP_INFO(NULL, "Year : 20%02u\n",
+				_mp->station[i].year);
+			TP_INFO_ARR(NULL, "Firmware Version : ",
+				   TYPE_U8, 8, _mp->station[i].fw_ver);
+
+			_memset(module_name, 0, sizeof(module_name));
+			_memcpy(module_name, _mp->station[i].module,
+				sizeof(_mp->station[i].module));
+			module_name[sizeof(_mp->station[i].module) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].module);
+			     j++) {
+				if ((u8)module_name[j] == 0xFF) {
+					module_name[j] = '\0';
+					break;
+				}
+			}
+			TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n",
+				module_name);
+
+			mp_result("Short Test", _mp->station[i].short_test);
+			mp_result("Open Test", _mp->station[i].open_test);
+			mp_result("Self Cap Test", _mp->station[i].self_test);
+			mp_result("Uniformity Test",
+				_mp->station[i].uniform_test);
+			mp_result("DAC Test", _mp->station[i].dac_test);
+			mp_result("Key Raw Test", _mp->station[i].key_test);;
+			mp_result("Painting Test", _mp->station[i].paint_test);
+			mp_result("MicroOpen Test", _mp->station[i].mopen_test);
+			mp_result("GPIO Test", _mp->station[i].gpio_test);
+			mp_result("Final", _mp->station[i].final_result);
+
+			_memset(bar_code, 0, sizeof(bar_code));
+			_memcpy(bar_code, _mp->station[i].bar_code,
+				sizeof(_mp->station[i].bar_code));
+			bar_code[sizeof(_mp->station[i].bar_code) + 1] = '\0';
+			for (j = 0; j < (int)sizeof(_mp->station[i].bar_code);
+			     j++) {
+				if ((u8)bar_code[j] == 0xFF) {
+					bar_code[j] = '\0';
+					break;
+				}
+			}
+
+			TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+			TP_INFO(NULL, "Customer ID : 0x%04x\n",
+				_mp->station[i].custom_id);
+			TP_INFO(NULL, "FWID : 0x%04x\n",
+				_mp->station[i].fwid);
+		}
+
+		return;
+	}
+
+	TP_INFO(NULL, "[MP Result]\n");
+	TP_INFO(NULL, "***Customer Info***\n");
+
+	TP_INFO(NULL, "MPResult Version:%02X.%02X.%02X.%02X\n",
+		(mp->info.mp_result_ver >> 24) & 0xFF,
+		(mp->info.mp_result_ver >> 16) & 0xFF,
+		(mp->info.mp_result_ver >> 8) & 0xFF,
+		mp->info.mp_result_ver & 0xFF);
+	TP_INFO(NULL, "Customer ID : 0x%04X\n", mp->info.customer_id);
+	TP_INFO(NULL, "FW ID : 0x%04X\n", mp->info.fwid);
+
+	for (i = 0; i < (int)ARRAY_SIZE(mp->station); i++) {
+		TP_INFO(NULL, "***Station%d***\n", i + 1);
+
+		if (!mp->station[i].week || mp->station[i].week == 0xFF) {
+			TP_INFO(NULL, "No Test\n");
+			continue;
+		}
+
+		TP_INFO(NULL, "Week of year : %d\n", mp->station[i].week);
+		TP_INFO(NULL, "Year : 20%02d\n", mp->station[i].year);
+		TP_INFO(NULL, "Firmware Version : 0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+			mp->station[i].fw_ver[7], mp->station[i].fw_ver[6],
+			mp->station[i].fw_ver[5], mp->station[i].fw_ver[4],
+			mp->station[i].fw_ver[3], mp->station[i].fw_ver[2],
+			mp->station[i].fw_ver[1], mp->station[i].fw_ver[0]);
+
+		_memset(module_name, 0, sizeof(module_name));
+		_memcpy(module_name, mp->station[i].module,
+			sizeof(mp->station[i].module));
+		module_name[sizeof(mp->station[i].module) + 1] = '\0';
+		for (j = 0; j < (int)sizeof(mp->station[i].module); j++) {
+			if ((u8)module_name[j] == 0xFF) {
+				module_name[j] = '\0';
+				break;
+			}
+		}
+		TP_INFO(NULL, "Module Name : [" PFMT_C8 "]\n", module_name);
+
+		mp_result("Short Test", mp->station[i].short_test);
+		mp_result("Open Test", mp->station[i].open_test);
+		mp_result("Self Cap Test", mp->station[i].self_test);
+		mp_result("Uniformity Test", mp->station[i].uniform_test);
+		mp_result("DAC Test", mp->station[i].dac_test);
+		mp_result("Key Raw Test", mp->station[i].key_test);;
+		mp_result("Painting Test", mp->station[i].paint_test);
+		mp_result("MicroOpen Test", mp->station[i].mopen_test);
+		mp_result("GPIO Test", mp->station[i].gpio_test);
+		mp_result("Final", mp->station[i].final_result);
+
+		TP_INFO(NULL, "Tool Version : " PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "." PFMT_U8 "\n",
+			mp->station[i].tool_ver[7], mp->station[i].tool_ver[6],
+			mp->station[i].tool_ver[5], mp->station[i].tool_ver[4],
+			mp->station[i].tool_ver[3], mp->station[i].tool_ver[2],
+			mp->station[i].tool_ver[1], mp->station[i].tool_ver[0]);
+
+		_memset(bar_code, 0, sizeof(bar_code));
+		_memcpy(bar_code, mp->station[i].bar_code,
+			sizeof(mp->station[i].bar_code));
+		bar_code[sizeof(mp->station[i].bar_code) + 1] = '\0';
+		TP_INFO(NULL, "Bar Code : " PFMT_C8 "\n", bar_code);
+		TP_INFO(NULL, "Customer ID : 0x%04x\n",
+			mp->station[i].custom_id);
+		TP_INFO(NULL, "FWID : 0x%04x\n", mp->station[i].fwid);
+	}
+}
+
+int api_read_tuning(void *handle, u8 *buf, int rlen)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	struct tuning_para_settings tuning;
+	int got, need;
+
+	if (dev->ic[0].mode == BL_MODE)
+		return -EINVAL;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	for (got = 0, need = rlen; need > 0; got += 1024, need -= 1024) {
+		tuning.len = MIN(need, 1024);
+		tuning.buf = buf + got;
+
+		tuning.func = 0x0; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+
+		tuning.func = 0x1; tuning.ctrl = 0x2; tuning.type = 0x0;
+		error = api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning);
+		if (error < 0)
+			return error;
+	}
+
+	TP_DBG_ARR(dev->id, "[tuning]:", TYPE_U8, rlen, buf);
+
+	return api_set_ctrl_mode(dev, mode_normal, false, true);
+}
+
+int api_write_data_m2v(void *handle, int wlen)
+{
+	return api_protocol_set_cmd(handle, WRITE_DATA_M2V, &wlen);
+}
+
+int api_to_bl_mode_m2v(void *handle, bool to_bl)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt = 0, retry = 15;
+	const u8 target_mode = (to_bl) ? BL_MODE : AP_MODE;
+	u8 mode;
+
+	if (dev->_interface != interface_usb)
+		return -EINVAL;
+
+	do {
+		dev->cb.delay_ms(100);//Reed Add : 20230927
+
+		if (api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) < 0)
+			continue;
+
+		if (mode == target_mode)
+			goto success_change_mode;
+
+		dev->cb.delay_ms(300);//Reed Add : 20230927
+
+		if (to_bl && api_access_slave(dev, 0x80, CMD_SET_BL_MODE,
+					      NULL) < 0)
+			continue;
+		else if (!to_bl && api_access_slave(dev, 0x80, CMD_SET_AP_MODE,
+						    NULL) < 0)
+			continue;
+
+		do {
+			dev->cb.delay_ms(100);//Reed Add : 20230927
+			if (!api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &mode) &&
+			    mode == target_mode)
+				goto success_change_mode;
+			dev->cb.delay_ms(5000);
+		} while (!dev->setting.no_retry && cnt++ < retry);
+		break;
+	} while (!!dev->setting.no_retry && cnt++ < retry);
+
+	TP_ERR(dev->id, "M2V current mode: 0x" PFMT_X8 ", change to " PFMT_C8 " mode failed\n",
+		mode, (to_bl) ? "BL" : "AP");
+	return -EFAULT;
+
+success_change_mode:
+	TP_MSG(dev->id, "M2V current mode: 0x" PFMT_X8 " " PFMT_C8 " mode\n",
+		mode, (to_bl) ? "BL" : "AP");
+
+	return 0;
+}
+
+int api_get_ic_crc(void *handle, u8 final_fw_mode)
+{
+	int error;
+	u8 i;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		break;
+	case PTL_V6:
+		for (i = 1; i < dev->tp_info.block_num; i++) {
+			dev->ic[0].crc[i] = api_get_block_crc_by_num(dev,
+				CRC_CALCULATE, i);
+		}
+		break;
+	default:
+		error = -EINVAL; break;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, final_fw_mode, false, true);
+
+	return error;
+}
+
+void api_print_ts_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 i;
+
+	TP_INFO(dev->id, "[Protocol Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X\n",
+		(dev->protocol.ver >> 16) & 0xff,
+		(dev->protocol.ver >> 8) & 0xff,
+		dev->protocol.ver & 0xff);
+
+	TP_INFO(dev->id, "[Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[0], dev->fw_ver[1],
+		dev->fw_ver[2], dev->fw_ver[3]);
+	TP_INFO(dev->id, "[Customer Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->fw_ver[4], dev->fw_ver[5],
+		dev->fw_ver[6], dev->fw_ver[7]);
+
+	TP_INFO(dev->id, "[Kernel Version]\n");
+	if (dev->protocol.flag == PTL_V6 &&
+	    support_mcu_info(dev)) {
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X (" PFMT_C8 ")\n",
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1],
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[4], dev->mcu_info.ic_name);
+	} else {
+		TP_INFO(dev->id, "0x%c%c.0x%c%c\n",
+			dev->mcu_info.ic_name[2], dev->mcu_info.ic_name[3],
+			dev->mcu_info.ic_name[0], dev->mcu_info.ic_name[1]);
+	}
+
+	TP_INFO(dev->id, "[Current Mode]\n");
+	TP_INFO(dev->id, "Master : " PFMT_C8 "\n", dev->ic[0].mode_str);
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	for (i = 1; i < dev->tp_info.ic_num; i++)
+		TP_INFO(dev->id, "Slave : " PFMT_C8 "\n", dev->ic[i].mode_str);
+
+	TP_INFO(dev->id, "[Module Name]\n");
+	TP_INFO(dev->id, PFMT_C8 "\n", dev->mcu_info.module_name);
+
+	TP_INFO(dev->id, "[Core Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+		dev->core_ver[0], dev->core_ver[1],
+		dev->core_ver[2], dev->core_ver[3]);
+
+	if (is_231x(dev)) {
+		switch (dev->core_ver[1]) {
+		case 0x00:
+			TP_INFO(dev->id, "(Dual Interface)\n"); break;
+		case 0x01:
+			TP_INFO(dev->id, "(USB Interface)\n"); break;
+		case 0x02:
+			TP_INFO(dev->id, "(I2C Interface)\n"); break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[Tuning Version]\n");
+		TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X\n",
+			dev->tuning_ver[0], dev->tuning_ver[1],
+			dev->tuning_ver[2], dev->tuning_ver[3]);
+	}
+
+	TP_INFO(dev->id, "[Panel Information]\n");
+	TP_INFO(dev->id, "X resolution : " PFMT_U16 "\n", dev->tp_info.x_resolution);
+	TP_INFO(dev->id, "Y resolution : " PFMT_U16 "\n", dev->tp_info.y_resolution);
+	TP_INFO(dev->id, "AA X Channel : " PFMT_U16 "\n", dev->tp_info.x_ch);
+	TP_INFO(dev->id, "AA Y Channel : " PFMT_U16 "\n", dev->tp_info.y_ch);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Fingers\n", dev->tp_info.max_fingers);
+	TP_INFO(dev->id, "Support " PFMT_U8 " Touch Keys\n", dev->tp_info.key_num);
+
+	if (dev->tp_info.key_num) {
+		switch (dev->key.info.mode) {
+		case key_disable:
+			TP_INFO(dev->id, "Key Mode : NO_Key\n");
+			break;
+		case key_hw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_1\n");
+			break;
+		case key_hsw:
+			TP_INFO(dev->id, "Key Mode : HW_Key_2\n");
+			break;
+		case key_vitual:
+			TP_INFO(dev->id, "Key Mode : Virtual_Key\n");
+			break;
+		case key_fw_disable:
+			TP_INFO(dev->id, "Key Mode : FW_disable\n");
+			break;
+		default:
+			TP_INFO(dev->id, "Key Mode : Unknown(0x" PFMT_X8 ")\n",
+				dev->key.info.mode);
+			break;
+		}
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "Support Pen Type : " PFMT_C8 "\n",
+			dev->pen_mode);
+		TP_INFO(dev->id, "Chip Counts : " PFMT_U8 "\n", dev->tp_info.ic_num);
+		TP_INFO(dev->id, "Report Format : " PFMT_U8 "\n", dev->tp_info.format);
+		TP_INFO(dev->id, "Block Number : " PFMT_U8 "\n", dev->tp_info.block_num);
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		TP_INFO(dev->id, "[FW CRC]\n");
+		TP_INFO(dev->id, "Master : 0x%04X\n", dev->ic[0].crc[0]);
+		for (i = 1; i < dev->tp_info.ic_num; i++)
+			TP_INFO(dev->id, "Slave : 0x%04X\n", dev->ic[i].crc[0]);
+	} else {
+		TP_INFO(dev->id, "[Check Code]\n");
+		TP_INFO(dev->id, "AP : 0x%08X\n", dev->ic[0].crc[0]);
+		TP_INFO(dev->id, "DATA : 0x%08X\n", dev->ic[0].crc[1]);
+	}
+}
+
+void api_read_then_print_m2v_info(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	u8 m2v_mode;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	if (dev->ic[0].mode != AP_MODE)
+		return;
+
+	api_set_ctrl_mode(dev, mode_suspend, false, true);
+	api_access_slave(dev, 0x80, CMD_GET_FW_VER, m2v_fw_ver);
+	api_access_slave(dev, 0x80, CMD_GET_MCU_MOD, &m2v_mode);
+	api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &m2v_checksum);
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	TP_INFO(dev->id, "[M2V Firmware Version]\n");
+	TP_INFO(dev->id, "0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X.0x%02X\n",
+		m2v_fw_ver[0], m2v_fw_ver[1], m2v_fw_ver[2], m2v_fw_ver[3],
+		m2v_fw_ver[4], m2v_fw_ver[5], m2v_fw_ver[6], m2v_fw_ver[7]);
+
+	TP_INFO(dev->id, "[M2V Current Mode]\n");
+	TP_INFO(dev->id, "Mode : " PFMT_C8 " Mode\n",
+		(m2v_mode == AP_MODE) ? "AP" : "BL");
+
+	TP_INFO(dev->id, "[M2V FW CheckSum]\n");
+	TP_INFO(dev->id, "FW CheckSum : 0x%08X\n", m2v_checksum);
+}
+
+int api_update_ts_info(void *handle)
+{
+	int error;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	/*
+	 * previous set suspend mode is in V6 format.
+	 * set suspend mode again if device protocol is checked as V3.
+	 */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_FW_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, NULL);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_PRODUCT_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_FWID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_SENSOR_ID, NULL);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_HID_INFO, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	/* BL mode should perform FW upgrade afterward */
+	if (dev->ic[0].mode != AP_MODE)
+		return 0;
+
+	/* V3 need to get DF CRC */
+	if (dev->protocol.flag == PTL_V3) {
+		error = api_protocol_set_cmd(dev, GET_DF_CRC, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	if (dev->protocol.flag == PTL_V6) {
+		error = api_protocol_set_cmd(dev, GET_TUNING_VER, NULL);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_CORE_VER, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_SCRN_RES, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		goto err_set_normal;
+	error = api_get_func_mode(dev);
+	if (error < 0)
+		goto err_set_normal;
+
+	if (dev->tp_info.ic_num > 1) {
+		error = api_protocol_set_cmd(dev, GET_AP_CRC,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+		error = api_protocol_set_cmd(dev, GET_MCU_MOD,
+					     &dev->tp_info.ic_num);
+		if (error < 0)
+			goto err_set_normal;
+	}
+
+err_set_normal:
+	api_set_ctrl_mode(dev, mode_normal, false, true);
+
+	return error;
+}
+
+void __ilitek_get_info(void *handle, struct ilitek_common_info *info)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!info || !dev)
+		return;
+
+	_memcpy(info, &dev->quirks, sizeof(struct ilitek_common_info));
+}
+
+void ilitek_dev_set_quirks(void *handle, u32 quirks)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	dev->quirks = quirks;
+}
+
+void ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->sys, sys, sizeof(struct ilitek_sys_info));
+}
+
+void ilitek_dev_setting(void *handle, struct ilitek_ts_settings *setting)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+#define X(_enum, _id, _size, _cnt)	\
+	touch_fmts[_id].size = _size;	\
+	touch_fmts[_id].max_cnt = _cnt;
+
+	ILITEK_TOUCH_REPORT_FORMAT;
+#undef X
+
+	if (!handle)
+		return;
+
+	_memcpy(&dev->setting, setting, sizeof(struct ilitek_ts_settings));
+
+	if (dev->setting.default_format_enabled) {
+		dev->fmt.touch_size = touch_fmts[touch_fmt_0x0].size;
+		dev->fmt.touch_max_cnt = touch_fmts[touch_fmt_0x0].max_cnt;
+	}
+
+	TP_MSG(dev->id, "no-retry: %d, no-INT-ack: %d\n",
+		dev->setting.no_retry, dev->setting.no_INT_ack);
+}
+
+void ilitek_dev_bind_callback(void *handle, struct ilitek_ts_callback *callback)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	if (callback) {
+		_memcpy(&dev->cb, callback, sizeof(struct ilitek_ts_callback));
+		if (dev->cb.msg)
+			g_msg = dev->cb.msg;
+	}
+}
+
+void *ilitek_dev_init(u8 _interface, const char *id,
+		      bool need_update_ts_info,
+		      struct ilitek_ts_callback *callback, void *_private)
+{
+	struct ilitek_ts_device *dev;
+
+	dev = (struct ilitek_ts_device *)MALLOC(sizeof(*dev));
+	if (!dev)
+		return NULL;
+
+	TP_MSG(NULL, "commonflow code version: 0x%x\n",
+		COMMONFLOW_CODE_VERSION);
+
+	TP_DBG(NULL, "sizeof(ilitek_ts_device): %u\n",
+		(unsigned int)sizeof(struct ilitek_ts_device));
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(dev, 0, sizeof(*dev));
+
+	_strcpy(dev->id, id, sizeof(dev->id));
+	ilitek_dev_bind_callback(dev, callback);
+
+	dev->_interface = _interface;
+	dev->_private = _private;
+
+	/* set protocol default V6 initially for comms. afterwards */
+	dev->protocol.flag = PTL_V6;
+	dev->tp_info.ic_num = 1;
+
+	dev->fw_mode = mode_unknown;
+
+	if (need_update_ts_info && api_update_ts_info(dev) < 0) {
+		ilitek_dev_exit(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+void ilitek_dev_exit(void *handle)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+
+	/*
+	 * LIBUSB would kill /dev/hidraw* and make system stop handling
+	 * device's usb. sw reset is required to re-enum usb then /dev/hidraw*
+	 * would be created and system would start to handle touch event.
+	 */
+	if (dev->quirks & QUIRK_LIBUSB || dev->setting.sw_reset_at_last)
+		api_protocol_set_cmd(dev, SET_SW_RST, NULL);
+
+	if (dev)
+		FREE(dev);
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_protocol.h b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
new file mode 100644
index 000000000000..ad9c5c18cd36
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_protocol.h
@@ -0,0 +1,916 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_PROTOCOL_H__
+#define __ILITEK_PROTOCOL_H__
+
+#include "ilitek_def.h"
+
+/* quirks definition */
+#define QUIRK_WAIT_ACK_DELAY		0x1
+#define QUIRK_BRIDGE			0x2
+#define QUIRK_DAEMON_I2C		0x4
+#define QUIRK_WIFI_ITS_I2C		0x8
+#define QUIRK_LIBUSB			0x10
+
+#define START_ADDR_LEGO			0x3000
+#define START_ADDR_29XX			0x4000
+#define END_ADDR_LEGO			0x40000
+
+#define MM_ADDR_LEGO			0x3020
+#define MM_ADDR_29XX			0x4020
+#define MM_ADDR_2501X			0x4038
+
+#define DF_START_ADDR_LEGO		0x3C000
+#define DF_START_ADDR_29XX		0x2C000
+
+#define ILITEK_TP_SYSTEM_READY		0x50
+
+#define CRC_CALCULATE			0
+#define CRC_GET				1
+
+#define ILTIEK_MAX_BLOCK_NUM		20
+
+#define PTL_ANY				0x00
+#define PTL_V3				0x03
+#define PTL_V6				0x06
+
+#define BL_PROTOCOL_V1_8		0x10800
+#define BL_PROTOCOL_V1_7		0x10700
+#define BL_PROTOCOL_V1_6		0x10600
+
+#define TOUT_CF_BLOCK_0			2500
+#define TOUT_CF_BLOCK_N			500
+#define TOUT_F1_SHORT			1600
+#define TOUT_F1_OPEN			12
+#define TOUT_F1_FREQ_MC			2
+#define TOUT_F1_FREQ_SC			1
+#define TOUT_F1_CURVE			13
+#define TOUT_F1_KEY			400
+#define TOUT_F1_OTHER			27
+#define TOUT_F2				7
+#define TOUT_CD				27
+#define TOUT_C3				100
+#define TOUT_65_WRITE			135
+#define TOUT_65_READ			3
+#define TOUT_68				24
+#define TOUT_CC_SLAVE			16000
+
+#define TOUT_F1_SHORT_RATIO		2
+#define TOUT_F1_OPEN_RATIO		3
+#define TOUT_F1_FREQ_RATIO		3
+#define TOUT_F1_CURVE_RATIO		3
+#define TOUT_F1_OTHER_RATIO		3
+#define TOUT_F2_RATIO			3
+#define TOUT_CD_RATIO			3
+#define TOUT_C3_RATIO			3
+#define TOUT_65_WRITE_RATIO		3
+#define TOUT_65_READ_RATIO		3
+#define TOUT_68_RATIO			3
+#define TOUT_CC_SLAVE_RATIO		2
+
+#define AP_MODE		0x5A
+#define BL_MODE		0x55
+
+#define STYLUS_MODES			\
+	X(STYLUS_WGP,	0x1,	"WGP")	\
+	X(STYLUS_USI,	0x2,	"USI")	\
+	X(STYLUS_MPP,	0x4,	"MPP")
+
+#define ILITEK_CMD_MAP							\
+	X(0x20, PTL_ANY, GET_TP_INFO, api_protocol_get_tp_info)		\
+	X(0x21, PTL_ANY, GET_SCRN_RES, api_protocol_get_scrn_res)	\
+	X(0x22, PTL_ANY, GET_KEY_INFO, api_protocol_get_key_info)	\
+	X(0x30, PTL_ANY, SET_IC_SLEEP, api_protocol_set_sleep)		\
+	X(0x31, PTL_ANY, SET_IC_WAKE, api_protocol_set_wakeup)		\
+	X(0x34, PTL_ANY, SET_MCU_IDLE, api_protocol_set_idle)		\
+	X(0x40, PTL_ANY, GET_FW_VER, api_protocol_get_fw_ver)		\
+	X(0x42, PTL_ANY, GET_PTL_VER, api_protocol_get_ptl_ver)		\
+	X(0x43, PTL_ANY, GET_CORE_VER, api_protocol_get_core_ver)	\
+	X(0x60, PTL_ANY, SET_SW_RST, api_protocol_set_sw_reset)		\
+	X(0x61, PTL_ANY, GET_MCU_VER, api_protocol_get_mcu_ver)		\
+	X(0x68, PTL_ANY, SET_FUNC_MOD, api_protocol_set_func_mode)	\
+	X(0x80, PTL_ANY, GET_SYS_BUSY, api_protocol_get_sys_busy)	\
+	X(0xC0, PTL_ANY, GET_MCU_MOD, api_protocol_get_mcu_mode)	\
+	X(0xC1, PTL_ANY, SET_AP_MODE, api_protocol_set_ap_mode)		\
+	X(0xC2, PTL_ANY, SET_BL_MODE, api_protocol_set_bl_mode)		\
+	X(0xC5, PTL_ANY, READ_FLASH, api_protocol_read_flash)		\
+	X(0xC7, PTL_ANY, GET_AP_CRC, api_protocol_get_ap_crc)		\
+	X(0xC8, PTL_ANY, SET_ADDR, api_protocol_set_flash_addr)		\
+									\
+	/* v3 only cmds */						\
+	X(0x25, PTL_V3, GET_CDC_INFO_V3, api_protocol_get_cdc_info_v3)	\
+	X(0x63, PTL_V3, TUNING_PARA_V3, api_protocol_tuning_para_v3)	\
+	X(0xC3, PTL_V3, WRITE_DATA_V3, api_protocol_write_data_v3)	\
+	X(0xC4, PTL_V3, WRITE_ENABLE, api_protocol_write_enable)	\
+	X(0xCA, PTL_V3, GET_DF_CRC, api_protocol_get_df_crc)		\
+	X(0xF2, PTL_V3, SET_TEST_MOD, api_protocol_set_mode_v3)		\
+	X(0xF3, PTL_V3, INIT_CDC_V3, api_protocol_set_cdc_init_v3)	\
+									\
+	/* v6 only cmds */						\
+	X(0x24, PTL_V6, POWER_STATUS, api_protocol_power_status)	\
+	X(0x27, PTL_V6, GET_SENSOR_ID, api_protocol_get_sensor_id)	\
+	X(0x44, PTL_V6, GET_TUNING_VER, api_protocol_get_tuning_ver)	\
+	X(0x45, PTL_V6, GET_PRODUCT_INFO, api_protocol_get_product_info)\
+	X(0x46, PTL_V6, GET_FWID, api_protocol_get_fwid)		\
+	X(0x47, PTL_V6, GET_CRYPTO_INFO, api_protocol_get_crypto_info)	\
+	X(0x48, PTL_V6, GET_HID_INFO, api_protocol_get_hid_info)	\
+	X(0x62, PTL_V6, GET_MCU_INFO, api_protocol_get_mcu_info)	\
+	X(0x65, PTL_V6, TUNING_PARA_V6, api_protocol_tuning_para_v6)	\
+	X(0x69, PTL_V6, SET_FS_INFO, api_protocol_set_fs_info)		\
+	X(0x6A, PTL_V6, SET_SHORT_INFO, api_protocol_set_short_info)	\
+	X(0x6B, PTL_V6, C_MODEL_INFO, api_protocol_c_model_info)	\
+	X(0x6C, PTL_V6, SET_P2P_INFO, api_protocol_set_p2p_info)	\
+	X(0x6D, PTL_V6, SET_OPEN_INFO, api_protocol_set_open_info)	\
+	X(0x6E, PTL_V6, SET_CHARGE_INFO, api_protocol_set_charge_info)	\
+	X(0x6F, PTL_V6, SET_PEN_FS_INFO, api_protocol_set_pen_fs_info)	\
+	X(0xB0, PTL_V6, WRITE_DATA_M2V, api_protocol_write_data_m2v)	\
+	X(0xC3, PTL_V6, WRITE_DATA_V6, api_protocol_write_data_v6)	\
+	X(0xC9, PTL_V6, SET_DATA_LEN, api_protocol_set_data_len)	\
+	X(0xCB, PTL_V6, ACCESS_SLAVE, api_protocol_access_slave)	\
+	X(0xCC, PTL_V6, SET_FLASH_EN, api_protocol_set_flash_enable)	\
+	X(0xCD, PTL_V6, GET_BLK_CRC_ADDR, api_protocol_get_crc_by_addr)	\
+	X(0xCF, PTL_V6, GET_BLK_CRC_NUM, api_protocol_get_crc_by_num)	\
+	X(0xF0, PTL_V6, SET_MOD_CTRL, api_protocol_set_mode_v6)		\
+	X(0xF1, PTL_V6, INIT_CDC_V6, api_protocol_set_cdc_init_v6)	\
+	X(0xF2, PTL_V6, GET_CDC_V6, api_protocol_get_cdc_v6)
+
+
+#define X(_cmd, _protocol, _cmd_id, _api)	_cmd_id,
+enum ilitek_cmd_ids {
+	ILITEK_CMD_MAP
+	/* ALWAYS keep at the end */
+	MAX_CMD_CNT
+};
+#undef X
+
+#define X(_cmd, _protocol, _cmd_id, _api)	CMD_##_cmd_id = _cmd,
+enum ilitek_cmds { ILITEK_CMD_MAP };
+#undef X
+
+enum ilitek_hw_interfaces {
+	interface_i2c = 0,
+	interface_hid_over_i2c,
+	interface_usb,
+};
+
+enum ilitek_fw_modes {
+	mode_unknown = -1,
+	mode_normal = 0,
+	mode_test,
+	mode_debug,
+	mode_suspend,
+};
+
+enum ilitek_key_modes {
+	key_disable = 0,
+	key_hw = 1,
+	key_hsw = 2,
+	key_vitual = 3,
+	key_fw_disable = 0xff,
+};
+
+#define ILITEK_TOUCH_REPORT_FORMAT			\
+	X(touch_fmt_0x0,	0x0, 5, 10)	\
+	X(touch_fmt_0x1,	0x1, 6, 10)	\
+	X(touch_fmt_0x2,	0x2, 10, 5)	\
+	X(touch_fmt_0x3,	0x3, 10, 5)	\
+	X(touch_fmt_0x4,	0x4, 10, 5)	\
+	X(touch_fmt_0x10,	0x10, 10, 6)	\
+	X(touch_fmt_0x11,	0x11, 5, 10)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_touch_fmts {
+	ILITEK_TOUCH_REPORT_FORMAT
+	touch_fmt_max = 0x100,
+};
+#undef X
+
+#define ILITEK_PEN_REPORT_FORMAT		\
+	X(pen_fmt_0x0, 0x0, 12, 1)	\
+	X(pen_fmt_0x1, 0x1, 18, 1)	\
+	X(pen_fmt_0x2, 0x2, 22, 1)
+
+#define X(_enum, _id, _size, _cnt)	_enum = _id,
+enum ilitek_pen_fmts {
+	ILITEK_PEN_REPORT_FORMAT
+	pen_fmt_max = 0x100,
+};
+#undef X
+
+struct ilitek_slave_access {
+	u8 slave_id;
+	u8 func;
+	void *data;
+};
+
+struct tuning_para_settings {
+	u8 func;
+	u8 ctrl;
+	u8 type;
+
+	u8 *buf;
+	u32 len;
+};
+
+struct reports {
+	bool touch_need_update;
+	bool pen_need_update;
+
+	u8 touch[64];
+	u8 pen[64];
+};
+
+struct grid_data {
+	bool need_update;
+	unsigned int X, Y;
+
+	s32 *data;
+};
+
+struct grids {
+	struct grid_data mc;
+	struct grid_data sc_x;
+	struct grid_data sc_y;
+	struct grid_data pen_x;
+	struct grid_data pen_y;
+
+	struct grid_data key_mc;
+	struct grid_data key_x;
+	struct grid_data key_y;
+
+	struct grid_data self;
+
+	/* touch/pen debug message along with frame update */
+	struct reports dmsg;
+};
+
+enum ilitek_enum_type {
+	enum_ap_bl = 0,
+	enum_sw_reset,
+};
+
+typedef void (*update_grid_t)(u32, u32, struct grids *, void *);
+typedef void (*update_report_rate_t)(unsigned int);
+
+typedef int (*write_then_read_t)(u8 *, int, u8 *, int, void *);
+typedef int (*read_ctrl_in_t)(u8 *, int, unsigned int, void *);
+typedef int (*read_interrupt_in_t)(u8 *, int, unsigned int, void *);
+typedef void (*init_ack_t)(unsigned int, void *);
+typedef int (*wait_ack_t)(u8, unsigned int, void *);
+typedef int (*hw_reset_t)(unsigned int, void *);
+typedef int (*re_enum_t)(u8, void *);
+typedef void (*delay_ms_t)(unsigned int);
+
+
+typedef int (*write_then_read_direct_t)(u8 *, int, u8 *, int, void *);
+typedef void (*mode_switch_notify_t)(bool, bool, void *);
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_fmt {
+	u8 id : 6;
+	u8 status : 1;
+	u8 reserve : 1;
+	u16 x;
+	u16 y;
+	u8 pressure;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ touch_iwb_fmt {
+	u8 status : 3;
+	u8 reserve : 5;
+	u8 id : 6;
+	u8 reserve_1 : 2;
+	u16 x;
+	u16 y;
+	u16 width;
+	u16 height;
+
+	u8 algo;
+};
+
+struct __PACKED__ pen_fmt {
+	union __PACKED__ {
+		u8 modes;
+		struct __PACKED__ {
+			u8 tip_sw : 1;
+			u8 barrel_sw : 1;
+			u8 eraser : 1;
+			u8 invert : 1;
+			u8 in_range : 1;
+			u8 reserve : 3;
+		};
+	};
+	u16 x;
+	u16 y;
+	u16 pressure;
+	s16 x_tilt;
+	s16 y_tilt;
+
+	u8 battery;
+
+	union __PACKED__ {
+		/* usi v1.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 width;
+			u8 style;
+		} usi_1;
+
+		/* usi v2.0 */
+		struct __PACKED__ {
+			u16 barrel_pressure;
+			u8 idx;
+			u8 color;
+			u8 color_24[3];
+			u8 no_color;
+			u8 width;
+			u8 style;
+		} usi_2;
+	};
+};
+
+struct __PACKED__ ilitek_report_fmt_info {
+	u32 touch_size;
+	u32 touch_max_cnt;
+
+	u32 pen_size;
+	u32 pen_max_cnt;
+};
+
+struct __PACKED__ ilitek_screen_info {
+	u16 x_min;
+	u16 y_min;
+	u16 x_max;
+	u16 y_max;
+	u16 pressure_min;
+	u16 pressure_max;
+	s16 x_tilt_min;
+	s16 x_tilt_max;
+	s16 y_tilt_min;
+	s16 y_tilt_max;
+	u16 pen_x_min;
+	u16 pen_y_min;
+	u16 pen_x_max;
+	u16 pen_y_max;
+};
+
+struct __PACKED__ ilitek_tp_info_v6 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u16 x_ch;
+	u16 y_ch;
+	u8 max_fingers;
+	u8 key_num;
+	u8 ic_num;
+	u8 support_modes;
+	u8 format;
+	u8 die_num;
+	u8 block_num;
+	u8 pen_modes;
+	u8 pen_format;
+	u16 pen_x_resolution;
+	u16 pen_y_resolution;
+};
+
+struct __PACKED__ ilitek_tp_info_v3 {
+	u16 x_resolution;
+	u16 y_resolution;
+	u8 x_ch;
+	u8 y_ch;
+	u8 max_fingers;
+	u8 reserve;
+	u8 key_num;
+	u8 reserve_1;
+	u8 touch_start_y;
+	u8 touch_end_y;
+	u8 touch_start_x;
+	u8 touch_end_x;
+	u8 support_modes;
+};
+
+struct __PACKED__ ilitek_key_info_v6 {
+	u8 mode;
+	u16 x_len;
+	u16 y_len;
+
+	struct __PACKED__ _ilitek_key_info_v6 {
+		u8 id;
+		u16 x;
+		u16 y;
+	} keys[50];
+};
+
+struct __PACKED__ ilitek_key_info_v3 {
+	u8 x_len[2];
+	u8 y_len[2];
+
+	struct __PACKED__ _ilitek_key_info_v3 {
+		u8 id;
+		u8 x[2];
+		u8 y[2];
+	} keys[20];
+};
+
+struct __PACKED__ ilitek_ts_kernel_info {
+	char ic_name[6];
+	char mask_ver[2];
+	u32 mm_addr;
+	u32 min_addr;
+	u32 max_addr;
+	char module_name[32];
+
+	char ic_full_name[16];
+};
+
+struct __PACKED__ ilitek_key_info {
+	struct ilitek_key_info_v6 info;
+	bool clicked[50];
+};
+
+struct __PACKED__ ilitek_power_status {
+	u16 header;
+	u8 vdd33_lvd_flag;
+	u8 vdd33_lvd_level_sel;
+};
+
+struct __PACKED__ ilitek_sensor_id {
+	u16 header;
+	u8 id;
+};
+
+struct __PACKED__ ilitek_func_mode {
+	u16 header;
+	u8 mode;
+};
+
+struct __PACKED__ ilitek_ts_protocol {
+	u32 ver;
+	u8 flag;
+};
+
+struct __PACKED__ ilitek_ts_ic {
+	u8 mode;
+	u32 crc[ILTIEK_MAX_BLOCK_NUM];
+
+	char mode_str[32];
+};
+
+struct __PACKED__ ilitek_hid_info {
+	u16 pid;
+	u16 vid;
+	u16 rev;
+};
+
+struct __PACKED__ freq_category {
+	u32 start;
+	u32 end;
+	u32 step;
+	u32 steps;
+
+		u32 size;
+	char limit[1024];
+
+	u8 mode;
+
+	s32 *data;
+};
+
+struct __PACKED__ freq_settings {
+	bool prepared;
+
+	unsigned int frame_cnt;
+
+	/* add from v6.0.A */
+	unsigned int mc_frame_cnt;
+	unsigned int dump_frame_cnt;
+
+	unsigned int scan_type;
+
+	struct freq_category sine;
+	struct freq_category mc_swcap;
+	struct freq_category sc_swcap;
+	struct freq_category pen;
+
+	struct freq_category dump1;
+	struct freq_category dump2;
+	u8 dump1_val;
+	u8 dump2_val;
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ short_settings {
+	bool prepared;
+
+	u8 dump_1;
+	u8 dump_2;
+	u8 v_ref_L;
+	u16 post_idle;
+};
+
+struct __PACKED__ open_settings {
+	bool prepared;
+
+	u16 freq;
+	u8 gain;
+	u8 gain_rfb;
+	u8 afe_res_sel;
+	u8 mc_fsel;
+	u16 frame;
+};
+
+struct __PACKED__ p2p_settings {
+	bool prepared;
+
+	u16 frame_cnt;
+	u8 type;
+
+	/* add from v6.0.A */
+	u16 freq;
+};
+
+struct __PACKED__ charge_curve_sweep {
+	u16 start;
+	u16 end;
+	u8 step;
+	u16 post_idle;
+	u16 fix_val;
+
+	u16 steps;
+};
+
+struct __PACKED__ charge_curve_settings {
+	bool prepared;
+
+	u8 scan_mode;
+
+	struct charge_curve_sweep dump;
+	struct charge_curve_sweep charge;
+
+	u16 c_sub;
+	u16 frame_cnt;
+
+	struct __PACKED__ charge_curve_point {
+		u16 x;
+		u16 y;
+		u16 *dump_max;
+		u16 *dump_avg;
+		u16 *charge_max;
+		u16 *charge_avg;
+	} pt[9];
+
+	u16 packet_steps;
+};
+
+struct __PACKED__ cdc_settings {
+	u8 cmd;
+	u16 config;
+
+	bool skip_checksum;
+
+	/* freq. */
+	struct freq_settings freq;
+	/* short */
+	struct short_settings _short;
+	/* open */
+	struct open_settings open;
+	/* p2p */
+	struct p2p_settings p2p;
+	/* charge curve */
+	struct charge_curve_settings curve;
+
+	/* status only writable by CDC commonflow */
+	bool is_key;
+	bool is_p2p;
+	bool is_freq;
+	bool is_curve;
+	bool is_short;
+	bool is_open;
+	bool is_16bit;
+	bool is_sign;
+	bool is_fast_mode;
+	unsigned int total_bytes;
+
+	/* error code during cdc data collection */
+	s32 error;
+};
+
+struct __PACKED__ mp_station_old {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test:2;
+		u8 open_test:2;
+		u8 self_test:2;
+		u8 uniform_test:2;
+
+		u8 dac_test:2;
+		u8 key_test:2;
+		u8 final_result:2;
+		u8 paint_test:2;
+
+		u8 mopen_test:2;
+		u8 gpio_test:2;
+		u8 reserve_1:4;
+
+		char bar_code[28];
+		u8 reserve_2[35];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[10];
+};
+
+struct __PACKED__ mp_station {
+	struct __PACKED__ {
+		u8 week;
+		u8 year;
+		u8 fw_ver[8];
+		char module[19];
+
+		u8 short_test : 2;
+		u8 open_test : 2;
+		u8 self_test : 2;
+		u8 uniform_test : 2;
+
+		u8 dac_test : 2;
+		u8 key_test : 2;
+		u8 final_result : 2;
+		u8 paint_test : 2;
+
+		u8 mopen_test : 2;
+		u8 gpio_test : 2;
+		u8 reserve : 4;
+
+		u8 tool_ver[8];
+		char bar_code[135];
+
+		u16 custom_id;
+		u16 fwid;
+		u8 idx;
+	} station[5];
+
+	struct __PACKED__ {
+		u8 reserve_1[91];
+		u32 mp_result_ver;
+		u16 customer_id;
+		u16 fwid;
+		u8 reserve_2;
+	} info;
+
+	u16 crc;
+};
+
+struct __PACKED__ ilitek_ts_settings {
+	bool no_retry;
+	bool no_INT_ack;
+
+	bool sw_reset_at_last;
+
+	u8 sensor_id_mask;
+
+	/* only used for QUIRK_WAIT_ACK_DELAY */
+	u32 wait_ack_delay;
+
+	/*
+	 * engineer mode would likely report default format
+	 * ex. IWB-format
+	 */
+	bool default_format_enabled;
+};
+
+struct __PACKED__ ilitek_sys_info {
+	u16 pid;
+};
+
+struct __PACKED__ ilitek_ts_callback {
+	/* Please don't use "repeated start" for I2C interface */
+	write_then_read_t write_then_read;
+	read_ctrl_in_t read_ctrl_in;
+	read_interrupt_in_t read_interrupt_in;
+	init_ack_t init_ack;
+	wait_ack_t wait_ack;
+	hw_reset_t hw_reset;
+	re_enum_t re_enum;
+	delay_ms_t delay_ms;
+	msg_t msg;
+
+	/* write cmd without adding any hid header */
+	write_then_read_direct_t write_then_read_direct;
+	/* notify caller after AP/BL mode switch command */
+	mode_switch_notify_t mode_switch_notify;
+};
+
+struct __PACKED__ ilitek_common_info {
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen;
+	struct ilitek_tp_info_v6 tp;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu;
+	struct ilitek_hid_info hid;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+};
+
+struct __PACKED__ ilitek_ts_device {
+	void *_private;
+	char id[64];
+	u32 reset_time;
+
+	struct ilitek_ts_settings setting;
+
+	u32 quirks;
+	u8 _interface;
+
+	u16 customer_id;
+	u16 fwid;
+
+	char pen_mode[64];
+	u8 fw_ver[8];
+	u8 core_ver[8];
+	u8 tuning_ver[4];
+	u8 product_info[8];
+
+	struct ilitek_sys_info sys;
+	struct ilitek_ts_protocol protocol;
+	struct ilitek_func_mode func;
+	struct ilitek_sensor_id sensor;
+	struct ilitek_ts_ic ic[32];
+	struct ilitek_screen_info screen_info;
+	struct ilitek_tp_info_v6 tp_info;
+	struct ilitek_key_info key;
+	struct ilitek_ts_kernel_info mcu_info;
+	struct ilitek_hid_info hid_info;
+	struct ilitek_report_fmt_info fmt;
+	struct ilitek_power_status pwr;
+
+	u8 fw_mode;
+	struct mp_station mp;
+
+	u8 wbuf[4096];
+	u8 rbuf[4096];
+	struct ilitek_ts_callback cb;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16 __DLL le16(const u8 *p);
+u16 __DLL be16(const u8 *p);
+u32 __DLL le32(const u8 *p, int bytes);
+u32 __DLL be32(const u8 *p, int bytes);
+
+bool __DLL is_29xx(void *handle);
+
+bool __DLL _is_231x(char *ic_name);
+bool __DLL is_231x(void *handle);
+
+bool __DLL has_hw_key(void *handle);
+
+u8 __DLL get_protocol_ver_flag(u32 ver);
+
+int __DLL grid_alloc(void *handle, struct grids *grid);
+void __DLL grid_free(struct grids *grid);
+void __DLL grid_reset(struct grids *grid);
+
+u16 __DLL get_crc(u32 start, u32 end,
+		  u8 *buf, u32 buf_size);
+
+u32 __DLL get_checksum(u32 start, u32 end,
+		       u8 *buf, u32 buf_size);
+
+bool __DLL is_checksum_matched(u8 checksum, int start, int end,
+			       u8 *buf, int buf_size);
+
+bool __DLL support_sensor_id(void *handle);
+bool __DLL support_production_info(void *handle);
+bool __DLL support_fwid(void *handle);
+bool __DLL support_mcu_info(void *handle);
+bool __DLL support_power_status(void *handle);
+int __DLL write_then_wait_ack(void *handle, u8 *cmd, int wlen, int timeout_ms);
+
+int __DLL bridge_set_int_monitor(void *handle, bool enable);
+int __DLL bridge_set_test_mode(void *handle, bool enable);
+
+int __DLL reset_helper(void *handle);
+
+int __DLL write_then_read(void *handle, u8 *cmd, int wlen,
+			  u8 *buf, int rlen);
+int __DLL write_then_read_direct(void *handle, u8 *cmd, int wlen,
+				 u8 *buf, int rlen);
+int __DLL read_interrupt_in(void *handle, u8 *buf, int rlen,
+			    unsigned int timeout_ms);
+int __DLL read_ctrl_in(void *handle, u8 cmd, u8 *buf, int rlen,
+		       unsigned int timeout_ms);
+
+void __DLL __ilitek_get_info(void *handle,
+			     struct ilitek_common_info *info);
+
+void __DLL ilitek_dev_set_quirks(void *handle, u32 quirks);
+void __DLL ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys);
+void __DLL ilitek_dev_setting(void *handle,
+			      struct ilitek_ts_settings *setting);
+
+void __DLL ilitek_dev_bind_callback(void *handle,
+				    struct ilitek_ts_callback *callback);
+
+void __DLL *ilitek_dev_init(u8 _interface, const char *id,
+			    bool need_update_ts_info,
+			    struct ilitek_ts_callback *callback,
+			    void *_private);
+void __DLL ilitek_dev_exit(void *handle);
+
+void __DLL api_print_ts_info(void *handle);
+void __DLL api_read_then_print_m2v_info(void *handle);
+
+int __DLL api_update_ts_info(void *handle);
+
+int __DLL api_protocol_set_cmd(void *handle, u8 idx, void *data);
+int __DLL api_set_ctrl_mode(void *handle, u8 mode, bool eng, bool force);
+
+u16 __DLL api_get_block_crc_by_addr(void *handle, u8 type,
+				    u32 start, u32 end);
+u16 __DLL api_get_block_crc_by_num(void *handle, u8 type,
+				   u8 block_num);
+
+int __DLL api_set_data_len(void *handle, u16 data_len);
+int __DLL api_write_enable_v6(void *handle, bool in_ap, bool is_slave,
+			      u32 start, u32 end);
+int __DLL api_write_data_v6(void *handle, int wlen);
+int __DLL api_access_slave(void *handle, u8 id, u8 func, void *data);
+int __DLL api_check_busy(void *handle, int timeout_ms, int delay_ms);
+int __DLL api_write_enable_v3(void *handle, bool in_ap,
+			      bool write_ap, u32 end, u32 checksum);
+int __DLL api_write_data_v3(void *handle);
+
+int __DLL api_to_bl_mode(void *handle, bool bl, u32 start, u32 end);
+
+int __DLL api_write_data_m2v(void *handle, int wlen);
+int __DLL api_to_bl_mode_m2v(void *handle, bool to_bl);
+
+int __DLL api_set_idle(void *handle, bool enable);
+int __DLL api_set_func_mode(void *handle, u8 mode);
+int __DLL api_get_func_mode(void *handle);
+
+int __DLL api_erase_data_v3(void *handle);
+
+int __DLL api_read_flash(void *handle, u8 *buf,
+			 u32 start_addr, u32 len);
+
+int __DLL _api_read_mp_result(void *handle);
+int __DLL api_read_mp_result(void *handle);
+int __DLL _api_write_mp_result(void *handle, struct mp_station *mp);
+int __DLL api_write_mp_result(void *handle, struct mp_station *mp);
+void __DLL api_decode_mp_result(void *handle);
+
+int __DLL api_read_tuning(void *handle, u8 *buf, int rlen);
+
+int __DLL api_get_ic_crc(void *handle, u8 final_fw_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.c b/drivers/input/touchscreen/ilitek/ilitek_report.c
new file mode 100644
index 000000000000..dbe8abfd64ef
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_report.h"
+#include "ilitek_crypto.h"
+
+static bool is_debug_packet_id(u8 id, u8 _interface)
+{
+	return ((_interface == interface_i2c && id == 0xdb) ||
+		(_interface == interface_usb && id == 0xaa));
+}
+
+static bool is_pen_packet_id(u8 id)
+{
+	return (id == 0x0c || id == 0x0d);
+}
+
+static void touch_decode(struct ilitek_ts_device *dev,
+			 struct ilitek_report *report,
+			 u8 *buf, u8 cnt)
+{
+	struct touch_fmt *parser, *finger;
+	struct touch_iwb_fmt *parser_iwb;
+	u8 i, j, offset;
+	u32 size = dev->fmt.touch_size;
+
+	offset = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	for (i = 0; i < cnt && i < dev->tp_info.max_fingers; i++) {
+		finger = &report->touch.finger[i];
+
+		do {
+			if (dev->protocol.flag == PTL_V3 &&
+			    dev->_interface == interface_i2c) {
+				finger->id = i;
+				finger->status = buf[1 + offset + i * 5] >> 7;
+				finger->x = ((buf[1 + offset + i * 5] & 0x3F) << 8) +
+					buf[2 + offset + i * 5];
+				finger->y = ((buf[3 + offset + i * 5] & 0x3F) << 8) +
+					buf[4 + offset + i * 5];
+				finger->pressure = buf[5 + offset + i * 5];
+				finger->height = 128;
+				finger->width = 1;
+
+				break;
+			}
+
+			parser = (struct touch_fmt *)
+				(buf + 1 + i * size);
+			parser_iwb = (struct touch_iwb_fmt *)
+				(buf + 1 + i * size);
+
+			finger->id = parser->id;
+			finger->status = parser->status;
+			finger->x = parser->x;
+			finger->y = parser->y;
+
+			if (dev->setting.default_format_enabled)
+				break;
+
+			switch (dev->tp_info.format) {
+			case touch_fmt_0x1:
+				finger->pressure = parser->pressure;
+				break;
+			case touch_fmt_0x2:
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x3:
+				finger->pressure = parser->pressure;
+				finger->width = parser->width;
+				finger->height = parser->height;
+				break;
+			case touch_fmt_0x4:
+				finger->id = parser_iwb->id;
+				finger->status =
+					(parser_iwb->status == 0x7) ? 1 : 0;
+				finger->x = parser_iwb->x;
+				finger->y = parser_iwb->y;
+				finger->width = parser_iwb->width;
+				finger->height = parser_iwb->height;
+				break;
+			case touch_fmt_0x10:
+				finger->width = le16(buf + i * size + 6);
+				finger->height = le16(buf + i * size + 8);
+				finger->algo = buf[i * size + 10];
+				report->touch.algo = 0;
+				break;
+			case touch_fmt_0x11:
+				finger->id =
+					(!finger->id) ? 0 : finger->id - 1;
+				break;
+			}
+		} while (false);
+
+		TP_DBG(dev->id,
+		       "[touch-report] id:%hhu, status:%hhu, x:%hu, y:%hu, p:%hhu, w:%hu, h:%hu, algo: 0x%hhx\n",
+			finger->id, finger->status, finger->x, finger->y,
+			finger->pressure, finger->width, finger->height,
+			finger->algo);
+
+		if (finger->id >= dev->tp_info.max_fingers) {
+			TP_ERR(dev->id, "invalid touch id: %hhu >= %hhu\n",
+				finger->id, dev->tp_info.max_fingers);
+			return;
+		}
+
+		/*
+		 * if x/y within key's range, skip touch range check.
+		 */
+		for (j = 0; j < dev->tp_info.key_num; j++) {
+			if ((finger->x < dev->key.info.keys[j].x &&
+			     finger->x > dev->key.info.keys[j].x +
+				     dev->key.info.x_len) &&
+			    (finger->y < dev->key.info.keys[j].y &&
+			     finger->y > dev->key.info.keys[j].y +
+				     dev->key.info.y_len))
+				continue;
+
+			goto skip_touch_range_check;
+		}
+
+		if (finger->status &&
+		    (finger->x - 1 > dev->screen_info.x_max ||
+		     finger->y - 1 > dev->screen_info.y_max ||
+		     finger->x < dev->screen_info.x_min ||
+		     finger->y < dev->screen_info.y_min)) {
+			TP_ERR(dev->id, "Point[%d]: (%d, %d), Limit: (%d:%d, %d:%d) OOB\n",
+				finger->id, finger->x, finger->y,
+				dev->screen_info.x_min, dev->screen_info.x_max,
+				dev->screen_info.y_min, dev->screen_info.y_max);
+			return;
+		}
+
+skip_touch_range_check:
+		continue;
+	}
+
+	report->touch.cnt = i;
+
+	/*
+	 * report touch event callback,
+	 * which includes actual count of finger report just parsed above.
+	 */
+	if (report->cb.report_touch_event)
+		report->cb.report_touch_event(&report->touch, report->_private);
+}
+
+static void pen_decode(struct ilitek_ts_device *dev,
+		       struct ilitek_report *report,
+		       u8 *buf)
+{
+	struct pen_fmt *parser = (struct pen_fmt *)(buf + 1);
+	struct pen_fmt *pen = &report->pen.pen;
+
+	report->pen.cnt = buf[61];
+	report->pen.algo = buf[62];
+
+	memcpy(pen, parser, sizeof(struct pen_fmt));
+
+	switch (dev->tp_info.pen_format) {
+	default:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery);
+		break;
+	case 1:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_1.barrel_pressure,
+			pen->usi_1.idx, pen->usi_1.color, pen->usi_1.width,
+			pen->usi_1.style);
+		break;
+	case 2:
+		TP_DBG(dev->id,
+		       "[stylus-report] state:0x%hhx, x:%hu, y:%hu, pressure: %hu, x_tilt: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color24: %u, no_color: %hhu, width: %hhu, style: %hhu\n",
+			pen->modes, pen->x, pen->y, pen->pressure,
+			pen->x_tilt, pen->y_tilt, pen->battery, pen->usi_2.barrel_pressure,
+			pen->usi_2.idx, le32(pen->usi_2.color_24, 3),
+			pen->usi_2.no_color, pen->usi_2.width,
+			pen->usi_2.style);
+		break;
+	}
+
+	/* report pen event callback */
+	if (report->cb.report_pen_event)
+		report->cb.report_pen_event(&report->pen, report->_private);
+}
+
+static void dmsg_decode(struct ilitek_ts_device *dev,
+			struct ilitek_report *report,
+			u8 *buf, int buf_size)
+{
+	if ((int)buf[1] >= buf_size)
+		return;
+
+	buf[buf[1]] = '\0';
+	TP_DBG(dev->id, "%s\n", (char *)(buf + 2));
+
+	if (report->cb.report_dmsg)
+		report->cb.report_dmsg((char *)(buf + 2),
+					buf_size - 2, report->_private);
+}
+
+static void report_buf(struct ilitek_report *report, u8 *buf,
+		       bool is_last)
+{
+	if (!report->cb.report_buf)
+		return;
+
+	report->cb.report_buf(buf, 64, is_last, report->_private);
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v3(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+
+	u8 cnt;
+	int idx = (dev->quirks & QUIRK_BRIDGE) ? 4 : 0;
+
+	UNUSED(buf_size);
+
+	if (dev->_interface == interface_i2c) {
+		if (dev->quirks & QUIRK_BRIDGE) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+
+			if (buf[0] != 0x03 || buf[1] != 0xa3 || buf[2] != 0x10)
+				return -EAGAIN;
+
+			/* move algo byte to index 62 */
+			buf[62] = buf[35];
+		} else if (dev->quirks & QUIRK_DAEMON_I2C ||
+			   dev->quirks & QUIRK_BRIDGE ||
+			   dev->quirks & QUIRK_WIFI_ITS_I2C) {
+			error = read_interrupt_in(dev, buf, 64, 1000);
+			if (error < 0)
+				return error;
+		} else {
+			dev->wbuf[0] = 0x10;
+			error = write_then_read(dev, dev->wbuf, 1, buf, 32);
+			if (error < 0)
+				return error;
+
+			report->touch.algo = buf[31];
+			buf[31] = 0;
+
+			if (buf[0] == 2) {
+				error = write_then_read(dev, NULL, 0,
+							 buf + 31, 20);
+				if (error < 0)
+					return error;
+			}
+
+			/* move algo byte to index 62 */
+			buf[62] = report->touch.algo;
+		}
+
+		switch (dev->rbuf[idx]) {
+		default:
+		case 1:
+			cnt = 6;
+			break;
+		case 0:
+			cnt = 0;
+			break;
+		case 2:
+			cnt = 10;
+			break;
+		}
+	} else {
+		error = read_interrupt_in(dev, buf, 64, 1000);
+		if (error < 0)
+			return error;
+
+		cnt = buf[55];
+
+		/* move algo byte to index 62 */
+		buf[62] = buf[56];
+	}
+
+	report->touch.algo = buf[62];
+
+	report->touch.dbg_size = 64;
+	memcpy(report->touch.dbg, buf, 64);
+
+	report_buf(report, buf, true);
+
+	return cnt;
+}
+
+static bool is_iwb_fmt(struct ilitek_ts_device *dev)
+{
+	return dev->tp_info.format == touch_fmt_0x4 &&
+		!dev->setting.default_format_enabled;
+}
+
+static bool need_skip_checksum(struct ilitek_ts_device *dev,
+			       struct ilitek_report *report,
+			       u8 packet_id, bool is_first)
+{
+	/*
+	 * don't check checksum for below situation:
+	 *   1. debug packet
+	 *   2. normal mode pen packet w/ HID.
+	 *   3. IWB format packet
+	 */
+
+	if (report->skip_checksum)
+		return true;
+
+	if (is_first) {
+		if (is_debug_packet_id(packet_id, dev->_interface))
+			return true;
+
+		if ((dev->_interface == interface_usb ||
+		     dev->_interface == interface_hid_over_i2c) &&
+		    packet_id == 0x0d)
+			return true;
+	}
+
+	if (is_iwb_fmt(dev))
+		return true;
+
+	return false;
+}
+
+/* return touch finger's count or negative value as error code */
+static int report_get_raw_v6(struct ilitek_ts_device *dev,
+			     struct ilitek_report *report,
+			     u8 *buf, int buf_size)
+{
+	int error;
+	u8 i, cnt;
+	u8 size = dev->fmt.touch_size, max_cnt = dev->fmt.touch_max_cnt;
+	u8 tmp[64];
+
+	UNUSED(buf_size);
+
+	error = read_interrupt_in(dev, tmp, 64, 1000);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.format == 5)
+		ilitek_decrypt(tmp + 1, 48);
+
+	/* set packet id to 0x48 forcely for using BRIDGE finger report */
+	if (dev->quirks & QUIRK_BRIDGE && !is_pen_packet_id(tmp[0]))
+		tmp[0] = 0x48;
+
+	if (!need_skip_checksum(dev, report, tmp[0], true) &&
+	    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+		return -EILIPROTO;
+
+	report->pen.algo = tmp[62];
+	report->touch.algo = tmp[62];
+	report->pen.dbg_size = 64;
+	report->touch.dbg_size = 64;
+	memcpy(report->pen.dbg, tmp, 64);
+	memcpy(report->touch.dbg, tmp, 64);
+	memcpy(buf, tmp, 64);
+
+	/*
+	 * no need to check contact count byte for debug packet and pen packet.
+	 */
+	if (is_pen_packet_id(tmp[0]) ||
+	    is_debug_packet_id(tmp[0], dev->_interface)) {
+		report_buf(report, tmp, true);
+		return 0;
+	}
+
+	cnt = is_iwb_fmt(dev) ? tmp[55] : tmp[61];
+	for (i = 1; i < DIV_ROUND_UP(cnt, max_cnt); i++) {
+		report_buf(report, tmp, false);
+
+		error = read_interrupt_in(dev, tmp, 64, 1000);
+		if (error < 0)
+			return error;
+
+		if (dev->tp_info.format == 5)
+			ilitek_decrypt(tmp + 1, 48);
+
+		if (!need_skip_checksum(dev, report, tmp[0], false) &&
+		    !is_checksum_matched(tmp[63], 0, 63, tmp, sizeof(tmp)))
+			return -EILIPROTO;
+
+		/* copy and skip the first rid byte */
+		memcpy(buf + i * size * max_cnt + 1, tmp + 1, 63);
+	}
+	report_buf(report, tmp, true);
+
+	return cnt;
+}
+
+int ilitek_report_update(void *handle, struct ilitek_report *report)
+{
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)handle;
+	int cnt;
+
+	if (!dev)
+		return -EINVAL;
+
+	/* initial report event */
+	memset(&report->touch, 0, sizeof(report->touch));
+	memset(&report->pen, 0, sizeof(report->pen));
+
+	memset(dev->rbuf, 0, sizeof(dev->rbuf));
+
+	switch (dev->protocol.flag) {
+	default: return -EPERM;
+	case PTL_V3:
+		cnt = report_get_raw_v3(dev, report, dev->rbuf,
+					 sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		break;
+
+	case PTL_V6:
+		cnt = report_get_raw_v6(dev, report, dev->rbuf,
+					sizeof(dev->rbuf));
+		if (cnt < 0)
+			return cnt;
+
+		/* pen packet (V6 only) */
+		if (is_pen_packet_id(dev->rbuf[0])) {
+			pen_decode(dev, report, dev->rbuf);
+			return 0;
+		}
+
+		break;
+	}
+
+	/* debug message packet */
+	if (is_debug_packet_id(dev->rbuf[0], dev->_interface)) {
+		dmsg_decode(dev, report, dev->rbuf, sizeof(dev->rbuf));
+		return 0;
+	}
+
+	touch_decode(dev, report, dev->rbuf, cnt);
+
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_report.h b/drivers/input/touchscreen/ilitek/ilitek_report.h
new file mode 100644
index 000000000000..e49333f9618b
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_report.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_REPORT_H__
+#define __ILITEK_REPORT_H__
+
+#include "ilitek_def.h"
+#include "ilitek_protocol.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ touch_data {
+	struct touch_fmt finger[40];
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+struct __PACKED__ pen_data {
+	struct pen_fmt pen;
+
+	u8 cnt;
+	u8 algo;
+	u8 dbg[64];
+	u32 dbg_size;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return touch event */
+typedef void(*report_touch_event_t)(struct touch_data *, void *);
+/* return pen event */
+typedef void(*report_pen_event_t)(struct pen_data *, void *);
+/* return debug msg */
+typedef void(*report_dmsg_t)(char *, int, void *);
+/* return raw data buf */
+typedef void(*report_buf_t)(u8 *, int, bool, void *);
+
+struct ilitek_report_callback {
+	report_touch_event_t report_touch_event;
+	report_pen_event_t report_pen_event;
+	report_dmsg_t report_dmsg;
+	report_buf_t report_buf;
+};
+
+struct ilitek_report {
+	struct touch_data touch;
+	struct pen_data pen;
+	struct ilitek_report_callback cb;
+
+	bool skip_checksum;
+
+	void *_private;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	int __DLL ilitek_report_update(void *handle, struct ilitek_report *report);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_tool.c b/drivers/input/touchscreen/ilitek/ilitek_tool.c
new file mode 100644
index 000000000000..689c1c09d397
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_tool.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ */
+
+#include "ilitek_ts.h"
+#include "ilitek_common.h"
+#include <linux/firmware.h>
+
+#include <linux/miscdevice.h>
+
+static struct proc_dir_entry *ilitek_proc;
+static struct proc_dir_entry *ilitek_proc_entry;
+
+#define ILITEK_IOCTL_BASE                       100
+#define ILITEK_IOCTL_I2C_WRITE_DATA             _IOWR(ILITEK_IOCTL_BASE, 0, uint64_t)
+#define ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT      _IOWR(ILITEK_IOCTL_BASE, 0, uint32_t)
+#define ILITEK_IOCTL_I2C_WRITE_LENGTH           _IOWR(ILITEK_IOCTL_BASE, 1, int32_t)
+#define ILITEK_IOCTL_I2C_READ_DATA              _IOWR(ILITEK_IOCTL_BASE, 2, uint64_t)
+#define ILITEK_IOCTL_I2C_READ_DATA_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 2, uint32_t)
+#define ILITEK_IOCTL_I2C_READ_LENGTH            _IOWR(ILITEK_IOCTL_BASE, 3, int32_t)
+#define ILITEK_IOCTL_DRIVER_INFORMATION		_IOWR(ILITEK_IOCTL_BASE, 8, int32_t)
+#define ILITEK_IOCTL_USB_UPDATE_RESOLUTION      _IOWR(ILITEK_IOCTL_BASE, 9, int32_t)
+#define ILITEK_IOCTL_I2C_INT_FLAG	        _IOWR(ILITEK_IOCTL_BASE, 10, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE                 _IOWR(ILITEK_IOCTL_BASE, 11, int32_t)
+#define ILITEK_IOCTL_STOP_READ_DATA             _IOWR(ILITEK_IOCTL_BASE, 12, int32_t)
+#define ILITEK_IOCTL_START_READ_DATA            _IOWR(ILITEK_IOCTL_BASE, 13, int32_t)
+#define ILITEK_IOCTL_I2C_SWITCH_IRQ		_IOWR(ILITEK_IOCTL_BASE, 15, int32_t)
+#define ILITEK_IOCTL_UPDATE_FLAG		_IOWR(ILITEK_IOCTL_BASE, 16, int32_t)
+#define ILITEK_IOCTL_I2C_UPDATE_FW		_IOWR(ILITEK_IOCTL_BASE, 18, int32_t)
+#define ILITEK_IOCTL_RESET			_IOWR(ILITEK_IOCTL_BASE, 19, int32_t)
+#define ILITEK_IOCTL_INT_STATUS			_IOWR(ILITEK_IOCTL_BASE, 20, int32_t)
+
+
+#ifdef ILITEK_TUNING_MESSAGE
+extern bool ilitek_debug_flag;
+#define ILITEK_IOCTL_DEBUG_SWITCH		_IOWR(ILITEK_IOCTL_BASE, 21, int32_t)
+#endif
+
+#define ILITEK_IOCTL_I2C_INT_CLR		_IOWR(ILITEK_IOCTL_BASE, 22, int32_t)
+#define ILITEK_IOCTL_I2C_INT_POLL		_IOWR(ILITEK_IOCTL_BASE, 23, uint64_t)
+#define ILITEK_IOCTL_I2C_INT_POLL_COMPAT	_IOWR(ILITEK_IOCTL_BASE, 23, uint32_t)
+#define ILITEK_IOCTL_I2C_ISR_TYPE		_IOWR(ILITEK_IOCTL_BASE, 24, uint32_t)
+#define ILITEK_IOCTL_I2C_NETLINK		_IOWR(ILITEK_IOCTL_BASE, 25, uint16_t)
+
+
+#define ILITEK_DEVICE_NODE_PERMISSON			0666
+
+static s32 ilitek_file_open(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = true;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static s32 ilitek_file_close(struct inode *inode, struct file *filp)
+{
+	ts->operation_protection = false;
+	TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+	return 0;
+}
+
+static ssize_t ilitek_file_write(struct file *filp,
+				 const char *buf, size_t size, loff_t *f_pos)
+{
+	s32 ret = 0, count = 0;
+	u8 buffer[512];
+	u32 *data;
+	char *token = NULL, *cur = NULL;
+
+	if (size > sizeof(buffer)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(buffer));
+		return -EINVAL;
+	}
+
+	ret = copy_from_user(buffer, buf, size);
+	if (ret < 0) {
+		TP_ERR(NULL, "copy data from user space, failed");
+		return -1;
+	}
+
+	token = cur = buffer;
+
+	data = kcalloc(size, sizeof(u32), GFP_KERNEL);
+
+	while ((token = strsep(&cur, ",")) != NULL) {
+		//data[count] = str2hex(token);
+		sscanf(token, "%x", &data[count]);
+		TP_MSG(NULL, "data[%d] = %x\n", count, data[count]);
+		count++;
+	}
+
+	if (buffer[size - 2] == 'I' && (size == 20 || size == 52) && buffer[0] == 0x77 && buffer[1] == 0x77) {
+
+		TP_MSG(NULL, "IOCTL_WRITE CMD = %d\n", buffer[2]);
+		switch (buffer[2]) {
+		case 13:
+			//ilitek_irq_enable();
+			TP_MSG(NULL, "ilitek_irq_enable. do nothing\n");
+			break;
+		case 12:
+			//ilitek_irq_disable();
+			TP_MSG(NULL, "ilitek_irq_disable. do nothing\n");
+			break;
+		case 19:
+			ilitek_reset(ts->dev->reset_time);
+			break;
+#ifdef ILITEK_TUNING_MESSAGE
+		case 21:
+			TP_MSG(NULL, "ilitek The ilitek_debug_flag = %d.\n", buffer[3]);
+			if (buffer[3] == 0) {
+				ilitek_debug_flag = false;
+			} else if (buffer[3] == 1) {
+				ilitek_debug_flag = true;
+			}
+			break;
+#endif
+		case 15:
+			if (buffer[3] == 0)
+				ilitek_irq_disable();
+			else
+				ilitek_irq_enable();
+
+			break;
+		case 16:
+			ts->operation_protection = buffer[3];
+			TP_MSG(NULL, "ts->operation_protection = %d\n", ts->operation_protection);
+			break;
+		case 18:
+			ilitek_irq_disable();
+			mutex_lock(&ts->ilitek_mutex);
+			ret = ilitek_write(&buffer[3], 33);
+			mutex_unlock(&ts->ilitek_mutex);
+			ilitek_irq_enable();
+			if (ret < 0)
+				TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+
+			return ret;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	if (buffer[size - 2] == 'W') {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write(buffer, size - 2);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c write error, ret %d\n", ret);
+			return ret;
+		}
+	} else if (!strncmp(buffer, "unhandle_irq", strlen("unhandle_irq"))) {
+		ts->unhandle_irq = !ts->unhandle_irq;
+		TP_MSG(NULL, "ts->unhandle_irq = %d.\n", ts->unhandle_irq);
+	} else if (!strncmp(buffer, "dbg_pkt", strlen("dbg_pkt"))) {
+		set_log_level(log_level_pkt);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_pkt);
+	} else if (!strncmp(buffer, "dbg_debug", strlen("dbg_debug"))) {
+		set_log_level(log_level_dbg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_dbg);
+	} else if (!strncmp(buffer, "dbg_info", strlen("dbg_info"))) {
+		set_log_level(log_level_msg);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_msg);
+	} else if (!strncmp(buffer, "dbg_err", strlen("dbg_err"))) {
+		set_log_level(log_level_err);
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", log_level_err);
+	} else if (!strncmp(buffer, "dbg_num", strlen("dbg_num"))) {
+		TP_MSG(NULL, "ilitek_log_level_value = %d.\n", tp_log_level);
+	}
+#ifdef ILITEK_TUNING_MESSAGE
+	else if (!strncmp(buffer, "truning_dbg_flag", strlen("truning_dbg_flag"))) {
+		ilitek_debug_flag = !ilitek_debug_flag;
+		TP_MSG(NULL, " %s debug_flag message(%X).\n", ilitek_debug_flag ? "Enabled" : "Disabled", ilitek_debug_flag);
+	}
+#endif
+	else if (!strncmp(buffer, "irq_status", strlen("irq_status"))) {
+		TP_MSG(NULL, "gpio_get_value(i2c.irq_gpio) = %d.\n", gpio_get_value(ts->irq_gpio));
+	} else if (!strncmp(buffer, "enable", strlen("enable"))) {
+		ilitek_irq_enable();
+		TP_MSG(NULL, "irq enable\n");
+	} else if (!strncmp(buffer, "disable", strlen("disable"))) {
+		ilitek_irq_disable();
+		TP_MSG(NULL, "irq disable\n");
+	} else if (!strncmp(buffer, "info", strlen("info"))) {
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		api_update_ts_info(ts->dev);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+	} else if (!strncmp(buffer, "reset", strlen("reset"))) {
+		ilitek_reset(ts->dev->reset_time);
+	}
+
+	TP_DBG(NULL, "ilitek return count = %zu\n", size);
+	kfree(data);
+	return size;
+}
+
+static FOPS_IOCTL_FUNC(ilitek_file_ioctl, uint32_t cmd, unsigned long arg)
+{
+	static u8 *buffer;
+	static unsigned long len;
+	s32 ret = 0;
+	int tmp;
+
+	buffer = kmalloc(ILITEK_IOCTL_MAX_TRANSFER, GFP_KERNEL);
+	memset(buffer, 0, ILITEK_IOCTL_MAX_TRANSFER);
+
+	switch (cmd) {
+	case ILITEK_IOCTL_I2C_WRITE_DATA:
+	case ILITEK_IOCTL_I2C_WRITE_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid write len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(buffer, (u8 *)arg, len)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, len, 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write failed, cmd: %x\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_I2C_READ_DATA:
+	case ILITEK_IOCTL_I2C_READ_DATA_COMPAT:
+		if (len > ILITEK_IOCTL_MAX_TRANSFER) {
+			TP_ERR(NULL, "invalid read len: %lu > %lu too large\n",
+				len, ILITEK_IOCTL_MAX_TRANSFER);
+			ret = -EINVAL;
+			break;
+		}
+
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(NULL, 0, 0, buffer, len);
+		mutex_unlock(&ts->ilitek_mutex);
+		if (ret < 0) {
+			TP_ERR(NULL, "i2c read failed, buf: %x\n", buffer[0]);
+			break;
+		}
+
+		if (copy_to_user((u8 *)arg, buffer, len)) {
+			ret = -EFAULT;
+			TP_ERR(NULL, "copy data to user space, failed\n");
+		}
+		break;
+	case ILITEK_IOCTL_I2C_WRITE_LENGTH:
+	case ILITEK_IOCTL_I2C_READ_LENGTH:
+		len = arg;
+		break;
+	case ILITEK_IOCTL_DRIVER_INFORMATION:
+		memcpy(buffer, driver_ver, 7);
+		if (copy_to_user((u8 *)arg, buffer, 7))
+			ret = -EFAULT;
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE:
+		break;
+	case ILITEK_IOCTL_I2C_INT_FLAG:
+		buffer[0] = !(gpio_get_value(ts->irq_gpio));
+		if (copy_to_user((u8 *)arg, buffer, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_FLAG = %d.\n", buffer[0]);
+		break;
+	case ILITEK_IOCTL_START_READ_DATA:
+		ilitek_irq_enable();
+		ts->unhandle_irq = false;
+		TP_MSG(NULL, "enable_irq and ts->unhandle_irq = false.\n");
+		break;
+	case ILITEK_IOCTL_STOP_READ_DATA:
+		ilitek_irq_disable();
+		ts->unhandle_irq = true;
+		TP_MSG(NULL, "disable_irq and ts->unhandle_irq = true.\n");
+		break;
+	case ILITEK_IOCTL_RESET:
+		ilitek_reset(ts->dev->reset_time);
+		break;
+	case ILITEK_IOCTL_INT_STATUS:
+		if (put_user(gpio_get_value(ts->irq_gpio), (s32 *)arg))
+			ret = -EFAULT;
+		break;
+#ifdef ILITEK_TUNING_MESSAGE
+	case ILITEK_IOCTL_DEBUG_SWITCH:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+		TP_MSG(NULL, "ilitek The debug_flag = %d.\n", buffer[0]);
+		if (buffer[0] == 0)
+			ilitek_debug_flag = false;
+		else if (buffer[0] == 1)
+			ilitek_debug_flag = true;
+		break;
+#endif
+	case ILITEK_IOCTL_I2C_SWITCH_IRQ:
+		if (copy_from_user(buffer, (u8 *)arg, 1)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		if (buffer[0] == 0)
+			ilitek_irq_disable();
+		else
+			ilitek_irq_enable();
+
+		break;
+	case ILITEK_IOCTL_UPDATE_FLAG:
+		ts->operation_protection = arg;
+		TP_MSG(NULL, "operation_protection = %d\n", ts->operation_protection);
+		break;
+	case ILITEK_IOCTL_I2C_UPDATE_FW:
+		if (copy_from_user(buffer, (u8 *)arg, 35)) {
+			TP_ERR(NULL, "copy data from user space, failed\n");
+			ret = -EFAULT;
+			break;
+		}
+
+		ilitek_irq_disable();
+		mutex_lock(&ts->ilitek_mutex);
+		ret = ilitek_write_and_read(buffer, buffer[34], 0, NULL, 0);
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+
+		if (ret < 0)
+			TP_ERR(NULL, "i2c write, failed\n");
+
+		break;
+	case ILITEK_IOCTL_I2C_INT_CLR:
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_CLR, set get_INT false\n");
+		atomic_set(&ts->get_INT, 0);
+		break;
+	case ILITEK_IOCTL_I2C_INT_POLL:
+	case ILITEK_IOCTL_I2C_INT_POLL_COMPAT:
+		tmp = atomic_read(&ts->get_INT);
+		TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_POLL, get_INT: %d\n", tmp);
+
+		if (copy_to_user((u8 *)arg, &tmp, 1)) {
+			TP_ERR(NULL, "copy data to user space, failed\n");
+			ret = -EFAULT;
+		}
+		break;
+	case ILITEK_IOCTL_I2C_ISR_TYPE:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_ISR_TYPE, set ISR type: %lu\n", arg);
+		ts->irq_handle_type = (arg >> 16);
+		ts->irq_read_len = arg & 0xFFFF;
+		break;
+	case ILITEK_IOCTL_I2C_NETLINK:
+		TP_MSG(NULL, "ILITEK_IOCTL_I2C_NETLINK, set netlink: %s with ETH: %hhu\n",
+			(arg >> 8) ? "ON" : "OFF", (u8)(arg & 0xFF));
+
+		if (arg >> 8)
+			ret = ilitek_netlink_init(arg & 0xFF);
+		else
+			ilitek_netlink_exit();
+
+		break;
+	default:
+		TP_ERR(NULL, "unrecognized ioctl cmd: 0x%04x\n", cmd);
+		ret = -EINVAL;
+		break;
+	}
+
+	kfree(buffer);
+	return (ret < 0) ? ret : 0;
+}
+
+static ssize_t ilitek_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	u8 *tmp;
+	s32 ret;
+
+	if (count > 8192)
+		count = 8192;
+
+	tmp = kmalloc(count, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = ilitek_read(tmp, count);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+	if (ret < 0) {
+		TP_ERR(NULL, "i2c read error, ret %d\n", ret);
+		goto err_free;
+	}
+
+	if (copy_to_user(buf, tmp, count)) {
+		ret = -EFAULT;
+		goto err_free;
+	}
+
+err_free:
+	kfree(tmp);
+
+	return ret > 0 ? count : ret;
+}
+
+/* compat ioctl for 32/64 bit user program compatibility */
+#ifdef CONFIG_COMPAT
+static long ilitek_file_compat_ioctl(struct file *fp, uint32_t cmd,
+				     unsigned long arg)
+{
+	return ilitek_file_ioctl(fp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+
+static struct file_operations ilitek_fops = {
+	.FOPS_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = ilitek_file_compat_ioctl,
+#endif
+
+	.read = ilitek_file_read,
+	.write = ilitek_file_write,
+	.open = ilitek_file_open,
+	.release = ilitek_file_close,
+};
+
+static struct PROC_FOPS_T ilitek_proc_fops = {
+	.PROC_IOCTL = ilitek_file_ioctl,
+
+#ifdef CONFIG_COMPAT
+	.PROC_COMPAT_IOCTL = ilitek_file_compat_ioctl,
+#endif
+
+	.PROC_READ = ilitek_file_read,
+	.PROC_WRITE = ilitek_file_write,
+	.PROC_OPEN = ilitek_file_open,
+	.PROC_RELEASE = ilitek_file_close,
+};
+
+static ssize_t ilitek_driver_version_read(struct file *fp, char __user *buf,
+					  size_t size, loff_t *off)
+{
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	cnt = scnprintf(str, sizeof(str), "driver-version-tag: [%*phD]\n",
+			7, driver_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_drv_version = {
+	.PROC_READ = ilitek_driver_version_read,
+};
+
+static ssize_t ilitek_update_fw_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int error;
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0) {
+		error = ilitek_upgrade_firmware("ilitek.bin");
+		if (error < 0)
+			cnt += scnprintf(str, sizeof(str),
+					 "upgrade failed, err: %d\n", error);
+	} else {
+		cnt += scnprintf(str, sizeof(str),
+				 "upgrade success, fw version: %*phD\n",
+				 8, ts->dev->fw_ver);
+	}
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwupdate = {
+	.PROC_READ = ilitek_update_fw_read,
+};
+
+static ssize_t ilitek_firmware_version_read(struct file *fp, char __user *buf,
+					    size_t size, loff_t *off)
+{
+	int error;
+	int cnt;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (error < 0)
+		cnt = scnprintf(str, sizeof(str), "read failed, err: %d\n",
+				error);
+	else
+		cnt = scnprintf(str, sizeof(str), "fw-version-tag: [%*phD]\n",
+				8, ts->dev->fw_ver);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_fwversion = {
+	.PROC_READ = ilitek_firmware_version_read,
+};
+
+static ssize_t ilitek_console_write(struct file *filp, const char *buf,
+				    size_t size, loff_t *f_pos)
+{
+	int error;
+	char tmp[256], str[256];
+	char *ptr, *cur = str;
+	size_t wlen, rlen, _wlen = 0;
+	u8 cmd[64], data[64];
+
+	if (size > sizeof(tmp)) {
+		TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n",
+			size, sizeof(tmp));
+		return -EINVAL;
+	}
+
+	error = copy_from_user(tmp, buf, size);
+	if (error < 0) {
+		TP_ERR(NULL, "copy_from_user failed, err: %d", error);
+		return error;
+	}
+
+	memset(str, 0, sizeof(str));
+	sscanf(tmp, "%zu:%zu:%s", &wlen, &rlen, str);
+	TP_MSG(NULL, "wlen: %zu, rlen: %zu, command: %s\n", wlen, rlen, str);
+
+	while ((ptr = strsep(&cur, "-")) && _wlen < sizeof(cmd))
+		sscanf(ptr, "%hhx", &cmd[_wlen++]);
+
+	if (wlen > 0 && wlen != _wlen) {
+		TP_ERR(NULL, "write cmd length: %zu not match with %s\n",
+			wlen, str);
+		return -EINVAL;
+	} else if (rlen > sizeof(data)) {
+		TP_ERR(NULL, "invalid read cmd length: %zu > %zu too large\n",
+			rlen, sizeof(data));
+		return -EINVAL;
+	}
+
+	if (wlen > 0)
+		TP_MSG(NULL, "[write]: %*phD, len: %zu\n",
+			(int)wlen, cmd, wlen);
+
+	error = ilitek_write_and_read(cmd, wlen, 1, data, rlen);
+	if (error < 0)
+		return error;
+
+	if (rlen > 0)
+		TP_MSG(NULL, "[read]: %*phD, len: %zu\n",
+			(int)rlen, data, rlen);
+
+	return size;
+}
+
+static struct PROC_FOPS_T ilitek_fops_console = {
+	.PROC_WRITE = ilitek_console_write,
+};
+
+static ssize_t ilitek_func_mode_write(struct file *fp, const char *buf,
+				      size_t size, loff_t *off)
+{
+	int error, cnt;
+	char str[64];
+	u8 func_mode;
+
+	cnt = MIN(size, sizeof(str));
+	memset(str, 0, sizeof(str));
+	if (copy_from_user(str, buf, cnt))
+		return -EFAULT;
+
+	sscanf(str, "%hhu", &func_mode);
+	TP_MSG(NULL, "set func mode: %hhu, support max: %hhu modes\n",
+		func_mode, ts->dev->tp_info.support_modes);
+
+	if (func_mode >= ts->dev->tp_info.support_modes)
+		return -EINVAL;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	error |= api_set_func_mode(ts->dev, func_mode);
+	error |= api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return (error < 0) ? error : cnt;
+}
+
+static ssize_t ilitek_func_mode_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 i;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_protocol_set_cmd(ts->dev, GET_TP_INFO, NULL);
+	api_get_func_mode(ts->dev);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "function mode: ");
+	for (i = 0; i < ts->dev->tp_info.support_modes; i++) {
+		if (i == ts->dev->func.mode)
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "[%hhu] ", i);
+		else
+			cnt += scnprintf(str + strlen(str), sizeof(str) - cnt,
+					 "%hhu ", i);
+	}
+	cnt += scnprintf(str + strlen(str), sizeof(str) - cnt, "\n");
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_func_mode = {
+	.PROC_READ = ilitek_func_mode_read,
+	.PROC_WRITE = ilitek_func_mode_write,
+};
+
+static ssize_t ilitek_crypto_key_write(struct file *fp, const char *buf,
+				       size_t size, loff_t *off)
+{
+	u8 crypto[AES_KEY_LEN * 2];
+
+	if (size != sizeof(crypto) ||
+	    copy_from_user(crypto, buf, sizeof(crypto)))
+		return -EFAULT;
+
+	TP_MSG(NULL, "set crypto_key: %*phD\n", AES_KEY_LEN, crypto);
+	TP_MSG(NULL, "set crypto_iv: %*phD\n", AES_KEY_LEN,
+		crypto + AES_KEY_LEN);
+
+	memcpy(crypto_key, crypto, AES_KEY_LEN);
+	memcpy(crypto_iv, crypto + AES_KEY_LEN, AES_KEY_LEN);
+
+	return sizeof(crypto);
+}
+
+static ssize_t ilitek_crypto_key_read(struct file *fp, char __user *buf,
+				      size_t size, loff_t *off)
+{
+	int cnt = 0;
+	u8 str[256];
+
+	if (*off)
+		return 0;
+
+	memset(str, 0, sizeof(str));
+	cnt += scnprintf(str, sizeof(str), "key: %*phD\n",
+			 AES_KEY_LEN, crypto_key);
+	cnt += scnprintf(str + strlen(str), sizeof(str), "iv: %*phD\n",
+			 AES_KEY_LEN, crypto_iv);
+
+	if (copy_to_user(buf, str, cnt))
+		return -EFAULT;
+
+	*off += cnt;
+
+	return cnt;
+}
+
+static struct PROC_FOPS_T ilitek_fops_crypto_key = {
+	.PROC_READ = ilitek_crypto_key_read,
+	.PROC_WRITE = ilitek_crypto_key_write,
+};
+
+static ssize_t ilitek_setmode_0_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 0);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_0 = {
+	.PROC_READ = ilitek_setmode_0_read,
+};
+
+static ssize_t ilitek_setmode_1_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 1);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_1 = {
+	.PROC_READ = ilitek_setmode_1_read,
+};
+
+static ssize_t ilitek_setmode_2_read(struct file *fp, char __user *buf,
+				     size_t size, loff_t *off)
+{
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	api_set_func_mode(ts->dev, 2);
+	api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	return 0;
+}
+
+static struct PROC_FOPS_T ilitek_fops_setmode_2 = {
+	.PROC_READ = ilitek_setmode_2_read,
+};
+
+static ssize_t ilitek_driver_version_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "driver-version-tag: [%*phD]\n",
+			 7, driver_ver);
+}
+
+static DEVICE_ATTR(driver_version, 0664, ilitek_driver_version_show, NULL);
+
+static ssize_t ilitek_eds_check_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	if (ts->esd_check)
+		return scnprintf(buf, PAGE_SIZE, "[enable] disable\n");
+
+	return scnprintf(buf, PAGE_SIZE, "enable [disable]\n");
+}
+
+static ssize_t ilitek_esd_check_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	TP_MSG(NULL, "set esd check: %hhx to %s\n", ts->esd_check, buf);
+
+	if (!strncmp(buf, "enable", 6) && !ts->esd_check) {
+		ilitek_create_esd_check_workqueue();
+		ts->esd_check = true;
+	} else if (!strncmp(buf, "disable", 7) && ts->esd_check) {
+		ilitek_remove_esd_check_workqueue();
+		ts->esd_check = false;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(esd_check, 0664,
+		   ilitek_eds_check_show,
+		   ilitek_esd_check_store);
+
+
+static ssize_t ilitek_low_power_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->low_power_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_low_power_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	int i;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_LOW_POWER_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set low power: %hhx to %s\n", ts->low_power_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		ts->low_power_status = modes[i].id;
+	}
+
+	return size;
+}
+static DEVICE_ATTR(low_power, 0664,
+		   ilitek_low_power_show,
+		   ilitek_low_power_store);
+
+static ssize_t ilitek_gesture_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int i;
+	ssize_t cnt = 0;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].id == ts->gesture_status) {
+			cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+					 "[%s] ", modes[i].str);
+			continue;
+		}
+
+		cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt,
+				 "%s ", modes[i].str);
+	}
+
+	cnt += scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, "\n");
+
+	return cnt;
+}
+
+static ssize_t ilitek_gesture_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int i;
+	u8 type = ts->gesture_status;
+
+#define X(_type, _id, _str)	{.str = _str, .id = _id},
+	struct {
+		char *str;
+		u8 id;
+	} modes[] = { ILITEK_GESTURE_TYPES };
+#undef X
+
+	TP_MSG(NULL, "set gesture: %hhx to %s\n", ts->gesture_status, buf);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strncmp(buf, modes[i].str, strlen(modes[i].str)))
+			continue;
+		type = modes[i].id;
+	}
+
+	if (!ts->gesture_status && type)
+		ilitek_register_gesture(ts, true);
+	else if (ts->gesture_status && !type)
+		ilitek_register_gesture(ts, false);
+
+	ts->gesture_status = type;
+
+	return size;
+}
+static DEVICE_ATTR(gesture, 0664, ilitek_gesture_show, ilitek_gesture_store);
+
+static ssize_t ilitek_firmware_version_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	int ret;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	ret = api_update_ts_info(ts->dev);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+	if (ret < 0) {
+		TP_ERR(NULL, "read tp info failed, err: %d\n", ret);
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n", ret);
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "fw-version-tag: [%*phD]\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(firmware_version, 0664, ilitek_firmware_version_show, NULL);
+
+static ssize_t ilitek_module_name_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	ilitek_irq_disable();
+	mutex_lock(&ts->ilitek_mutex);
+	error = api_set_ctrl_mode(ts->dev, mode_suspend, false, true);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_protocol_set_cmd(ts->dev, GET_MCU_VER, NULL);
+	if (error < 0) {
+		mutex_unlock(&ts->ilitek_mutex);
+		ilitek_irq_enable();
+		goto out;
+	}
+	error = api_set_ctrl_mode(ts->dev, mode_normal, false, true);
+	if (error < 0)
+		TP_ERR(NULL, "read mcu ver. failed, err: %d\n", error);
+	mutex_unlock(&ts->ilitek_mutex);
+	ilitek_irq_enable();
+
+out:
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "read failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE, "module-name-tag: [%s]\n",
+			 ts->dev->mcu_info.module_name);
+}
+
+static DEVICE_ATTR(product_id, 0664, ilitek_module_name_show, NULL);
+
+static ssize_t ilitek_update_fw_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	int error;
+
+	error = ilitek_upgrade_firmware("ilitek.hex");
+	if (error < 0)
+		error = ilitek_upgrade_firmware("ilitek.bin");
+	if (error < 0)
+		return scnprintf(buf, PAGE_SIZE, "upgrade failed, err: %d\n",
+				 error);
+
+	return scnprintf(buf, PAGE_SIZE,
+			 "upgrade success, current fw version: %*phD\n",
+			 8, ts->dev->fw_ver);
+}
+
+static DEVICE_ATTR(update_fw, 0664, ilitek_update_fw_show, NULL);
+
+static struct attribute *ilitek_sysfs_attrs_ctrl[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_firmware_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_gesture.attr,
+	&dev_attr_low_power.attr,
+	&dev_attr_esd_check.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static struct attribute_group ilitek_attribute_group[] = {
+	{ .attrs = ilitek_sysfs_attrs_ctrl },
+};
+
+int ilitek_create_sysfsnode(void)
+{
+	int error;
+
+	error = sysfs_create_group(&ts->device->kobj, ilitek_attribute_group);
+	if (error < 0) {
+		TP_ERR(NULL, "sysfs_create_group failed, err: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+void ilitek_remove_sys_node(void)
+{
+	sysfs_remove_group(&ts->device->kobj, ilitek_attribute_group);
+}
+
+static struct miscdevice ilitek_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "ilitek_ctrl",
+	.fops = &ilitek_fops,
+	.mode = 0666,
+};
+
+int ilitek_create_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+	misc_register(&ilitek_misc);
+
+	ilitek_proc = proc_create("ilitek_ctrl", ILITEK_DEVICE_NODE_PERMISSON,
+				  NULL, &ilitek_proc_fops);
+
+	if (!ilitek_proc)
+		TP_ERR(NULL, "proc_create(ilitek_ctrl, ILITEK_DEVICE_NODE_PERMISSON, NULL, &ilitek_fops) fail\n");
+
+	ilitek_proc_entry = proc_mkdir("ilitek", NULL);
+	if (!ilitek_proc_entry) {
+		TP_ERR(NULL, "Error, failed to creat procfs.\n");
+		return -EINVAL;
+	}
+
+	if (!proc_create("driver_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_drv_version))
+		TP_ERR(NULL, "failed to create procfs driver_version.\n");
+
+	if (!proc_create("firmware_version", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwversion))
+		TP_ERR(NULL, "failed to create procfs firmware_version.\n");
+
+	if (!proc_create("console", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_console))
+		TP_ERR(NULL, "failed to create procfs console.\n");
+
+	if (!proc_create("update_fw", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_fwupdate))
+		TP_ERR(NULL, "failed to create procfs update_fw.\n");
+
+	if (!proc_create("func_mode", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_func_mode))
+		TP_ERR(NULL, "failed to create procfs func_mode.\n");
+
+	if (!proc_create("crypto_key", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_crypto_key))
+		TP_ERR(NULL, "failed to create procfs crypto_key.\n");
+
+	/*
+	 * below setmode_X is historical setting for some customer need
+	 * to set function mode by cat procfs node.
+	 * please make sure it's not risky to modifiy below.
+	 */
+	if (!proc_create("setmode_0", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_0))
+		TP_ERR(NULL, "failed to create procfs setmode_0.\n");
+	if (!proc_create("setmode_1", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_1))
+		TP_ERR(NULL, "failed to create procfs setmode_1.\n");
+	if (!proc_create("setmode_2", ILITEK_DEVICE_NODE_PERMISSON,
+			 ilitek_proc_entry, &ilitek_fops_setmode_2))
+		TP_ERR(NULL, "failed to create procfs setmode_2.\n");
+#endif
+
+	return 0;
+}
+
+int ilitek_remove_tool_node(void)
+{
+#ifdef ILITEK_TOOL
+
+	misc_deregister(&ilitek_misc);
+
+	if (ilitek_proc) {
+		TP_MSG(NULL, "remove procfs ilitek_ctrl.\n");
+		remove_proc_entry("ilitek_ctrl", NULL);
+		ilitek_proc = NULL;
+	}
+
+	if (ilitek_proc_entry) {
+		TP_MSG(NULL, "remove procfs inode\n");
+		remove_proc_entry("driver_version", ilitek_proc_entry);
+		remove_proc_entry("firmware_version", ilitek_proc_entry);
+		remove_proc_entry("console", ilitek_proc_entry);
+		remove_proc_entry("update_fw", ilitek_proc_entry);
+		remove_proc_entry("func_mode", ilitek_proc_entry);
+		remove_proc_entry("crypto_key", ilitek_proc_entry);
+
+		remove_proc_entry("setmode_0", ilitek_proc_entry);
+		remove_proc_entry("setmode_1", ilitek_proc_entry);
+		remove_proc_entry("setmode_2", ilitek_proc_entry);
+
+		remove_proc_entry("ilitek", NULL);
+		ilitek_proc_entry = NULL;
+	}
+#endif
+	return 0;
+}
diff --git a/drivers/input/touchscreen/ilitek/ilitek_ts.h b/drivers/input/touchscreen/ilitek/ilitek_ts.h
new file mode 100644
index 000000000000..9ee0b13e08cd
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_ts.h
@@ -0,0 +1,268 @@
+/*
+ * ILITEK Touch IC driver
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ *
+ * Author: Luca Hsu <luca_hsu@ilitek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ *
+ */
+
+#ifndef __ILITEK_TS_H__
+#define __ILITEK_TS_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+
+#include <linux/init.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+//#if defined(CONFIG_FB)
+#if 0
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#define ILITEK_BLANK_POWERDOWN			FB_BLANK_POWERDOWN
+#define	ILITEK_BLANK_UNBLANK			FB_BLANK_UNBLANK
+#define	ILITEK_EVENT_BLANK			FB_EVENT_BLANK
+#define	ILITEK_BLANK_NORMAL			FB_BLANK_NORMAL
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#define ILITEK_BL_ADDR				0x41
+#define ILITEK_SENSOR_ID_MASK			0xff
+
+#define ILITEK_PLAT_QCOM			1
+#define ILITEK_PLAT_MTK				2
+#define ILITEK_PLAT_ROCKCHIP			3
+#define ILITEK_PLAT_ALLWIN			4
+#define ILITEK_PLAT_AMLOGIC			5
+#define ILITEK_PLAT				ILITEK_PLAT_ROCKCHIP
+//#define CONFIG_QCOM_DRM
+#if ILITEK_PLAT == ILITEK_PLAT_QCOM
+#ifdef CONFIG_QCOM_DRM
+#include <linux/msm_drm_notify.h>
+#define ILITEK_BLANK_POWERDOWN			MSM_DRM_BLANK_POWERDOWN
+#define ILITEK_BLANK_UNBLANK			MSM_DRM_BLANK_UNBLANK
+#define ILITEK_EVENT_BLANK			MSM_DRM_EVENT_BLANK
+#define ILITEK_BLANK_NORMAL			MSM_DRM_BLANK_UNBLANK
+#endif
+#endif
+
+#define ILITEK_GESTURE_TYPES			\
+	X(Disable, 0, "disable")		\
+	X(Single_Click, 1, "single-click")	\
+	X(Double_Click, 2, "double-click")
+#define ILITEK_GESTURE_DEFAULT			Gesture_Disable
+
+#define X(_type, _id, _str)	Gesture_##_type = _id,
+enum Gesture_Type {
+	ILITEK_GESTURE_TYPES
+};
+#undef X
+
+#define ILITEK_LOW_POWER_TYPES		\
+	X(Sleep,	0, "sleep")	\
+	X(Idle,		1, "idle")	\
+	X(PowerOff,	2, "poweroff")
+#define ILITEK_LOW_POWER_DEFAULT		Low_Power_Sleep
+
+#define X(_type, _id, _str)	Low_Power_##_type = _id,
+enum Low_Power_Type {
+	ILITEK_LOW_POWER_TYPES
+};
+#undef X
+
+#define ILITEK_CHECKSUM_FAILED_RELEASE		true
+
+#define ILITEK_TOUCH_PROTOCOL_B
+//#define ILITEK_REPORT_PRESSURE
+//#define ILITEK_USE_LCM_RESOLUTION
+//#define ILITEK_ISR_PROTECT
+
+#define ILITEK_TUNING_MESSAGE
+#define ILITEK_REGISTER_SUSPEND_RESUME
+#define ILITEK_ESD_CHECK_ENABLE			0
+
+#define ILITEK_TOOL
+
+#define ILITEK_ROTATE_FLAG			0
+#define ILITEK_REVERT_X				0
+#define ILITEK_REVERT_Y				0
+#define TOUCH_SCREEN_X_MAX			1080	//LCD_WIDTH
+#define TOUCH_SCREEN_Y_MAX			1920	//LCD_HEIGHT
+#define ILITEK_RESOLUTION_MAX			16384
+//#define ILITEK_ENABLE_REGULATOR_POWER_ON
+#define ILITEK_GET_GPIO_NUM
+
+#define ILITEK_GET_TIME_FUNC_WITH_TIME		0
+#define ILITEK_GET_TIME_FUNC_WITH_JIFFIES	1
+#define ILITEK_GET_TIME_FUNC			ILITEK_GET_TIME_FUNC_WITH_JIFFIES
+
+#define DOUBLE_CLICK_DISTANCE			1000
+#define DOUBLE_CLICK_ONE_CLICK_USED_TIME	800
+#define DOUBLE_CLICK_NO_TOUCH_TIME		1000
+#define DOUBLE_CLICK_TOTAL_USED_TIME		(DOUBLE_CLICK_NO_TOUCH_TIME + (DOUBLE_CLICK_ONE_CLICK_USED_TIME * 2))
+
+//#define ILITEK_WAKELOCK_SUPPORT
+#if defined(ILITEK_WAKELOCK_SUPPORT)
+#include <linux/wakelock.h>
+#endif
+
+#define ILITEK_TS_NAME				"ilitek_ts"
+
+#define ABSSUB(a, b) ((a > b) ? (a - b) : (b - a))
+
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+//#define MTK_UNDTS //no use dts and for mtk old version
+
+#define ILITEK_ENABLE_DMA
+#define ILITEK_DMA_SIZE				4096
+#define ILITEK_USE_MTK_INPUT_DEV
+
+#if define(ILITEK_USE_MTK_INPUT_DEV) && !defined(ILITEK_USE_LCM_RESOLUTION)
+#define ILITEK_USE_LCM_RESOLUTION
+#endif
+
+#ifdef ILITEK_GET_GPIO_NUM
+#undef ILITEK_GET_GPIO_NUM
+#endif
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#include <linux/namei.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#ifdef MTK_UNDTS
+
+#define TPD_KEY_COUNT   4
+#define key_1           {60, 17000}	//auto define
+#define key_2           {180, 17000}
+#define key_3           {300, 17000}
+#define key_4           {420, 17000}
+
+#define TPD_KEYS        {KEY_MENU, KEY_HOMEPAGE, KEY_BACK, KEY_SEARCH}	//change for you panel key info
+#define TPD_KEYS_DIM    {{key_1, 50, 30 }, {key_2, 50, 30 }, {key_3, 50, 30 }, {key_4, 50, 30 } }
+
+struct touch_vitual_key_map_t {
+	int point_x;
+	int point_y;
+};
+
+extern struct touch_vitual_key_map_t touch_key_point_maping_array[];
+
+#include <mach/mt_pm_ldo.h>
+#include <cust_eint.h>
+#include "cust_gpio_usage.h"
+#include <mach/mt_gpio.h>
+#include <mach/mt_typedefs.h>
+#include <pmic_drv.h>
+#include <mach/mt_boot.h>
+#include <linux/dma-mapping.h>
+
+#else
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_MTK_BOOT
+#include "mt_boot_common.h"
+#endif
+
+#endif /* MTK_UNDTS */
+
+#include "tpd.h"
+extern struct tpd_device *tpd;
+#endif /* ILITEK_PLAT == ILITEK_PLAT_MTK */
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#include <linux/irq.h>
+#include <linux/init-input.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+extern struct ctp_config_info config_info;
+#endif
+
+
+#ifndef ILITEK_GET_GPIO_NUM
+#if ILITEK_PLAT == ILITEK_PLAT_MTK
+#ifdef MTK_UNDTS
+#define ILITEK_IRQ_GPIO				GPIO_CTP_EINT_PIN
+#define ILITEK_RESET_GPIO			GPIO_CTP_RST_PIN
+#else
+#define ILITEK_IRQ_GPIO				GTP_INT_PORT
+#define ILITEK_RESET_GPIO			GTP_RST_PORT
+#endif
+#elif ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+#define ILITEK_IRQ_GPIO				config_info.int_number
+#define ILITEK_RESET_GPIO			config_info.wakeup_gpio.gpio
+#else
+#define ILITEK_IRQ_GPIO				9
+#define ILITEK_RESET_GPIO			10
+#endif
+#endif
+
+#define ILITEK_I2C_RETRY_COUNT			3
+
+#if ILITEK_PLAT == ILITEK_PLAT_ALLWIN
+extern int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t mesg);
+extern int ilitek_resume_allwin(struct i2c_client *client);
+#endif
+
+#endif
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.c b/drivers/input/touchscreen/ilitek/ilitek_update.c
new file mode 100644
index 000000000000..d7e873a3c204
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include "ilitek_update.h"
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+#ifndef __KERNEL__
+static int hex_to_bin(u8 ch)
+{
+	u8 cu = ch & 0xdf;
+	return -1 +
+		((ch - '0' +  1) & (unsigned)((ch - '9' - 1) &
+		('0' - 1 - ch)) >> 8) +
+		((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) &
+		('A' - 1 - cu)) >> 8);
+}
+
+static int hex2bin(u8 *dst, const u8 *src, size_t count)
+{
+	int hi = 0, lo = 0;
+
+	while (count--) {
+		hi = hex_to_bin(*src++);
+		lo = hex_to_bin(*src++);
+		if (hi < 0 || lo < 0) {
+			TP_ERR(NULL, "hex_to_bin failed, hi: %d, lo: %d\n",
+				hi, lo);
+			return -EINVAL;
+		}
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
+#endif
+
+static u32 get_tag_addr(u32 start, u32 end,
+			const u8 *buf, unsigned int buf_size,
+			const u8 *tag, unsigned int tag_size)
+{
+	unsigned int i;
+
+	for (i = start; i <= end - tag_size && i < buf_size - tag_size; i++) {
+		if (!memcmp(buf + i, tag, tag_size))
+			return i + tag_size + 1;
+	}
+
+	return end;
+}
+
+static u32 get_endaddr(u32 start, u32 end, const u8 *buf,
+		       unsigned int buf_size, bool is_AP)
+{
+	u32 addr;
+	u8 tag[32];
+	const u8 ap_tag[] = "ILITek AP CRC   ";
+	const u8 blk_tag[] = "ILITek END TAG  ";
+
+	_memset(tag, 0xFF, sizeof(tag));
+	_memcpy(tag + 16, (is_AP) ? ap_tag : blk_tag, 16);
+
+	addr = get_tag_addr(start, end, buf, buf_size, tag, sizeof(tag));
+	TP_DBG(NULL, "find tag in start/end: 0x%x/0x%x, tag addr: 0x%x\n",
+		start, end, addr);
+
+	return addr;
+}
+
+static int decode_mm(struct ilitek_fw_handle *fw, u32 addr,
+		     u8 *buf, u32 buf_size)
+{
+	u8 i;
+	union mapping_info *mapping;
+
+	TP_INFO(NULL, "------------Memory Mapping information------------\n");
+	TP_INFO(NULL, "memory-mapping-info addr: 0x%x\n", addr);
+
+	mapping = (union mapping_info *)(buf + addr);
+	_memset(fw->file.ic_name, 0, sizeof(fw->file.ic_name));
+
+	switch (mapping->mapping_ver[2]) {
+	case 0x2:
+		_memcpy(fw->file.ic_name, mapping->ic_name,
+			sizeof(mapping->ic_name));
+		break;
+	default:
+	case 0x1:
+		_sprintf(fw->file.ic_name, 0, "%02x%02x",
+			mapping->ic_name[1], mapping->ic_name[0]);
+		break;
+	}
+
+	if (!strcmp(fw->file.ic_name, "2133"))
+		_sprintf(fw->file.ic_name, 0, "2132S");
+
+	if (fw->dev && strcmp(fw->dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " not matched\n",
+			fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EINVAL;
+	}
+
+	TP_MSG(NULL, "Hex Mapping Ver.: 0x%x\n",
+		le32(mapping->mapping_ver, 3));
+	TP_MSG(NULL, "Hex Protocol: 0x%x\n",
+		le32(mapping->protocol_ver, 3));
+	TP_MSG(NULL, "Hex MCU Ver.: " PFMT_C8 "\n", fw->file.ic_name);
+
+	_memset(fw->file.fw_ver, 0, sizeof(fw->file.fw_ver));
+	fw->file.fwid = 0xffff;
+
+	fw->file.mm_addr = addr;
+	switch (addr) {
+	case 0x4038:
+	case 0x4020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x2C007];
+		fw->file.fw_ver[5] = buf[0x2C006];
+		fw->file.fw_ver[6] = buf[0x2C005];
+		fw->file.fw_ver[7] = buf[0x2C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x3020:
+		fw->file.mm_size = 128;
+		fw->file.fw_ver[0] = mapping->_lego.fw_ver[3];
+		fw->file.fw_ver[1] = mapping->_lego.fw_ver[2];
+		fw->file.fw_ver[2] = mapping->_lego.fw_ver[1];
+		fw->file.fw_ver[3] = mapping->_lego.fw_ver[0];
+		fw->file.fw_ver[4] = buf[0x3C007];
+		fw->file.fw_ver[5] = buf[0x3C006];
+		fw->file.fw_ver[6] = buf[0x3C005];
+		fw->file.fw_ver[7] = buf[0x3C004];
+
+		fw->file.fwid = mapping->_lego.fwid;
+		break;
+	case 0x2020:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x2033];
+		fw->file.fw_ver[1] = buf[0x2032];
+		fw->file.fw_ver[2] = buf[0x2031];
+		fw->file.fw_ver[3] = buf[0x2030];
+		fw->file.fw_ver[4] = buf[0xF004];
+		fw->file.fw_ver[5] = buf[0xF005];
+		fw->file.fw_ver[6] = buf[0xF006];
+		fw->file.fw_ver[7] = buf[0xF007];
+
+		/* for V3 251x IC, get AP crc and DF checksum */
+		fw->file.blocks[0].check = get_crc(fw->file.blocks[0].start,
+			fw->file.blocks[0].end - 1, buf, buf_size);
+
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+
+	case 0x500:
+		fw->file.mm_size = 132;
+		fw->file.fw_ver[0] = buf[0x52D];
+		fw->file.fw_ver[1] = buf[0x52C];
+		fw->file.fw_ver[2] = buf[0x52B];
+		fw->file.fw_ver[3] = buf[0x52A];
+		fw->file.fw_ver[4] = buf[0x1F404];
+		fw->file.fw_ver[5] = buf[0x1F405];
+		fw->file.fw_ver[6] = buf[0x1F406];
+		fw->file.fw_ver[7] = buf[0x1F407];
+
+		/* for V3 231x IC, get AP checksum and DF checksum */
+		fw->file.blocks[0].check = get_checksum(
+			fw->file.blocks[0].start,
+			fw->file.blocks[0].end + 1, buf, buf_size);
+		if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+			fw->file.blocks[1].check = get_checksum(
+				fw->file.blocks[1].start,
+				fw->file.blocks[1].end + 1, buf, buf_size);
+		}
+		break;
+	default:
+		fw->file.mm_size = 0;
+		break;
+	}
+
+	TP_MSG(NULL, "file fwid: 0x%04x\n", fw->file.fwid);
+
+	TP_INFO(NULL, "File FW Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[0], fw->file.fw_ver[1],
+		fw->file.fw_ver[2], fw->file.fw_ver[3]);
+	TP_INFO(NULL, "File Customer Version: %02x-%02x-%02x-%02x\n",
+		fw->file.fw_ver[4], fw->file.fw_ver[5],
+		fw->file.fw_ver[6], fw->file.fw_ver[7]);
+
+	if (le32(mapping->mapping_ver, 3) < 0x10000)
+		goto memory_mapping_end;
+
+	TP_INFO(NULL, "File Tuning Version: %02x-%02x-%02x-%02x\n",
+		mapping->_lego.tuning_ver[3], mapping->_lego.tuning_ver[2],
+		mapping->_lego.tuning_ver[1], mapping->_lego.tuning_ver[0]);
+
+	if (mapping->_lego.block_num > ARRAY_SIZE(fw->file.blocks)) {
+		TP_ERR(NULL, "Unexpected block num: " PFMT_U8 " > %u\n",
+			mapping->_lego.block_num,
+			(unsigned int)ARRAY_SIZE(fw->file.blocks));
+		goto memory_mapping_end;
+	}
+
+	fw->file.block_num = mapping->_lego.block_num;
+
+	TP_MSG(NULL, "Total " PFMT_U8 " blocks\n", fw->file.block_num);
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].start =
+			le32(mapping->_lego.blocks[i].addr, 3);
+		fw->file.blocks[i].end = (i == fw->file.block_num - 1) ?
+			le32(mapping->_lego.end_addr, 3) :
+			le32(mapping->_lego.blocks[i + 1].addr, 3);
+
+		/*
+		 * get end addr. of block,
+		 * i.e. address of block's final byte of crc.
+		 */
+		fw->file.blocks[i].end = get_endaddr(
+			fw->file.blocks[i].start, fw->file.blocks[i].end,
+			buf, buf_size, i == 0);
+
+		fw->file.blocks[i].check = get_crc(fw->file.blocks[i].start,
+			fw->file.blocks[i].end - 1,
+			buf, buf_size);
+
+		TP_MSG(NULL, "Block[%u], start:0x%x end:0x%x, crc:0x%x\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			fw->file.blocks[i].check);
+	}
+
+memory_mapping_end:
+	TP_INFO(NULL, "--------------------------------------------------\n");
+
+	return 0;
+}
+
+static int decode_hex(struct ilitek_fw_handle *fw, u8 *hex,
+		      u32 start, u32 end,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	u8 info[4], data[16];
+	unsigned int i, len, addr, type, exaddr = 0;
+	u32 mapping_info_addr = 0;
+
+	/* m2v hex has another block at the end of hex file */
+	u8 j = (fw->m2v) ? fw->file.block_num : 0;
+
+	fw->file.blocks[j].start = (~0U);
+	fw->file.blocks[j].end = 0x0;
+	fw->file.blocks[j].check = 0x0;
+	fw->file.blocks[j + 1].start = (~0U);
+	fw->file.blocks[j + 1].end = 0x0;
+	fw->file.blocks[j + 1].check = 0x0;
+
+	for (i = start; i < end; i++) {
+		/* filter out non-hexadecimal characters */
+		if (hex_to_bin(hex[i]) < 0)
+			continue;
+
+		error = hex2bin(info, hex + i, sizeof(info));
+		if (error < 0)
+			return error;
+
+		len = info[0];
+		addr = be32(info + 1, 2);
+		type = info[3];
+
+		error = hex2bin(data, hex + i + 8, len);
+		if (error < 0)
+			return error;
+
+		switch (type) {
+		case 0xAC:
+			mapping_info_addr = be32(data, len);
+			break;
+
+		case 0xAD:
+			fw->file.blocks[1].start = be32(data, len);
+			_memset(buf + fw->file.blocks[1].start, 0, 0x1000);
+			break;
+
+		case 0xBA:
+			if (be32(data, len) != 2U)
+				break;
+
+			TP_MSG(NULL, "start to decode M2V part of hex file\n");
+			fw->m2v = true;
+			////Reed Add : 20230721��������2326��������Decode_mm,�ټ�������M2V���֡���
+			if (mapping_info_addr)
+				decode_mm(fw, mapping_info_addr, buf, buf_size);
+			return decode_hex(fw, hex, i + 10 + len * 2 + 1, end,
+					  fw->m2v_buf, ILITEK_FW_BUF_SIZE);
+
+		case 0x01:
+			goto success_return;
+
+		case 0x02:
+			exaddr = be32(data, len) << 4;
+			break;
+
+		case 0x04:
+			exaddr = be32(data, len) << 16;
+			break;
+
+		case 0x05:
+			TP_MSG(NULL, "hex data type: 0x%x, start linear address: 0x%x\n",
+				type, be32(data, len));
+			break;
+
+		case 0x00:
+			addr += exaddr;
+
+			if (addr + len > buf_size) {
+				TP_ERR(NULL, "hex addr: 0x%x, buf size: 0x%x OOB\n",
+					addr + len, buf_size);
+				return -ENOBUFS;
+			}
+			_memcpy(buf + addr, data, len);
+
+			fw->file.blocks[j].start =
+				MIN(fw->file.blocks[j].start, addr);
+
+			if (addr + len < fw->file.blocks[j + 1].start) {
+				fw->file.blocks[j].end =
+					MAX(fw->file.blocks[j].end,
+					    addr + len - 1);
+				fw->file.blocks[j].check += get_checksum(
+					0, len, data, sizeof(data));
+			} else {
+				fw->file.blocks[j + 1].end =
+					MAX(fw->file.blocks[j + 1].end,
+					    addr + len - 1);
+				fw->file.blocks[j + 1].check += get_checksum(
+					0, len, data, sizeof(data));
+			}
+
+			break;
+		default:
+			TP_ERR(NULL, "unexpected type:0x%x in hex, len:%u, addr:0x%x\n",
+				type, len, addr);
+			return -EINVAL;
+		}
+
+		i = i + 10 + len * 2;
+	}
+
+success_return:
+	if (fw->m2v)
+		fw->m2v_checksum = fw->file.blocks[fw->file.block_num].check;
+	if (mapping_info_addr)
+		return decode_mm(fw, mapping_info_addr, fw->file.buf, buf_size);
+
+	return 0;
+}
+
+static int decode_bin(struct ilitek_fw_handle *fw,
+		      u8 *bin, u32 bin_size,
+		      u8 *buf, u32 buf_size)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 mapping_info_addr;
+
+	if (!dev) {
+		TP_ERR(NULL, "offline decode bin file is not supported\n");
+		return -EINVAL;
+	}
+
+	if (bin_size > buf_size) {
+		TP_ERR(dev->id, "bin file size: 0x%x, buf size: 0x%x OOB\n",
+			bin_size, buf_size);
+		return -ENOBUFS;
+	}
+	_memcpy(buf, bin, bin_size);
+
+	error = api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	if (error < 0)
+		return error;
+	error = api_protocol_set_cmd(dev, GET_MCU_VER, NULL);
+	if (error < 0)
+		return error;
+
+	switch (dev->protocol.flag) {
+	case PTL_V6:
+		mapping_info_addr = dev->mcu_info.mm_addr;
+		break;
+
+	case PTL_V3:
+		/*
+		 * For 231x: AP checksum and DF checksum, DF start addr: 0x1f000
+		 * For 251x: AP crc and DF checksum, DF start addr: 0xf000
+		 */
+		if (is_231x(dev)) {
+			mapping_info_addr = 0x500;
+
+			fw->file.blocks[1].start = 0x1f000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x0;
+
+			/* +2 to get end addr. of last byte of 4 bytes checksum */
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true) + 2;
+
+		} else {
+			mapping_info_addr = 0x2020;
+			fw->file.blocks[1].start = 0xf000;
+			fw->file.blocks[1].end = bin_size - 1;
+			fw->file.blocks[0].start = 0x2000;
+			fw->file.blocks[0].end =
+				get_endaddr(fw->file.blocks[0].start,
+					    fw->file.blocks[1].start,
+					    bin, bin_size, true);
+		}
+
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * take the whole "buf" into decode_mm, "buf" should be
+	 * properly initialized, and the size should be
+	 * larger than "bin", which reduce OOB issue.
+	 */
+	return decode_mm(fw, mapping_info_addr, buf, buf_size);
+}
+
+#ifdef ILITEK_BOOT_UPDATE
+#include "ilitek_fw.h"
+
+static int decode_ili(struct ilitek_fw_handle *fw,
+		      u8 *buf, u32 buf_size)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 *ili_buf = CTPM_FW;
+	int size = sizeof(CTPM_FW);
+	u8 id;
+
+#if defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		if (!support_sensor_id(dev)) {
+			TP_WARN(dev->id,
+				"protocol: 0x%x, mode: 0x" PFMT_X8 ", sensor-id not supported, take default fw(id: 0x%x)\n",
+				dev->protocol.ver, dev->ic[0].mode,
+				ILITEK_BOOT_UPDATE_DEF_ID);
+			fw->file.id = ILITEK_BOOT_UPDATE_DEF_ID;
+			break;
+		}
+
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id >= ARRAY_SIZE(ili_arr)) {
+			TP_ERR(dev->id, "invalid sensor id: " PFMT_U8 " >= %d\n",
+				dev->sensor.id, (int)ARRAY_SIZE(ili_arr));
+			return -EINVAL;
+		}
+
+		fw->file.id = id;
+		ili_buf = ili_arr[id].buf;
+		size = ili_arr[id].size;
+		break;
+	case ili_default:
+		break;
+	default:
+		TP_ERR(dev->id, "unexpected ili-select-type: %u\n",
+			__ili_select_type__);
+		return -EINVAL;
+	}
+#endif
+
+	if (!ili_buf || size < 32)
+		return -EINVAL;
+
+	fw->setting.fw_ver_check = true;
+	fw->setting.fw_ver_policy = 0;
+	_memcpy(fw->setting.fw_ver, ili_buf + 18, 8);
+
+	TP_MSG_ARR(dev->id, "IC  fw ver:", TYPE_U8, 8, dev->fw_ver);
+	TP_MSG_ARR(dev->id, "Hex fw ver:", TYPE_U8, 8, fw->setting.fw_ver);
+
+	return decode_bin(fw, ili_buf + 32, size - 32, buf, buf_size);
+}
+#endif
+
+static bool need_retry(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 id;
+
+#if defined(ILITEK_BOOT_UPDATE) && defined(ILITEK_BOOT_UPDATE_ILI_VER)
+	if (fw->file.type != fw_ili)
+		return false;
+
+	switch (__ili_select_type__) {
+	case ili_by_sensor_id:
+		id = dev->sensor.id & dev->setting.sensor_id_mask;
+		if (id == fw->file.id)
+			break;
+
+		/* reload correct fw if sensor-id not matched with default fw */
+		TP_MSG(dev->id, "sensor id: 0x%x, file: 0x%x not matched, "
+			"reload fw again\n",
+			id, fw->file.id);
+
+		if (ilitek_update_load_fw(fw, "ilitek.ili") < 0) {
+			TP_ERR(dev->id, "reload ilitek.ili failed\n");
+			return false;
+		}
+
+		/* MUST DISABLE fw ver check for the second try */
+		fw->setting.fw_ver_check = false;
+
+		return true;
+	default:
+		break;
+	}
+#endif
+
+	UNUSED(dev);
+	UNUSED(id);
+
+	return false;
+}
+
+static int decode_firmware(struct ilitek_fw_handle *fw, WCHAR *filename)
+{
+	int error;
+	int size = 0;
+	u8 *buf;
+	WCHAR *file_ext;
+
+	/* initialization */
+	_memset(fw->file.buf, 0xFF, fw->file.buf_size);
+	_memset(fw->m2v_buf, 0xFF, ILITEK_FW_BUF_SIZE);
+	fw->m2v = false;
+	/*
+	 * set block num 2 for V3 AP and Data Flash as default,
+	 * for V6, block num would be updated after decoding memory mapping.
+	 */
+	fw->file.block_num = 2;
+	fw->file.blocks[0].start = (~0U);
+	fw->file.blocks[0].end = 0x0;
+	fw->file.blocks[0].check = 0x0;
+	fw->file.blocks[1].start = (~0U);
+	fw->file.blocks[1].end = 0x0;
+	fw->file.blocks[1].check = 0x0;
+
+	TP_MSG(NULL, "start to read fw file: " PFMT_C16 "\n", filename);
+
+	buf = (u8 *)CALLOC(ILITEK_FW_FILE_SIZE, 1);
+	if (!buf)
+		return -ENOMEM;
+
+	file_ext = WCSRCHR(filename, '.');
+	if (!file_ext)
+		return -ENOENT;
+
+	/* no need to read .ili file */
+	if (WCSCASECMP(file_ext, ".ili")) {
+		if (!fw->cb.read_fw) {
+			error = -EFAULT;
+			TP_ERR(NULL, "read fw callback not registered\n");
+			goto err_free;
+		}
+
+		size = fw->cb.read_fw(filename, buf, ILITEK_FW_FILE_SIZE,
+				      fw->_private);
+
+		error = size;
+		if (error < 0) {
+			TP_ERR(NULL, "read fw file: " PFMT_C16 " failed, err: %d\n",
+				filename, error);
+			goto err_free;
+		}
+	}
+
+	if (!WCSCASECMP(file_ext, ".hex")) {
+		fw->file.type = fw_hex;
+		error = decode_hex(fw, buf, 0, size, fw->file.buf,
+				   fw->file.buf_size);
+	} else if (!WCSCASECMP(file_ext, ".bin")) {
+		fw->file.type = fw_bin;
+		error = decode_bin(fw, buf, size, fw->file.buf,
+				   fw->file.buf_size);
+	}
+#ifdef ILITEK_BOOT_UPDATE
+	else if (!WCSCASECMP(file_ext, ".ili")) {
+		fw->file.type = fw_ili;
+		error = decode_ili(fw, fw->file.buf, fw->file.buf_size);
+	}
+#endif
+	else {
+		error = -EINVAL;
+	}
+
+err_free:
+	CFREE(buf);
+
+	return error;
+}
+
+static bool need_fw_update_v3(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool fw_file_has_data_flash =
+		(fw->file.blocks[1].start < fw->file.blocks[1].end);
+	bool need = false;
+
+	TP_INFO(dev->id, "------------V3 AP/DF Info.------------\n");
+
+	fw->file.blocks[0].check_match = (fw->setting.force_update) ?
+		false : dev->ic[0].crc[0] == fw->file.blocks[0].check;
+
+	TP_INFO(dev->id, "AP block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+		fw->file.blocks[0].start, fw->file.blocks[0].end,
+		dev->ic[0].crc[0], fw->file.blocks[0].check,
+		(fw->file.blocks[0].check_match) ? "matched" : "not matched");
+
+	fw->file.blocks[1].check_match = true;
+	if (fw_file_has_data_flash) {
+		fw->file.blocks[1].check_match = (fw->setting.force_update) ?
+			false : dev->ic[0].crc[1] == fw->file.blocks[1].check;
+
+		TP_INFO(dev->id, "DF block Start/End Addr.: 0x%x/0x%x, IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(fw->file.blocks[1].check_match) ?
+			"matched" : "not matched");
+	} else if (!is_231x(dev)) {
+		/*
+		 * for 251x ICs, if no data flash in fw file,
+		 * need to switch to BL mode then erase data flash forcely.
+		 */
+		need = true;
+	}
+
+	TP_INFO(dev->id, "--------------------------------------\n");
+
+	need |= (!fw->file.blocks[0].check_match ||
+		 !fw->file.blocks[1].check_match);
+
+	if (dev->ic[0].mode == BL_MODE)
+		return true;
+
+	return need;
+}
+
+static bool need_fw_update_v6(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	bool need = false;
+
+	TP_INFO(dev->id, "------------Lego Block Info.------------\n");
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		fw->file.blocks[i].check_match = (fw->setting.force_update) ?
+			false : (dev->ic[0].crc[i] == fw->file.blocks[i].check);
+
+		need = (!fw->file.blocks[i].check_match) ? true : need;
+
+		TP_INFO(dev->id, "Block[" PFMT_U8 "]: Start/End Addr.: 0x%x/0x%x, IC/File CRC: 0x%x/0x%x " PFMT_C8 "\n",
+			i, fw->file.blocks[i].start, fw->file.blocks[i].end,
+			dev->ic[0].crc[i], fw->file.blocks[i].check,
+			(fw->file.blocks[i].check_match) ?
+			"matched" : "not matched");
+	}
+
+	/* check BL mode firstly before AP-cmd related varaible, ex: ic_num */
+	if (dev->ic[0].mode == BL_MODE) {
+		need = true;
+		goto force_return;
+	}
+
+	for (i = 1; i < dev->tp_info.ic_num; i++) {
+		TP_INFO(dev->id, "Master/Slave[" PFMT_U8 "] CRC: 0x%x/0x%x " PFMT_C8 ", Slave Mode: 0x" PFMT_X8 " " PFMT_C8 "\n",
+			i, fw->file.blocks[0].check, dev->ic[i].crc[0],
+			(fw->file.blocks[0].check == dev->ic[i].crc[0]) ?
+			"matched" : "not matched",
+			dev->ic[i].mode, dev->ic[i].mode_str);
+
+		if (dev->ic[i].crc[0] == fw->file.blocks[0].check &&
+			dev->ic[i].mode == AP_MODE)
+			continue;
+		need = true;
+	}
+
+	if (fw->m2v) {
+		api_access_slave(dev, 0x80, CMD_GET_AP_CRC, &fw->m2v_checksum);
+
+		fw->file.blocks[fw->file.block_num].check_match =
+			fw->file.blocks[fw->file.block_num].check ==
+			fw->m2v_checksum;
+		TP_INFO(dev->id, "M2V IC/File Checksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->m2v_checksum,
+			fw->file.blocks[fw->file.block_num].check,
+			(fw->file.blocks[fw->file.block_num].check_match) ?
+			"matched" : "not matched");
+
+		fw->m2v_need_update =
+			!fw->file.blocks[fw->file.block_num].check_match ||
+			fw->setting.force_update;
+		need |= fw->m2v_need_update;
+	}
+
+force_return:
+	TP_INFO(dev->id, "----------------------------------------\n");
+
+	return need;
+}
+
+static bool need_fw_update(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	bool need = false;
+
+	struct ilitek_fw_settings *set = &fw->setting;
+	u64 dev_fw_ver, file_fw_ver;
+
+	if (dev->protocol.flag == PTL_V3)
+		need = need_fw_update_v3(fw);
+	else if (dev->protocol.flag == PTL_V6)
+		need = need_fw_update_v6(fw);
+
+	if (fw->cb.update_fw_ic_info)
+		fw->cb.update_fw_ic_info(false,
+					 dev->fw_ver, dev->ic[0].crc,
+					 fw->file.block_num, fw->_private);
+
+	if (set->force_update)
+		return true;
+	else if (set->fw_check_only)
+		return false;
+
+	if (set->fw_ver_check && dev->ic[0].mode == AP_MODE) {
+		TP_INFO(dev->id, "IC FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			dev->fw_ver[0], dev->fw_ver[1], dev->fw_ver[2],
+			dev->fw_ver[3], dev->fw_ver[4], dev->fw_ver[5],
+			dev->fw_ver[6], dev->fw_ver[7]);
+		TP_INFO(dev->id, "File FW version: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+			set->fw_ver[0], set->fw_ver[1],
+			set->fw_ver[2], set->fw_ver[3],
+			set->fw_ver[4], set->fw_ver[5],
+			set->fw_ver[6], set->fw_ver[7]);
+
+		dev_fw_ver =
+			U82U64(dev->fw_ver[0], 7) + U82U64(dev->fw_ver[1], 6) +
+			U82U64(dev->fw_ver[2], 5) + U82U64(dev->fw_ver[3], 4) +
+			U82U64(dev->fw_ver[4], 3) + U82U64(dev->fw_ver[5], 2) +
+			U82U64(dev->fw_ver[6], 1) + U82U64(dev->fw_ver[7], 0);
+		file_fw_ver =
+			U82U64(set->fw_ver[0], 7) + U82U64(set->fw_ver[1], 6) +
+			U82U64(set->fw_ver[2], 5) + U82U64(set->fw_ver[3], 4) +
+			U82U64(set->fw_ver[4], 3) + U82U64(set->fw_ver[5], 2) +
+			U82U64(set->fw_ver[6], 1) + U82U64(set->fw_ver[7], 0);
+
+		TP_MSG(dev->id, "IC fw ver: 0x" PFMT_X64 ", File fw ver: 0x" PFMT_X64 "\n",
+			(long long unsigned int)dev_fw_ver,
+			(long long unsigned int)file_fw_ver);
+
+		if (file_fw_ver > dev_fw_ver) {
+			TP_INFO(dev->id, "IC FW version is older than File FW version\n");
+			return true;
+		} else if (file_fw_ver == dev_fw_ver) {
+			TP_INFO(dev->id, "File FW version is the same, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_same) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_same) ?
+				need : false;
+		} else {
+			TP_INFO(dev->id, "File FW version is older, " PFMT_C8 " to update\n",
+				(set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				"still need" : "no need");
+			return (set->fw_ver_policy & allow_fw_ver_downgrade) ?
+				need : false;
+		}
+	}
+
+	return need;
+}
+
+static int update_master(struct ilitek_fw_handle *fw, int idx, u32 len)
+{
+	int error = 0;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+	u16 file_crc;
+	int retry = 3;
+
+	TP_MSG(dev->id, "updating block[%d], data len: %u, start/end addr: 0x%x/0x%x\n",
+		idx, len, fw->file.blocks[idx].start, fw->file.blocks[idx].end);
+
+err_retry:
+	if ((dev->setting.no_retry && error < 0) || retry-- < 0)
+		return (error < 0) ? error : -EINVAL;
+
+	error = api_write_enable_v6(dev, false, false,
+				     fw->file.blocks[idx].start,
+				     fw->file.blocks[idx].end);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[idx].start;
+	     i < fw->file.blocks[idx].end; i += len) {
+		/*
+		 * check end addr. of data write buffer is within valid range.
+		 */
+		if (i + len > END_ADDR_LEGO) {
+			TP_ERR(dev->id, "block[%d] write addr. 0x%x + 0x%x > 0x%x OOB\n",
+				idx, i, len, END_ADDR_LEGO);
+			return -EINVAL;
+		}
+
+		_memcpy(dev->wbuf + 1, fw->file.buf + i, len);
+		error = api_write_data_v6(dev, len + 1);
+
+		if (error < 0)
+			goto err_retry;
+
+		fw->progress_curr = MIN(i + len - fw->file.blocks[idx].offset,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "block[%d] update progress: " PFMT_U8 "%%\n",
+			idx, fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	file_crc = get_crc(fw->file.blocks[idx].start,
+			   fw->file.blocks[idx].end - 1,
+			   fw->file.buf, fw->file.buf_size);
+	dev->ic[0].crc[idx] =
+		api_get_block_crc_by_addr(dev, CRC_GET,
+					  fw->file.blocks[idx].start,
+					  fw->file.blocks[idx].end);
+
+	TP_INFO(dev->id, "block[%d]: start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+		idx, fw->file.blocks[idx].start, fw->file.blocks[idx].end,
+		dev->ic[0].crc[idx], file_crc,
+		(file_crc == dev->ic[0].crc[idx]) ?
+		"matched" : "not matched");
+
+	if (file_crc != dev->ic[0].crc[idx]) {
+		error = -EINVAL;
+		goto err_retry;
+	}
+
+	return 0;
+}
+
+static int update_slave(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		dev->ic[0].crc[i] = api_get_block_crc_by_addr(dev,
+			CRC_CALCULATE, fw->file.blocks[i].start,
+			fw->file.blocks[i].end);
+	}
+
+	error = api_protocol_set_cmd(dev, GET_AP_CRC, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[0].crc[0] == dev->ic[i].crc[0] &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "updating slave, master/slave[" PFMT_U8 "] crc: 0x%x/0x%x\n",
+			i, dev->ic[0].crc[0], dev->ic[i].crc[0]);
+		error = api_access_slave(dev, 0x3, CMD_WRITE_DATA_V6, NULL);
+		if (error < 0)
+			return error;
+		error = api_write_enable_v6(dev, false, true,
+					    fw->file.blocks[0].start,
+					    fw->file.blocks[0].end);
+		if (error < 0)
+			return error;
+
+		goto success_return;
+	}
+
+	error = api_protocol_set_cmd(dev, GET_MCU_MOD, &dev->tp_info.ic_num);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < dev->tp_info.ic_num; i++) {
+		if (dev->ic[i].mode == AP_MODE &&
+		    !fw->setting.force_update)
+			continue;
+
+		TP_INFO(dev->id, "changing slave[" PFMT_U8 "]: 0x" PFMT_X8 " to AP mode\n",
+			i, dev->ic[i].mode);
+		error = api_access_slave(dev, 0x3, CMD_SET_AP_MODE, NULL);
+		if (error < 0)
+			return error;
+		break;
+	}
+
+success_return:
+	return 0;
+}
+
+static int update_M3_M2V(struct ilitek_fw_handle *fw, u32 len)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u32 i;
+	u8 buf[6];
+	u32 AdressRange, M2V_Buf_Len, CheckSumRange;
+	u16 AddDataCount;
+
+	u8 j = fw->file.block_num;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+	api_protocol_set_cmd(dev, GET_PTL_VER, NULL);
+
+	//Reed Add : 20230721
+	AdressRange = fw->file.blocks[j].end - fw->file.blocks[j].start;
+	M2V_Buf_Len = fw->file.blocks[j].end - fw->file.blocks[j].start + 1;
+	CheckSumRange = fw->file.blocks[j].check;
+	AddDataCount = len - M2V_Buf_Len % len;
+	AddDataCount = (AddDataCount >= 8) ? AddDataCount : AddDataCount + len;
+	AdressRange += AddDataCount;
+	CheckSumRange += AddDataCount * 0xFF;
+
+	error = api_to_bl_mode_m2v(dev, true);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_set_data_len(dev, len);
+	if (error < 0)
+		return error;
+
+	TP_INFO(dev->id, "updating M2V, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+		fw->file.blocks[j].start, fw->file.blocks[j].end,
+		fw->file.blocks[j].check);
+
+	//Reed Add : 20230721
+	buf[0] = (AdressRange >> 16) & 0xFF;
+	buf[1] = (AdressRange >> 8) & 0xFF;
+	buf[2] = AdressRange & 0xFF;
+	buf[3] = (CheckSumRange >> 16) & 0xFF;
+	buf[4] = (CheckSumRange >> 8) & 0xFF;
+	buf[5] = CheckSumRange & 0xFF;
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+	error = api_access_slave(dev, 0x80, CMD_WRITE_ENABLE, buf);
+	if (error < 0)
+		return error;
+
+	_memset(dev->wbuf, 0xff, sizeof(dev->wbuf));
+	for (i = fw->file.blocks[j].start;
+	     i < fw->file.blocks[j].end; i += len) {
+		_memcpy(dev->wbuf + 1, fw->m2v_buf + i, len);
+
+		error = api_write_data_m2v(dev, len + 1);
+		if (error < 0)
+			return error;
+
+		fw->progress_curr = MIN(fw->progress_curr + len,
+					fw->progress_max);
+		fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+		TP_DBG(dev->id, "m2v update progress: " PFMT_U8 "%%\n", fw->progress);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(fw->progress, fw->_private);
+	}
+
+	error = api_to_bl_mode_m2v(dev, false);
+	if (error < 0)
+		return error;
+
+	dev->cb.delay_ms(100);//Reed Add : 20230927
+
+	error = api_access_slave(dev, 0x80, CMD_GET_FW_VER, fw->m2v_fw_ver);
+	if (error < 0)
+		return error;
+
+	TP_MSG_ARR(dev->id, "update M2V success, fw version:", TYPE_U8,
+		   8, fw->m2v_fw_ver);
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_8(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+
+	error = api_set_data_len(dev, fw->update_len);
+	if (error < 0)
+		return error;
+
+	for (i = 0; i < fw->file.block_num; i++) {
+		if (fw->file.blocks[i].check_match)
+			continue;
+
+		error = update_master(fw, i, fw->update_len);
+		if (error < 0) {
+			TP_ERR(dev->id, "Upgrade Block:" PFMT_U8 " failed, err: %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	error = api_to_bl_mode(dev, false, fw->file.blocks[0].start,
+			       fw->file.blocks[0].end);
+	if (error < 0)
+		return error;
+
+	error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+	if (error < 0)
+		return error;
+
+	/* get tp info. for updating ic_num */
+	error = api_protocol_set_cmd(dev, GET_TP_INFO, NULL);
+	if (error < 0)
+		return error;
+
+	if (dev->tp_info.ic_num > 1) {
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(true, fw->_private);
+		error = update_slave(fw);
+
+		if (fw->cb.slave_update_notify)
+			fw->cb.slave_update_notify(false, fw->_private);
+
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade slave failed, err: %d\n",
+				error);
+			return error;
+		}
+	}
+
+	if (fw->m2v && fw->m2v_need_update) {
+		error = update_M3_M2V(fw, 1024);
+		if (error < 0) {
+			TP_ERR(dev->id, "upgrade m2v slave failed, err: %d\n", error);
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_7(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	unsigned int i;
+
+	/*
+	 * Erase data initially for the case that hex w/o data flash section.
+	 * Due to historical factor, please double confirm before modify here.
+	 */
+	error = api_erase_data_v3(dev);
+	if (error < 0)
+		return error;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false,
+					     fw->file.blocks[1].end + 1,
+					     fw->file.blocks[1].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start;
+		     i <= fw->file.blocks[1].end; i += 32) {
+			_memset(dev->wbuf + 1, 0, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->cb.delay_ms(50);
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[1] = (le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+
+		TP_INFO(dev->id, "DF block, start/end addr.: 0x%x/0x%x, ic/file checkksum: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			dev->ic[0].crc[1], fw->file.blocks[1].check,
+			(dev->ic[0].crc[1] == fw->file.blocks[1].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[1] != fw->file.blocks[1].check)
+			return -EFAULT;
+	}
+
+	/*
+	 * Update AP code forcely if AP crc/checksum not match or
+	 * Data Flash has been updated.
+	 */
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_MSG(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true,
+					     fw->file.blocks[0].end + 1,
+					     fw->file.blocks[0].check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start;
+		     i <= fw->file.blocks[0].end; i += 32) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			dev->cb.delay_ms(2);
+
+			error = api_check_busy(dev, 1000, 10);
+			if (error < 0)
+				return error;
+
+			fw->progress_curr += 32;
+			fw->progress_curr = MIN(fw->progress_curr,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) /
+				fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n",
+				fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress,
+						       fw->_private);
+		}
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, NULL, 0);
+		if (error < 0)
+			return error;
+		error = api_check_busy(dev, 1000, 10);
+		if (error < 0)
+			return error;
+
+		dev->wbuf[0] = CMD_GET_AP_CRC;
+		error = write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4);
+		if (error < 0)
+			return error;
+
+		dev->ic[0].crc[0] =
+			(le16(dev->rbuf + 2) << 16) | le16(dev->rbuf);
+		TP_INFO(dev->id, "AP block, start/end addr.: 0x%x/0x%x, ic/file crc: 0x%x/0x%x " PFMT_C8 "\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			dev->ic[0].crc[0], fw->file.blocks[0].check,
+			(dev->ic[0].crc[0] == fw->file.blocks[0].check) ?
+			"matched" : "not matched");
+
+		if (dev->ic[0].crc[0] != fw->file.blocks[0].check)
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int ilitek_update_BL_v1_6(struct ilitek_fw_handle *fw)
+{
+	int error;
+	struct ilitek_ts_device *dev = fw->dev;
+	struct ilitek_ts_callback *cb = &dev->cb;
+	unsigned int i, j;
+
+	u32 bytes, end, check;
+
+	if (fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating DF block, start/end addr.: 0x%x/0x%x, file checksum: 0x%x\n",
+			fw->file.blocks[1].start, fw->file.blocks[1].end,
+			fw->file.blocks[1].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[1].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[1].end + 1) % 32) : 31;
+		end = fw->file.blocks[1].end + bytes;
+		check = fw->file.blocks[1].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) DF start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[1].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, false, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[1].start, j = 0;
+		     i <= fw->file.blocks[1].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[1].end - i + 1, 32));
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "DF update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+	}
+
+	/*
+	* Update AP code forcely if AP crc/checksum not match or
+	* Data Flash has been updated.
+	*/
+	if (!fw->file.blocks[0].check_match ||
+	    fw->file.blocks[1].end > fw->file.blocks[1].start) {
+		TP_INFO(dev->id, "updating AP block, start/end addr.: 0x%x/0x%x, file crc: 0x%x\n",
+			fw->file.blocks[0].start, fw->file.blocks[0].end,
+			fw->file.blocks[0].check);
+
+		/* BL 1.6 need more 32 byte 0xFF, end addr need to be multiples of 32 */
+		bytes = ((fw->file.blocks[0].end + 1) % 32) ?
+			31 + 32 - ((fw->file.blocks[0].end + 1) % 32) : 31;
+		end = fw->file.blocks[0].end + bytes;
+		check = fw->file.blocks[0].check + (bytes * 0xff);
+		TP_MSG(dev->id, "(after modified) AP start/end: 0x%x/0x%x, checksum: 0x%x\n",
+			fw->file.blocks[0].start, end, check);
+
+		/* end + 1 as W.A. for V3 BL bug */
+		error = api_write_enable_v3(dev, false, true, end + 1, check);
+		if (error < 0)
+			return error;
+
+		for (i = fw->file.blocks[0].start, j = 0;
+			i <= fw->file.blocks[0].end; i += 32, j++) {
+			_memset(dev->wbuf + 1, 0xFF, 32);
+			_memcpy(dev->wbuf + 1, fw->file.buf + i,
+				MIN(fw->file.blocks[0].end - i + 1, 32));
+
+			error = api_write_data_v3(dev);
+			if (error < 0)
+				return error;
+
+			cb->delay_ms((j % 16) ? 1 : 5);
+
+			fw->progress_curr = MIN(fw->progress_curr + 32,
+				fw->progress_max);
+			fw->progress = (100 * fw->progress_curr) / fw->progress_max;
+			TP_DBG(dev->id, "AP update progress: " PFMT_U8 "%%\n", fw->progress);
+
+			if (fw->cb.update_progress)
+				fw->cb.update_progress(fw->progress, fw->_private);
+		}
+		cb->delay_ms(10);
+		/* write 31 bytes 0xFF at the end */
+		_memset(dev->wbuf + 1, 0xFF, 32); dev->wbuf[32] = 0;
+		error = api_write_data_v3(dev);
+		if (error < 0)
+			return error;
+		cb->delay_ms(10);
+
+#ifdef ILITEK_BOOT_UPDATE
+		/*
+		 * .ili file fill DF section with default 0xFF, but not 0
+		 * which make flow stuck in BL mode.
+		 * so HW-reset is need before switching to AP mode.
+		 * remove HW-reset after fixing .ili converter bug.
+		 */
+		reset_helper(dev);
+#endif
+	}
+
+	return 0;
+}
+
+static void update_progress(struct ilitek_fw_handle *fw)
+{
+	struct ilitek_ts_device *dev = fw->dev;
+	u8 i;
+	unsigned int last_end = 0, last_offset = 0;
+
+	fw->progress = 0;
+	fw->progress_max = 0;
+	fw->progress_curr = 0;
+
+	switch (dev->protocol.flag) {
+	case PTL_V3:
+		fw->progress_max +=
+			(fw->file.blocks[1].end > fw->file.blocks[1].start) ?
+			fw->file.blocks[1].end - fw->file.blocks[1].start : 0;
+		fw->progress_max +=
+			(fw->file.blocks[0].end > fw->file.blocks[0].start) ?
+			fw->file.blocks[0].end - fw->file.blocks[0].start : 0;
+		break;
+
+	case PTL_V6:
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->file.blocks[i].check_match)
+				continue;
+
+			fw->progress_max +=
+				fw->file.blocks[i].end -
+				fw->file.blocks[i].start;
+			last_offset += fw->file.blocks[i].start - last_end;
+			fw->file.blocks[i].offset = last_offset;
+
+			last_end = fw->file.blocks[i].end;
+		}
+
+		if (fw->m2v_need_update) {
+			fw->progress_max +=
+				fw->file.blocks[fw->file.block_num].end -
+				fw->file.blocks[fw->file.block_num].start;
+		}
+
+		break;
+	}
+}
+
+void *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			 struct ilitek_update_callback *cb, void *_private)
+{
+	struct ilitek_fw_handle *fw;
+	struct ilitek_ts_device *dev = (struct ilitek_ts_device *)_dev;
+
+	if (need_update_ts_info && dev && api_update_ts_info(dev) < 0)
+		return NULL;
+
+	fw = (struct ilitek_fw_handle *)MALLOC(sizeof(*fw));
+	if (!fw)
+		return NULL;
+
+	/* initial all member to 0/ false/ NULL */
+	_memset(fw, 0, sizeof(*fw));
+	fw->dev = (dev) ? dev : NULL;
+
+	/* initial update-len to default UPDATE_LEN */
+	fw->update_len = UPDATE_LEN;
+
+	fw->dev = dev;
+	fw->_private = _private;
+	fw->file.buf_size = ILITEK_FW_BUF_SIZE;
+	fw->file.buf = (u8 *)CALLOC(fw->file.buf_size, 1);
+	if (!fw->file.buf)
+		goto err_free_fw;
+
+	fw->m2v_buf = (u8 *)CALLOC(ILITEK_FW_BUF_SIZE, 1);
+	if (!fw->m2v_buf)
+		goto err_free_fw_buf;
+
+	if (cb)
+		_memcpy(&fw->cb, cb, sizeof(*cb));
+
+	return fw;
+
+err_free_fw_buf:
+	CFREE(fw->file.buf);
+err_free_fw:
+	FREE(fw);
+
+	return NULL;
+}
+
+void ilitek_update_exit(void *handle)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	if (fw->file.buf)
+		CFREE(fw->file.buf);
+
+	if (fw->m2v_buf)
+		CFREE(fw->m2v_buf);
+
+	if (fw)
+		FREE(fw);
+}
+
+void ilitek_update_set_data_length(void *handle, u16 len)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!fw)
+		return;
+
+	fw->update_len = len;
+}
+
+int ilitek_update_load_fw(void *handle, WCHAR *fw_name)
+{
+	int error;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	u32 i;
+
+	if (!handle)
+		return -EINVAL;
+
+	error = decode_firmware(fw, fw_name);
+	if (error < 0)
+		return error;
+
+	if (fw->cb.update_fw_file_info)
+		fw->cb.update_fw_file_info(&fw->file, fw->_private);
+
+	if (!fw->dev)
+		return 0;
+
+	/* for Lego and V6 IC, check block's start/end address validity */
+	if (fw->dev->protocol.flag == PTL_V6) {
+		for (i = 0; i < fw->file.block_num; i++) {
+			if (fw->dev->mcu_info.min_addr <=
+				fw->file.blocks[i].start &&
+			    fw->dev->mcu_info.max_addr >
+				fw->file.blocks[i].end)
+				continue;
+
+			if (!(fw->file.blocks[i].start % 0x1000))
+				continue;
+
+			TP_ERR(fw->dev->id, "Block[%u] addr. OOB (0x%x <= 0x%x/0x%x < 0x%x) or invalid start addr\n",
+				i, fw->dev->mcu_info.min_addr,
+				fw->file.blocks[i].start,
+				fw->file.blocks[i].end,
+				fw->dev->mcu_info.max_addr);
+			return -EINVAL;
+		}
+	}
+
+	TP_MSG(fw->dev->id, "IC: " PFMT_C8 ", Firmware File: " PFMT_C8 " matched\n",
+		fw->dev->mcu_info.ic_name, fw->file.ic_name);
+
+	return 0;
+}
+
+int ilitek_update_start(void *handle)
+{
+	int error;
+	s8 retry = 0;
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+	struct ilitek_ts_device *dev;
+
+	if (!handle)
+		return -EINVAL;
+	dev = fw->dev;
+
+	/*
+	 * Some platform (ITS-Bridge) might change touch controller
+	 * after loading fw file, get panel info. forcely and
+	 * re-check the ic/file are matched.
+	 */
+	error = api_update_ts_info(dev);
+	if (error < 0 || strcmp(dev->mcu_info.ic_name, fw->file.ic_name)) {
+		TP_ERR(fw->dev->id, "get ic info failed, err: %d or ic/file (" PFMT_C8 "/" PFMT_C8 ") not matched\n",
+			error, fw->dev->mcu_info.ic_name, fw->file.ic_name);
+		return -EPERM;
+	}
+
+	TP_INFO(dev->id, "[ilitek_update_start] start\n");
+
+	do {
+		TP_DBG(dev->id, "retry: %hhd, retry_limit: %hhd\n",
+			retry, fw->setting.retry);
+		if (retry)
+			reset_helper(dev);
+
+		error = api_set_ctrl_mode(dev, mode_suspend, false, true);
+		if (error < 0)
+			continue;
+
+		if (!need_fw_update(fw)) {
+			if (is_231x(dev))
+				goto erase_data_flash_231x;
+			goto success_return;
+		}
+
+		update_progress(fw);
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(0, fw->_private);
+
+		error = api_to_bl_mode(dev, true, 0, 0);
+		if (error < 0)
+			continue;
+
+		TP_INFO_ARR(dev->id, "[BL Firmware Version]",
+			    TYPE_U8, 8, dev->fw_ver);
+		TP_INFO(dev->id, "[ilitek_update_start] start to program\n");
+
+		switch (dev->protocol.ver & 0xFFFF00) {
+		case BL_PROTOCOL_V1_8:
+			error = ilitek_update_BL_v1_8(fw);
+			break;
+		case BL_PROTOCOL_V1_7:
+			error = ilitek_update_BL_v1_7(fw);
+			break;
+		case BL_PROTOCOL_V1_6:
+			error = ilitek_update_BL_v1_6(fw);
+			break;
+		default:
+			TP_ERR(dev->id, "BL protocol ver: 0x%x not supported\n",
+				dev->protocol.ver);
+			continue;
+		}
+		if (error < 0)
+			continue;
+
+		error = api_to_bl_mode(dev, false,
+				       fw->file.blocks[0].start,
+				       fw->file.blocks[0].end);
+		if (error < 0)
+			continue;
+
+erase_data_flash_231x:
+		/*
+		 * If no data flash section in firmware file,
+		 * 231x need to erase data flash after change to AP mode.
+		 */
+		if (fw->file.blocks[1].end < fw->file.blocks[1].start &&
+		    is_231x(dev)) {
+			error = api_erase_data_v3(dev);
+			if (error < 0)
+				continue;
+		}
+
+success_return:
+		error = api_update_ts_info(dev);
+		if (error < 0)
+			continue;
+
+		error = api_set_ctrl_mode(dev, mode_normal, false, true);
+		if (error < 0)
+			continue;
+
+		if (fw->cb.update_fw_ic_info)
+			fw->cb.update_fw_ic_info(true,
+						 dev->fw_ver, dev->ic[0].crc,
+						 fw->file.block_num,
+						 fw->_private);
+
+		if (fw->cb.update_progress)
+			fw->cb.update_progress(100, fw->_private);
+
+		if (need_retry(fw))
+			continue;
+
+		TP_INFO(dev->id, "[ilitek_update_start] success\n");
+
+		return 0;
+	} while (!dev->setting.no_retry && ++retry < fw->setting.retry);
+
+	TP_ERR(dev->id, "[ilitek_update_start] fw update failed, err: %d\n",
+		error);
+
+	return (error < 0) ? error : -EFAULT;
+}
+
+void ilitek_update_setting(void *handle, struct ilitek_fw_settings *setting)
+{
+	struct ilitek_fw_handle *fw = (struct ilitek_fw_handle *)handle;
+
+	if (!handle)
+		return;
+
+	_memcpy(&fw->setting, setting, sizeof(struct ilitek_fw_settings));
+}
+
diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.h b/drivers/input/touchscreen/ilitek/ilitek_update.h
new file mode 100644
index 000000000000..b7987b9fb121
--- /dev/null
+++ b/drivers/input/touchscreen/ilitek/ilitek_update.h
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of ILITEK CommonFlow
+ *
+ * Copyright (c) 2022 ILI Technology Corp.
+ * Copyright (c) 2022 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#ifndef __ILITEK_UPDATE_H__
+#define __ILITEK_UPDATE_H__
+
+#include "ilitek_protocol.h"
+
+#define UPDATE_LEN			1024
+
+#define ILITEK_FW_FILE_SIZE		(512 * 1024)
+#define ILITEK_FW_BUF_SIZE		(256 * 1024)
+
+enum fw_file_type {
+	fw_hex = 0,
+	fw_bin,
+	fw_ili,
+};
+
+#ifdef _WIN32
+/* packed below structures by 1 byte */
+#pragma pack(1)
+#endif
+
+struct __PACKED__ mapping_info_lego {
+	u8 tuning_ver[4];
+	u8 fw_ver[4];
+	u8 core_test;
+	u8 core_day;
+	u8 core_month;
+	u8 core_year;
+	u32 core_ver;
+	u8 vendor_ver[6];
+	u8 _reserve_1[8];
+	u16 customer_id;
+	u16 fwid;
+	u16 i2c_addr;
+	u8 _reserve_2[2];
+	char model_name[16];
+	u8 _reserve_3[2];
+	u8 ic_num;
+	u8 total_tuning_num;
+	u16 sizeof_tuning;
+	u16 sizeof_tp_param;
+	u16 sizeof_sys_info;
+	u16 sizeof_sys_algo;
+	u16 sizeof_key_info;
+	u8 block_num;
+	u8 support_tuning_num;
+	u8 _reserve_4[2];
+
+	struct __PACKED__ {
+		u8 addr[3];
+	} blocks[10];
+
+	u8 _reserve_5[9];
+	u8 end_addr[3];
+	u8 _reserve_6[2];
+};
+
+union __PACKED__ mapping_info {
+	struct __PACKED__ {
+		u8 mapping_ver[3];
+		u8 protocol_ver[3];
+		u8 ic_name[6];
+
+		struct mapping_info_lego _lego;
+	};
+};
+
+/*
+ * for V3, "check" is checksum, block[0] for AP and block[1] for Data Flash.
+ * for V6, "check" is CRC.
+ */
+struct __PACKED__ ilitek_block {
+	bool check_match;
+	u32 start;
+	u32 end;
+	u32 check;
+	u32 offset;
+};
+
+struct __PACKED__ ilitek_fw_file_info {
+	char ic_name[8];
+	u8 fw_ver[8];
+	u16 fwid;
+
+	u8 block_num;
+	struct ilitek_block blocks[ILTIEK_MAX_BLOCK_NUM];
+
+	u32 mm_addr;
+	u32 mm_size;
+
+	u32 buf_size;
+	u8 *buf;
+
+	/* fw file's sensor-id or fwid or other id */
+	u32 id;
+	u8 type;
+};
+
+#ifdef _WIN32
+#pragma pack()
+#endif
+
+/* return file size in # of bytes, or negative error code */
+typedef int (*read_fw_t)(WCHAR *, u8 *, int, void *);
+/* update progress of fw updating */
+typedef void (*update_progress_t)(u8, void *);
+/* update fw info to callers */
+typedef void (*update_fw_file_info_t)(struct ilitek_fw_file_info *, void *);
+
+/* notify caller before/after slave upgrade (for ITS only) */
+typedef void (*slave_update_notify_t)(bool, void *);
+/* update fw version and crc/checksum before/after update (for ITS only) */
+typedef void (*update_fw_ic_info_t)(bool, u8 *, u32 *, int, void *);
+
+struct ilitek_update_callback {
+	read_fw_t read_fw;
+	update_progress_t update_progress;
+	update_fw_file_info_t update_fw_file_info;
+
+	slave_update_notify_t slave_update_notify;
+	update_fw_ic_info_t update_fw_ic_info;
+};
+
+enum fw_ver_check_policy {
+	allow_fw_ver_downgrade = 1,
+	allow_fw_ver_same = 2,
+};
+
+struct ilitek_fw_settings {
+	s8 retry;
+	bool fw_check_only;
+	bool force_update;
+
+	bool fw_ver_check;
+	u8 fw_ver_policy;
+	u8 fw_ver[8];
+};
+
+struct ilitek_fw_handle {
+	struct ilitek_ts_device *dev;
+	void *_private;
+
+	/* upgrade options */
+	struct ilitek_fw_settings setting;
+
+	/* common variable */
+	int update_len;
+
+	struct ilitek_fw_file_info file;
+
+	/* M3 + M2V */
+	bool m2v;
+	bool m2v_need_update;
+	u8 *m2v_buf;
+	u32 m2v_checksum;
+	u8 m2v_fw_ver[8];
+
+	/* upgrade status */
+	unsigned int progress_curr;
+	unsigned int progress_max;
+	u8 progress;
+
+	/* callbacks */
+	struct ilitek_update_callback cb;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __DLL *ilitek_update_init(void *_dev, bool need_update_ts_info,
+			       struct ilitek_update_callback *callback,
+			       void *_private);
+
+void __DLL ilitek_update_exit(void *handle);
+
+void __DLL ilitek_update_set_data_length(void *handle, u16 len);
+
+void __DLL ilitek_update_setting(void *handle,
+				 struct ilitek_fw_settings *setting);
+
+int __DLL ilitek_update_load_fw(void *handle, WCHAR *fw_name);
+
+int __DLL ilitek_update_start(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git "a/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" "b/drivers/input/touchscreen/ilitek/~$inline \344\273\243\347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx"
new file mode 100644
index 0000000000000000000000000000000000000000..54af506a4b7c4546aaa12ec99f136c9083b62a83
GIT binary patch
literal 162
zcmZQCuPRR|FHUA439vJiGgJX_3J@1FfHc>ZFflPOG_>^}{sH8{XrBE*K_md<!NeuG
f)w8dM-eF8(e4Ne9!oaZdltBfE57ScAbow{|sQnt1

literal 0
HcmV?d00001

-- 
2.25.1


^ permalink raw reply related

* Re: [RFC PATCH v2 3/3] HID: input: Add support for multiple batteries per device
From: Lucas Zampieri @ 2025-11-16 11:54 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: linux-input, linux-kernel, Jiri Kosina, Sebastian Reichel,
	Bastien Nocera, linux-pm
In-Reply-To: <my3aleryrl57cp3lc7y2puh45rccgxmkvlkyib5537zvhjnnwc@bfbyknryvp2u>

On Thu, Nov 13, 2025 at 11:08 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
>
> On Nov 13 2025, Lucas Zampieri wrote:
> > Enable HID devices to register and manage multiple batteries by
> > maintaining a list of hid_battery structures, each identified by
> > its report ID.
> >
> > The legacy dev->battery field and related fields are maintained for
> > backward compatibility, pointing to the first battery in the list.
> > This allows existing code to continue working unchanged while
> > enabling new functionality for multi-battery devices.
> >
> > Example hardware that can benefit from this:
> > - Gaming headsets with charging docks (e.g., SteelSeries Arctis Nova Pro
> >   Wireless)
> > - Graphics tablets with stylus batteries (Wacom)
> > - Wireless earbuds with per-earbud batteries plus charging case
> > - Split keyboards with independent battery per side
> >
> > Signed-off-by: Lucas Zampieri <lzampier@redhat.com>
> > ---
> >  drivers/hid/hid-core.c  |  4 ++
> >  drivers/hid/hid-input.c | 99 +++++++++++++++++++++++++++--------------
> >  include/linux/hid.h     | 12 ++++-
> >  3 files changed, 80 insertions(+), 35 deletions(-)
> >
> > diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> > index a5b3a8ca2fcb..76d628547e9a 100644
> > --- a/drivers/hid/hid-core.c
> > +++ b/drivers/hid/hid-core.c
> > @@ -2990,6 +2990,10 @@ struct hid_device *hid_allocate_device(void)
> >       mutex_init(&hdev->ll_open_lock);
> >       kref_init(&hdev->ref);
> >
> > +#ifdef CONFIG_HID_BATTERY_STRENGTH
> > +     INIT_LIST_HEAD(&hdev->batteries);
> > +#endif
> > +
> >       ret = hid_bpf_device_init(hdev);
> >       if (ret)
> >               goto out_err;
> > diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> > index 0e71efea9da3..9d0be3d4ce04 100644
> > --- a/drivers/hid/hid-input.c
> > +++ b/drivers/hid/hid-input.c
> > @@ -520,14 +520,20 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >       unsigned quirks;
> >       s32 min, max;
> >       int error;
> > +     int battery_num = 0;
> >
> > -     if (dev->battery)
> > -             return 0;       /* already initialized? */
> > +     /* Check if battery with this report_id already exists */
> > +     list_for_each_entry(bat, &dev->batteries, list) {
> > +             if (bat->report_id == field->report->id)
> > +                     return 0;       /* already initialized */
>
> I wonder if we should not make this test stick out a little bit more.
>
> Something like "get_battery(field)" which would return a battery if the
> field matches. This way, if we ever encounter multiple batteries
> reported on the same report ID, we can always split them by logical
> collection, physical, or something else.
>
I'll add that, and test it in my custom fw.

> > +             battery_num++;
> > +     }
> >
> >       quirks = find_battery_quirk(dev);
> >
> > -     hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
> > -             dev->bus, dev->vendor, dev->product, dev->version, quirks);
> > +     hid_dbg(dev, "device %x:%x:%x %d quirks %d report_id %d\n",
> > +             dev->bus, dev->vendor, dev->product, dev->version, quirks,
> > +             field->report->id);
> >
> >       if (quirks & HID_BATTERY_QUIRK_IGNORE)
> >               return 0;
> > @@ -542,9 +548,17 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >               goto err_free_bat;
> >       }
> >
> > -     psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
> > -                                strlen(dev->uniq) ?
> > -                                     dev->uniq : dev_name(&dev->dev));
> > +     /* Create unique name for each battery based on report ID */
> > +     if (battery_num == 0) {
> > +             psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
> > +                                        strlen(dev->uniq) ?
> > +                                             dev->uniq : dev_name(&dev->dev));
> > +     } else {
> > +             psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery-%d",
> > +                                        strlen(dev->uniq) ?
> > +                                             dev->uniq : dev_name(&dev->dev),
> > +                                        battery_num);
> > +     }
>
> Not sure how much conservative you need to be here, but I would prefer
> we stick to the same naming pattern whether this is the first or second
> battery.
>
Agree completely, honestly that was the ugly bit for me, and the only
reason I keep that was for 'backwards compatibility' but now I know
better, and Ill made those consistent.

> >       if (!psy_desc->name) {
> >               error = -ENOMEM;
> >               goto err_free_desc;
> > @@ -597,15 +611,23 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >
> >       power_supply_powers(bat->ps, &dev->dev);
> >
> > -     /* Maintain legacy single battery fields for backward compatibility */
> > -     dev->battery = bat->ps;
> > -     dev->battery_min = bat->min;
> > -     dev->battery_max = bat->max;
> > -     dev->battery_report_type = bat->report_type;
> > -     dev->battery_report_id = bat->report_id;
> > -     dev->battery_charge_status = bat->charge_status;
> > -     dev->battery_status = bat->status;
> > -     dev->battery_avoid_query = bat->avoid_query;
> > +     list_add_tail(&bat->list, &dev->batteries);
> > +
> > +     /*
> > +      * The legacy single battery API is preserved by exposing the first
> > +      * discovered battery. Systems relying on a single battery view maintain
> > +      * unchanged behavior.
> > +      */
> > +     if (battery_num == 0) {
> > +             dev->battery = bat->ps;
> > +             dev->battery_min = bat->min;
> > +             dev->battery_max = bat->max;
> > +             dev->battery_report_type = bat->report_type;
> > +             dev->battery_report_id = bat->report_id;
> > +             dev->battery_charge_status = bat->charge_status;
> > +             dev->battery_status = bat->status;
> > +             dev->battery_avoid_query = bat->avoid_query;
> > +     }
>
> Again, why keeping the old fields? Are they used anywhere?
>
Ack, and updating those on v3.

> >
> >       return 0;
> >
> > @@ -620,21 +642,33 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >
> >  static void hidinput_cleanup_battery(struct hid_device *dev)
> >  {
> > -     struct hid_battery *bat;
> > +     struct hid_battery *bat, *next;
> >       const struct power_supply_desc *psy_desc;
> >
> > -     if (!dev->battery)
> > -             return;
> > +     list_for_each_entry_safe(bat, next, &dev->batteries, list) {
> > +             psy_desc = bat->ps->desc;
> > +             power_supply_unregister(bat->ps);
> > +             kfree(psy_desc->name);
> > +             kfree(psy_desc);
> > +             list_del(&bat->list);
> > +             kfree(bat);
> > +     }
>
> Really, switching to devm the batteries would greatly help not making
> mistakes here.
>
> >
> > -     bat = power_supply_get_drvdata(dev->battery);
> > -     psy_desc = dev->battery->desc;
> > -     power_supply_unregister(dev->battery);
> > -     kfree(psy_desc->name);
> > -     kfree(psy_desc);
> > -     kfree(bat);
> >       dev->battery = NULL;
> >  }
> >
> > +static struct hid_battery *hidinput_find_battery(struct hid_device *dev,
> > +                                              int report_id)
> > +{
> > +     struct hid_battery *bat;
> > +
> > +     list_for_each_entry(bat, &dev->batteries, list) {
> > +             if (bat->report_id == report_id)
> > +                     return bat;
> > +     }
> > +     return NULL;
> > +}
>
> Oh, so you already have this function. So why not making use of it in
> hidinput_setup_battery()?
>
"backwards compatibility" lol, but yeah in v2 I implemented it
half-way, in v3 I'll make a proper use of it with the full list
conversion.

> > +
> >  static bool hidinput_update_battery_charge_status(struct hid_battery *bat,
> >                                                 unsigned int usage, int value)
> >  {
> > @@ -652,17 +686,16 @@ static bool hidinput_update_battery_charge_status(struct hid_battery *bat,
> >       return false;
> >  }
> >
> > -static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> > -                                 int value)
> > +static void hidinput_update_battery(struct hid_device *dev, int report_id,
> > +                                 unsigned int usage, int value)
> >  {
> >       struct hid_battery *bat;
> >       int capacity;
> >
> > -     if (!dev->battery)
> > +     bat = hidinput_find_battery(dev, report_id);
> > +     if (!bat)
> >               return;
> >
> > -     bat = power_supply_get_drvdata(dev->battery);
> > -
> >       if (hidinput_update_battery_charge_status(bat, usage, value)) {
> >               power_supply_changed(bat->ps);
> >               return;
> > @@ -705,8 +738,8 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
> >  {
> >  }
> >
> > -static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> > -                                 int value)
> > +static void hidinput_update_battery(struct hid_device *dev, int report_id,
> > +                                 unsigned int usage, int value)
>
> Why not hidinput_update_battery(struct hid_battery *bat, unsigned int usage, int value)?
>
I see, this is much more elegant, as I can avoid the lookup inside the
funcion that way, let me use that on the v3.

> >  {
> >  }
> >  #endif       /* CONFIG_HID_BATTERY_STRENGTH */
> > @@ -1574,7 +1607,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
> >               return;
> >
> >       if (usage->type == EV_PWR) {
> > -             hidinput_update_battery(hid, usage->hid, value);
> > +             hidinput_update_battery(hid, report->id, usage->hid, value);
>
> With the suggested change in hidinput_update_battery, first query the
> battery and then change it.
>
Ack
> >               return;
> >       }
> >
> > diff --git a/include/linux/hid.h b/include/linux/hid.h
> > index 63422130de20..a6e36835fb3c 100644
> > --- a/include/linux/hid.h
> > +++ b/include/linux/hid.h
> > @@ -700,8 +700,16 @@ struct hid_device {
> >  #ifdef CONFIG_HID_BATTERY_STRENGTH
> >       /*
> >        * Power supply information for HID devices which report
> > -      * battery strength. power_supply was successfully registered if
> > -      * battery is non-NULL.
> > +      * battery strength. Each battery is tracked separately in the
> > +      * batteries list.
> > +      */
> > +     struct list_head batteries;             /* List of hid_battery structures */
> > +
> > +     /*
> > +      * Legacy single battery support - kept for backwards compatibility.
> > +      * Points to the first battery in the list if any exists.
> > +      * power_supply was successfully registered if battery is non-NULL.
> > +      * DEPRECATED: New code should iterate through batteries list instead.
> >        */
> >       struct power_supply *battery;
>
> Nah. hid_device is pure internal interface. So change all the users if
> needed, but don't keep something around in the hope that others will do
> the work for you.
>
> For references there has been a very long discussion with Linus about
> API changes, and the result was that any API change that introduced a
> duplicate API was probably a bad design ;)
>
Ack, and that was the knowledge that I was missing and why I stuck to
much to making it backwards compatible, thanks for that really.

> Cheers,
> Benjamin
>
> >       __s32 battery_capacity;
> > --
> > 2.51.1
> >
>

Best,
Lucas Zampieri


^ permalink raw reply

* Re: [RFC PATCH v2 2/3] HID: input: Refactor battery code to use struct hid_battery
From: Lucas Zampieri @ 2025-11-16 11:40 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: linux-input, linux-kernel, Jiri Kosina, Sebastian Reichel,
	Bastien Nocera, linux-pm
In-Reply-To: <jil4fvsfddgdj46yjy5rzw7zg6xlb2u2pgzlpkn2bte6du2ht6@5plrpdaf4u7w>

On Thu, Nov 13, 2025 at 10:54 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
>
> On Nov 13 2025, Lucas Zampieri wrote:
> > Refactor the battery handling code to use the newly introduced
> > struct hid_battery internally, replacing direct access to individual
> > power_supply and state fields.
> >
> > The legacy dev->battery and dev->battery_* fields are maintained and
> > synchronized for backward compatibility. This refactoring prepares
> > the code for supporting multiple batteries per device in a subsequent
> > patch.
> >
> > Signed-off-by: Lucas Zampieri <lzampier@redhat.com>
> > ---
> >  drivers/hid/hid-input.c | 125 ++++++++++++++++++++++++++--------------
> >  1 file changed, 83 insertions(+), 42 deletions(-)
> >
> > diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> > index e56e7de53279..0e71efea9da3 100644
> > --- a/drivers/hid/hid-input.c
> > +++ b/drivers/hid/hid-input.c
> > @@ -454,7 +454,8 @@ static int hidinput_get_battery_property(struct power_supply *psy,
> >                                        enum power_supply_property prop,
> >                                        union power_supply_propval *val)
> >  {
> > -     struct hid_device *dev = power_supply_get_drvdata(psy);
> > +     struct hid_battery *bat = power_supply_get_drvdata(psy);
> > +     struct hid_device *dev = bat->dev;
> >       int value;
> >       int ret = 0;
> >
> > @@ -465,13 +466,13 @@ static int hidinput_get_battery_property(struct power_supply *psy,
> >               break;
> >
> >       case POWER_SUPPLY_PROP_CAPACITY:
> > -             if (dev->battery_status != HID_BATTERY_REPORTED &&
> > -                 !dev->battery_avoid_query) {
> > +             if (bat->status != HID_BATTERY_REPORTED &&
> > +                 !bat->avoid_query) {
> >                       value = hidinput_query_battery_capacity(dev);
> >                       if (value < 0)
> >                               return value;
> >               } else  {
> > -                     value = dev->battery_capacity;
> > +                     value = bat->capacity;
> >               }
> >
> >               val->intval = value;
> > @@ -482,20 +483,20 @@ static int hidinput_get_battery_property(struct power_supply *psy,
> >               break;
> >
> >       case POWER_SUPPLY_PROP_STATUS:
> > -             if (dev->battery_status != HID_BATTERY_REPORTED &&
> > -                 !dev->battery_avoid_query) {
> > +             if (bat->status != HID_BATTERY_REPORTED &&
> > +                 !bat->avoid_query) {
> >                       value = hidinput_query_battery_capacity(dev);
> >                       if (value < 0)
> >                               return value;
> >
> > -                     dev->battery_capacity = value;
> > -                     dev->battery_status = HID_BATTERY_QUERIED;
> > +                     bat->capacity = value;
> > +                     bat->status = HID_BATTERY_QUERIED;
> >               }
> >
> > -             if (dev->battery_status == HID_BATTERY_UNKNOWN)
> > +             if (bat->status == HID_BATTERY_UNKNOWN)
> >                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> >               else
> > -                     val->intval = dev->battery_charge_status;
> > +                     val->intval = bat->charge_status;
> >               break;
> >
> >       case POWER_SUPPLY_PROP_SCOPE:
> > @@ -513,8 +514,9 @@ static int hidinput_get_battery_property(struct power_supply *psy,
> >  static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >                                 struct hid_field *field, bool is_percentage)
> >  {
> > +     struct hid_battery *bat;
> >       struct power_supply_desc *psy_desc;
> > -     struct power_supply_config psy_cfg = { .drv_data = dev, };
> > +     struct power_supply_config psy_cfg;
> >       unsigned quirks;
> >       s32 min, max;
> >       int error;
> > @@ -530,16 +532,22 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >       if (quirks & HID_BATTERY_QUIRK_IGNORE)
> >               return 0;
> >
> > -     psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
> > -     if (!psy_desc)
> > +     bat = kzalloc(sizeof(*bat), GFP_KERNEL);
> > +     if (!bat)
> >               return -ENOMEM;
> >
> > +     psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
> > +     if (!psy_desc) {
> > +             error = -ENOMEM;
> > +             goto err_free_bat;
> > +     }
> > +
> >       psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
> >                                  strlen(dev->uniq) ?
> >                                       dev->uniq : dev_name(&dev->dev));
> >       if (!psy_desc->name) {
> >               error = -ENOMEM;
> > -             goto err_free_mem;
> > +             goto err_free_desc;
>
> I wonder if a devm conversion of the whole battery support handling
> would not be beneficial here.
>
I'll give it a try and see how it looks.

> >       }
> >
> >       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
> > @@ -559,63 +567,85 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
> >       if (quirks & HID_BATTERY_QUIRK_FEATURE)
> >               report_type = HID_FEATURE_REPORT;
> >
> > -     dev->battery_min = min;
> > -     dev->battery_max = max;
> > -     dev->battery_report_type = report_type;
> > -     dev->battery_report_id = field->report->id;
> > -     dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
> > +     /* Initialize battery structure */
> > +     bat->dev = dev;
> > +     bat->min = min;
> > +     bat->max = max;
> > +     bat->report_type = report_type;
> > +     bat->report_id = field->report->id;
> > +     bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
> > +     bat->status = HID_BATTERY_UNKNOWN;
> >
> >       /*
> >        * Stylus is normally not connected to the device and thus we
> >        * can't query the device and get meaningful battery strength.
> >        * We have to wait for the device to report it on its own.
> >        */
> > -     dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
> > -                                field->physical == HID_DG_STYLUS;
> > +     bat->avoid_query = report_type == HID_INPUT_REPORT &&
> > +                        field->physical == HID_DG_STYLUS;
> >
> >       if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
> > -             dev->battery_avoid_query = true;
> > +             bat->avoid_query = true;
> >
> > -     dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
> > -     if (IS_ERR(dev->battery)) {
> > -             error = PTR_ERR(dev->battery);
> > +     psy_cfg.drv_data = bat;
> > +     bat->ps = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
> > +     if (IS_ERR(bat->ps)) {
> > +             error = PTR_ERR(bat->ps);
> >               hid_warn(dev, "can't register power supply: %d\n", error);
> >               goto err_free_name;
> >       }
> >
> > -     power_supply_powers(dev->battery, &dev->dev);
> > +     power_supply_powers(bat->ps, &dev->dev);
> > +
> > +     /* Maintain legacy single battery fields for backward compatibility */
> > +     dev->battery = bat->ps;
> > +     dev->battery_min = bat->min;
> > +     dev->battery_max = bat->max;
> > +     dev->battery_report_type = bat->report_type;
> > +     dev->battery_report_id = bat->report_id;
> > +     dev->battery_charge_status = bat->charge_status;
> > +     dev->battery_status = bat->status;
> > +     dev->battery_avoid_query = bat->avoid_query;
>
> Already mentioned in the cover letter, but what's the point of keeping
> those legacy fields when the exact same data is stored in bat?
>
Yep, now that I know that is better to have cohereand-API instead of
maintaining backwards compatiliby to a single battery structure that
was only internal, I'll update those as well.

> > +
> >       return 0;
> >
> >  err_free_name:
> >       kfree(psy_desc->name);
> > -err_free_mem:
> > +err_free_desc:
> >       kfree(psy_desc);
> > -     dev->battery = NULL;
> > +err_free_bat:
> > +     kfree(bat);
> >       return error;
> >  }
> >
> >  static void hidinput_cleanup_battery(struct hid_device *dev)
> >  {
> > +     struct hid_battery *bat;
> >       const struct power_supply_desc *psy_desc;
> >
> >       if (!dev->battery)
> >               return;
> >
> > +     bat = power_supply_get_drvdata(dev->battery);
> >       psy_desc = dev->battery->desc;
> >       power_supply_unregister(dev->battery);
> >       kfree(psy_desc->name);
> >       kfree(psy_desc);
> > +     kfree(bat);
> >       dev->battery = NULL;
> >  }
> >
> > -static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> > +static bool hidinput_update_battery_charge_status(struct hid_battery *bat,
> >                                                 unsigned int usage, int value)
> >  {
> >       switch (usage) {
> >       case HID_BAT_CHARGING:
> > -             dev->battery_charge_status = value ?
> > -                                          POWER_SUPPLY_STATUS_CHARGING :
> > -                                          POWER_SUPPLY_STATUS_DISCHARGING;
> > +             bat->charge_status = value ?
> > +                                  POWER_SUPPLY_STATUS_CHARGING :
> > +                                  POWER_SUPPLY_STATUS_DISCHARGING;
> > +             /* Update legacy field for backward compatibility */
> > +             if (bat->dev->battery == bat->ps)
> > +                     bat->dev->battery_charge_status = bat->charge_status;
> >               return true;
> >       }
> >
> > @@ -625,32 +655,43 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
> >  static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
> >                                   int value)
> >  {
> > +     struct hid_battery *bat;
> >       int capacity;
> >
> >       if (!dev->battery)
> >               return;
> >
> > -     if (hidinput_update_battery_charge_status(dev, usage, value)) {
> > -             power_supply_changed(dev->battery);
> > +     bat = power_supply_get_drvdata(dev->battery);
> > +
> > +     if (hidinput_update_battery_charge_status(bat, usage, value)) {
> > +             power_supply_changed(bat->ps);
> >               return;
> >       }
> >
> >       if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value == 0)
> >               return;
> >
> > -     if (value < dev->battery_min || value > dev->battery_max)
> > +     if (value < bat->min || value > bat->max)
> >               return;
> >
> >       capacity = hidinput_scale_battery_capacity(dev, value);
> >
> > -     if (dev->battery_status != HID_BATTERY_REPORTED ||
> > -         capacity != dev->battery_capacity ||
> > -         ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
> > -             dev->battery_capacity = capacity;
> > -             dev->battery_status = HID_BATTERY_REPORTED;
> > -             dev->battery_ratelimit_time =
> > +     if (bat->status != HID_BATTERY_REPORTED ||
> > +         capacity != bat->capacity ||
> > +         ktime_after(ktime_get_coarse(), bat->ratelimit_time)) {
> > +             bat->capacity = capacity;
> > +             bat->status = HID_BATTERY_REPORTED;
> > +             bat->ratelimit_time =
> >                       ktime_add_ms(ktime_get_coarse(), 30 * 1000);
> > -             power_supply_changed(dev->battery);
> > +
> > +             /* Update legacy fields for backward compatibility */
> > +             if (dev->battery == bat->ps) {
> > +                     dev->battery_capacity = bat->capacity;
> > +                     dev->battery_status = bat->status;
> > +                     dev->battery_ratelimit_time = bat->ratelimit_time;
> > +             }
> > +
> > +             power_supply_changed(bat->ps);
> >       }
> >  }
> >  #else  /* !CONFIG_HID_BATTERY_STRENGTH */
> > --
> > 2.51.1
> >
>
> Cheers,
> Benjamin
>

Best,
Lucas Zampieri


^ permalink raw reply

* Re: [RFC PATCH v2 0/3] HID: Add support for multiple batteries per device
From: Lucas Zampieri @ 2025-11-16 11:38 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: linux-input, linux-kernel, Jiri Kosina, Sebastian Reichel,
	Bastien Nocera, linux-pm
In-Reply-To: <em2u4ncbsu3ivwkiucthvqzhzbklu72eo3ovhshzy4m6iqiv4v@bvlk4w4ux2qh>

Hey Benjamin

On Thu, Nov 13, 2025 at 10:44 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
>
> Hi Lucas,
>
> On Nov 13 2025, Lucas Zampieri wrote:
> > This RFC introduces support for multiple batteries per HID device, addressing
> > a long-standing architectural limitation in the HID battery reporting subsystem.
>
> (TBH, that cover letter screams AI :-P)
(partly yes, but also the cover letter kept growing and growing with
more things to point out/data to make this request seem sane)

>
> Stating a "long-standing architectural limitation" is a bit rough. The
> need wasn't there or nobody took the time and effort to plug this hole
> :)
>
> General comment on this cover letter actually: it is way too detailed,
> and try to iron out every corner case the AI bot thought would be fought
> against.
> Which leads to people actually nitpicking on the details below:
> for example, I immediately thought the Wacom bit down below was wrong.
>
> But really, all you need is multiple battery support on devices.
> And this is fine. A couple of example would be good enough, and we can
> move on.
>
Ack, will make it simpler and remove those many examples.

> >
> > ## Background
> >
> > The current HID implementation explicitly prevents multiple batteries per device
> > through an early return in hidinput_setup_battery() that enforces a single-battery
> > assumption. Linux treats peripheral batteries (scope=Device) differently from system
> > batteries, with desktop environments often displaying them separately or ignoring
> > them entirely. However, this design doesn't account for modern multi-battery hardware patterns.
> >
> > ## Problem Statement
> >
> > Multiple battery scenarios that cannot be properly reported today:
> >
> > 1. Gaming headsets with charging docks (e.g., SteelSeries Arctis Nova Pro
> >    Wireless) - headset battery reported, dock battery invisible
> > 2. Graphics tablets with stylus batteries (Wacom) - requires driver-specific
> >    workarounds
>
> Technically, most Wacom styluses are battery-less for their pencils. The
> battery issue that was related below is for a AES device, which is an
> integrated wacom tablet in a laptop, so the only battery we have is on
> the stylus itself. Arguably we can have several styluses, but only one
> can be used at the same time, so reusing the same power supply device is
> fine.
>
> (see, I told you, that's the "someone is wrong on the internet effect")
>
oh okay, thats the only type of driver that I found did a similar
thing to what I'm proposing, but yeah I missed the part we most
styluses where battery-less.

> > 3. Wireless earbuds with per-earbud batteries plus charging case
> > 4. Multi-device receivers (Logitech Unifying) - requires proprietary HID++
> >    protocol parsing
>
> That's an entire different reason why there is HID++, and the fact that
> Solaar handles batteries itself is lack of support in the kernel for
> some devices. (and because sometimes it's much simpler to update a user
> space client than a kernel driver)
>
I know I was picking an specific HID++ feature, over the many others
that it provides just to make a point.

> >
> > This forces manufacturers to use proprietary protocols and vendor-specific
> > software. Community projects parse USB packets directly because standard HID
> > battery reporting cannot handle multi-battery scenarios.
>
> I don't think Linux ever forced any manufacturer to use proprietary
> protocols or vendor-specific software. For Logitech (or any gaming mouse
> FWIW), those protocols were in place well before Linux ever had support
> for them.
>
Yes, what I mean, it that there's no path to make the multi-battery
reporting native to Linux(Other than ofk you can report only the
lowest of the average of all batteries, but not individual batteries),
so I feel manufactures have no other option other than write their own
perfiferal-sw (eg, steelseries sonar, Logitech GHUB, etc.)(and sure,
many of those dont even work on linux, but then we are left
reverse-engineering protocols in Solaar, HeadsetControl and others).

> >
> > ## Why This Matters
> >
> > The current limitation creates a cycle: OS lacks support, so manufacturers
> > implement proprietary protocols, which makes vendor software necessary, which
> > reduces pressure to fix the OS limitation. Improving HID core support for
> > multiple batteries would enable standardized reporting, reduce the need for
> > vendor software, improve OS integration, reduce driver duplication, and provide
> > a foundation for future multi-battery devices.
>
> This is completely BS and AI generated. There is no such cycle.
>
> But again, the need is there, so we need to push this series forward.
> The packaging of the cover letter is wrong. We are not salesmen, there
> is no need to pitch this like that.
>
Well, BS maybe, but completely human fabricated :p
And sure, I have no ground in reality for this supposition other than
inferring from the peripherals that I've seen. But my questions here
mostly is, why would manufactures spend time and money creating and
maintaining their custom protocols and accompanying desktop-software
if there was a native way? But I guess you got to the same conclusion
as I did, so then, I'll remove my suppositions, and only keep the real
world examples.

> >
> > ## Proposed Solution
> >
> > This series introduces struct hid_battery to encapsulate individual battery
> > state, refactors the code to use this structure internally, and adds support
> > for multiple batteries tracked in a list within struct hid_device. Batteries
> > are identified by report ID. The implementation maintains full backwards
> > compatibility with existing single-battery code.
>
> That paragraph is good. Keep it please :)
>
> >
> > ## Testing
> >
> > Tested with split keyboard hardware (Dactyl 5x6) using custom ZMK firmware
> > that implements per-side HID battery reporting. Each battery (left and right
> > keyboard halves) reports independently through the power supply interface with
> > distinct report IDs (0x05 and 0x06).
> >
> > Test firmware available on my personal fork at:
> > https://github.com/zampierilucas/zmk/tree/feat/individual-hid-battery-reporting
> > If this series gets merged, these changes will be proposed to upstream ZMK.
> >
> > HID descriptor and recording captured with hid-recorder:
> >
> > D: 0
> > R: 162 05 01 09 06 a1 01 85 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 05 07 75 08 95 01 81 03 05 07 15 00 25 01 19 00 29 67 75 01 95 68 81 02 c0 05 0c 09 01 a1 01 85 02 05 0c 15 00 26 ff 0f 19 00 2a ff 0f 75 10 95 06 81 00 c0 05 84 09 05 a1 01 05 85 85 05 09 44 15 00 25 01 35 00 45 01 75 08 95 01 81 02 09 65 15 00 25 64 35 00 45 64 75 08 95 01 81 02 c0 05 84 09 05 a1 01 05 85 85 06 09 44 15 00 25 01 35 00 45 01 75 08 95 01 81 02 09 65 15 00 25 64 35 00 45 64 75 08 95 01 81 02 c0
> > N: ZMK Project Dactyl 5x6
> > P: usb-0000:2d:00.3-4.2/input2
> > I: 3 1d50 615e
> > D: 0
> > E: 0.000000 3 05 00 56
> > E: 0.000977 3 05 00 56
> > E: 1.490974 3 06 00 52
> > E: 1.491958 3 06 00 52
> > E: 6.492979 3 06 00 53
> > E: 6.493962 3 06 00 53
> >
> > The recording shows both batteries reporting with different charge levels
> > (Report ID 05: 86%, Report ID 06: 82%-83%), demonstrating the multi-battery
> > functionality. This can be used to verify UPower compatibility.
>
> Same, please keep that testing note in future versions.
>
> >
> > ## Future Work: Userspace Integration
> >
> > As suggested by Bastien, semantic battery differentiation (e.g., "left
> > earbud" vs "right earbud") requires userspace coordination, as HID
> > reports typically lack role metadata.
>
> The sad part is that HID can report a lot of things, but manufacturers
> don't make a full use of it.
>
> For example, in the split keyboard case, you could make use of the
> Handedness NAry of the generic device Controls page (0x06) which can
> help describing Left Hand (0x32) and Right Hand (0x33).
>
> But the problem is more likely that no known manufacturer will follow
> your lead on this, and the NAry code you put here will only be used by
> your firmware.
>
> For the bluetooth earbuds, the Logical collection Channel Left (0x161,
> page Consumer Page 0x0c) and Channel Right (0x162, same page) could be
> used.
>
> >
> > This will require:
> > 1. systemd/hwdb entries for device-specific battery role mappings
> > 2. UPower updates to enumerate and group multi-battery devices
> > 3. Desktop environment changes to display batteries with meaningful labels
> >
> > This kernel infrastructure is a prerequisite for that userspace work.
> >
> > ## Request for Comments
> >
> > Is list-based storage appropriate or would another structure work better?
>
> List based storage should be fine. We don't receive battery updates that
> often, so if that is simpler for you, that's all right.
>
> > Should we support usage-based identification in addition to report ID for
> > devices using the same report ID?
>
> If the question is:
> - should we enforce each battery to have a different name, the answer is
>         yes
> - should we take the report ID as the battery identifier in the name:
>         why not?
> - should we make use of other collections to split up batteries: maybe.
>         If you have a device which reports multiple batteries on the same
>         report ID, then yes, we will need a way to split them.
>
> But again, we are only theorically speaking here, so it's hard to
> answer. I really wish we had an actual consumer device we could use as a
> reference, not a custom firmware we can tune in any way we want.
>
yeah, but I guess we are also showing the way of how it could be
implemented in a future device,  so might as well stress the
theoretical aspect of it, so we can provide a well rounded api. For
now I'll work on a v3 with report ID, but I'm open to changing things
around if we come up with something better.

> > Is sequential naming (battery-N) sufficient
> > or should batteries have semantic role identifiers like "main", "stylus", "dock"?
>
> Semantic would be ideal, but at least having a unique identifier is a
> requirement. That's more a question for the upower guys.
>
> >
> > To HID maintainers (Jiri Kosina, Benjamin Tissoires): Does this belong in
> > hid-input.c or should it be separate?
>
> hid-input.c seems fine to me. There are technically not a lot of
> differences from the existing AFAICT, it's just that we iterate over a
> list.
>
> > Any concerns about the backwards
> > compatibility approach? Meaning, should I have removed the whole
> > dev->bat legacy mapping and use the new struct?
>
> I'm not sure I follow that backwards compatibility. AFAIU that API is
> kernel internal, so it should be easier to drop it entirely instead of
> duplicating the data for no benefit.
>
Got it, I was mostly worried that it was expected somewhere else, but
given your feedback, I'll make sure to have a proper new api without
so many edge case handling for a single device.

> >
> > To power supply maintainers (Sebastian Reichel): Any issues with multiple
> > power_supply devices from a single HID device?
>
> Not a power supply maintainer, but unless upower builds a graph of the
> devices it sees, I don't see why this would be a problem.
>
> >
> > Related commits:
> > - c6838eeef2fb: HID: hid-input: occasionally report stylus battery
> > - a608dc1c0639: HID: input: map battery system charging
> > - fd2a9b29dc9c: HID: wacom: Remove AES power_supply after inactivity
>
> What the point of those?
Mostly to say, "Hey I know it has been done there!", but I'll get rid
of those in v3 as its just noise.

>
> >
> > Community projects demonstrating the need:
> > - HeadsetControl: https://github.com/Sapd/HeadsetControl
> > - Solaar: https://github.com/pwr-Solaar/Solaar
> > - OpenRazer: https://github.com/openrazer/openrazer
> >
> > Lucas Zampieri (3):
> >   HID: input: Introduce struct hid_battery
> >   HID: input: Refactor battery code to use struct hid_battery
> >   HID: input: Add support for multiple batteries per device
> >
> > Changes in v2:
> > - Split the monolithic v1 patch into three logical patches for easier review:
> >   1. Introduce struct hid_battery (pure structure addition)
> >   2. Refactor existing code to use the new structure (internal changes)
> >   3. Add multi-battery support (new functionality)
> > - Added detailed testing section with hardware specifics
> > - Added hid-recorder output (dactyl-hid-recording.txt) demonstrating two-battery
> >   HID descriptor for UPower validation
> > - Added "Future Work: Userspace Integration" section addressing Bastien's feedback
> >   about semantic battery differentiation
> > - Added hardware examples with product links to commit messages (per Bastien's
> >   suggestion)
> > - No functional changes from v1, only improved patch organization and documentation
> >
> >  drivers/hid/hid-core.c  |   4 +
> >  drivers/hid/hid-input.c | 196 +++++++++++++++++++++++++++-------------
> >  include/linux/hid.h     |  42 ++++++++-
> >  3 files changed, 179 insertions(+), 63 deletions(-)
> >
> > --
> > 2.51.1
> >
>
> And now after all of this bikeshedding, I can move on to the review of
> the series.
>
> Cheers,
> Benjamin
>
Thank you for the bikeshedding, and sorry for such a long cover
letter, I just wanted to justify every aspect of what/why/how was
needed, and ended up with this monstruosity :D I'll clean this up for
v3 and make sure only what is relevant needed for this to the throught
is there.

Best,
Lucas Zampieri


^ permalink raw reply

* Re: [PATCH v2] HID: memory leak in dualshock4_get_calibration_data
From: Max Staudt @ 2025-11-16  4:34 UTC (permalink / raw)
  To: Eslam Khafagy, roderick.colenbrander, jikos, bentiss
  Cc: linux-input, linux-kernel, stable, syzbot+4f5f81e1456a1f645bf8
In-Reply-To: <20251116022723.29857-1-eslam.medhat1993@gmail.com>

On 11/16/25 11:27 AM, Eslam Khafagy wrote:
> function dualshock4_get_calibration_data allocates memory to pointer
> buf. however the function may exit prematurely due to transfer_failure
> in this case it does not handle freeing memory.
> 
> this patch handles memory deallocation at exit.

Reviewed-by: Max Staudt <max@enpas.org>


One minor thing that I forgot: I see that the commit message is all 
lower case. Proper capitalisation helps readability and matches the 
kernel style. No need to resend this time, but in case you're sending a 
v3 for any other reason, it'd be great if you picked this up as well :)


Thanks for your patch!

Max


^ permalink raw reply

* [PATCH v2] HID: memory leak in dualshock4_get_calibration_data
From: Eslam Khafagy @ 2025-11-16  2:27 UTC (permalink / raw)
  To: roderick.colenbrander, jikos, bentiss, max
  Cc: linux-input, linux-kernel, stable, eslam.medhat1993,
	syzbot+4f5f81e1456a1f645bf8

function dualshock4_get_calibration_data allocates memory to pointer
buf. however the function may exit prematurely due to transfer_failure
in this case it does not handle freeing memory.

this patch handles memory deallocation at exit.

Reported-by: syzbot+4f5f81e1456a1f645bf8@syzkaller.appspotmail.com
Tested-by: syzbot+4f5f81e1456a1f645bf8@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/691560c4.a70a0220.3124cb.0019.GAE@google.com/T/
Fixes: 947992c7fa9e0 ("HID: playstation: DS4: Fix calibration workaround for clone devices")
Cc: stable@vger.kernel.org
Signed-off-by: Eslam Khafagy <eslam.medhat1993@gmail.com>
---
v2:
* Adding tag "Cc: stable@vger.kernel.org"
v1: https://lore.kernel.org/all/20251115022323.1395726-1-eslam.medhat1993@gmail.com/
---
 drivers/hid/hid-playstation.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 63f6eb9030d1..fef81b7e27c1 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -1992,9 +1992,6 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
 	acc_z_plus       = get_unaligned_le16(&buf[31]);
 	acc_z_minus      = get_unaligned_le16(&buf[33]);
 
-	/* Done parsing the buffer, so let's free it. */
-	kfree(buf);
-
 	/*
 	 * Set gyroscope calibration and normalization parameters.
 	 * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s.
@@ -2041,6 +2038,10 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
 	ds4->accel_calib_data[2].sens_denom = range_2g;
 
 transfer_failed:
+	/* First free buf if still allocated */
+	if(buf)
+		kfree(buf);
+
 	/*
 	 * Sanity check gyro calibration data. This is needed to prevent crashes
 	 * during report handling of virtual, clone or broken devices not implementing
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] HID: memory leak in dualshock4_get_calibration_data
From: Eslam Khafagy @ 2025-11-16  2:13 UTC (permalink / raw)
  To: Max Staudt, roderick.colenbrander, jikos, bentiss
  Cc: linux-input, linux-kernel, syzbot+4f5f81e1456a1f645bf8
In-Reply-To: <3aca217a-4cf3-4793-855a-39ed99eb81d2@enpas.org>


On 11/15/25 15:22, Max Staudt wrote:
> Thank you Eslam for catching this!
>
> That mistake was mine, and your fix looks good to me.
>
>
> Reviewed-by: Max Staudt <max@enpas.org>
>
>
>
> I think your patch may be a candidate for the -stable tree:
>
>  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/stable-kernel-rules.rst 
>
>
>
>
> Thank you for your help :)
>
> Max
>
>
I'll send v2 for this to cc stable mailing list.

^ permalink raw reply

* Re: [PATCH] HID: memory leak in dualshock4_get_calibration_data
From: Eslam Khafagy @ 2025-11-15 16:42 UTC (permalink / raw)
  To: Max Staudt, roderick.colenbrander, jikos, bentiss
  Cc: linux-input, linux-kernel, syzbot+4f5f81e1456a1f645bf8
In-Reply-To: <3aca217a-4cf3-4793-855a-39ed99eb81d2@enpas.org>


On 11/15/25 15:22, Max Staudt wrote:
> Thank you Eslam for catching this!
>
> That mistake was mine, and your fix looks good to me.
>
>
> Reviewed-by: Max Staudt <max@enpas.org>
>
>
>
> I think your patch may be a candidate for the -stable tree:
>
>  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/stable-kernel-rules.rst 
>
>
>
sure, will submit a backport there too.
>
> Thank you for your help :)
>
No thank you 🙂
> Max
>
>

^ permalink raw reply

* Re: [PATCH] HID: memory leak in dualshock4_get_calibration_data
From: Eslam Khafagy @ 2025-11-15 16:40 UTC (permalink / raw)
  To: Max Staudt, roderick.colenbrander, jikos, bentiss
  Cc: linux-input, linux-kernel, syzbot+4f5f81e1456a1f645bf8
In-Reply-To: <3aca217a-4cf3-4793-855a-39ed99eb81d2@enpas.org>


On 11/15/25 15:22, Max Staudt wrote:
> Thank you Eslam for catching this!
>
> That mistake was mine, and your fix looks good to me.
>
>
> Reviewed-by: Max Staudt <max@enpas.org>
>
>
>
> I think your patch may be a candidate for the -stable tree:
>
>  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/stable-kernel-rules.rst 
>
>
sure, will submit a backport there too.
>
>
> Thank you for your help :)
No thank you :)
>
> Max
>
>

^ permalink raw reply

* Re: [PATCH] HID: memory leak in dualshock4_get_calibration_data
From: Max Staudt @ 2025-11-15 13:22 UTC (permalink / raw)
  To: Eslam Khafagy, roderick.colenbrander, jikos, bentiss
  Cc: linux-input, linux-kernel, syzbot+4f5f81e1456a1f645bf8
In-Reply-To: <20251115022323.1395726-1-eslam.medhat1993@gmail.com>

Thank you Eslam for catching this!

That mistake was mine, and your fix looks good to me.


Reviewed-by: Max Staudt <max@enpas.org>



I think your patch may be a candidate for the -stable tree:

  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/stable-kernel-rules.rst



Thank you for your help :)

Max



^ permalink raw reply

* [PATCH] HID: memory leak in dualshock4_get_calibration_data
From: Eslam Khafagy @ 2025-11-15  2:23 UTC (permalink / raw)
  To: roderick.colenbrander, jikos, bentiss, max
  Cc: linux-input, linux-kernel, eslam.medhat1993,
	syzbot+4f5f81e1456a1f645bf8

function dualshock4_get_calibration_data allocates memory to pointer
buf. however the function may exit prematurely due to transfer_failure
in this case it does not handle freeing memory.

this patch handles memory deallocation at exit.

Reported-by: syzbot+4f5f81e1456a1f645bf8@syzkaller.appspotmail.com
Tested-by: syzbot+4f5f81e1456a1f645bf8@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/691560c4.a70a0220.3124cb.0019.GAE@google.com/T/
Fixes: 947992c7fa9e0 ("HID: playstation: DS4: Fix calibration workaround for clone devices")
Signed-off-by: Eslam Khafagy <eslam.medhat1993@gmail.com>
---
 drivers/hid/hid-playstation.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 63f6eb9030d1..fef81b7e27c1 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -1992,9 +1992,6 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
 	acc_z_plus       = get_unaligned_le16(&buf[31]);
 	acc_z_minus      = get_unaligned_le16(&buf[33]);
 
-	/* Done parsing the buffer, so let's free it. */
-	kfree(buf);
-
 	/*
 	 * Set gyroscope calibration and normalization parameters.
 	 * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s.
@@ -2041,6 +2038,10 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
 	ds4->accel_calib_data[2].sens_denom = range_2g;
 
 transfer_failed:
+	/* First free buf if still allocated */
+	if(buf)
+		kfree(buf);
+
 	/*
 	 * Sanity check gyro calibration data. This is needed to prevent crashes
 	 * during report handling of virtual, clone or broken devices not implementing
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] HID: i2c-hid: Add API to wait for device reset completion
From: Mario Limonciello (AMD) (kernel.org) @ 2025-11-14 16:13 UTC (permalink / raw)
  To: LI Qingwu, jikos, bentiss, dianders, treapking, alex.vinarskis,
	dan.carpenter, guanwentao, kl, linux-input, linux-kernel
In-Reply-To: <20251114112418.3275211-1-Qing-wu.Li@leica-geosystems.com.cn>



On 11/14/2025 5:24 AM, LI Qingwu wrote:
> Some HID over I2C devices need to signal reset completion to the host
> after firmware updates or device resets. Per the HID over I2C spec,
> devices signal completion by sending an empty input report (0x0000).
> 
> Add i2c_hid_wait_reset_complete() to allow drivers to synchronize
> with device reset operations. The function sets I2C_HID_RESET_PENDING
> and waits for the device's completion signal.
> 
> Returns: 0 on success, -ETIMEDOUT on timeout, -ENODEV if invalid device.
> 
> Upstream-Status: Pending

This tag is probably useful for your downstream tree, but it's not 
useful upstream.  You should strip it when submitting upstream.

> Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn>
> ---
>   drivers/hid/i2c-hid/i2c-hid-core.c | 24 ++++++++++++++++++++++++
>   drivers/hid/i2c-hid/i2c-hid.h      |  1 +
>   2 files changed, 25 insertions(+)
> 
> diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
> index d3912e3f2f13a..4feab2327e92d 100644
> --- a/drivers/hid/i2c-hid/i2c-hid-core.c
> +++ b/drivers/hid/i2c-hid/i2c-hid-core.c
> @@ -1365,6 +1365,30 @@ const struct dev_pm_ops i2c_hid_core_pm = {
>   };
>   EXPORT_SYMBOL_GPL(i2c_hid_core_pm);
>   
> +int i2c_hid_wait_reset_complete(struct device *dev, unsigned long timeout_ms)
> +{
> +	struct i2c_client *client;
> +	struct i2c_hid *ihid;
> +	long ret;
> +
> +	if (!dev)
> +		return -ENODEV;
> +
> +	client = to_i2c_client(dev);

Check if client is NULL?

> +	ihid = i2c_get_clientdata(client);
> +	if (!ihid)
> +		return -ENODEV;
> +	set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
> +	ret = wait_event_timeout(ihid->wait,
> +				 !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
> +				 msecs_to_jiffies(timeout_ms));
> +	if (ret == 0) {

Shouldn't need curly braces for a one line statement.

> +		clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
> +	}
> +	return ret ? 0 : -ETIMEDOUT;

Why not just:

return ret;

> +}
> +EXPORT_SYMBOL_GPL(i2c_hid_wait_reset_complete);

Can you please include a second patch in your series demonstrating the 
usage of this in a driver?

> +
>   MODULE_DESCRIPTION("HID over I2C core driver");
>   MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
>   MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/i2c-hid/i2c-hid.h b/drivers/hid/i2c-hid/i2c-hid.h
> index 2c7b66d5caa0f..1c6d959716858 100644
> --- a/drivers/hid/i2c-hid/i2c-hid.h
> +++ b/drivers/hid/i2c-hid/i2c-hid.h
> @@ -40,6 +40,7 @@ void i2c_hid_core_remove(struct i2c_client *client);
>   
>   void i2c_hid_core_shutdown(struct i2c_client *client);
>   
> +int i2c_hid_wait_reset_complete(struct device *dev, unsigned long timeout_ms);
>   extern const struct dev_pm_ops i2c_hid_core_pm;
>   
>   #endif


^ permalink raw reply

* [PATCH] HID: i2c-hid: Add API to wait for device reset completion
From: LI Qingwu @ 2025-11-14 11:24 UTC (permalink / raw)
  To: jikos, bentiss, dianders, treapking, alex.vinarskis,
	dan.carpenter, superm1, guanwentao, kl, Qing-wu.Li, linux-input,
	linux-kernel

Some HID over I2C devices need to signal reset completion to the host
after firmware updates or device resets. Per the HID over I2C spec,
devices signal completion by sending an empty input report (0x0000).

Add i2c_hid_wait_reset_complete() to allow drivers to synchronize
with device reset operations. The function sets I2C_HID_RESET_PENDING
and waits for the device's completion signal.

Returns: 0 on success, -ETIMEDOUT on timeout, -ENODEV if invalid device.

Upstream-Status: Pending
Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn>
---
 drivers/hid/i2c-hid/i2c-hid-core.c | 24 ++++++++++++++++++++++++
 drivers/hid/i2c-hid/i2c-hid.h      |  1 +
 2 files changed, 25 insertions(+)

diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index d3912e3f2f13a..4feab2327e92d 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -1365,6 +1365,30 @@ const struct dev_pm_ops i2c_hid_core_pm = {
 };
 EXPORT_SYMBOL_GPL(i2c_hid_core_pm);
 
+int i2c_hid_wait_reset_complete(struct device *dev, unsigned long timeout_ms)
+{
+	struct i2c_client *client;
+	struct i2c_hid *ihid;
+	long ret;
+
+	if (!dev)
+		return -ENODEV;
+
+	client = to_i2c_client(dev);
+	ihid = i2c_get_clientdata(client);
+	if (!ihid)
+		return -ENODEV;
+	set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+	ret = wait_event_timeout(ihid->wait,
+				 !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
+				 msecs_to_jiffies(timeout_ms));
+	if (ret == 0) {
+		clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+	}
+	return ret ? 0 : -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(i2c_hid_wait_reset_complete);
+
 MODULE_DESCRIPTION("HID over I2C core driver");
 MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
 MODULE_LICENSE("GPL");
diff --git a/drivers/hid/i2c-hid/i2c-hid.h b/drivers/hid/i2c-hid/i2c-hid.h
index 2c7b66d5caa0f..1c6d959716858 100644
--- a/drivers/hid/i2c-hid/i2c-hid.h
+++ b/drivers/hid/i2c-hid/i2c-hid.h
@@ -40,6 +40,7 @@ void i2c_hid_core_remove(struct i2c_client *client);
 
 void i2c_hid_core_shutdown(struct i2c_client *client);
 
+int i2c_hid_wait_reset_complete(struct device *dev, unsigned long timeout_ms);
 extern const struct dev_pm_ops i2c_hid_core_pm;
 
 #endif
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v4 1/2] dt-bindings: input: i2c-hid: Introduce FocalTech FT8112
From: Krzysztof Kozlowski @ 2025-11-14  7:14 UTC (permalink / raw)
  To: Daniel Peng
  Cc: Dmitry Torokhov, linux-input, LKML, Conor Dooley,
	Krzysztof Kozlowski, Rob Herring, devicetree
In-Reply-To: <CAAq2-DHouqsbOB75mifXVxQT+jZaho9RBB0zdZ0a3bq3FxDT7w@mail.gmail.com>

On 14/11/2025 07:13, Daniel Peng wrote:
>> On 13/11/2025 11:44, Daniel Peng wrote:
>>> From: Daniel Peng <Daniel_Peng@pegatron.corp-partner.google.com>
>>>
>>> The FocalTech FT8112 touch screen chip same as Ilitek ili2901 controller
>>
>> So keep the device in that binding under enum. No need to create
>> document for every device, even if they were different but here it is
>> pretty obvious - same chip.
>>
>> Best regards,
>> Krzysztof
> 
> Sorry for the confusion. Might be I use the wrong description, and make you
> feel that is the same chip and no need to create new binding file.
> 
> Here are my question:
> 1. I had referred to ilitek,ili2901.yaml and copied as new file for
> focaltech,ft8112.yaml. Moreover, I also modified to replace related
> information for Focaltech FT8112 touchscreen.
> There are many different descriptions in the file, such as "compatible",
> "reg", "interrupt-parent", "interrupts", "reset-gpios", ... .etc.
> If "No need to create document for every device" now, could you help to
> provide what file that I need to keep the device in the binding under enum?

You wrote it is the same chip as ili2901, so obviously in ili2901.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH] Input: ti_am335x_tsc: fix off-by-one error in wire_order validation
From: Junjie Cao @ 2025-11-14  6:28 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input, linux-kernel, junjie.cao

The current validation 'wire_order[i] > ARRAY_SIZE(config_pins)' allows
wire_order[i] to equal ARRAY_SIZE(config_pins), which causes out-of-bounds
access when used as index in 'config_pins[wire_order[i]]'.

Since config_pins has 4 elements (indices 0-3), the valid range for
wire_order should be 0-3. Fix the off-by-one error by using >= instead
of > in the validation check.

Signed-off-by: Junjie Cao <junjie.cao@intel.com>
---
 drivers/input/touchscreen/ti_am335x_tsc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 93d659ff90aa..73980142f492 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -85,7 +85,7 @@ static int titsc_config_wires(struct titsc *ts_dev)
 		wire_order[i] = ts_dev->config_inp[i] & 0x0F;
 		if (WARN_ON(analog_line[i] > 7))
 			return -EINVAL;
-		if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins)))
+		if (WARN_ON(wire_order[i] >= ARRAY_SIZE(config_pins)))
 			return -EINVAL;
 	}
 
-- 
2.43.0


^ permalink raw reply related

* Re: hid_multitouch - random non-responsive, multitouch, scrolling and zooming
From: Steven Haigh @ 2025-11-14  4:04 UTC (permalink / raw)
  To: Benjamin Tissoires; +Cc: Stéphane Chatty, Peter Hutterer, linux-input
In-Reply-To: <ed69f310-8a4d-4023-b1ce-72b78e512177@app.fastmail.com>

On 11/10/25 00:54, Benjamin Tissoires wrote:
> 
> Right now the most valuable would be to test the series at
> https://lore.kernel.org/linux-input/20251008-fix-sticky-fingers-v1-0-760f1f26fce3@kernel.org/T/#m0fba507cbd43acd36bef28151ecb90b7077606a4
> 
> If this work and you want a quick and dirty fix you can then return to the default kernel, and use the BPF from https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/204, the latest version should be compatible with your device, just re-download and install it again.
> 
> But definitely a tested-by on the kernel patch would be appreciated :)

Hi all, I know its been a while - and I almost forgot about this after 
installing the BPF update to test.

That is until I did a reinstall on that laptop of a fresh Fedora 43 KDE 
install. Oh my, what I didn't realise was actually fixed :)

I checked here again:
https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/204/pipelines

It looks like the artifacts that I downloaded prior don't seem to be on 
the gitlab instance anymore.

I grabbed a different artifact, but I'm pretty sure that was based on 
the main branch, so it didn't apply to my touchpad.

I had a look around, and noticed that the kernel side fixes seem to be 
in the works for 6.18.0 - am I understanding that right?

I can see some 6.18.0-rc5 packages for Fedora that I can probably 
shoe-horn into Fedora 43 if that is a reasonable way forward.

What's the best way at the moment to go forwards now?

-- 
Steven Haigh

📧 netwiz@crc.id.au
💻 https://crc.id.au



^ permalink raw reply

* Re: [PATCH v8 00/10] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling
From: Antheas Kapenekakis @ 2025-11-13 22:41 UTC (permalink / raw)
  To: luke
  Cc: Denis Benato, Ilpo Järvinen, platform-driver-x86,
	linux-input, LKML, Jiri Kosina, Benjamin Tissoires,
	Corentin Chary, Hans de Goede
In-Reply-To: <5440F7FC-14A0-45A0-9E84-1DE5285F9F3F@ljones.dev>

On Thu, 13 Nov 2025 at 23:09, <luke@ljones.dev> wrote:
>
>
>
> > On 14 Nov 2025, at 10:17, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> >
> > On Thu, 13 Nov 2025 at 21:23, <luke@ljones.dev> wrote:
> >>
> >>
> >>> On 13 Nov 2025, at 21:44, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> >>>
> >>> On Thu, 13 Nov 2025 at 02:14, Denis Benato <benato.denis96@gmail.com> wrote:
> >>>>
> >>>>
> >>>> On 11/12/25 23:08, Antheas Kapenekakis wrote:
> >>>>> On Wed, 12 Nov 2025 at 20:51, Denis Benato <benato.denis96@gmail.com> wrote:
> >>>>>>
> >>>>>> On 11/12/25 14:41, Antheas Kapenekakis wrote:
> >>>>>>> On Wed, 12 Nov 2025 at 14:22, Ilpo Järvinen
> >>>>>>> <ilpo.jarvinen@linux.intel.com> wrote:
> >>>>>>>> On Wed, 12 Nov 2025, Antheas Kapenekakis wrote:
> >>>>>>>>
> >>>>>>>>> On Sat, 1 Nov 2025 at 11:47, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> >>>>>>>>>> This is a two part series which does the following:
> >>>>>>>>>> - Clean-up init sequence
> >>>>>>>>>> - Unify backlight handling to happen under asus-wmi so that all Aura
> >>>>>>>>>>   devices have synced brightness controls and the backlight button works
> >>>>>>>>>>   properly when it is on a USB laptop keyboard instead of one w/ WMI.
> >>>>>>>>>>
> >>>>>>>>>> For more context, see cover letter of V1. Since V5, I removed some patches
> >>>>>>>>>> to make this easier to merge.
> >>>>>>>>> Small bump for this.
> >>>>>>>> I looked at v8 earlier but then got an impression some of Denis' comments
> >>>>>>>> against v7 were not taken into account in v8, which implies there will be
> >>>>>>>> delay until I've time to delve into the details (I need to understand
> >>>>>>>> things pretty deeply in such a case, which does take lots of time).
> >>>>>>>>
> >>>>>>>> Alternatively, if Denis says v8 is acceptable, then I don't need to spend
> >>>>>>>> so much time on it, but somehow I've a feeling he isn't happy with v8
> >>>>>>>> but just hasn't voiced it again...
> >>>>>>>>
> >>>>>>>> Please do realize that ignoring reviewer feedback without a very very good
> >>>>>>>> reason always risks adding delay or friction into getting things
> >>>>>>>> upstreamed. Especially, when the review comes from a person who has been
> >>>>>>>> around for me to learn to trust their reviews or from a maintainer of the
> >>>>>>>> code in question.
> >>>>>>> Sure, sorry if it came out this way. Dennis had two comments on the V7
> >>>>>>> version of the series.
> >>>>>>>
> >>>>>>> The first one was that asusctl has a hang bug, which he hasn't had
> >>>>>>> time to look into yet. This should have been fixed by dropping the
> >>>>>>> HID_QUIRK_INPUT_PER_APP. I retested the series and that QUIRK was a
> >>>>>>> bit of a NOOP that does not need to be added in the future.
> >>>>>> So it is supposed to not regress it now, correct?
> >>>>>>> The second is he is concerned with dropping the 0x5d/0x5e inits. Luke
> >>>>>>> said (back in March) that it is fine to drop 0x5e because it is only
> >>>>>>> used for ANIME displays. However, for 0x5d, it is hard to verify some
> >>>>>>> of the older laptops because they have only been tested with 0x5d and
> >>>>>>> we do not have the hardware in question to test.
> >>>>>>>
> >>>>>>> For this series, I re-added "initialize LED endpoint early for old
> >>>>>>> NKEY keyboards" that re-adds 0x5d for the keyboards that cannot be
> >>>>>>> tested again so this comment should be resolved too. With that in
> >>>>>>> mind, we do end up with an additional quirk/command that may be
> >>>>>>> unneeded and will remain there forever, but since it was a point of
> >>>>>>> contention, it is not worth arguing over.
> >>>>>>>
> >>>>>>> So both comments should be resolved
> >>>>>> The driver should also not late-initialize anything.
> >>>>>>
> >>>>>> Windows doesn't do it and the official asus application
> >>>>>> can freely send LEDs changing commands to either WMI or USB
> >>>>>> so I don't see any reason to do things differently [than windows]
> >>>>>> and not prepare every USB endpoint to receive commands,
> >>>>>> this has not been addressed unless I confused v7 and v8?
> >>>>> Yes, it's been added on v8. 0x5d is init for the laptops it is
> >>>>> problematic for. Not because it does not work, but because it has not
> >>>>> been verified to work for those laptops.
> >>>> I am not sure I am reading this right:
> >>>> are you telling me that on recent models the windows driver
> >>>> doesn't issue 0x5d?
> >>>
> >>> Try to add spaces in your replies. This is hard to follow.
> >>>
> >>> Do not conflate driver with software. 0x5a (over the application
> >>> 0xff310076) has traditionally been used by a driver in Windows to
> >>> control the backlight level, as it is done in this driver. 0x5d (over
> >>> the application 0xff310079) is only used by laptops with RGB by
> >>> Armoury crate. But this driver does not do RGB. No device
> >>> functionality relies on it being sent for any device I've seen. The
> >>> device remembers its Windows settings, incl. the backlight color, in
> >>> the absence of a driver.
> >>>
> >>> Laptops without RGB such as the Duo series which I would like to add
> >>> support for next only have a 0x5a endpoint. But, they are sent garbage
> >>> inits for 0x5d and 0x5e currently. This should be fixed.
> >>>
> >>> Moreso, it seems that Armoury crate on the Xbox Ally series uses
> >>> exclusively 0x5a commands and if you use 0x5d it ignores them (perhaps
> >>> RGB still works though). With the previous generation, commands worked
> >>> for either report id.
> >>>
> >>>>>>> @Denis: can give an ack if this is the case?
> >>>>>>>
> >>>>>>> As for Derek's comment, he has a PR for his project where he removes
> >>>>>>> the name match for Ally devices with ample time for it to be merged
> >>>>>>> until kernel 6.19 is released. In addition, that specific software for
> >>>>>>> full functionality relies on OOT drivers on the distros it ships with,
> >>>>>>> so it is minimally affected in either case.
> >>>>>> The part we are talking about depends on this driver (hid-asus)
> >>>>>> and there are people on asus-linux community using inputplumber
> >>>>>> for non-ally devices (the OOT driver is only for ally devices)
> >>>>>> therefore it is very important to us (and various other distributions)
> >>>>>> not to break that software in any way.
> >>>>> This driver is only used for Ally devices. If you mean that people
> >>>>> remap their keyboards using inputplumber I guess they could but I have
> >>>>> not seen it.
> >>>> I meant people remap keyboards using IP. I am sure there were
> >>>> (and very probably still are) people doing that.
> >>>>>> Weighting pros and cons of changing the name I am not sure
> >>>>>> changing name brings any benefit? Or am I missing something here?
> >>>>>> It's simply used by userspace so the hardware should be loading
> >>>>>> regardless of the name...
> >>>>> Users see the name of their devices in their settings menu. They
> >>>>> should be accurate. Also, the early entry needs to be added anyway to
> >>>>> prevent kicking devices.
> >>>> If it's just aesthetics I don't see much reasons in changing the name.
> >>>>
> >>>> "the early entry needs to be added anyway ...." has no meaning to me,
> >>>> please rephrase. Sorry.
> >>>
> >>> Early exit-
> >>>
> >>>>>> Along with IP and your tool and asusctl there is also openrgb,
> >>>>>> and a newborn application for asus devices (I don't have contacts
> >>>>>> with that dev nor I remember the name of the tool)
> >>>>>> and I am not even that sure these are all asus-related
> >>>>>> applications.
> >>>>> My tool never checked for names, it briefly did for around a month
> >>>>> after its creation for some devices until capability matches. Around
> >>>>> 6.1 and 6.7 the kernel changed the names of most USB devices and that
> >>>>> caused issues. It currently only uses name matches for VID/PID 0/0
> >>>>> devices created by the kernel. Specifically, WMI and AT Keyboards. I
> >>>>> am not sure there is a workaround for those. Asusctl also does not use
> >>>>> names either.
> >>>> But IP does, so I would like to hear confirmation from at least Derek
> >>>> before the merge that there won't be future issues.
> >>>>
> >>>> Interpret what I say here as a broad topic, not just name/PER_APP flag:
> >>>> avoid changing data flow on older models...
> >>>
> >>> In [1] Derek removes the name matches
> >>>
> >>> There are no other name matches concerning this driver in it.
> >>>
> >>> The data flow is not changed in this series; you should go through the
> >>> patches once again if you think that. The only difference is 0x5e is
> >>> not sent, and 0x5d is not sent for newer devices.
> >>>
> >>> [1] https://github.com/ShadowBlip/InputPlumber/pull/453
> >>>
> >>>>>> Excercise EXTRA care touching this area as these are enough changes
> >>>>>> to make it difficult to understand what exactly is the problem if
> >>>>>> someone shows up with LEDs malfunctioning, laptop not entering sleep
> >>>>>> anymore or something else entirely. Plus over time
> >>>>>> ASUS has used various workarounds for windows problems
> >>>>>> and I am not eager to find out what those are since there is only
> >>>>>> a realistic way it's going to happen....
> >>>>> These changes are not doing something extraordinary. It's just a minor cleanup.
> >>>>>
> >>>>>>> Moreover, that specific commit is needed for Xbox Ally devices anyway,
> >>>>>>> as the kernel kicks one of the RGB endpoints because it does not
> >>>>>>> register an input device (the check skipped by early return) so
> >>>>>>> userspace becomes unable to control RGB on a stock kernel
> >>>>>>> (hidraw/hiddev nodes are gone). For more context here, this specific
> >>>>>>> endpoint implements the RGB Lamparray protocol for Windows dynamic
> >>>>>>> lighting, and I think in an attempt to make it work properly in
> >>>>>>> Windows, Asus made it so Windows has to first disable dynamic lighting
> >>>>>>> for armoury crate RGB commands to work (the 0x5a ones over the 0xff31
> >>>>>>> hid page).
>
> Ah… this was an annoyance. In the hid-asus driver I did I ended up defaulting the LED control to the lamp-array style because it enabled faster per-led control. Then on sleep/resume it applied static colour matching first LED to keep some consistency. It works fine and there is no noticeable delay between switching to/from since the LampArray commands are instant (no set/apply required).
>
> Anyhow, that driver created proper new LED device just for LED control. But it could only do that by taking the Ally HID off of the current driver and managing the whole lot. The end result I thought was much cleaner and separated the actual endpoints out to specific functions instead of how the current driver takes *all* the endpoints and tries to work off usage pages or report ID only.
>
> For example use endpoint 0x83 for configuration (of gamepad) and LED. 0x87 is typically keyboard press events etc.
>
> It did make me wonder if a newer cleaner driver for new MCU 0x19b6 onwards would have worked better instead of trying to shoehorn stuff in to the current driver constantly. It’s dead easy to bring up a new driver for this as an experiment. Maybe both you and Denis could investigate doing so?

Yeah, this driver is not ideal for the Ally devices. They are not
keyboards. At least for now we can make sure that endpoint is not
ejected so that userspace programs work.

At this point its a party, disable this do that, then flip around for
the RGB to work.

I barely have time to cleanup and upstream the TDP stuff, less so
having the luxury of doing the controller in kernel as well.

>
> >>>>>> Yes once ASUS introduces something new it sticks with that for
> >>>>>> future models so it's expected more and more laptops will have
> >>>>>> the same problem: I am not questioning if these patches are needed
> >>>>>> as they very clearly are; I am questioning if everything that these
> >>>>>> patches are doing are worth doing and aren't breaking/regressing
> >>>>>> either tools or the flow of actions between the EC and these USB devices.
> >>>>> Well, this series is needed to account for that. Sending the disable
> >>>>> command is out of scope for now though.
> >>>> Here I apologize for confusion: my comments were mostly about
> >>>> older models: I absolutely don't want to break those, but if you find a way
> >>>> to distinguish them from newer models that would give you more freedom with those.
> >>>
> >>> Yes, we know their specific PIDs, so if you see the patch that adds
> >>> the 0x5d init, it is only added for those models.
> >>
> >> I’m only half keeping up to date on this. I do recall however that the 0x5D init was definitely required for the first ASUS laptop I worked on, and old GX502 - the PID for keyboard is 0x1866 and I think that was the last of that generation MCU. All the previous MCU also required it.
> >
> > You recall if it was needed to enable the RGB commands or was it only
> > for keyboard shortcuts? If it is needed for keyboard shortcuts it is
> > correct for it to stay. If RGB does not turn on where it has been
> > enabled before, it should also stay.
>
> It was for shortcuts and the ROG buttons above the keyboard. There may have been some laptops using the same MCU that required it to enable LEDs too.

Ok, so we should keep it for those then. This series keeps it for those.

> >
> >> I saw some messages in perhaps another thread where it was mentioned that 0x5E init should be removed? That I agreed with that?
> >> I know there are AniMe and Slash versions that use that init, and they are on the same MCU as the keyboard. I had expected that just one init (on 0x5A or whatever) would work but it doesn’t - what I don’t recall is if an incomplete init affected the keyboard features.
> >
> > Well, the way these devices work is that there are three hiddev
> > devices, usually nested within the same hid endpoint under up to three
> > collections. Each has one report ID. 0x5a is for brightness controls,
> > 0x5d is for RGB, and 0x5e is for anime. For the first two, I know the
> > usages are 76 and 79 (see above). I am not sure what the usage for
> > anime is because I do not have a hid descriptor for that device.
> >
> > In order to start talking to one of the hiddev devices, you are
> > supposed to start with an init. The init is bidirectional, so after
> > reading it back software knows it is talking to an Asus device (as it
> > is done in this series). Likewise, even though it is not the case for
> > all MCUs, the MCUs themselves can use it to verify they are talking to
> > an Asus application (as you said) and reject commands if it is not
> > sent.
>
> Yes, I know.
> Before I stopped on all this I built up a rather large (untidy) collection of dumps for various things here https://gitlab.com/asus-linux/reverse-engineering/-/tree/master
>
> >
> > For this reason, I think it is a good idea before asusctl starts
> > controlling RGB, to always start with a 0x5d init to verify it is
> > talking to an Asus device. And before Anime, with a 0x5e init (if the
> > specific application for it is available). So since Dennis you are the
> > new maintainer, you should try to work that in. Sending it twice does
> > not hurt, even if not ideal.
> >
> > Similarly, because this driver does not do Anime currently, there is
> > no reason for it to send 0x5e. It also does not do RGB, so there is no
> > reason to send 0x5D (unless not sending it causes issues). For the RGB
> > patch I did, I delayed the init purposely until userspace interacts
> > with the sysfs RGB endpoint, partly to interfere with userspace
> > software less as well. So if the user does not use the sysfs RGB e.g.
> > asusctl is completely unaffected.
> >
> >> In all reality unless the full set of init is causing issues it’s best to leave them in. If it is then I guess this driver is going to become a little more complex and have a few more quirks.
> >>
> >> Unfortunately I didn’t keep good records of my findings on this so it’s just my remembered observations that you’ll have to take at my word.
> >>
> >> It would be a good idea for you both to perhaps collaborate with Sergei from ghelper, he has put a huge amount of effort in to that tool and due to it being windows he gets a hell of a lot more use and bug reports/data than this driver does. There’s no shame in looking to others for inspiration, ideas, or guidance.
> >
> > Good idea. From a quick look, indeed slash/anime is 0x5e. We could
> > interact with him more in the future.
> >
> > Although looking into it, to find the correct endpoint he does a dirty
> > check for report length being more than 128[1]. SIgh
>
> Sergei would appreciate any friendly hints for sure. He’s a very nice guy.
>
> >
> > I think it would be productive to try to merge this for 6.19. So
> > Dennis can you try to be a bit more cooperative?
>
> He’s not being intentionally recalcitrant. You both have vested interest in the outcomes of this review process and from what I’ve seen he has provided some excellent feedback.  If something isn’t going the way you want it doesn’t mean it’s personal. You will both converge on acceptable solutions through good faith and communication.
>
> I’ll try to get a look in early on the next patch version and help a little if I can - it would be good to get this work in kernel and you both build off it.

I do not have any comments to go through for a next revision yet. I
tried to fix Denis' comments in this revision, I have not received
something actionable from him for this revision yet.

> >
> > I already have 6 more patches for duo keyboards. Although the keyboard
> > brightness button on those seems to not work (?)[2]. I am waiting on a
> > reply for that. Perhaps it uses a slightly different ID code. However,
> > it seems that brightness works even when disconnecting and connecting
> > the keyboard. Which is great.
>
> Do the keys emit any codes? Maybe checking the raw output before it all gets filtered by the driver could help (like printing the raw array as hex) in asus_raw_event(). If there is nothing it could be emitting WMI events. ASUS did a dirty on some laptops and left the default WMI (I probably misremember, but ACPI event at least) in the ACPI, but made them emit nothing and used HID for brightness control instead.
>
> That was this patch: https://github.com/torvalds/linux/commit/a720dee5e039238a44c0142dfccdc0e35c1125f7

This series fixes that problem. It makes all HID and WMI RGB stuff go
under a single device under asus-wmi and be controlled together.
Solves a lot of problems. For the Z13, it has two devices for
brightness: lightbar and keyboard. The duo has a detacheable keyboard
that switches from USB to bluetooth. The devices on that patch have
both WMI and HID. And while you say that only one controls RGB, there
is probably something little like a lid light that is controlled by
WMI/HID and was missed by that patch. Also makes us not require a
quirk.

It also means that a WMI event would then control the USB backlight too.

> Seems likely that because it appears to be a single button brightness cycle it could be a new code. In any case, debug printing the raw array as hex will show it if it’s being emitted.

Yeah, its not an unmapped event because it did not show up in the log.
It is probably mapped to something. I am waiting for the user to tell
me the event.

> While I remember, if you ever start playing with per-key RGB I mapped a lot of laptops https://gitlab.com/asus-linux/reverse-engineering/-/blob/master/keyboard/per_key_raw_bytes.ods?ref_type=heads - something to note is that each packet takes 1ms, but due to kernel internals it may attempt a burst of a few, or there could be up to 5ms delay. So a full sequence per row can be 8-20ms or more.
>
> Oh, small reminder: if any patch changes dramatically from what I reviewed my tags should be removed.

I removed some reviewed-by when i rewrote the first part of the
series. The latter part is mostly unchanged, although perhaps after
switching to the work queue, there was a leftover reviewed-by that was
kept.

Antheas

> >
> > Antheas
> >
> > [1] https://github.com/seerge/g-helper/blob/610b11749b4da97346012e5d47f0a9bbc93b94af/app/AnimeMatrix/Communication/Platform/WindowsUsbProvider.cs#L37
> > [2] https://github.com/bazzite-org/kernel-bazzite/issues/35
> >
> >> Cheers,
> >> Luke.
> >>
> >>>
> >>>> No disable commands unless we find hard evidence those are strictly needed.
> >>>
> >>> They are needed for the Xbox Ally series, but since this driver does
> >>> not do RGB it is out of scope.
> >>>
> >>>>> Antheas
> >>>>>
> >>>>>>> Hopefully this clears things up
> >>>>>>>
> >>>>>>> Antheas
> >>>>>>>
> >>>>>>>>> Unrelated but I was b4ing this series on Ubuntu 24 and got BADSIG:
> >>>>>>>>> DKIM/antheas.dev. Is there a reference for fixing this on my host?
> >>>>>>>>> Perhaps it would help with spam
> >>>>>>>> I see BADSIG very often these days from b4 (thanks to gmail expiring
> >>>>>>>> things after 7 days or so, I recall hearing somewhere), I just ignore them
> >>>>>>>> entirely.
> >>>>>>>>
> >>>>>>>> AFAIK, that has never caused any delay to any patch in pdx86 domain so if
> >>>>>>>> that is what you thought is happening here, it's not the case.
> >>>>>>>> If your patch does appear in the pdx86 patchwork, there's even less reason
> >>>>>>>> to worry as I mostly pick patches to process using patchwork's list.
> >>>>>>> Turns out I had to update my DNS records. It should be good now.
> >>>>>>>
> >>>>>>>> --
> >>>>>>>> i.
> >>>>>> snipp
> >>>>>>>>>> 2.51.2
> >>
> >>
> >>
> >
>
>


^ permalink raw reply

* Re: [PATCH v8 00/10] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling
From: luke @ 2025-11-13 22:09 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: Denis Benato, Ilpo Järvinen, platform-driver-x86,
	linux-input, LKML, Jiri Kosina, Benjamin Tissoires,
	Corentin Chary, Hans de Goede
In-Reply-To: <CAGwozwGwtivKnC6ucCGwJiu2yLLbddFXG+jYTaMTwU4Zt=CAfQ@mail.gmail.com>



> On 14 Nov 2025, at 10:17, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> 
> On Thu, 13 Nov 2025 at 21:23, <luke@ljones.dev> wrote:
>> 
>> 
>>> On 13 Nov 2025, at 21:44, Antheas Kapenekakis <lkml@antheas.dev> wrote:
>>> 
>>> On Thu, 13 Nov 2025 at 02:14, Denis Benato <benato.denis96@gmail.com> wrote:
>>>> 
>>>> 
>>>> On 11/12/25 23:08, Antheas Kapenekakis wrote:
>>>>> On Wed, 12 Nov 2025 at 20:51, Denis Benato <benato.denis96@gmail.com> wrote:
>>>>>> 
>>>>>> On 11/12/25 14:41, Antheas Kapenekakis wrote:
>>>>>>> On Wed, 12 Nov 2025 at 14:22, Ilpo Järvinen
>>>>>>> <ilpo.jarvinen@linux.intel.com> wrote:
>>>>>>>> On Wed, 12 Nov 2025, Antheas Kapenekakis wrote:
>>>>>>>> 
>>>>>>>>> On Sat, 1 Nov 2025 at 11:47, Antheas Kapenekakis <lkml@antheas.dev> wrote:
>>>>>>>>>> This is a two part series which does the following:
>>>>>>>>>> - Clean-up init sequence
>>>>>>>>>> - Unify backlight handling to happen under asus-wmi so that all Aura
>>>>>>>>>>   devices have synced brightness controls and the backlight button works
>>>>>>>>>>   properly when it is on a USB laptop keyboard instead of one w/ WMI.
>>>>>>>>>> 
>>>>>>>>>> For more context, see cover letter of V1. Since V5, I removed some patches
>>>>>>>>>> to make this easier to merge.
>>>>>>>>> Small bump for this.
>>>>>>>> I looked at v8 earlier but then got an impression some of Denis' comments
>>>>>>>> against v7 were not taken into account in v8, which implies there will be
>>>>>>>> delay until I've time to delve into the details (I need to understand
>>>>>>>> things pretty deeply in such a case, which does take lots of time).
>>>>>>>> 
>>>>>>>> Alternatively, if Denis says v8 is acceptable, then I don't need to spend
>>>>>>>> so much time on it, but somehow I've a feeling he isn't happy with v8
>>>>>>>> but just hasn't voiced it again...
>>>>>>>> 
>>>>>>>> Please do realize that ignoring reviewer feedback without a very very good
>>>>>>>> reason always risks adding delay or friction into getting things
>>>>>>>> upstreamed. Especially, when the review comes from a person who has been
>>>>>>>> around for me to learn to trust their reviews or from a maintainer of the
>>>>>>>> code in question.
>>>>>>> Sure, sorry if it came out this way. Dennis had two comments on the V7
>>>>>>> version of the series.
>>>>>>> 
>>>>>>> The first one was that asusctl has a hang bug, which he hasn't had
>>>>>>> time to look into yet. This should have been fixed by dropping the
>>>>>>> HID_QUIRK_INPUT_PER_APP. I retested the series and that QUIRK was a
>>>>>>> bit of a NOOP that does not need to be added in the future.
>>>>>> So it is supposed to not regress it now, correct?
>>>>>>> The second is he is concerned with dropping the 0x5d/0x5e inits. Luke
>>>>>>> said (back in March) that it is fine to drop 0x5e because it is only
>>>>>>> used for ANIME displays. However, for 0x5d, it is hard to verify some
>>>>>>> of the older laptops because they have only been tested with 0x5d and
>>>>>>> we do not have the hardware in question to test.
>>>>>>> 
>>>>>>> For this series, I re-added "initialize LED endpoint early for old
>>>>>>> NKEY keyboards" that re-adds 0x5d for the keyboards that cannot be
>>>>>>> tested again so this comment should be resolved too. With that in
>>>>>>> mind, we do end up with an additional quirk/command that may be
>>>>>>> unneeded and will remain there forever, but since it was a point of
>>>>>>> contention, it is not worth arguing over.
>>>>>>> 
>>>>>>> So both comments should be resolved
>>>>>> The driver should also not late-initialize anything.
>>>>>> 
>>>>>> Windows doesn't do it and the official asus application
>>>>>> can freely send LEDs changing commands to either WMI or USB
>>>>>> so I don't see any reason to do things differently [than windows]
>>>>>> and not prepare every USB endpoint to receive commands,
>>>>>> this has not been addressed unless I confused v7 and v8?
>>>>> Yes, it's been added on v8. 0x5d is init for the laptops it is
>>>>> problematic for. Not because it does not work, but because it has not
>>>>> been verified to work for those laptops.
>>>> I am not sure I am reading this right:
>>>> are you telling me that on recent models the windows driver
>>>> doesn't issue 0x5d?
>>> 
>>> Try to add spaces in your replies. This is hard to follow.
>>> 
>>> Do not conflate driver with software. 0x5a (over the application
>>> 0xff310076) has traditionally been used by a driver in Windows to
>>> control the backlight level, as it is done in this driver. 0x5d (over
>>> the application 0xff310079) is only used by laptops with RGB by
>>> Armoury crate. But this driver does not do RGB. No device
>>> functionality relies on it being sent for any device I've seen. The
>>> device remembers its Windows settings, incl. the backlight color, in
>>> the absence of a driver.
>>> 
>>> Laptops without RGB such as the Duo series which I would like to add
>>> support for next only have a 0x5a endpoint. But, they are sent garbage
>>> inits for 0x5d and 0x5e currently. This should be fixed.
>>> 
>>> Moreso, it seems that Armoury crate on the Xbox Ally series uses
>>> exclusively 0x5a commands and if you use 0x5d it ignores them (perhaps
>>> RGB still works though). With the previous generation, commands worked
>>> for either report id.
>>> 
>>>>>>> @Denis: can give an ack if this is the case?
>>>>>>> 
>>>>>>> As for Derek's comment, he has a PR for his project where he removes
>>>>>>> the name match for Ally devices with ample time for it to be merged
>>>>>>> until kernel 6.19 is released. In addition, that specific software for
>>>>>>> full functionality relies on OOT drivers on the distros it ships with,
>>>>>>> so it is minimally affected in either case.
>>>>>> The part we are talking about depends on this driver (hid-asus)
>>>>>> and there are people on asus-linux community using inputplumber
>>>>>> for non-ally devices (the OOT driver is only for ally devices)
>>>>>> therefore it is very important to us (and various other distributions)
>>>>>> not to break that software in any way.
>>>>> This driver is only used for Ally devices. If you mean that people
>>>>> remap their keyboards using inputplumber I guess they could but I have
>>>>> not seen it.
>>>> I meant people remap keyboards using IP. I am sure there were
>>>> (and very probably still are) people doing that.
>>>>>> Weighting pros and cons of changing the name I am not sure
>>>>>> changing name brings any benefit? Or am I missing something here?
>>>>>> It's simply used by userspace so the hardware should be loading
>>>>>> regardless of the name...
>>>>> Users see the name of their devices in their settings menu. They
>>>>> should be accurate. Also, the early entry needs to be added anyway to
>>>>> prevent kicking devices.
>>>> If it's just aesthetics I don't see much reasons in changing the name.
>>>> 
>>>> "the early entry needs to be added anyway ...." has no meaning to me,
>>>> please rephrase. Sorry.
>>> 
>>> Early exit-
>>> 
>>>>>> Along with IP and your tool and asusctl there is also openrgb,
>>>>>> and a newborn application for asus devices (I don't have contacts
>>>>>> with that dev nor I remember the name of the tool)
>>>>>> and I am not even that sure these are all asus-related
>>>>>> applications.
>>>>> My tool never checked for names, it briefly did for around a month
>>>>> after its creation for some devices until capability matches. Around
>>>>> 6.1 and 6.7 the kernel changed the names of most USB devices and that
>>>>> caused issues. It currently only uses name matches for VID/PID 0/0
>>>>> devices created by the kernel. Specifically, WMI and AT Keyboards. I
>>>>> am not sure there is a workaround for those. Asusctl also does not use
>>>>> names either.
>>>> But IP does, so I would like to hear confirmation from at least Derek
>>>> before the merge that there won't be future issues.
>>>> 
>>>> Interpret what I say here as a broad topic, not just name/PER_APP flag:
>>>> avoid changing data flow on older models...
>>> 
>>> In [1] Derek removes the name matches
>>> 
>>> There are no other name matches concerning this driver in it.
>>> 
>>> The data flow is not changed in this series; you should go through the
>>> patches once again if you think that. The only difference is 0x5e is
>>> not sent, and 0x5d is not sent for newer devices.
>>> 
>>> [1] https://github.com/ShadowBlip/InputPlumber/pull/453
>>> 
>>>>>> Excercise EXTRA care touching this area as these are enough changes
>>>>>> to make it difficult to understand what exactly is the problem if
>>>>>> someone shows up with LEDs malfunctioning, laptop not entering sleep
>>>>>> anymore or something else entirely. Plus over time
>>>>>> ASUS has used various workarounds for windows problems
>>>>>> and I am not eager to find out what those are since there is only
>>>>>> a realistic way it's going to happen....
>>>>> These changes are not doing something extraordinary. It's just a minor cleanup.
>>>>> 
>>>>>>> Moreover, that specific commit is needed for Xbox Ally devices anyway,
>>>>>>> as the kernel kicks one of the RGB endpoints because it does not
>>>>>>> register an input device (the check skipped by early return) so
>>>>>>> userspace becomes unable to control RGB on a stock kernel
>>>>>>> (hidraw/hiddev nodes are gone). For more context here, this specific
>>>>>>> endpoint implements the RGB Lamparray protocol for Windows dynamic
>>>>>>> lighting, and I think in an attempt to make it work properly in
>>>>>>> Windows, Asus made it so Windows has to first disable dynamic lighting
>>>>>>> for armoury crate RGB commands to work (the 0x5a ones over the 0xff31
>>>>>>> hid page).

Ah… this was an annoyance. In the hid-asus driver I did I ended up defaulting the LED control to the lamp-array style because it enabled faster per-led control. Then on sleep/resume it applied static colour matching first LED to keep some consistency. It works fine and there is no noticeable delay between switching to/from since the LampArray commands are instant (no set/apply required).

Anyhow, that driver created proper new LED device just for LED control. But it could only do that by taking the Ally HID off of the current driver and managing the whole lot. The end result I thought was much cleaner and separated the actual endpoints out to specific functions instead of how the current driver takes *all* the endpoints and tries to work off usage pages or report ID only.

For example use endpoint 0x83 for configuration (of gamepad) and LED. 0x87 is typically keyboard press events etc.

It did make me wonder if a newer cleaner driver for new MCU 0x19b6 onwards would have worked better instead of trying to shoehorn stuff in to the current driver constantly. It’s dead easy to bring up a new driver for this as an experiment. Maybe both you and Denis could investigate doing so?


>>>>>> Yes once ASUS introduces something new it sticks with that for
>>>>>> future models so it's expected more and more laptops will have
>>>>>> the same problem: I am not questioning if these patches are needed
>>>>>> as they very clearly are; I am questioning if everything that these
>>>>>> patches are doing are worth doing and aren't breaking/regressing
>>>>>> either tools or the flow of actions between the EC and these USB devices.
>>>>> Well, this series is needed to account for that. Sending the disable
>>>>> command is out of scope for now though.
>>>> Here I apologize for confusion: my comments were mostly about
>>>> older models: I absolutely don't want to break those, but if you find a way
>>>> to distinguish them from newer models that would give you more freedom with those.
>>> 
>>> Yes, we know their specific PIDs, so if you see the patch that adds
>>> the 0x5d init, it is only added for those models.
>> 
>> I’m only half keeping up to date on this. I do recall however that the 0x5D init was definitely required for the first ASUS laptop I worked on, and old GX502 - the PID for keyboard is 0x1866 and I think that was the last of that generation MCU. All the previous MCU also required it.
> 
> You recall if it was needed to enable the RGB commands or was it only
> for keyboard shortcuts? If it is needed for keyboard shortcuts it is
> correct for it to stay. If RGB does not turn on where it has been
> enabled before, it should also stay.

It was for shortcuts and the ROG buttons above the keyboard. There may have been some laptops using the same MCU that required it to enable LEDs too.

> 
>> I saw some messages in perhaps another thread where it was mentioned that 0x5E init should be removed? That I agreed with that?
>> I know there are AniMe and Slash versions that use that init, and they are on the same MCU as the keyboard. I had expected that just one init (on 0x5A or whatever) would work but it doesn’t - what I don’t recall is if an incomplete init affected the keyboard features.
> 
> Well, the way these devices work is that there are three hiddev
> devices, usually nested within the same hid endpoint under up to three
> collections. Each has one report ID. 0x5a is for brightness controls,
> 0x5d is for RGB, and 0x5e is for anime. For the first two, I know the
> usages are 76 and 79 (see above). I am not sure what the usage for
> anime is because I do not have a hid descriptor for that device.
> 
> In order to start talking to one of the hiddev devices, you are
> supposed to start with an init. The init is bidirectional, so after
> reading it back software knows it is talking to an Asus device (as it
> is done in this series). Likewise, even though it is not the case for
> all MCUs, the MCUs themselves can use it to verify they are talking to
> an Asus application (as you said) and reject commands if it is not
> sent.

Yes, I know. 
Before I stopped on all this I built up a rather large (untidy) collection of dumps for various things here https://gitlab.com/asus-linux/reverse-engineering/-/tree/master

> 
> For this reason, I think it is a good idea before asusctl starts
> controlling RGB, to always start with a 0x5d init to verify it is
> talking to an Asus device. And before Anime, with a 0x5e init (if the
> specific application for it is available). So since Dennis you are the
> new maintainer, you should try to work that in. Sending it twice does
> not hurt, even if not ideal.
> 
> Similarly, because this driver does not do Anime currently, there is
> no reason for it to send 0x5e. It also does not do RGB, so there is no
> reason to send 0x5D (unless not sending it causes issues). For the RGB
> patch I did, I delayed the init purposely until userspace interacts
> with the sysfs RGB endpoint, partly to interfere with userspace
> software less as well. So if the user does not use the sysfs RGB e.g.
> asusctl is completely unaffected.
> 
>> In all reality unless the full set of init is causing issues it’s best to leave them in. If it is then I guess this driver is going to become a little more complex and have a few more quirks.
>> 
>> Unfortunately I didn’t keep good records of my findings on this so it’s just my remembered observations that you’ll have to take at my word.
>> 
>> It would be a good idea for you both to perhaps collaborate with Sergei from ghelper, he has put a huge amount of effort in to that tool and due to it being windows he gets a hell of a lot more use and bug reports/data than this driver does. There’s no shame in looking to others for inspiration, ideas, or guidance.
> 
> Good idea. From a quick look, indeed slash/anime is 0x5e. We could
> interact with him more in the future.
> 
> Although looking into it, to find the correct endpoint he does a dirty
> check for report length being more than 128[1]. SIgh

Sergei would appreciate any friendly hints for sure. He’s a very nice guy.

> 
> I think it would be productive to try to merge this for 6.19. So
> Dennis can you try to be a bit more cooperative?

He’s not being intentionally recalcitrant. You both have vested interest in the outcomes of this review process and from what I’ve seen he has provided some excellent feedback.  If something isn’t going the way you want it doesn’t mean it’s personal. You will both converge on acceptable solutions through good faith and communication.

I’ll try to get a look in early on the next patch version and help a little if I can - it would be good to get this work in kernel and you both build off it.

> 
> I already have 6 more patches for duo keyboards. Although the keyboard
> brightness button on those seems to not work (?)[2]. I am waiting on a
> reply for that. Perhaps it uses a slightly different ID code. However,
> it seems that brightness works even when disconnecting and connecting
> the keyboard. Which is great.

Do the keys emit any codes? Maybe checking the raw output before it all gets filtered by the driver could help (like printing the raw array as hex) in asus_raw_event(). If there is nothing it could be emitting WMI events. ASUS did a dirty on some laptops and left the default WMI (I probably misremember, but ACPI event at least) in the ACPI, but made them emit nothing and used HID for brightness control instead.

That was this patch: https://github.com/torvalds/linux/commit/a720dee5e039238a44c0142dfccdc0e35c1125f7

Seems likely that because it appears to be a single button brightness cycle it could be a new code. In any case, debug printing the raw array as hex will show it if it’s being emitted.

While I remember, if you ever start playing with per-key RGB I mapped a lot of laptops https://gitlab.com/asus-linux/reverse-engineering/-/blob/master/keyboard/per_key_raw_bytes.ods?ref_type=heads - something to note is that each packet takes 1ms, but due to kernel internals it may attempt a burst of a few, or there could be up to 5ms delay. So a full sequence per row can be 8-20ms or more.

Oh, small reminder: if any patch changes dramatically from what I reviewed my tags should be removed.

> 
> Antheas
> 
> [1] https://github.com/seerge/g-helper/blob/610b11749b4da97346012e5d47f0a9bbc93b94af/app/AnimeMatrix/Communication/Platform/WindowsUsbProvider.cs#L37
> [2] https://github.com/bazzite-org/kernel-bazzite/issues/35
> 
>> Cheers,
>> Luke.
>> 
>>> 
>>>> No disable commands unless we find hard evidence those are strictly needed.
>>> 
>>> They are needed for the Xbox Ally series, but since this driver does
>>> not do RGB it is out of scope.
>>> 
>>>>> Antheas
>>>>> 
>>>>>>> Hopefully this clears things up
>>>>>>> 
>>>>>>> Antheas
>>>>>>> 
>>>>>>>>> Unrelated but I was b4ing this series on Ubuntu 24 and got BADSIG:
>>>>>>>>> DKIM/antheas.dev. Is there a reference for fixing this on my host?
>>>>>>>>> Perhaps it would help with spam
>>>>>>>> I see BADSIG very often these days from b4 (thanks to gmail expiring
>>>>>>>> things after 7 days or so, I recall hearing somewhere), I just ignore them
>>>>>>>> entirely.
>>>>>>>> 
>>>>>>>> AFAIK, that has never caused any delay to any patch in pdx86 domain so if
>>>>>>>> that is what you thought is happening here, it's not the case.
>>>>>>>> If your patch does appear in the pdx86 patchwork, there's even less reason
>>>>>>>> to worry as I mostly pick patches to process using patchwork's list.
>>>>>>> Turns out I had to update my DNS records. It should be good now.
>>>>>>> 
>>>>>>>> --
>>>>>>>> i.
>>>>>> snipp
>>>>>>>>>> 2.51.2
>> 
>> 
>> 
> 


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox