[<prev] [next>] [day] [month] [year] [list]
Message-ID: <tencent_87671B2466EFC9AEF481CDD1412057445506@qq.com>
Date: Sun, 16 Nov 2025 21:38:31 +0800
From: 2724853925@...com
To: Dmitry Torokhov <dmitry.torokhov@...il.com>,
Henrik Rydberg <rydberg@...math.org>
Cc: linux-input@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-gpio@...r.kernel.org,
2724853925@...com
Subject: [PATCH] input: touchscreen: Add ilitek touchscreen driver support
From: weisicheng <2724853925@...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@...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@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.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@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.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@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...tek.com>
+ * Copyright (c) 2022 Joe Hung <joe_hung@...tek.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@...Hc>ZFflPOG_>^}{sH8{XrBE*K_md<!NeuG
f)w8dM-eF8(e4Ne9!oaZdltBfE57ScAbow{|sQnt1
literal 0
HcmV?d00001
--
2.25.1
Powered by blists - more mailing lists