From nobody Sun Feb 8 22:17:55 2026 Received: from out162-62-57-49.mail.qq.com (out162-62-57-49.mail.qq.com [162.62.57.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D5BB22F7AAF; Sun, 16 Nov 2025 13:49:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=162.62.57.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763301013; cv=none; b=td3bNemEhpy/3/ZoYqB292QOSDHw+FMWV9/gbgAWWNz58xBFMCZZqwNx1jHMZQbi0kCq3sTKIUQFPMvxuXDunDVle99fiu9RsFk+H+/A1GYxWoj88aALaaG8Pq6CpQEXIXNE54krGVDup0wiPh0R2altpP9Lb1DTfwthg3nqakw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763301013; c=relaxed/simple; bh=Z8NdLH/on83SRs6ddzs5pUDmVTkGKbcx9gQ5ZntZyJE=; h=Message-ID:From:To:Cc:Subject:Date:MIME-Version:Content-Type; b=eXZhHHLm//iObgX4Px0lNRcaw1uPFDnC5l3djFhWkAk4Ks9vAEBUYPUcH1Jc1RX8sK4NadG5T1mPxCeotLYDQ9K2RG874+XNKdbow95TxAsboaNuCiEgC29nzPA7nrD1b3Hr5rGzWIRZbU2zbWa+A3JcdGsZ1S6R5WyUGp/0Di8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=qq.com; spf=pass smtp.mailfrom=qq.com; dkim=pass (1024-bit key) header.d=qq.com header.i=@qq.com header.b=IVPgEryX; arc=none smtp.client-ip=162.62.57.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=qq.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=qq.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=qq.com header.i=@qq.com header.b="IVPgEryX" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s201512; t=1763300969; bh=V9KamUA25dismNhaalQDjQ6T2C/1+t8laxrNNCjHOjw=; h=From:To:Cc:Subject:Date; b=IVPgEryXrV0PpgqEiK0Xoa06V97oMsggy/4WtxIwKt2OrIBRpMCE5ODvfllVkfChH qNkHIxMRLlfNsI/eQXdVS7vDFtinK3RUC3OC358wxz2rFecDgUu9sL5N5yNVL77Cvj 6NL3Qs9K9A1lF22HoHw7ld2ebK02fDM2LRgAREMs= Received: from ubuntu.localdomain ([223.166.202.212]) by newxmesmtplogicsvrsza63-0.qq.com (NewEsmtp) with SMTP id C5BB7869; Sun, 16 Nov 2025 21:49:27 +0800 X-QQ-mid: xmsmtpt1763300967tn09cdeqi Message-ID: X-QQ-XMAILINFO: OVFdYp27KdlJI0zjrUX7zgHp0MT4fJOzJms16Xbt7saJ7jGdQ0+sz5HPWYlMpq oKB+BcPErt3ACaFl8PDPldJ04C91MAe4keBUcQJ2aYsHRvmkFCyWD3voRtaGqfRmQKYkR8q1V4ow p4BXOCvqDTKRq0BXd1dNQSfukvTPA9gFetYoB1OAX+c7LrrnppHb4MmSzwwmU7lzbksRf++lCPvQ yShFmPugnHNwDhQecGJE/2dc5juqepqV1C4arnRbM0S9bbuDqAoD7NplD9dJyihu793+X1EByHGd xxvgFCAIgdxth6R/YnNi1QvsOj2uiTmBMkN03bRfFV9CS/0FSYcxxITXTDHNBXgWKc8lT54sip0o fPGUtQkLneBWcmrxKbVYg9kHLpHnCDkmSYNlBdamVZxBH+4zYpD0D4BEc/iJA1m9/eJrYCRZAvzJ B8Zw+VPIQMD2KhOJyMEERQ/E3PXF5pVMQmz3H8ANAceSv6PR047PgkWU2PNSHPYFQtf9YwZrBrez 7zYeKwWl340AOl3A7gg7A01C8Xnqp9U+KfvLJdnozNmueQGwMPxck+cDOwiSQREBUHrxvUNuNNLb dIQ2/F7H+9o+uNa58e3U5hFBMKuYhi/tQcGJqJCDB5jkOO9dtGUn070SwManGvatzy/PASc8LgLb NWX6JsByFe8IzN39++ySep8XxSm8a7PY2ypPqNSk1P4W26GcPnoX+avmn+Lo+/FkH9nRs2vqfKGa ic79LDKaoBIUOBUUwPNQAIpOAAhW13SrrF/7lzuYeYwwzdQRTZiK68x+6DljD3hBaLXQIcpLTaJ1 4dQZ5ie1QAD9ieb79xgqGFlrWn7NIF/jXsOdfFx9mIYYCYrpLi/Qdnl1v9eV/qhcwl134kD47jay Hu40EtQKxNtnT5QqYNpoY0ysDJayQ/mAnBjcr6cfA9SsRsZVr68XfCZZZpPclvZY9yCj/BGzB7k2 jkMq1ZlhP+4/0NYviqvupqcQWFVHQrkfjoMuflywTEi9R8UNZZBbIHfmzXuVgaAiJR2i46ZO3KPr SNk6AhfkgE0nEsrJ3jigVo8UPswlaWMbX9R6Ff70pUyC+KWepzzK+8edt7drwD6/S3qsEcIPZPxL Nn137Imch7g8JhBNlhPDK0vKNZIQbdHSH5mv5fAqpzNJGuvOs= X-QQ-XMRINFO: MSVp+SPm3vtS1Vd6Y4Mggwc= From: 2724853925@qq.com To: Dmitry Torokhov , Henrik Rydberg Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, 2724853925@qq.com Subject: [PATCH] input: touchscreen: Add ilitek touchscreen driver support Date: Sun, 16 Nov 2025 21:49:24 +0800 X-OQ-MSGID: <20251116134924.411907-1-2724853925@qq.com> X-Mailer: git-send-email 2.25.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: weisicheng <2724853925@qq.com> Add basic support for Ilitek I2C touchscreen controllers (e.g., ILitek2511,= ILitek2701). This patch includes: 1. Kconfig entry for Ilitek driver configuration 2. Makefile entry to compile the ilitek driver module 3. Core driver files under drivers/input/touchscreen/ilitek/ (I2C communica= tion, touch event handling, power management) The driver is compatible with standard input subsystem and supports: - Multi-touch detection - Pressure sensing - Dynamic power saving mode Signed-off-by: weisicheng <2724853925@qq.com> --- drivers/input/touchscreen/Kconfig | 13 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ilitek/Makefile | 34 + .../input/touchscreen/ilitek/ilitek_common.h | 264 ++ .../input/touchscreen/ilitek/ilitek_crypto.c | 465 +++ .../input/touchscreen/ilitek/ilitek_crypto.h | 61 + drivers/input/touchscreen/ilitek/ilitek_def.c | 235 ++ drivers/input/touchscreen/ilitek/ilitek_def.h | 554 +++ .../input/touchscreen/ilitek/ilitek_main.c | 2198 ++++++++++ .../touchscreen/ilitek/ilitek_platform_init.c | 404 ++ .../touchscreen/ilitek/ilitek_protocol.c | 3644 +++++++++++++++++ .../touchscreen/ilitek/ilitek_protocol.h | 916 +++++ .../input/touchscreen/ilitek/ilitek_report.c | 455 ++ .../input/touchscreen/ilitek/ilitek_report.h | 78 + .../input/touchscreen/ilitek/ilitek_tool.c | 1156 ++++++ drivers/input/touchscreen/ilitek/ilitek_ts.h | 268 ++ .../input/touchscreen/ilitek/ilitek_update.c | 1657 ++++++++ .../input/touchscreen/ilitek/ilitek_update.h | 199 + ...344\272\244\346\265\201\347\250\213 .docx" | Bin 0 -> 162 bytes 19 files changed, 12602 insertions(+) create mode 100644 drivers/input/touchscreen/ilitek/Makefile create mode 100644 drivers/input/touchscreen/ilitek/ilitek_common.h create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_crypto.h create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_def.h create mode 100644 drivers/input/touchscreen/ilitek/ilitek_main.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_platform_init.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_protocol.h create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_report.h create mode 100644 drivers/input/touchscreen/ilitek/ilitek_tool.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_ts.h create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.c create mode 100644 drivers/input/touchscreen/ilitek/ilitek_update.h create mode 100644 "drivers/input/touchscreen/ilitek/~$inline \344\273\243= \347\240\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/= Kconfig index 7d5b72ee07fa..3552629c4d4d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -515,6 +515,19 @@ config TOUCHSCREEN_ILITEK To compile this driver as a module, choose M here: the module will be called ilitek_ts_i2c. =20 +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) +=3D hynitron_c= stxxx.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X) +=3D hynitron-cst816x.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) +=3D ili210x.o obj-$(CONFIG_TOUCHSCREEN_ILITEK) +=3D ilitek_ts_i2c.o +obj-$(CONFIG_TOUCHSCREEN_ILI2511) +=3D ilitek/ obj-$(CONFIG_TOUCHSCREEN_IMAGIS) +=3D imagis.o obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) +=3D imx6ul_tsc.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) +=3D inexio.o diff --git a/drivers/input/touchscreen/ilitek/Makefile b/drivers/input/touc= hscreen/ilitek/Makefile new file mode 100644 index 000000000000..c912438a3b3e --- /dev/null +++ b/drivers/input/touchscreen/ilitek/Makefile @@ -0,0 +1,34 @@ +ccflags-y +=3D -Wall + +ifndef __KERNEL__ +ccflags-y +=3D -D__KERNEL__ +endif + +#Un-mark below item to enable FW upgrade on boot +#ccflags-y +=3D -DILITEK_BOOT_UPDATE + +ILITEK_INTERFACE =3D i2c +#ILITEK_INTERFACE =3D spi + + +ifeq ($(ILITEK_INTERFACE), spi) +$(info start to build ilitek SPI driver) +ccflags-y +=3D -DILITEK_SPI_INTERFACE +ilitek :=3D ilitek_spi +else +$(info start to build ilitek I2C driver) +ilitek :=3D ilitek_i2c +endif + +$(ilitek)-objs +=3D \ + 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) +=3D $(ilitek).o + diff --git a/drivers/input/touchscreen/ilitek/ilitek_common.h b/drivers/inp= ut/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 + * + * 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 +#include + +#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)] =3D (val); \ + } while (0) + +/* i2c clock rate for rk3288 */ +#if ILITEK_PLAT =3D=3D ILITEK_PLAT_ROCKCHIP && \ + KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE +#define SCL_RATE(rate) .scl_rate =3D (rate), +#else +#define SCL_RATE(rate) +#endif + +/* netlink */ +#if KERNEL_VERSION(3, 6, 0) <=3D LINUX_VERSION_CODE +#define NETLINK_KERNEL_CFG_DECLARE(cfg, func) \ + struct netlink_kernel_cfg cfg =3D { \ + .groups =3D 0, \ + .input =3D func, \ + } +#if KERNEL_VERSION(3, 7, 0) <=3D 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) <=3D 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) <=3D 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 =3D (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 =3D (val); return _val; }) +#endif + +enum ilitek_irq_handle_type { + irq_type_normal =3D 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/inp= ut/touchscreen/ilitek/ilitek_crypto.c new file mode 100644 index 000000000000..ed33defb57ed --- /dev/null +++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.c @@ -0,0 +1,465 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of ILITEK CommonFlow + * + * Copyright (c) 2022 ILI Technology Corp. + * Copyright (c) 2022 Luca Hsu + * Copyright (c) 2022 Joe Hung + */ + +#include "ilitek_crypto.h" + +/* + * The lookup-tables are marked const so they can be placed in read-only s= torage 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] =3D { + 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] =3D { + 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 fi= eld GF(2^8) + */ +static const u8 rcon[11] =3D { + 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 =3D (*state)[3][1]; + (*state)[3][1] =3D (*state)[2][1]; + (*state)[2][1] =3D (*state)[1][1]; + (*state)[1][1] =3D (*state)[0][1]; + (*state)[0][1] =3D tmp; + + /* Rotate second row 2 columns to right */ + tmp =3D (*state)[0][2]; + (*state)[0][2] =3D (*state)[2][2]; + (*state)[2][2] =3D tmp; + tmp =3D (*state)[1][2]; + (*state)[1][2] =3D (*state)[3][2]; + (*state)[3][2] =3D tmp; + + /* Rotate third row 3 columns to right */ + tmp =3D (*state)[0][3]; + (*state)[0][3] =3D (*state)[1][3]; + (*state)[1][3] =3D (*state)[2][3]; + (*state)[2][3] =3D (*state)[3][3]; + (*state)[3][3] =3D tmp; +} + +static void inv_sub_bytes(state_t *state) +{ + u8 i, j; + + for (i =3D 0; i < 4; ++i) + for (j =3D 0; j < 4; ++j) + (*state)[j][i] =3D 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 generatin= g a smaller binary + * The compiler seems to be able to vectorize the operation better t= his 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 =3D 0; i < 4; ++i) { + a =3D (*state)[i][0]; + b =3D (*state)[i][1]; + c =3D (*state)[i][2]; + d =3D (*state)[i][3]; + + (*state)[i][0] =3D multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ + multiply(c, 0x0d) ^ multiply(d, 0x09); + (*state)[i][1] =3D multiply(a, 0x09) ^ multiply(b, 0x0e) ^ + multiply(c, 0x0b) ^ multiply(d, 0x0d); + (*state)[i][2] =3D multiply(a, 0x0d) ^ multiply(b, 0x09) ^ + multiply(c, 0x0e) ^ multiply(d, 0x0b); + (*state)[i][3] =3D 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 =3D 0; i < 4; ++i) + for (j =3D 0; j < 4; ++j) + (*state)[i][j] ^=3D 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 =3D 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 =3D 0; i < Nk; ++i) { + round_key[(i * 4) + 0] =3D key[(i * 4) + 0]; + round_key[(i * 4) + 1] =3D key[(i * 4) + 1]; + round_key[(i * 4) + 2] =3D key[(i * 4) + 2]; + round_key[(i * 4) + 3] =3D key[(i * 4) + 3]; + } + + /* All other round keys are found from the previous round keys. */ + for (i =3D Nk; i < Nb * (Nr + 1); ++i) { + k =3D (i - 1) * 4; + tmp_a[0] =3D round_key[k + 0]; + tmp_a[1] =3D round_key[k + 1]; + tmp_a[2] =3D round_key[k + 2]; + tmp_a[3] =3D 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 =3D tmp_a[0]; + tmp_a[0] =3D tmp_a[1]; + tmp_a[1] =3D tmp_a[2]; + tmp_a[2] =3D tmp_a[3]; + tmp_a[3] =3D tmp_b; + + tmp_a[0] =3D sbox[tmp_a[0]]; + tmp_a[1] =3D sbox[tmp_a[1]]; + tmp_a[2] =3D sbox[tmp_a[2]]; + tmp_a[3] =3D sbox[tmp_a[3]]; + + tmp_a[0] =3D tmp_a[0] ^ rcon[i / Nk]; + } + + j =3D i * 4; + k =3D (i - Nk) * 4; + round_key[j + 0] =3D round_key[k + 0] ^ tmp_a[0]; + round_key[j + 1] =3D round_key[k + 1] ^ tmp_a[1]; + round_key[j + 2] =3D round_key[k + 2] ^ tmp_a[2]; + round_key[j + 3] =3D round_key[k + 3] ^ tmp_a[3]; + } +} + +u8 crypto_key[AES_KEY_LEN] =3D { 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, + 0xc, 0xd, 0xe, 0xf }; + +u8 crypto_iv[AES_KEY_LEN] =3D { 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 =3D 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 =3D 0; i < len; i +=3D AES_KEY_LEN, tmp +=3D AES_KEY_LEN) { + memcpy(iv_tmp, tmp, AES_KEY_LEN); + + aes_decrypt((state_t *)tmp, ctx.key_dec); + for (j =3D 0; j < AES_KEY_LEN; j++) + tmp[j] ^=3D iv[j]; + + memcpy(iv, iv_tmp, AES_KEY_LEN); + } +} + +static void sha256_init(struct ilitek_sha256_ctx *ctx) +{ + ctx->datalen =3D 0; + ctx->bitlen[0] =3D 0; + ctx->bitlen[1] =3D 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] =3D 0x6a09e667; + ctx->state[1] =3D 0xbb67ae85; + ctx->state[2] =3D 0x3c6ef372; + ctx->state[3] =3D 0xa54ff53a; + ctx->state[4] =3D 0x510e527f; + ctx->state[5] =3D 0x9b05688c; + ctx->state[6] =3D 0x1f83d9ab; + ctx->state[7] =3D 0x5be0cd19; +} + +static void sha256_transform(struct ilitek_sha256_ctx *ctx, u8 *data) +{ + static u32 key[64] =3D { + 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 =3D 0, j =3D 0; i < 16; ++i, j +=3D 4) + m[i] =3D (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (d= ata[j + 3]); + + for (; i < 64; ++i) + m[i] =3D SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a =3D ctx->state[0]; + b =3D ctx->state[1]; + c =3D ctx->state[2]; + d =3D ctx->state[3]; + e =3D ctx->state[4]; + f =3D ctx->state[5]; + g =3D ctx->state[6]; + h =3D ctx->state[7]; + + for (i =3D 0; i < 64; ++i) { + t1 =3D h + EP1(e) + CH(e, f, g) + key[i] + m[i]; + t2 =3D EP0(a) + MAJ(a, b, c); + h =3D g; + g =3D f; + f =3D e; + e =3D d + t1; + d =3D c; + c =3D b; + b =3D a; + a =3D t1 + t2; + } + + ctx->state[0] +=3D a; + ctx->state[1] +=3D b; + ctx->state[2] +=3D c; + ctx->state[3] +=3D d; + ctx->state[4] +=3D e; + ctx->state[5] +=3D f; + ctx->state[6] +=3D g; + ctx->state[7] +=3D h; +} + +static void sha256_update(struct ilitek_sha256_ctx *ctx, u8 byte) +{ + ctx->data[ctx->datalen] =3D byte; + ctx->datalen++; + if (ctx->datalen =3D=3D 64) { + sha256_transform(ctx, ctx->data); + DBL_INT_ADD(ctx->bitlen[0], ctx->bitlen[1], 512); + ctx->datalen =3D 0; + } +} + +static void sha256_final(struct ilitek_sha256_ctx *ctx, u8 *hash) +{ + u32 i; + + i =3D ctx->datalen; + + /* Pad whatever data is left in the buffer. */ + if (ctx->datalen < 56) { + ctx->data[i++] =3D 0x80; + while (i < 56) + ctx->data[i++] =3D 0x00; + } else { + ctx->data[i++] =3D 0x80; + while (i < 64) + ctx->data[i++] =3D 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] =3D ctx->bitlen[0]; + ctx->data[62] =3D ctx->bitlen[0] >> 8; + ctx->data[61] =3D ctx->bitlen[0] >> 16; + ctx->data[60] =3D ctx->bitlen[0] >> 24; + ctx->data[59] =3D ctx->bitlen[1]; + ctx->data[58] =3D ctx->bitlen[1] >> 8; + ctx->data[57] =3D ctx->bitlen[1] >> 16; + ctx->data[56] =3D ctx->bitlen[1] >> 24; + sha256_transform(ctx, ctx->data); + + /* + * Since this implementation uses little endian byte ordering and SHA use= s big endian, + * reverse all the bytes when copying the final state to the output hash. + */ + for (i =3D 0; i < 4; ++i) { + hash[i] =3D (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] =3D (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] =3D (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] =3D (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] =3D (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] =3D (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] =3D (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] =3D (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} + +void get_sha256(u32 start, u32 end, u8 *buf, u32 buf_size, u8 sha256[32]) +{ + struct ilitek_sha256_ctx ctx; + u32 i; + + sha256_init(&ctx); + + if (end >=3D 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 =3D start; i <=3D 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/inp= ut/touchscreen/ilitek/ilitek_crypto.h new file mode 100644 index 000000000000..8085961df031 --- /dev/null +++ b/drivers/input/touchscreen/ilitek/ilitek_crypto.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of ILITEK CommonFlow + * + * Copyright (c) 2022 ILI Technology Corp. + * Copyright (c) 2022 Luca Hsu + * Copyright (c) 2022 Joe Hung + */ + +#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) +=3D (c); \ + } while (0) + +#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) +#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) + +#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) +#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) +#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) + +struct ilitek_sha256_ctx { + u8 data[64]; + u32 datalen; + u32 bitlen[2]; + u32 state[8]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void __DLL ilitek_decrypt(u8 *buf, u32 len); + +void __DLL get_sha256(u32 start, u32 end, + u8 *buf, u32 buf_size, u8 sha256[32]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/input/touchscreen/ilitek/ilitek_def.c b/drivers/input/= touchscreen/ilitek/ilitek_def.c new file mode 100644 index 000000000000..e9934cac240d --- /dev/null +++ b/drivers/input/touchscreen/ilitek/ilitek_def.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of ILITEK CommonFlow + * + * Copyright (c) 2022 ILI Technology Corp. + * Copyright (c) 2022 Luca Hsu + * Copyright (c) 2022 Joe Hung + */ + +#include "ilitek_def.h" + +int tp_log_level =3D log_level_msg; +bool tp_print_en =3D 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 =3D 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 =3D wtm.wYear - 1900; + tm.tm_mon =3D wtm.wMonth - 1; + tm.tm_mday =3D wtm.wDay; + tm.tm_hour =3D wtm.wHour; + tm.tm_min =3D wtm.wMinute; + tm.tm_sec =3D wtm.wSecond; + tm.tm_isdst =3D -1; + clock =3D mktime(&tm); + tp->tv_sec =3D (long)clock; + tp->tv_usec =3D 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 =3D t.tv_sec * 1000 + t.tv_usec / 1000; + time_ms_init =3D (!time_ms_init) ? time_ms : time_ms_init; + + *t_ms =3D 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 =3D 64; + int i, idx =3D 0; + u32 time_ms; + int error; + + if (level > tp_log_level || !buf) + return; + + error =3D 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 =3D 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 =3D 0; + + MUTEX_INIT(q->mutex); + + MUTEX_LOCK(q->mutex); + + do { + q->item_size =3D item_size; + q->curr_size =3D 0; + q->max_size =3D max_items; + + q->buf =3D (u8 *)CALLOC(max_items, item_size); + if (!q->buf) { + error =3D -ENOMEM; + break; + } + + q->push_ptr =3D q->buf; + q->pop_ptr =3D q->buf; + q->end_ptr =3D 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 >=3D q->max_size) + goto release_push_lock; + + q->curr_size++; + if (q->push_ptr =3D=3D q->end_ptr) + q->push_ptr =3D q->buf; + else + q->push_ptr +=3D q->item_size; + + if (q->push_ptr =3D=3D 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 =3D=3D q->end_ptr) + q->pop_ptr =3D q->buf; + else + q->pop_ptr +=3D q->item_size; + +release_pop_lock: + MUTEX_UNLOCK(q->mutex); +} + +void set_print_en(bool enable) +{ + tp_print_en =3D enable; +} + +void set_log_level(int level) +{ + tp_log_level =3D level; +} + +int set_log_fopen(WCHAR *filename) +{ + int error; + + if (tp_fp) + return -EINVAL; + + error =3D WFOPEN(&tp_fp, filename, "w+"); + if (error < 0) { + tp_fp =3D NULL; + return error; + } + + return 0; +} + +void set_log_fclose(void) +{ + if (!tp_fp) + return; + + _fclose(tp_fp); + tp_fp =3D 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 + * Copyright (c) 2022 Joe Hung + */ + +#ifndef __ILITEK_DEF_H__ +#define __ILITEK_DEF_H__ + +#define COMMONFLOW_CODE_VERSION 0x00000301 + +/* + * Windows + */ +#ifdef _WIN32 +#include +#include +#include +#include + +#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)) =3D _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 +#define TO_WCHAR(x) \ + std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t > ().from_b= ytes(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) =3D CreateMutex(NULL, false, NULL)) +#define MUTEX_LOCK(x) (WaitForSingleObject((x), INFINITE)) +#define MUTEX_UNLOCK(x) (ReleaseMutex((x))) +#define MUTEX_EXIT(x) (CloseHandle((x))) + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MALLOC(size) malloc(size) +#define CALLOC(num, size) calloc(num, size) +#define FREE(ptr) \ + do { \ + free((ptr)); \ + (ptr) =3D NULL; \ + } while (0) +#define CFREE(ptr) FREE(ptr) + +/* + * UEFI Driver + */ +#elif defined(__UEFI_DXE__) +#include +#include +#include +#include + +#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 =3D NULL; + + do { + if (*s =3D=3D c) + found =3D (char *)s; + } while (*s++ !=3D '\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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MALLOC(size) AllocatePool(size) +#define CALLOC(num, size) AllocateZeroPool(num * size) +#define FREE(ptr) \ + do { \ + FreePool((ptr)); \ + (ptr) =3D 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__ =3D 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 +#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 +#include +#include + +#define MALLOC(size) kmalloc(size, GFP_KERNEL) +#define CALLOC(num, size) vmalloc(num * size) +#define FREE(ptr) \ + do { \ + kfree((ptr)); \ + (ptr) =3D NULL; \ + } while (0) +#define CFREE(ptr) \ + do { \ + vfree((ptr)); \ + (ptr) =3D NULL; \ + } while (0) + +#define TP_PRINTF(fmt, ...) printk(fmt, ##__VA_ARGS__) +#define TP_LOG(fp, str) + +#else +#include +#include + +#define _fopen(pfp, filename, mode) \ + ((!((*(pfp)) =3D 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 +#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 +#include +#include +#include +#include +#include +#include +#include + +#define MALLOC(size) malloc(size) +#define CALLOC(num, size) calloc(num, size) +#define FREE(ptr) \ + do { \ + free((ptr)); \ + (ptr) =3D NULL; \ + } while (0) +#define CFREE(ptr) FREE(ptr) + +#ifdef __ANDROID__ +#include +#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__ =3D (_id); \ + u32 __time_ms__; \ + \ + if (level > tp_log_level) \ + break; \ + \ + g_str[0] =3D '\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 =3D -1, /* no log displayed */ + log_level_err =3D 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 =3D 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 + * + * 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[] =3D { + 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 =3D 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 i= gnore the packet"); + + +#if ILITEK_PLAT =3D=3D 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 =3D 100, ilitek_seq =3D 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 =3D NLMSG_SPACE(size); + int ret; + int pid =3D ilitek_pid, seq =3D ilitek_seq; + + TP_DBG(NULL, "[%s] ilitek_debug_flag: %d\n", __func__, ilitek_debug_flag); + if (!ilitek_debug_flag) + return; + + skb =3D alloc_skb(len, GFP_ATOMIC); + if (!skb) { + TP_ERR(NULL, "alloc skb error\n"); + return; + } + + nlh =3D nlmsg_put(skb, pid, seq, 0, size, 0); + if (!nlh) + goto nlmsg_failure; + + nlh->nlmsg_flags =3D 0; + memcpy(NLMSG_DATA(nlh), payload, size); + + NETLINK_CB(skb).portid =3D 0; /* from kernel */ + NETLINK_CB(skb).dst_group =3D 0; /* unicast */ + + ret =3D 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 =3D 0, ret =3D 0, i =3D 0; + u8 *data; + struct nlmsghdr *nlh; + + nlh =3D (struct nlmsghdr *)skb->data; + ilitek_pid =3D NETLINK_CREDS(skb)->pid; + ilitek_seq =3D nlh->nlmsg_seq; + + TP_DBG(NULL, "netlink received, pid: %d, seq: %d\n", + ilitek_pid, ilitek_seq); + + data =3D (u8 *) NLMSG_DATA(nlh); + count =3D nlmsg_len(nlh); + if (!strcmp(data, "Open!")) { + TP_MSG(NULL, "data is :%s\n", (char *)data); + ts->operation_protection =3D true; + ilitek_udp_reply(data, sizeof("Open!")); + } else if (!strcmp(data, "Close!")) { + TP_MSG(NULL, "data is :%s\n", (char *)data); + ts->operation_protection =3D false; + } else if (!strcmp(data, "Wifi_Paint_Start") || + !strcmp(data, "Daemon_Debug_Start")) { + ilitek_debug_flag =3D true; + } else if (!strcmp(data, "Wifi_Paint_End") || + !strcmp(data, "Daemon_Debug_End")) { + ilitek_debug_flag =3D false; + } + + + TP_DBG(NULL, "count =3D %d data[count -3] =3D %d data[count -2] =3D %c\n= ", + count, data[count - 3], data[count - 2]); + for (i =3D 0; i < count; i++) + //TP_MSG(NULL, "data[%d] =3D 0x%x\n", i, data[i]); + if (data[count - 2] =3D=3D 'I' && (count =3D=3D 20 || count =3D=3D 52) && + data[0] =3D=3D 0x77 && data[1] =3D=3D 0x77) { + TP_DBG(NULL, "IOCTL_WRITE CMD =3D %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 =3D %d.\n", data[3]); + if (data[3] =3D=3D 0) + ilitek_debug_flag =3D false; + else if (data[3] =3D=3D 1) + ilitek_debug_flag =3D true; + break; + case 15: + if (data[3] =3D=3D 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 =3D data[3]; + TP_MSG(NULL, "ts->operation_protection =3D %d\n", ts->operation_protect= ion); + 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 =3D ilitek_write(&data[3], 33); + if (ret < 0) + TP_ERR(NULL, "i2c write error, ret %d\n", ret); + if (ret < 0) + data[0] =3D 1; + else + data[0] =3D 0; + ilitek_udp_reply(data, 1); + return; + default: + return; + } + } else if (data[count - 2] =3D=3D 'W') { + ret =3D ilitek_write(data, count - 2); + if (ret < 0) + TP_ERR(NULL, "i2c write error, ret %d\n", ret); + if (ret < 0) + data[0] =3D 1; + else + data[0] =3D 0; + ilitek_udp_reply(data, 1); + } else if (data[count - 2] =3D=3D 'R') { + ret =3D ilitek_read(data, count - 2); + if (ret < 0) + TP_ERR(NULL, "i2c read error, ret %d\n", ret); + if (ret < 0) + data[count - 2] =3D 1; + else + data[count - 2] =3D 0; + ilitek_udp_reply(data, count - 1); + } +#endif /* ILITEK_TUNING_MESSAGE */ +} + +static void ilitek_esd_check(struct work_struct *work) +{ + int retry =3D 3; + static bool is_first_run =3D true; + static u32 protocol_ver; + + /* + * update protocol version at the first run + */ + if (is_first_run) { + is_first_run =3D false; + protocol_ver =3D 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 !=3D 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 =3D (struct i2c_client *)ts->client; + int err; + + + if (len < 8) { + client->ext_flag =3D client->ext_flag & (~I2C_DMA_FLAG); + return i2c_master_recv(client, buf, len); + } + + client->ext_flag =3D client->ext_flag | I2C_DMA_FLAG; + err =3D 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 =3D (struct i2c_client *)ts->client; + + if (len <=3D 8) { + client->ext_flag =3D client->ext_flag & (~I2C_DMA_FLAG); + return i2c_master_send(client, cmd, len); + } + + memcpy(I2CDMABuf_va, cmd, len); + + client->ext_flag =3D 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 =3D 0; + struct i2c_client *client =3D (struct i2c_client *)ts->client; + int count =3D ILITEK_I2C_RETRY_COUNT; + +#ifdef ILITEK_ENABLE_DMA + int i; + + for (i =3D 0; i < cnt; i++) { + count =3D ILITEK_I2C_RETRY_COUNT; + while (count-- >=3D 0) { + msgs[i].ext_flag =3D 0; + if (msgs[i].flags =3D=3D I2C_M_RD) + err =3D ilitek_dma_i2c_read(msgs[i].buf, msgs[i].len); + else if (!msgs[i].flags) + err =3D 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-- >=3D 0) { + err =3D 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 =3D ILITEK_BL_ADDR; + struct i2c_client *client =3D (struct i2c_client *)ts->client; + struct i2c_msg msgs[2] =3D { + {.addr =3D addr, .flags =3D 0, .len =3D w_len, + .buf =3D cmd, SCL_RATE(400000)}, + {.addr =3D addr, .flags =3D I2C_M_RD, .len =3D r_len, + .buf =3D 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 =3D (addr =3D=3D ILITEK_BL_ADDR) ? + client->addr : ILITEK_BL_ADDR; + msgs[0].addr =3D msgs[1].addr =3D 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 =3D (addr =3D=3D ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR; + msgs[0].addr =3D msgs[1].addr =3D addr; + + error =3D 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 =3D (addr =3D=3D ILITEK_BL_ADDR) ? client->addr : ILITEK_BL_ADDR; + msgs[0].addr =3D msgs[1].addr =3D 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 =3D 0; + u8 *wbuf, *rbuf; + struct spi_device *spi =3D (struct spi_device *)ts->client; + struct spi_transfer xfer =3D { + .len =3D r_len + 4, + .speed_hz =3D ((struct spi_device *)ts->client)->max_speed_hz, + }; + struct spi_message msg; + + if (w_len > 0 && r_len > 0) { + error =3D 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 =3D CALLOC(4096, sizeof(u8)); + rbuf =3D CALLOC(4096, sizeof(u8)); + + if (!wbuf || !rbuf) { + error =3D -ENOMEM; + goto exit; + } + + xfer.tx_buf =3D wbuf; + xfer.rx_buf =3D rbuf; + + wbuf[1] =3D 0xAA; + + /* wbuf[0] set as 0x83 for spi data read */ + if (r_len > 0) { + wbuf[0] =3D 0x83; + memset(wbuf + 2, 0, xfer.len - 2); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + error =3D 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] =3D 0x82; + wbuf[2] =3D cmd[0]; + wbuf[3] =3D 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 =3D 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 =3D ilitek_spi_write(cmd, len); +#else + error =3D 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 =3D ilitek_spi_read(buf, len); +#else + error =3D 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 =3D ilitek_spi_write_and_read(cmd, w_len, delay_ms, buf, r_len); +#else + error =3D 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 =3D=3D 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 =3D=3D 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 =3D ts->dev->screen_info.pen_x_min; + int y_min =3D ts->dev->screen_info.pen_y_min; + int x_max =3D ts->dev->screen_info.pen_x_max; + int y_max =3D ts->dev->screen_info.pen_y_max; + + input =3D 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] =3D 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 =3D "ILITEK STYLUS"; + input->id.bustype =3D BUS_I2C; + input->dev.parent =3D 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 =3D 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 =3D 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 =3D tpd->dev; + if (!input) + return -ENOMEM; +#ifdef MTK_UNDTS + if (tpd_dts_data.use_tpd_button) { + for (i =3D 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 =3D ts->dev->screen_info.x_min; + int y_min =3D ts->dev->screen_info.y_min; + int x_max =3D ts->dev->screen_info.x_max; + int y_max =3D ts->dev->screen_info.y_max; + + input =3D 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 =3D 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 =3D ILITEK_TS_NAME; + input->id.bustype =3D BUS_I2C; + input->dev.parent =3D ts->device; + + __set_bit(INPUT_PROP_DIRECT, input->propbit); + input->evbit[0] =3D BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] =3D BIT_MASK(BTN_TOUCH); + +#ifdef ILITEK_USE_LCM_RESOLUTION + x_min =3D 0; y_min =3D 0; + x_max =3D TOUCH_SCREEN_X_MAX; y_max =3D 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 =3D 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 =3D 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 =3D ts->input_dev; + +#ifdef ILITEK_USE_LCM_RESOLUTION + x =3D (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX / + (ts->dev->screen_info.x_max - ts->dev->screen_info.x_min); + y =3D (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 =3D=3D ILITEK_PLAT_MTK +#ifdef CONFIG_MTK_BOOT +#ifndef MTK_UNDTS + if (tpd_dts_data.use_tpd_button) { + if (FACTORY_BOOT =3D=3D get_boot_mode() || RECOVERY_BOOT =3D=3D get_boot= _mode()) { + tpd_button(x, y, 1); + TP_DBG(NULL, "tpd_button(x, y, 1) =3D tpd_button(%d, %d, 1)\n", x, y); + } + } +#endif +#endif +#endif + return 0; +} + +static int ilitek_touch_release(int id) +{ + struct input_dev *input =3D ts->input_dev; + +#ifdef ILITEK_TOUCH_PROTOCOL_B + if (ts->touch_flag[id] =3D=3D 1) { + TP_DBG(NULL, "release point id =3D %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 =3D=3D ILITEK_PLAT_MTK +#ifdef CONFIG_MTK_BOOT +#ifndef MTK_UNDTS + if (tpd_dts_data.use_tpd_button) { + if (FACTORY_BOOT =3D=3D get_boot_mode() || + RECOVERY_BOOT =3D=3D get_boot_mode()) { + tpd_button(0, 0, 0); + TP_DBG(NULL, "tpd_button(x, y, 0) =3D tpd_button(0, 0, 0)\n"); + } + } +#endif +#endif +#endif + + return 0; +} + +static int ilitek_touch_release_all_point(void) +{ + struct input_dev *input =3D ts->input_dev; + int i =3D 0; + +#ifdef ILITEK_TOUCH_PROTOCOL_B + input_report_key(input, BTN_TOUCH, 0); + for (i =3D 0; i < ts->dev->tp_info.max_fingers; i++) + ilitek_touch_release(i); +#else + for (i =3D 0; i < ts->dev->tp_info.max_fingers; i++) + set_arr(ts->touch_flag, i, 0); + ilitek_touch_release(0); +#endif + ts->is_touched =3D false; + input_sync(input); + return 0; +} + +static int ilitek_check_key_down(int x, int y) +{ + int j; + + for (j =3D 0; j < ts->dev->tp_info.key_num; j++) { + if ((x >=3D ts->dev->key.info.keys[j].x && + x <=3D ts->dev->key.info.keys[j].x + + ts->dev->key.info.x_len) && + (y >=3D ts->dev->key.info.keys[j].y && + y <=3D ts->dev->key.info.keys[j].y + + ts->dev->key.info.y_len)) { +#if ILITEK_PLAT !=3D 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 =3D tpd_dts_data.tpd_key_dim_local[j].key_x; + y =3D tpd_dts_data.tpd_key_dim_local[j].key_y; + TP_DBG(NULL, "key index=3D%x, tpd_dts_data.tpd_key_local[%d]=3D%d key = down\n", + j, j, tpd_dts_data.tpd_key_local[j]); + ilitek_touch_down(0, x, y, 10, 128, 1); + } +#else + x =3D touch_key_point_maping_array[j].point_x; + y =3D touch_key_point_maping_array[j].point_y; + ilitek_touch_down(0, x, y, 10, 128, 1); +#endif +#endif + ts->dev->key.clicked[j] =3D true; + ts->touch_key_hold_press =3D true; + ts->is_touched =3D true; + TP_DBG(NULL, "Key, Keydown ID=3D%d, X=3D%d, Y=3D%d, key_status=3D%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 =3D 0; + + for (j =3D 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 !=3D 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=3D%x, tpd_dts_data.tpd_key_local[%d]=3D%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] =3D false; + ts->touch_key_hold_press =3D false; + TP_DBG(NULL, "Key, Keyout ID=3D%d, X=3D%d, Y=3D%d, key_status=3D%d\n", + ts->dev->key.info.keys[j].id, x, y, + ts->dev->key.clicked[j]); + break; + } + } else { +#if ILITEK_PLAT !=3D 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=3D%x, tpd_dts_data.tpd_key_local[%d]=3D%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] =3D false; + ts->touch_key_hold_press =3D false; + TP_DBG(NULL, "Key, Keyout ID=3D%d, X=3D%d, Y=3D%d, key_status=3D%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 =3D=3D 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 =3D 0; +#if ILITEK_GET_TIME_FUNC =3D=3D ILITEK_GET_TIME_FUNC_WITH_TIME + struct timeval time_now; + + do_gettimeofday(&time_now); + diff_milliseconds +=3D (time_now.tv_sec - start_event_time.tv_sec) * 1000; + + if (time_now.tv_usec < start_event_time.tv_usec) { + diff_milliseconds -=3D 1000; + diff_milliseconds +=3D (1000 * 1000 + time_now.tv_usec - start_event_tim= e.tv_usec) / 1000; + } else + diff_milliseconds +=3D (time_now.tv_usec - start_event_time.tv_usec) / 1= 000; + + if (diff_milliseconds < (-10000)) + diff_milliseconds =3D 10000; + TP_MSG(NULL, "time_now.tv_sec =3D %d start_event_time.tv_sec =3D %d time_= now.tv_usec =3D %d start_event_time.tv_usec =3D %d diff_milliseconds =3D %d= \n", + (int)time_now.tv_sec, (int)start_event_time.tv_sec, (int)time_now.tv_us= ec, (int)start_event_time.tv_usec, diff_milliseconds); +#else + diff_milliseconds =3D jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_= event_time_jiffies); + TP_MSG(NULL, "jiffies_to_msecs(jiffies) =3D %u jiffies_to_msecs(start_eve= nt_time_jiffies) =3D %u diff_milliseconds =3D %d\n", jiffies_to_msecs(jiffi= es), + 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 =3D %d\n", finger_state); + if (finger_id > 0) { + finger_state =3D 0; + goto out; + } + if (finger_state =3D=3D 0 || finger_state =3D=3D 5) { + + finger_state =3D 1; + start_x =3D x; + start_y =3D y; + current_x =3D 0; + current_y =3D 0; + event_spacing =3D 0; +#if ILITEK_GET_TIME_FUNC =3D=3D ILITEK_GET_TIME_FUNC_WITH_TIME + do_gettimeofday(&start_event_time); +#else + start_event_time_jiffies =3D jiffies; +#endif + } else if (finger_state =3D=3D 1) { + event_spacing =3D ilitek_get_time_diff(); + if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME) + finger_state =3D 4; + } else if (finger_state =3D=3D 2) { + finger_state =3D 3; + current_x =3D x; + current_y =3D y; + event_spacing =3D ilitek_get_time_diff(); + if (event_spacing > (DOUBLE_CLICK_ONE_CLICK_USED_TIME + DOUBLE_CLICK_NO_= TOUCH_TIME)) + finger_state =3D 0; + } else if (finger_state =3D=3D 3) { + current_x =3D x; + current_y =3D y; + event_spacing =3D ilitek_get_time_diff(); + if (event_spacing > DOUBLE_CLICK_TOTAL_USED_TIME) { + start_x =3D current_x; + start_y =3D current_y; + finger_state =3D 4; + } + } +out: + TP_MSG(NULL, "finger_state =3D %d event_spacing =3D %d\n", finger_state, = event_spacing); + return finger_state; +} + +static u8 ilitek_double_click_release(u8 finger_state) +{ + TP_MSG(NULL, "start finger_state =3D %d\n", finger_state); + if (finger_state =3D=3D 1) { + finger_state =3D 2; + event_spacing =3D ilitek_get_time_diff(); + if (event_spacing > DOUBLE_CLICK_ONE_CLICK_USED_TIME) + finger_state =3D 0; + } + if (finger_state =3D=3D 3) { + event_spacing =3D 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 =3D 5; + goto out; + } else + finger_state =3D 0; + } else if (finger_state =3D=3D 4) + finger_state =3D 0; +out: + TP_MSG(NULL, "finger_state =3D %d event_spacing =3D %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 =3D ts->input_dev; + + if (ts->gesture_status =3D=3D Gesture_Double_Click) { + if (touch) { + finger_state =3D ilitek_double_click_touch(idx, x, y, finger_state); + return; + } + finger_state =3D ilitek_double_click_release(finger_state); + + if (finger_state !=3D 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 =3D true; +} + +static void ilitek_report_touch_event(struct touch_data *touch, void *_pri= vate) +{ + struct input_dev *input =3D ts->input_dev; + u8 i, released_cnt =3D 0; + + u16 x_max =3D ts->dev->screen_info.x_max; + u16 x_min =3D ts->dev->screen_info.x_min; + u16 y_max =3D ts->dev->screen_info.y_max; + u16 y_min =3D 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 =3D 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 =3D (ILITEK_REVERT_X) ? + x_max - touch->finger[i].x + x_min : touch->finger[i].x; + touch->finger[i].y =3D (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 =3D 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 =3D=3D released_cnt) { + if (ts->is_touched) + ilitek_touch_release_all_point(); + + ilitek_check_key_release(0, 0, 0); + ts->is_touched =3D 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 =3D BTN_TOOL_PEN; + struct input_dev *pen_input =3D ts->pen_input_dev; + int tool; + + u16 x_max =3D ts->dev->screen_info.pen_x_max; + u16 x_min =3D ts->dev->screen_info.pen_x_min; + u16 y_max =3D ts->dev->screen_info.pen_y_max; + u16 y_min =3D ts->dev->screen_info.pen_y_min; + + if (!pen_input) + return; + + tool =3D (pen->pen.in_range && pen->pen.invert) ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + if (curr_tool !=3D tool) { + input_report_key(pen_input, curr_tool, 0); + input_sync(pen_input); + curr_tool =3D tool; + } + + pen->pen.x =3D (ILITEK_REVERT_X) ? + x_max - pen->pen.x + x_min : pen->pen.x; + pen->pen.y =3D (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 =3D ilitek_report_touch_event; + report.cb.report_pen_event =3D ilitek_report_pen_event; + report.cb.report_buf =3D ilitek_report_buf; + + switch (ts->irq_handle_type) { + case irq_type_c_model: + memset(ts->buf, 0, sizeof(ts->buf)); + + for (i =3D 0, count =3D 1; i < count; i++) { + ilitek_read(ts->buf, ts->irq_read_len); + ilitek_udp_reply(ts->buf, ts->irq_read_len); + count =3D ts->buf[ts->irq_read_len - 1]; + } + + break; + + case irq_type_debug: + case irq_type_normal: + default: + error =3D ilitek_report_update(ts->dev, &report); + if (error < 0) { + if (error =3D=3D -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 =3D 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 =3D 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 =3D 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_TYP= E, ilitek_i2c_isr, 1); + mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); +#else + +#if ILITEK_PLAT =3D=3D ILITEK_PLAT_MTK + struct device_node *node; + + node =3D of_find_matching_node(NULL, touch_of_match); + if (node) + ts->irq =3D irq_of_parse_and_map(node, 0); +#else + ts->irq =3D gpio_to_irq(ts->irq_gpio); +#endif + + TP_MSG(NULL, "ts->irq: %d\n", ts->irq); + if (ts->irq <=3D 0) + return -EINVAL; + + error =3D 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 =3D true; + atomic_set(&ts->irq_enabled, 1); + + return 0; +} + +static int ilitek_read_fw(char *filename, unsigned char *buf, int size, vo= id *data) +{ + int error, fw_size; + const struct firmware *fw; + struct device *device =3D (struct device *)data; + + error =3D 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 =3D -EFBIG; + goto release_fw; + } + + fw_size =3D fw->size; + memcpy(buf, fw->data, fw->size); + +release_fw: + release_firmware(fw); + + return fw_size; +} + +struct ilitek_update_callback update_cb =3D { + .read_fw =3D ilitek_read_fw, + .update_progress =3D NULL, + .update_fw_file_info =3D NULL, + + .slave_update_notify =3D NULL, + .update_fw_ic_info =3D 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 =3D true; + + handle =3D ilitek_update_init(ts->dev, false, &update_cb, ts->device); + + setting.force_update =3D false; + setting.fw_check_only =3D false; + setting.fw_ver_check =3D false; + setting.retry =3D 3; + ilitek_update_setting(handle, &setting); + + error =3D ilitek_update_load_fw(handle, filename); + if (error < 0) + goto err_return; + error =3D ilitek_update_start(handle); + if (error < 0) + goto err_return; + +err_return: + ilitek_update_exit(handle); + + ts->operation_protection =3D 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 =3D ilitek_upgrade_firmware("ilitek.ili"); + if (error < 0) { + error =3D ilitek_upgrade_firmware("ilitek.hex"); + if (error < 0) { + error =3D ilitek_upgrade_firmware("ilitek.bin"); + if (error < 0) + return error; + } + } + + error =3D 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 =3D 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 =3D (enable_irq_wake(ts->irq) =3D=3D 0); + + if (ts->low_power_status =3D=3D 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 =3D=3D 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 =3D false; + ts->system_suspend =3D 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 =3D=3D Low_Power_Idle) { + mutex_lock(&ts->ilitek_mutex); + api_set_idle(ts->dev, false); + mutex_unlock(&ts->ilitek_mutex); + } + + if (ts->gesture_status =3D=3D Gesture_Double_Click) + finger_state =3D 0; + + if (ts->wake_irq_enabled) { + disable_irq_wake(ts->irq); + ts->wake_irq_enabled =3D false; + } + } else { + if (ts->dev->protocol.flag =3D=3D 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 =3D=3D 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 =3D 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 =3D false; + + ilitek_irq_enable(); +} + +#if ILITEK_PLAT =3D=3D 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 !=3D 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 =3D data; +#else + struct fb_event *ev_data =3D data; +#endif + int *blank; + + TP_MSG(NULL, "FB EVENT event: %lu\n", event); + +#ifdef CONFIG_QCOM_DRM + if (!ev_data || (ev_data->id !=3D 0)) + return 0; +#endif + if (ev_data && ev_data->data && event =3D=3D ILITEK_EVENT_BLANK) { + blank =3D ev_data->data; + TP_MSG(NULL, "blank: %d\n", *blank); + if (*blank =3D=3D ILITEK_BLANK_POWERDOWN) { + ilitek_suspend(); + } else if (*blank =3D=3D ILITEK_BLANK_UNBLANK || + *blank =3D=3D 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 =3D=3D ILITEK_PLAT_ALLWIN + TP_MSG(NULL, "(config_info.wakeup_gpio.gpio) =3D %d (config_info.int_numb= er) =3D %d\n", (config_info.wakeup_gpio.gpio), (config_info.int_number)); + ts->reset_gpio =3D (config_info.wakeup_gpio.gpio); + ts->irq_gpio =3D (config_info.int_number); +#else +#ifdef CONFIG_OF + ts->reset_gpio =3D of_get_named_gpio(ts->device->of_node, "ilitek,reset-g= pio", 0); + if (ts->reset_gpio < 0) + TP_ERR(NULL, "reset_gpio =3D %d\n", ts->reset_gpio); + ts->irq_gpio =3D of_get_named_gpio(ts->device->of_node, "ilitek,irq-gpio"= , 0); + if (ts->irq_gpio < 0) + TP_ERR(NULL, "irq_gpio =3D %d\n", ts->irq_gpio); +#endif +#endif +#else + ts->reset_gpio =3D ILITEK_RESET_GPIO; + ts->irq_gpio =3D ILITEK_IRQ_GPIO; +#endif + + TP_MSG(NULL, "reset_gpio =3D %d irq_gpio =3D %d\n", ts->reset_gpio, ts->i= rq_gpio); + + +#if defined(ILITEK_GPIO_DEBUG) + do { + ts->test_gpio =3D of_get_named_gpio(ts->device->of_node, "ilitek,test-gp= io", 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 =3D 0; + + ts->irq_gpio =3D -ENODEV; + ts->reset_gpio =3D -ENODEV; + + ilitek_get_gpio_num(); + +#if ILITEK_PLAT !=3D ILITEK_PLAT_MTK + if (ts->reset_gpio > 0) { + ret =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D create_singlethread_workqueue("ilitek_esd_wq"); + if (!ts->esd_workq) + return -ENOMEM; + + ts->esd_skip =3D false; + ts->esd_delay =3D 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 =3D NULL; + } +} + +static int ilitek_register_resume_suspend(void) +{ +#ifdef ILITEK_REGISTER_SUSPEND_RESUME +#if ILITEK_PLAT !=3D ILITEK_PLAT_MTK +//#if defined(CONFIG_FB) || defined(CONFIG_QCOM_DRM) +#if defined(CONFIG_QCOM_DRM) + int error; + + ts->fb_notif.notifier_call =3D ilitek_notifier_callback; + +#ifdef CONFIG_QCOM_DRM + error =3D msm_drm_register_client(&ts->fb_notif); +#else + error =3D 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 =3D EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend =3D ilitek_early_suspend; + ts->early_suspend.resume =3D ilitek_late_resume; + register_early_suspend(&ts->early_suspend); +#endif +#endif /* ILITEK_PLAT !=3D ILITEK_PLAT_MTK */ + +#if ILITEK_PLAT =3D=3D 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 =3D 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 =3D NULL; +#endif +} + +static int __maybe_unused ilitek_alloc_dma(void) +{ +#ifdef ILITEK_ENABLE_DMA + tpd->dev->dev.coherent_dma_mask =3D DMA_BIT_MASK(32); + I2CDMABuf_va =3D (u8 *) dma_alloc_coherent(&tpd->dev->dev, ILITEK_DMA_SIZ= E, &I2CDMABuf_pa, GFP_KERNEL); + if (!I2CDMABuf_va) { + TP_ERR(NULL, "ilitek [TPD] tpd->dev->dev dma_alloc_coherent error\n"); + I2CDMABuf_va =3D (u8 *) dma_alloc_coherent(NULL, ILITEK_DMA_SIZE, &I2CDM= ABuf_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 =3D NULL; + I2CDMABuf_pa =3D 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 =3D=3D ILITEK_PLAT_ALLWIN + input_set_power_enable(&(config_info.input_type), status); +#else + + if (status) { + if (ts->vdd) { + error =3D regulator_enable(ts->vdd); + if (error < 0) { + TP_ERR(NULL, "regulator_enable vdd fail\n"); + return error; + } + } + if (ts->vdd_i2c) { + error =3D regulator_enable(ts->vdd_i2c); + if (error < 0) { + TP_ERR(NULL, "regulator_enable vdd_i2c fail\n"); + return error; + } + } + } else { + if (ts->vdd) { + error =3D regulator_disable(ts->vdd); + if (error < 0) { + TP_ERR(NULL, "regulator_enable vdd fail\n"); + return error; + } + } + if (ts->vdd_i2c) { + error =3D 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 =3D 0; + char *vdd_name =3D "vdd"; + char *vcc_i2c_name =3D "vcc_i2c"; + +#if ILITEK_PLAT =3D=3D ILITEK_PLAT_MTK + vdd_name =3D "vtouch"; + ts->vdd =3D regulator_get(tpd->tpd_dev, vdd_name); + tpd->reg =3D ts->vdd; + if (IS_ERR(ts->vdd)) { + TP_ERR(NULL, "regulator_get vdd fail\n"); + ts->vdd =3D NULL; + } else { + ret =3D regulator_set_voltage(ts->vdd, 3000000, 3300000); + if (ret) + TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n"); + } +#elif ILITEK_PLAT !=3D ILITEK_PLAT_ALLWIN + ts->vdd =3D regulator_get(ts->device, vdd_name); + if (IS_ERR(ts->vdd)) { + TP_ERR(NULL, "regulator_get vdd fail\n"); + ts->vdd =3D NULL; + } else { + ret =3D regulator_set_voltage(ts->vdd, 3000000, 3300000); + if (ret) + TP_ERR(NULL, "Could not set vdd to 3000~3300mv.\n"); + + } + + ts->vdd_i2c =3D 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 =3D NULL; + } else { + ret =3D regulator_set_voltage(ts->vdd_i2c, 3000000, 3300000); + if (ret) + TP_ERR(NULL, "Could not set i2c to 3000~3300mv.\n"); + } +#endif /* ILITEK_PLAT =3D=3D 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 !=3D ILITEK_P= LAT_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 =3D true; + atomic_set(&ts->get_INT, 0); +} + +static int _ilitek_wait_ack(u8 cmd, unsigned int tout_ms, void *data) +{ + unsigned int t_ms =3D 0; + int tmp, error =3D -ETIME; + + UNUSED(cmd); + + do { + tmp =3D atomic_read(&ts->get_INT); + if (tmp) { + error =3D 0; + break; + } + + udelay(1000); + t_ms++; + } while (t_ms < tout_ms); + + ts->unhandle_irq =3D 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 =3D { + .write_then_read =3D _ilitek_write_then_read, + .read_interrupt_in =3D ilitek_read_interrupt_in, + .init_ack =3D _ilitek_init_ack, + .wait_ack =3D _ilitek_wait_ack, + .hw_reset =3D _ilitek_reset, + .re_enum =3D NULL, + .delay_ms =3D _ilitek_delay, + .msg =3D NULL, + + .write_then_read_direct =3D NULL, + .mode_switch_notify =3D 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 =3D kzalloc(sizeof(*ts), GFP_KERNEL); + if (!ts) { + TP_ERR(NULL, "allocate ts failed\n"); + return -ENOMEM; + } + + ts->client =3D client; + ts->device =3D device; + + mutex_init(&ts->ilitek_mutex); + ts->unhandle_irq =3D false; + + ilitek_alloc_dma(); + ilitek_request_regulator(ts); + ilitek_power_on(true); + ilitek_request_gpio(); + + ilitek_reset(600); + + ts->dev =3D 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 =3D 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 =3D (ts->dev->protocol.flag =3D=3D PTL_V6) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + + if (ilitek_request_irq()) + goto err_dev_exit; + +#ifdef ILITEK_BOOT_UPDATE + ts->update_thread =3D 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 =3D ILITEK_ESD_CHECK_ENABLE; + if (ts->esd_check) + ilitek_create_esd_check_workqueue(); + + ts->gesture_status =3D ILITEK_GESTURE_DEFAULT; + if (ts->gesture_status) + ilitek_register_gesture(ts, true); + + ts->low_power_status =3D 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/driv= ers/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 + * + * 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 =3D=3D ILITEK_PLAT_MTK + +#define TPD_OK (0) + +#ifdef MTK_UNDTS +static int tpd_keys_local[TPD_KEY_COUNT] =3D TPD_KEYS; +static int tpd_keys_dim_local[TPD_KEY_COUNT][4] =3D TPD_KEYS_DIM; +struct touch_vitual_key_map_t touch_key_point_maping_array[] =3D { {key_1}= , {key_2}, {key_3}, {key_4} }; + +static struct i2c_board_info __initdata ilitek_i2c_tpd =3D { + 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 =3D 0; + + TP_MSG(NULL, "TPD probe\n"); + + if (!client) { + TP_ERR(NULL, "i2c client is NULL\n"); + return -1; + } + + ret =3D ilitek_main_probe(client, &client->dev); + if (ret =3D=3D 0) // If probe is success, then enable the below flag. + tpd_load_status =3D 1; + + TP_MSG(NULL, "TPD probe done\n"); + return TPD_OK; +} + +static int tpd_detect(struct i2c_client *client, struct i2c_board_info *in= fo) +{ + 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 driv= er. */ +static const struct i2c_device_id tpd_device_id[] =3D { + {ILITEK_TS_NAME, 0}, + {}, /* should not omitted */ +}; + +MODULE_DEVICE_TABLE(i2c, tpd_device_id); + +const struct of_device_id touch_dt_match_table[] =3D { + { .compatible =3D "mediatek,cap_touch",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, touch_dt_match_table); + +static struct i2c_driver tpd_i2c_driver =3D { + + .driver =3D { + .name =3D ILITEK_TS_NAME, + .of_match_table =3D of_match_ptr(touch_dt_match_table), + }, + .probe =3D tpd_probe, + .remove =3D tpd_remove, + .id_table =3D tpd_device_id, + .detect =3D tpd_detect, +}; + +static int tpd_local_init(void) +{ + TP_MSG(NULL, "TPD init device driver\n"); + + if (i2c_add_driver(&tpd_i2c_driver) !=3D 0) { + TP_ERR(NULL, "Unable to add i2c driver.\n"); + return -1; + } + if (tpd_load_status =3D=3D 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 =3D 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 =3D { + .tpd_device_name =3D ILITEK_TS_NAME, + .tpd_local_init =3D tpd_local_init, + .suspend =3D tpd_suspend, + .resume =3D tpd_resume, + .tpd_have_button =3D 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 =3D 8; + spi->mode =3D SPI_MODE_0; + error =3D 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[] =3D { + {.compatible =3D "tchip,ilitek",}, + {}, +}; +#endif + +static const struct i2c_device_id ilitek_touch_device_id[] =3D { + {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[] =3D { + {"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_resum= e); +#endif + +#ifdef ILITEK_SPI_INTERFACE +static struct spi_driver ilitek_touch_device_driver =3D { +#else +static struct i2c_driver ilitek_touch_device_driver =3D { +#endif + .driver =3D { + .name =3D ILITEK_TS_NAME, + .owner =3D THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table =3D ilitek_touch_match_table, +#endif +#ifdef CONFIG_ACPI + .acpi_match_table =3D ACPI_PTR(ilitekts_acpi_id), +#endif + +#ifndef ILITEK_REGISTER_SUSPEND_RESUME + .pm =3D &ilitek_pm_ops, +#endif + }, + + .probe =3D ilitek_touch_driver_probe, + .remove =3D ilitek_touch_driver_remove, + +#if !defined(ILITEK_SPI_INTERFACE) + .id_table =3D ilitek_touch_device_id, +#endif +}; + +#if ILITEK_PLAT =3D=3D ILITEK_PLAT_ALLWIN +static const unsigned short normal_i2c[2] =3D { 0x41, I2C_CLIENT_END }; +struct ctp_config_info config_info =3D { + .input_type =3D CTP_TYPE, + .name =3D NULL, + .int_number =3D 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 =3D config_info.twi_id; + screen_max_x =3D config_info.screen_max_x; + screen_max_y =3D config_info.screen_max_y; + TP_MSG(NULL, "Ilitek: screen_max_x =3D %d\n", screen_max_x); + revert_x_flag =3D config_info.revert_x_flag; + revert_y_flag =3D config_info.revert_y_flag; + exchange_x_y_flag =3D config_info.exchange_x_y_flag; + if ((screen_max_x =3D=3D 0) || (screen_max_y =3D=3D 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 =3D client->adapter; + + if (twi_id =3D=3D adapter->nr) { + strlcpy(info->type, ILITEK_TS_NAME, I2C_NAME_SIZE); + return 0; + } else { + return -ENODEV; + } +} + +static struct i2c_board_info i2c_info_dev =3D { + I2C_BOARD_INFO(ILITEK_TS_NAME, 0x41), + .platform_data =3D NULL, +}; + +static int add_ctp_device(void) +{ + struct i2c_adapter *adap; + //script_parser_fetch("ctp_para", "ctp_twi_id", &twi_id, 1); + adap =3D i2c_get_adapter(twi_id); + i2c_new_device(adap, &i2c_info_dev); + return 0; +} + +static int ilitek_init_allwin(void) +{ + + int ret =3D 0; + + if (input_fetch_sysconfig_para(&(config_info.input_type))) { + TP_ERR(NULL, "Ilitek: ctp_fetch_sysconfig_para err.\n"); + return -1; + } else { + ret =3D input_init_platform_resource(&(config_info.input_type)); + if (0 !=3D ret) + TP_ERR(NULL, "Ilitek: ctp_ops.init_platform_resource err.\n"); + } + + if (config_info.ctp_used =3D=3D 0) { + TP_ERR(NULL, "Ilitek: *** if use ctp,please put the sys_config.fex ctp_u= sed 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 =3D normal_i2c; + ilitek_touch_device_driver.detect =3D ctp_ts_detect; + return 0; + +} + +#endif +static int __init ilitek_touch_driver_init(void) +{ +#if ILITEK_PLAT =3D=3D 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/i= nput/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 + * Copyright (c) 2022 Joe Hung + */ + +#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[] =3D { 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 =3D 0; + + while (bytes--) + val +=3D (p[bytes] << (8 * bytes)); + + return val; +} + +u32 be32(const u8 *p, int bytes) +{ + u32 val =3D 0; + + while (bytes--) + val =3D (val << 8) | (*p++); + + return val; +} + +static bool is_2501x(void *handle) +{ + struct ilitek_ts_device *dev =3D (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 =3D (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 =3D (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 =3D (struct ilitek_ts_device *)handle; + + if (!handle || !dev->tp_info.key_num) + return false; + + if (dev->key.info.mode =3D=3D key_hw || + dev->key.info.mode =3D=3D key_hsw) + return true; + + return false; +} + +u8 get_protocol_ver_flag(u32 ver) +{ + if (((ver >> 16) & 0xFF) =3D=3D 0x3 || + (ver & 0xFFFF00) =3D=3D BL_PROTOCOL_V1_6 || + (ver & 0xFFFF00) =3D=3D BL_PROTOCOL_V1_7) + return PTL_V3; + + if (((ver >> 16) & 0xFF) =3D=3D 0x6 || + (ver & 0xFFFF00) =3D=3D BL_PROTOCOL_V1_8) + return PTL_V6; + + return PTL_ANY; +} + +void grid_reset(struct grids *grid) +{ + grid->mc.need_update =3D false; + grid->sc_x.need_update =3D false; + grid->sc_y.need_update =3D false; + grid->pen_x.need_update =3D false; + grid->pen_y.need_update =3D false; + + grid->key_mc.need_update =3D false; + grid->key_x.need_update =3D false; + grid->key_y.need_update =3D false; + + grid->self.need_update =3D 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 =3D false; + grid->dmsg.touch_need_update =3D 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 =3D (struct ilitek_ts_device *)handle; + int X, Y, key; + + _memset(grid, 0, sizeof(*grid)); + + if (!dev) + return -EINVAL; + + X =3D dev->tp_info.x_ch; + Y =3D dev->tp_info.y_ch; + key =3D dev->tp_info.key_num; + + grid->mc.data =3D (s32 *)CALLOC(X * Y, sizeof(s32)); + grid->sc_x.data =3D (s32 *)CALLOC(X, sizeof(s32)); + grid->sc_y.data =3D (s32 *)CALLOC(Y, sizeof(s32)); + grid->pen_x.data =3D (s32 *)CALLOC(X * 8, sizeof(s32)); + grid->pen_y.data =3D (s32 *)CALLOC(Y * 8, sizeof(s32)); + grid->key_mc.data =3D (s32 *)CALLOC(key, sizeof(s32)); + grid->key_x.data =3D (s32 *)CALLOC(key, sizeof(s32)); + grid->key_y.data =3D (s32 *)CALLOC(1, sizeof(s32)); + grid->self.data =3D (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 =3D X; grid->mc.Y =3D Y; + grid->sc_x.X =3D X; grid->sc_x.Y =3D 1; + grid->sc_y.X =3D 1; grid->sc_y.Y =3D Y; + grid->pen_x.X =3D X; grid->pen_x.Y =3D 8; + grid->pen_y.X =3D 8; grid->pen_y.Y =3D Y; + + grid->key_mc.X =3D key; grid->key_mc.Y =3D 1; + grid->key_x.X =3D key; grid->key_x.Y =3D 1; + grid->key_y.X =3D 1; grid->key_y.Y =3D 1; + + grid->self.X =3D 4, grid->self.Y =3D 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 =3D 0x8408; + + crc ^=3D newbyte; + + for (i =3D 0; i < 8; i++) { + if (crc & 0x01) + crc =3D (crc >> 1) ^ crc_poly; + else + crc =3D crc >> 1; + } + + return crc; +} + +u16 get_crc(u32 start, u32 end, + u8 *buf, u32 buf_size) +{ + u16 crc =3D 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 =3D start; i < end && i < buf_size; i++) + crc =3D update_crc(crc, buf[i]); + + return crc; +} + +u32 get_checksum(u32 start, u32 end, + u8 *buf, u32 buf_size) +{ + u32 sum =3D 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 =3D start; i < end && i < buf_size; i++) + sum +=3D buf[i]; + + return sum; +} + +bool is_checksum_matched(u8 checksum, int start, int end, + u8 *buf, int buf_size) +{ + u8 check; + + check =3D ~(get_checksum(start, end, buf, buf_size)) + 1; + if (check !=3D 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 =3D (struct ilitek_ts_device *)handle; + + if ((dev->ic[0].mode =3D=3D BL_MODE && dev->protocol.ver < 0x010803) || + (dev->ic[0].mode =3D=3D AP_MODE && dev->protocol.ver < 0x060009)) + return false; + + return true; +} + +bool support_sensor_id(void *handle) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + + if ((dev->ic[0].mode =3D=3D BL_MODE && dev->protocol.ver < 0x010803) || + (dev->ic[0].mode =3D=3D AP_MODE && dev->protocol.ver < 0x060004)) + return false; + + return true; +} + +bool support_production_info(void *handle) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + + if ((dev->ic[0].mode =3D=3D BL_MODE && dev->protocol.ver < 0x010803) || + (dev->ic[0].mode =3D=3D AP_MODE && dev->protocol.ver < 0x060007)) + return false; + + return true; +} + +bool support_fwid(void *handle) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + + if ((dev->ic[0].mode =3D=3D BL_MODE && dev->protocol.ver < 0x010802) || + (dev->ic[0].mode =3D=3D AP_MODE && dev->protocol.ver < 0x060007)) + return false; + + return true; +} + +bool support_power_status(void *handle) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + + if ((dev->ic[0].mode =3D=3D BL_MODE) || + (dev->ic[0].mode =3D=3D AP_MODE && dev->protocol.ver < 0x06000a)) + return false; + + return true; +} + +int bridge_set_int_monitor(void *handle, bool enable) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + u8 wbuf[64]; + + _memset(wbuf, 0, sizeof(wbuf)); + wbuf[0] =3D 0x03; + wbuf[1] =3D 0xf3; + wbuf[2] =3D (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 =3D (struct ilitek_ts_device *)handle; + u8 wbuf[64]; + + _memset(wbuf, 0, sizeof(wbuf)); + wbuf[0] =3D 0x03; + wbuf[1] =3D 0xf2; + wbuf[2] =3D (enable) ? 0x01 : 0x00; + + return write_then_read_direct(dev, wbuf, 3, NULL, 0); +} + +int reset_helper(void *handle) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + bool need_re_enum =3D true; + + if (dev->_interface =3D=3D 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 =3D 5; + + if (!dev->cb.re_enum) + return -EINVAL; + + do { + error =3D 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 =3D (struct ilitek_ts_device *)handle; + int error; + + if (!dev->cb.read_interrupt_in) + return -EINVAL; + + error =3D 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 =3D (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] =3D 0x03; + dev->wbuf[1] =3D 0xA4; + dev->wbuf[2] =3D 0; + dev->wbuf[3] =3D 0; + dev->wbuf[4] =3D (rlen + 6) & 0xFF; + dev->wbuf[5] =3D ((rlen + 6) >> 8) & 0xFF; + dev->wbuf[6] =3D cmd; + + error =3D dev->cb.write_then_read_direct(dev->wbuf, 64, + dev->rbuf, 64, dev->_private); + if (error < 0) + return error; + } + + error =3D 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 =3D (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] =3D 0x03; + dev->wbuf[1] =3D 0xA3; + dev->wbuf[2] =3D 0; + dev->wbuf[3] =3D rlen; + + error =3D write_then_read_direct(dev, dev->wbuf, 64, NULL, 0); + if (error < 0) + return error; + } + + error =3D 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 =3D (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 =3D 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 =3D (struct ilitek_ts_device *)handle; + struct ilitek_ts_callback *cb =3D &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 =3D 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 =3D 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 =3D write_then_read(dev, cmd, wlen, NULL, 0); + if (error < 0) + return error; + + ack_cmd =3D (cmd[0] =3D=3D CMD_ACCESS_SLAVE) ? cmd[2] : cmd[0]; + error =3D 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 *d= ata) +{ + int error; + struct ilitek_screen_info *screen_info; + + UNUSED(data); + + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 28); + if (error < 0) + return error; + + screen_info =3D (struct ilitek_screen_info *)dev->rbuf; + + dev->screen_info.x_min =3D screen_info->x_min; + dev->screen_info.y_min =3D screen_info->y_min; + dev->screen_info.x_max =3D screen_info->x_max; + dev->screen_info.y_max =3D screen_info->y_max; + + TP_DBG(dev->id, "screen x: " PFMT_U16 "~" PFMT_U16 ", screen y: " PFMT_U1= 6 "~" 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 =3D 0; + dev->screen_info.pressure_max =3D 0; + dev->screen_info.x_tilt_min =3D 0; + dev->screen_info.x_tilt_max =3D 0; + dev->screen_info.y_tilt_min =3D 0; + dev->screen_info.y_tilt_max =3D 0; + if (dev->protocol.ver > 0x60006) { + dev->screen_info.pressure_min =3D screen_info->pressure_min; + dev->screen_info.pressure_max =3D screen_info->pressure_max; + dev->screen_info.x_tilt_min =3D screen_info->x_tilt_min; + dev->screen_info.x_tilt_max =3D screen_info->x_tilt_max; + dev->screen_info.y_tilt_min =3D screen_info->y_tilt_min; + dev->screen_info.y_tilt_max =3D screen_info->y_tilt_max; + + dev->screen_info.pen_x_min =3D screen_info->pen_x_min; + dev->screen_info.pen_y_min =3D screen_info->pen_y_min; + dev->screen_info.pen_x_max =3D screen_info->pen_x_max; + dev->screen_info.pen_y_max =3D 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 =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 15); + if (error < 0) + return error; + + tp_info =3D (struct ilitek_tp_info_v3 *)dev->rbuf; + + dev->tp_info.block_num =3D 2; + dev->tp_info.x_resolution =3D tp_info->x_resolution; + dev->tp_info.y_resolution =3D tp_info->y_resolution; + dev->tp_info.x_ch =3D tp_info->x_ch; + dev->tp_info.y_ch =3D tp_info->y_ch; + dev->tp_info.max_fingers =3D tp_info->max_fingers; + dev->tp_info.key_num =3D tp_info->key_num; + + dev->tp_info.support_modes =3D tp_info->support_modes; + if (dev->tp_info.support_modes > 3 || !dev->tp_info.support_modes) + dev->tp_info.support_modes =3D 1; + + TP_DBG(dev->id, "touch ch.(start/end) x: " PFMT_U8 "/" PFMT_U8 ", y: " PF= MT_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 =3D + (tp_info->touch_start_y =3D=3D 0xff && + tp_info->touch_end_y =3D=3D 0xff && + tp_info->touch_start_x =3D=3D 0xff && + tp_info->touch_end_x =3D=3D 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[] =3D { STYLUS_MODES }; +#undef X + + UNUSED(data); + + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 21); + if (error < 0) + return error; + + tp_info =3D (struct ilitek_tp_info_v6 *)dev->rbuf; + dev->tp_info.x_resolution =3D tp_info->x_resolution; + dev->tp_info.y_resolution =3D tp_info->y_resolution; + dev->tp_info.x_ch =3D tp_info->x_ch; + dev->tp_info.y_ch =3D tp_info->y_ch; + dev->tp_info.max_fingers =3D tp_info->max_fingers; + dev->tp_info.key_num =3D tp_info->key_num; + dev->tp_info.ic_num =3D tp_info->ic_num; + dev->tp_info.format =3D tp_info->format; + dev->tp_info.support_modes =3D tp_info->support_modes; + dev->tp_info.die_num =3D tp_info->die_num; + + if (dev->tp_info.format =3D=3D 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 =3D 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 =3D 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 =3D 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 =3D tp_info->pen_format; + dev->tp_info.pen_x_resolution =3D tp_info->pen_x_resolution; + dev->tp_info.pen_y_resolution =3D 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 "/" P= FMT_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 *da= ta) +{ + int error; + +#define X(_enum, _id, _size, _cnt) \ + touch_fmts[_id].size =3D _size; \ + touch_fmts[_id].max_cnt =3D _cnt; + + ILITEK_TOUCH_REPORT_FORMAT; +#undef X + +#define X(_enum, _id, _size, _cnt) \ + pen_fmts[_id].size =3D _size; \ + pen_fmts[_id].max_cnt =3D _cnt; + + ILITEK_PEN_REPORT_FORMAT; +#undef X + + if (dev->protocol.flag =3D=3D PTL_V3) + error =3D api_protocol_get_tp_info_v3(dev, data); + else if (dev->protocol.flag =3D=3D PTL_V6) + error =3D 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 =3D touch_fmts[dev->tp_info.format].size; + dev->fmt.touch_max_cnt =3D 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 =3D touch_fmts[touch_fmt_0x0].size; + dev->fmt.touch_max_cnt =3D 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 =3D pen_fmts[dev->tp_info.pen_format].size; + dev->fmt.pen_max_cnt =3D pen_fmts[dev->tp_info.pen_format].max_cnt; + break; + default: + case pen_fmt_0x0: + dev->fmt.pen_size =3D pen_fmts[pen_fmt_0x0].size; + dev->fmt.pen_max_cnt =3D 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 =3D 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 !=3D 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 =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 29); + if (error < 0) + return error; + + for (i =3D 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 =3D write_then_read(dev, NULL, 0, + dev->rbuf + 29 + 5 * i, 25); + if (error < 0) + return error; + } + + key_info =3D (struct ilitek_key_info_v3 *)dev->rbuf; + dev->key.info.x_len =3D be16(key_info->x_len); + dev->key.info.y_len =3D 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 =3D 0; i < dev->tp_info.key_num; i++) { + dev->key.info.keys[i].id =3D key_info->keys[i].id; + dev->key.info.keys[i].x =3D be16(key_info->keys[i].x); + dev->key.info.keys[i].y =3D 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.ke= y_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 =3D write_then_read(dev, dev->wbuf, 1, NULL, 0); + if (error < 0) + return error; + error =3D read_ctrl_in(dev, CMD_GET_KEY_INFO, + dev->rbuf, + 5 + dev->tp_info.key_num * 5, 2000); + if (error < 0) + return error; + offset =3D (dev->quirks & QUIRK_BRIDGE) ? 1 : 0; + } else { + error =3D write_then_read(dev, dev->wbuf, 1, + dev->rbuf, 5 + dev->tp_info.key_num * 5); + if (error < 0) + return error; + offset =3D 0; + } + break; + + case interface_usb: + error =3D write_then_read(dev, dev->wbuf, 1, NULL, 0); + if (error < 0) + return error; + error =3D write_then_read(dev, NULL, 0, dev->rbuf, 256); + if (error < 0) + return error; + offset =3D 6; + break; + case interface_hid_over_i2c: + error =3D write_then_read(dev, dev->wbuf, 1, NULL, 0); + if (error < 0) + return error; + error =3D write_then_read(dev, NULL, 0, dev->rbuf, 256); + if (error < 0) + return error; + offset =3D 4; + break; + default: return -EINVAL; + }; + + key_info =3D (struct ilitek_key_info_v6 *)(dev->rbuf + offset); + dev->key.info.mode =3D key_info->mode; + TP_MSG(dev->id, "[Panel Information] key mode: " PFMT_U8 "\n", dev->key.i= nfo.mode); + + dev->key.info.x_len =3D key_info->x_len; + dev->key.info.y_len =3D 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 =3D 0; i < dev->tp_info.key_num; i++) { + dev->key.info.keys[i].id =3D key_info->keys[i].id; + dev->key.info.keys[i].x =3D key_info->keys[i].x; + dev->key.info.keys[i].y =3D 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 *d= ata) +{ + if (dev->protocol.flag =3D=3D PTL_V3) + return api_protocol_get_key_info_v3(dev, data); + else if (dev->protocol.flag =3D=3D 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 *da= ta) +{ + int error; + + UNUSED(data); + + dev->protocol.flag =3D PTL_V6; + dev->reset_time =3D 1000; + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3); + if (error < 0) + return error; + + dev->protocol.ver =3D (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 =3D get_protocol_ver_flag(dev->protocol.ver); + switch (dev->protocol.flag) { + case PTL_V3: + dev->reset_time =3D 200; + break; + case PTL_V6: + dev->reset_time =3D 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 *dat= a) +{ + int error; + + UNUSED(data); + + error =3D 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 =3D=3D 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 *d= ata) +{ + int error; + u8 i, ic_num =3D (data) ? *(u8 *)data : 1; + + if (ic_num > ARRAY_SIZE(dev->ic)) + return -EINVAL; + + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 2 * ic_num); + if (error < 0) + return error; + + for (i =3D 0; i < ic_num; i++) { + dev->ic[i].mode =3D dev->rbuf[i * 2]; + + if (dev->ic[i].mode =3D=3D AP_MODE) + _sprintf(dev->ic[i].mode_str, 0, "AP"); + else if (dev->ic[i].mode =3D=3D 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 =3D 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 *d= ata) +{ + 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 =3D ((*(u16 *)data) >> 8) & 0xff; + lvd_level_sel =3D (*(u16 *)data) & 0xff; + + dev->wbuf[1] =3D func; + switch (func) { + /* get level select */ + case 0x02: + dev->wbuf[2] =3D 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 =3D write_then_read(dev, dev->wbuf, 2, + dev->rbuf, 4); + if (error < 0) + return error; + break; + + default: + return -EINVAL; + } + + dev->pwr.header =3D be16(dev->rbuf); + dev->pwr.vdd33_lvd_flag =3D dev->rbuf[2]; + dev->pwr.vdd33_lvd_level_sel =3D 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 =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3); + if (error < 0) + return error; + + dev->sensor.header =3D be16(dev->rbuf); + dev->sensor.id =3D 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, voi= d *data) +{ + int error; + + UNUSED(data); + + /* return 0 to skip error check */ + if (!support_production_info(dev)) + return 0; + + error =3D 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 =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4); + if (error < 0) + return error; + + dev->customer_id =3D le16(dev->rbuf); + dev->fwid =3D 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 =3D 0; + return 0; + } + + crypto_ver =3D le16(dev->rbuf); + crypto_opt =3D 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 =3D 0; + + return 0; +} + +static int api_protocol_get_hid_info(struct ilitek_ts_device *dev, void *d= ata) +{ + int error; + + UNUSED(data); + + if (dev->protocol.ver < 0x060009) + return 0; + + error =3D 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 >=3D 'A' && c <=3D 'Z') || (c >=3D 'a' && c <=3D 'z') || + (c >=3D '0' && c <=3D '9')) ? false : true; +} + +static int api_protocol_get_mcu_ver(struct ilitek_ts_device *dev, void *da= ta) +{ + 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 =3D=3D PTL_V6 && support_mcu_info(dev)) { + error =3D api_protocol_set_cmd(dev, GET_MCU_INFO, NULL); + if (error < 0) + return error; + } else { + error =3D write_then_read(dev, dev->wbuf, 1, + dev->rbuf, 32); + if (error < 0) + return error; + + parser =3D (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 =3D=3D PTL_V6) { + if (is_29xx(dev)) { + /* modify reset time to 100ms for 29xx ICs */ + dev->reset_time =3D 100; + + /* set mm_addr for bin file update */ + dev->mcu_info.mm_addr =3D + is_2501x(dev) ? MM_ADDR_2501X : MM_ADDR_29XX; + dev->mcu_info.min_addr =3D START_ADDR_29XX; + dev->mcu_info.max_addr =3D END_ADDR_LEGO; + } else { + dev->mcu_info.mm_addr =3D MM_ADDR_LEGO; + dev->mcu_info.min_addr =3D START_ADDR_LEGO; + dev->mcu_info.max_addr =3D END_ADDR_LEGO; + } + } + + for (i =3D 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] =3D 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 *d= ata) +{ + 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 !=3D PTL_V6 || !support_mcu_info(dev)) + return 0; + + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 32); + if (error < 0) + return error; + + parser =3D (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 =3D le32(parser->mm_addr, 3); + + for (i =3D 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] =3D 0; + } + + return 0; +} + +static int api_protocol_set_fs_info(struct ilitek_ts_device *dev, void *da= ta) +{ + int error; + struct freq_settings *freq =3D (struct freq_settings *)data; + + if (!data) + return -EINVAL; + + dev->wbuf[1] =3D freq->sine.start & 0xFF; + dev->wbuf[2] =3D freq->sine.start >> 8; + dev->wbuf[3] =3D freq->sine.end & 0xFF; + dev->wbuf[4] =3D freq->sine.end >> 8; + dev->wbuf[5] =3D freq->sine.step; + dev->wbuf[6] =3D freq->mc_swcap.start & 0xFF; + dev->wbuf[7] =3D freq->mc_swcap.start >> 8; + dev->wbuf[8] =3D freq->mc_swcap.end & 0xFF; + dev->wbuf[9] =3D freq->mc_swcap.end >> 8; + dev->wbuf[10] =3D freq->mc_swcap.step; + dev->wbuf[11] =3D freq->sc_swcap.start & 0xFF; + dev->wbuf[12] =3D freq->sc_swcap.start >> 8; + dev->wbuf[13] =3D freq->sc_swcap.end & 0xFF; + dev->wbuf[14] =3D freq->sc_swcap.end >> 8; + dev->wbuf[15] =3D freq->sc_swcap.step; + dev->wbuf[16] =3D freq->frame_cnt & 0xFF; + dev->wbuf[17] =3D freq->frame_cnt >> 8; + dev->wbuf[18] =3D 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 =3D write_then_read(dev, dev->wbuf, 19, + dev->rbuf, 5); + break; + } + + dev->wbuf[16] =3D freq->dump1.start & 0xFF; + dev->wbuf[17] =3D freq->dump1.start >> 8; + dev->wbuf[18] =3D freq->dump1.end & 0xFF; + dev->wbuf[19] =3D freq->dump1.end >> 8; + dev->wbuf[20] =3D freq->dump1.step; + dev->wbuf[21] =3D freq->dump1_val; + dev->wbuf[22] =3D freq->dump2.start & 0xFF; + dev->wbuf[23] =3D freq->dump2.start >> 8; + dev->wbuf[24] =3D freq->dump2.end & 0xFF; + dev->wbuf[25] =3D freq->dump2.end >> 8; + dev->wbuf[26] =3D freq->dump2.step; + dev->wbuf[27] =3D freq->dump2_val; + dev->wbuf[28] =3D freq->frame_cnt & 0xFF; + dev->wbuf[29] =3D freq->frame_cnt >> 8; + dev->wbuf[30] =3D freq->scan_type; + + if (dev->protocol.ver < 0x6000a) { + error =3D write_then_read(dev, dev->wbuf, 31, + dev->rbuf, 5); + break; + } + + dev->wbuf[31] =3D freq->mc_frame_cnt & 0xFF; + dev->wbuf[32] =3D freq->mc_frame_cnt >> 8; + dev->wbuf[33] =3D freq->dump_frame_cnt & 0xFF; + dev->wbuf[34] =3D freq->dump_frame_cnt >> 8; + + error =3D write_then_read(dev, dev->wbuf, 35, dev->rbuf, 5); + + } while (0); + + if (error < 0) + return error; + + freq->packet_steps =3D le16(dev->rbuf + 3); + + if (dev->rbuf[0] !=3D 0x5a || dev->rbuf[1] !=3D 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 =3D (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] =3D _short->dump_1; + dev->wbuf[2] =3D _short->dump_2; + dev->wbuf[3] =3D _short->v_ref_L; + dev->wbuf[4] =3D _short->post_idle & 0xFF; + dev->wbuf[5] =3D (_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 =3D (struct open_settings *)data; + int wlen =3D 1; + + if (!data) + return -EINVAL; + + TP_DBG(dev->id, + "[open info] freq.: " PFMT_U16 ", gain: 0x" PFMT_X8 ", gain_rfb: 0x" PFM= T_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++] =3D open->freq & 0xFF; + dev->wbuf[wlen++] =3D (open->freq >> 8) & 0xFF; + dev->wbuf[wlen++] =3D open->gain; + dev->wbuf[wlen++] =3D open->gain_rfb; + dev->wbuf[wlen++] =3D open->afe_res_sel; + dev->wbuf[wlen++] =3D open->mc_fsel; + + if (dev->protocol.ver > 0x060009) { + TP_DBG(dev->id, "[open info] frame: " PFMT_U16 "\n", + open->frame); + + dev->wbuf[wlen++] =3D open->frame & 0xFF; + dev->wbuf[wlen++] =3D (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 =3D + (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] =3D curve->scan_mode; + dev->wbuf[2] =3D curve->dump.start & 0xFF; + dev->wbuf[3] =3D (curve->dump.start >> 8) & 0xFF; + dev->wbuf[4] =3D curve->dump.end & 0xFF; + dev->wbuf[5] =3D (curve->dump.end >> 8) & 0xFF; + dev->wbuf[6] =3D curve->dump.step; + dev->wbuf[7] =3D curve->dump.post_idle & 0xFF; + dev->wbuf[8] =3D (curve->dump.post_idle >> 8) & 0xFF; + dev->wbuf[9] =3D curve->dump.fix_val & 0xFF; + dev->wbuf[10] =3D (curve->dump.fix_val >> 8) & 0xFF; + dev->wbuf[11] =3D curve->charge.start & 0xFF; + dev->wbuf[12] =3D (curve->charge.start >> 8) & 0xFF; + dev->wbuf[13] =3D curve->charge.end & 0xFF; + dev->wbuf[14] =3D (curve->charge.end >> 8) & 0xFF; + dev->wbuf[15] =3D curve->charge.step; + dev->wbuf[16] =3D curve->charge.post_idle & 0xFF; + dev->wbuf[17] =3D (curve->charge.post_idle >> 8) & 0xFF; + dev->wbuf[18] =3D curve->charge.fix_val & 0xFF; + dev->wbuf[19] =3D (curve->charge.fix_val >> 8) & 0xFF; + dev->wbuf[20] =3D curve->c_sub & 0xFF; + dev->wbuf[21] =3D (curve->c_sub >> 8) & 0xFF; + dev->wbuf[22] =3D curve->frame_cnt & 0xFF; + dev->wbuf[23] =3D (curve->frame_cnt >> 8) & 0xFF; + + for (i =3D 0; i < (int)ARRAY_SIZE(curve->pt); i++) { + dev->wbuf[24 + i * 4] =3D curve->pt[i].x & 0xFF; + dev->wbuf[24 + i * 4 + 1] =3D (curve->pt[i].x >> 8) & 0xFF; + dev->wbuf[24 + i * 4 + 2] =3D curve->pt[i].y & 0xFF; + dev->wbuf[24 + i * 4 + 3] =3D (curve->pt[i].y >> 8) & 0xFF; + } + + error =3D write_then_read(dev, dev->wbuf, 60, dev->rbuf, 5); + if (error < 0) + return error; + + curve->packet_steps =3D le16(dev->rbuf + 3); + + if (dev->rbuf[0] !=3D AP_MODE || dev->rbuf[1] !=3D 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 *d= ata) +{ + struct p2p_settings *p2p =3D (struct p2p_settings *)data; + int wlen =3D 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++] =3D p2p->frame_cnt & 0xFF; + dev->wbuf[wlen++] =3D (p2p->frame_cnt >> 8) & 0xFF; + + if (dev->protocol.ver > 0x060009) { + dev->wbuf[wlen++] =3D p2p->type & 0xFF; + dev->wbuf[wlen++] =3D p2p->freq & 0xFF; + dev->wbuf[wlen++] =3D (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 =3D (struct freq_settings *)data; + + if (!data) + return -EINVAL; + + dev->wbuf[1] =3D freq->pen.mode; + dev->wbuf[2] =3D freq->pen.start & 0xFF; + dev->wbuf[3] =3D freq->pen.start >> 8; + dev->wbuf[4] =3D freq->pen.end & 0xFF; + dev->wbuf[5] =3D freq->pen.end >> 8; + dev->wbuf[6] =3D freq->frame_cnt & 0xFF; + dev->wbuf[7] =3D freq->frame_cnt >> 8; + + error =3D 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 *d= ata) +{ + int error; + + UNUSED(data); + + error =3D 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 =3D 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 *d= ata) +{ + int error; + int wlen =3D 1; + bool need_re_enum =3D (data) ? *(bool *)data : false; + bool force_reset =3D (!data) ? true : false; + + /* make sure touch report in default I2C-HID mode after force reset */ + if (dev->_interface =3D=3D interface_hid_over_i2c && !force_reset) + return 0; + + dev->wbuf[1] =3D 0; + error =3D 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 =3D=3D 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 *d= ata) +{ + int error; + + if (data) + *(u8 *)data =3D 0; + + _memset(dev->rbuf, 0, 64); + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 1); + if (error < 0) + return error; + + if (data) + *(u8 *)data =3D 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 =3D (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 !=3D AP_MODE) + return 0; + + error =3D write_then_read(dev, dev->wbuf, 1, + dev->rbuf, 2 * ic_num); + if (error < 0) + return error; + + dev->ic[0].crc[0] =3D le16(dev->rbuf); + TP_MSG(dev->id, "[FW CRC] Master: 0x%x\n", dev->ic[0].crc[0]); + + for (i =3D 1; i < ic_num; i++) { + dev->ic[i].crc[0] =3D 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 =3D (is_231x(dev)) ? 4 : 2; + + if (dev->_interface =3D=3D interface_i2c) { + error =3D write_then_read(dev, dev->wbuf, 1, NULL, 0); + if (error < 0) + return error; + dev->cb.delay_ms(600); + error =3D write_then_read(dev, NULL, 0, dev->rbuf, rlen); + if (error < 0) + return error; + } else { + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, rlen); + if (error < 0) + return error; + } + + dev->ic[0].crc[0] =3D le16(dev->rbuf); + if (is_231x(dev)) + dev->ic[0].crc[0] |=3D (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 *dat= a) +{ + if (dev->protocol.flag =3D=3D PTL_V6) + return api_protocol_get_ap_crc_v6(dev, data); + else if (dev->protocol.flag =3D=3D 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 *da= ta) +{ + int error; + u8 mode =3D dev->wbuf[1]; + + UNUSED(data); + + error =3D 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 *d= ata) +{ + int error; + bool in_ap =3D (data) ? *(bool *)data : true; + + error =3D 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 *da= ta) +{ + int error; + + UNUSED(data); + + dev->ic[0].crc[1] =3D 0; + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4); + if (error < 0) + return error; + + dev->ic[0].crc[1] =3D 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 *da= ta) +{ + 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 =3D (data) ? *(u8 *)data : 0; + u32 start, end, t_ms; + + dev->wbuf[1] =3D type; + + if (type =3D=3D CRC_CALCULATE) { + start =3D le32(dev->wbuf + 2, 3); + end =3D le32(dev->wbuf + 5, 3); + t_ms =3D ((end - start) / 4096 + 1) * TOUT_CD * TOUT_CD_RATIO; + + error =3D write_then_wait_ack(dev, dev->wbuf, 8, t_ms); + if (error < 0) + return error; + type =3D 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 =3D (data) ? *(u8 *)data : 0; + u32 t_ms =3D (dev->wbuf[2] =3D=3D 0) ? TOUT_CF_BLOCK_0 : TOUT_CF_BLOCK_N; + + dev->wbuf[1] =3D type; + + if (type =3D=3D CRC_CALCULATE) { + error =3D write_then_wait_ack(dev, dev->wbuf, 3, t_ms); + if (error < 0) + return error; + type =3D 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 *dat= a) +{ + int error; + u32 code =3D *(u32 *)data; + bool prepare; + int rlen; + + if (dev->ic[0].mode !=3D BL_MODE) + return -EINVAL; + + if (dev->protocol.flag =3D=3D PTL_V3) { + if ((dev->protocol.ver & 0xFFFF00) =3D=3D 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 =3D (code >> 16) ? true : false; + rlen =3D code & 0xFFFF; + + if (prepare) { + error =3D write_then_read(dev, dev->wbuf, 2, NULL, 0); + dev->cb.delay_ms(100); + + return error; + } + + if (dev->_interface =3D=3D interface_i2c) + error =3D write_then_read(dev, dev->wbuf, 2, dev->rbuf, rlen); + else + error =3D 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 =3D *(u32 *)data; + + if (!data) + return -EINVAL; + + if (dev->protocol.flag =3D=3D PTL_V3) { + dev->wbuf[3] =3D addr & 0xFF; + dev->wbuf[2] =3D (addr >> 8) & 0xFF; + dev->wbuf[1] =3D (addr >> 16) & 0xFF; + + error =3D write_then_read(dev, dev->wbuf, 4, NULL, 0); + if (error < 0) + return error; + + dev->cb.delay_ms(5); + + return 0; + } + + dev->wbuf[1] =3D addr & 0xFF; + dev->wbuf[2] =3D (addr >> 8) & 0xFF; + dev->wbuf[3] =3D (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 *d= ata) +{ + 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 =3D (data) ? *(u8 *)data : 0; + int wlen, rlen; + bool in_ap =3D ((type & 0x1) !=3D 0) ? true : false; + bool is_slave =3D ((type & 0x2) !=3D 0) ? true : false; + + u32 set_start, set_end, get_start, get_end; + + if (!is_slave) { + wlen =3D (in_ap) ? 3 : 9; + rlen =3D (in_ap || dev->protocol.ver < 0x010803) ? 0 : 6; + + set_start =3D le32(dev->wbuf + 3, 3); + set_end =3D le32(dev->wbuf + 6, 3); + + error =3D 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 =3D le32(dev->rbuf, 3); + get_end =3D le32(dev->rbuf + 3, 3); + + if (set_start !=3D get_start || set_end !=3D 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 =3D 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 =3D=3D 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 =3D *(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 =3D *(int *)data; + + return write_then_wait_ack(dev, dev->wbuf, wlen, 30000); +} + +static int api_protocol_access_slave(struct ilitek_ts_device *dev, void *d= ata) +{ + int error; + struct ilitek_slave_access *access; + + if (!data) + return -EINVAL; + + access =3D (struct ilitek_slave_access *)data; + + dev->wbuf[1] =3D access->slave_id; + dev->wbuf[2] =3D access->func; + _memset(dev->rbuf, 0, sizeof(dev->rbuf)); + + switch (access->func) { + case CMD_GET_AP_CRC: + error =3D write_then_read(dev, dev->wbuf, 3, dev->rbuf, 4); + *((u32 *)access->data) =3D le32(dev->rbuf, 4); + break; + + case CMD_GET_MCU_MOD: + error =3D write_then_read(dev, dev->wbuf, 3, dev->rbuf, 1); + *((u8 *)access->data) =3D dev->rbuf[0]; + break; + + case CMD_GET_FW_VER: + error =3D 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] =3D ((u8 *)access->data)[0]; + dev->wbuf[4] =3D ((u8 *)access->data)[1]; + dev->wbuf[5] =3D ((u8 *)access->data)[2]; + dev->wbuf[6] =3D ((u8 *)access->data)[3]; + dev->wbuf[7] =3D ((u8 *)access->data)[4]; + dev->wbuf[8] =3D ((u8 *)access->data)[5]; + + error =3D write_then_wait_ack(dev, dev->wbuf, 9, 5000); + break; + + default: + error =3D 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 *da= ta) +{ + int error; + + UNUSED(data); + + if (dev->cb.mode_switch_notify) + dev->cb.mode_switch_notify(true, false, dev->_private); + + error =3D 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 *da= ta) +{ + int error; + + UNUSED(data); + + if (dev->cb.mode_switch_notify) + dev->cb.mode_switch_notify(true, false, dev->_private); + + error =3D 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 *dat= a) +{ + 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 =3D (data) ? *(bool *)data : true; + + if (!data) + return -EINVAL; + + if (get) { + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 3); + if (error < 0) + return error; + + dev->func.header =3D be16(dev->rbuf); + dev->func.mode =3D dev->rbuf[2]; + TP_MSG(dev->id, "[FW Mode] 0x" PFMT_X8 "\n", dev->func.mode); + + return 0; + } + + if (dev->protocol.flag =3D=3D PTL_V3) { + error =3D write_then_read(dev, dev->wbuf, 4, NULL, 0); + if (error < 0) + return error; + error =3D api_check_busy(dev, 1000, 10); + if (error < 0) + return error; + return 0; + } else if (dev->protocol.flag =3D=3D PTL_V6) { + error =3D 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 *d= ata) +{ + 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 =3D + *(struct tuning_para_settings *)data; + u32 wlen; + + int header; + int tout_ms; + + if (!data) + return -EINVAL; + + dev->wbuf[1] =3D tuning.func; + dev->wbuf[2] =3D tuning.ctrl; + dev->wbuf[3] =3D tuning.type; + + if (tuning.func =3D=3D 0x0) { + wlen =3D 4; + tout_ms =3D TOUT_65_READ * TOUT_65_READ_RATIO; + + switch (tuning.ctrl) { + case 0x3: case 0x5: case 0x10: + wlen +=3D tuning.len; + tout_ms =3D 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 =3D write_then_read(dev, dev->wbuf, 4, NULL, 0); + if (error < 0) + return error; + error =3D read_ctrl_in(dev, CMD_TUNING_PARA_V6, + dev->rbuf, 1024, 5000); + if (error < 0) + return error; + + header =3D (dev->_interface =3D=3D interface_i2c) ? 5 : 6; + header =3D (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 =3D (struct cdc_settings *)data; + + if (!data) + return -EINVAL; + + if (set->is_freq) { + dev->wbuf[1] =3D 0x0F; + dev->wbuf[2] =3D set->freq.sine.start; + dev->wbuf[3] =3D set->freq.sine.end; + dev->wbuf[4] =3D set->freq.sine.step; + + error =3D write_then_read(dev, dev->wbuf, 5, NULL, 0); + if (error < 0) + return error; + + dev->cb.delay_ms(200); + } else { + dev->wbuf[1] =3D set->cmd; + dev->wbuf[2] =3D 0; + dev->wbuf[3] =3D set->config & 0xFF; + wlen =3D 4; + + if (set->config & 0xFF00) { + dev->wbuf[3] =3D (set->config >> 8) & 0xFF; + dev->wbuf[4] =3D set->config & 0xFF; + wlen =3D 5; + } + + error =3D 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 *dat= a) +{ + 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 =3D (struct cdc_settings *)data; + int wlen =3D 1, tout_ms; + + if (!data) + return -EINVAL; + + dev->wbuf[wlen++] =3D set->cmd; + + if (set->is_freq) { + tout_ms =3D set->freq.sine.steps * TOUT_F1_FREQ_MC; + tout_ms +=3D (set->freq.mc_swcap.steps * TOUT_F1_FREQ_SC); + tout_ms +=3D (set->freq.sc_swcap.steps * TOUT_F1_FREQ_SC); + tout_ms +=3D (set->freq.dump1.steps * TOUT_F1_FREQ_SC); + tout_ms +=3D (set->freq.dump2.steps * TOUT_F1_FREQ_SC); + + tout_ms +=3D (set->freq.pen.steps * TOUT_F1_FREQ_MC); + + tout_ms *=3D MAX(set->freq.frame_cnt, 1); + tout_ms *=3D TOUT_F1_FREQ_RATIO; + + return write_then_wait_ack(dev, dev->wbuf, wlen, tout_ms); + } + + if (set->is_p2p) { + tout_ms =3D 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++] =3D set->config & 0xFF; + + tout_ms =3D TOUT_F1_OTHER * TOUT_F1_OTHER_RATIO; + if (set->is_curve) { + tout_ms =3D set->curve.dump.steps * TOUT_F1_CURVE; + tout_ms +=3D (set->curve.charge.steps * TOUT_F1_CURVE); + tout_ms *=3D MAX(set->curve.frame_cnt, 1); + tout_ms *=3D TOUT_F1_CURVE_RATIO; + } else if (set->is_short) { + tout_ms =3D TOUT_F1_SHORT * TOUT_F1_SHORT_RATIO; + } else if (set->is_open) { + tout_ms =3D MAX(set->open.frame, 1) * + TOUT_F1_OPEN * TOUT_F1_OPEN_RATIO; + } else if (set->is_key && dev->protocol.ver < 0x6000a) { + tout_ms =3D 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 =3D (u32 *)data; + + if (!data) + return -EINVAL; + + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4); + if (error < 0) + return error; + + *cdc_info =3D le32(dev->rbuf, 4); + + return 0; +} + +int api_protocol_set_cmd(void *handle, u8 idx, void *data) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + int error; + + if (!dev || idx >=3D ARRAY_SIZE(protocol_maps)) + return -EINVAL; + + if (!(dev->protocol.flag & protocol_maps[idx].flag) && + protocol_maps[idx].flag !=3D 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] =3D protocol_maps[idx].cmd; + error =3D 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 =3D (struct ilitek_ts_device *)handle; + int error; + u8 cmd =3D 0; + + if (!force && dev->fw_mode =3D=3D mode) + return 0; + + _memset(dev->wbuf, 0, sizeof(dev->wbuf)); + + if (dev->protocol.flag =3D=3D PTL_V3) { + /* V3 only support suspend and normal mode */ + if (mode !=3D mode_normal && + mode !=3D mode_suspend && + mode !=3D mode_test) + return -EPROTONOSUPPORT; + dev->wbuf[1] =3D (mode =3D=3D mode_normal) ? 0x00 : 0x01; + cmd =3D SET_TEST_MOD; + } else if (dev->protocol.flag =3D=3D PTL_V6) { + dev->wbuf[1] =3D mode; + dev->wbuf[2] =3D (eng) ? 0x01 : 0x00; + cmd =3D SET_MOD_CTRL; + } + + error =3D 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 =3D=3D PTL_V6 && + dev->fw_mode =3D=3D mode_test && mode =3D=3D mode_normal) + dev->cb.delay_ms(1000); + else + dev->cb.delay_ms(100); + + dev->fw_mode =3D mode; + + return 0; +} + +u16 api_get_block_crc_by_addr(void *handle, u8 type, + u32 start, u32 end) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + + _memset(dev->wbuf, 0, 64); + + dev->wbuf[2] =3D start; + dev->wbuf[3] =3D (start >> 8) & 0xFF; + dev->wbuf[4] =3D (start >> 16) & 0xFF; + dev->wbuf[5] =3D end & 0xFF; + dev->wbuf[6] =3D (end >> 8) & 0xFF; + dev->wbuf[7] =3D (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 =3D (struct ilitek_ts_device *)handle; + + _memset(dev->wbuf, 0, 64); + + dev->wbuf[2] =3D 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 =3D (struct ilitek_ts_device *)handle; + + _memset(dev->wbuf, 0, 64); + + dev->wbuf[1] =3D data_len & 0xFF; + dev->wbuf[2] =3D (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 =3D (struct ilitek_ts_device *)handle; + u8 type; + + _memset(dev->wbuf, 0, 64); + dev->wbuf[1] =3D 0x5A; + dev->wbuf[2] =3D 0xA5; + dev->wbuf[3] =3D start & 0xFF; + dev->wbuf[4] =3D (start >> 8) & 0xFF; + dev->wbuf[5] =3D start >> 16; + dev->wbuf[6] =3D end & 0xFF; + dev->wbuf[7] =3D (end >> 8) & 0xFF; + dev->wbuf[8] =3D end >> 16; + + type =3D (in_ap) ? 0x1 : 0x0; + type |=3D (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 =3D id; + access.func =3D func; + access.data =3D 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 =3D (struct ilitek_ts_device *)handle; + + _memset(dev->wbuf, 0, 64); + dev->wbuf[1] =3D 0x5A; + dev->wbuf[2] =3D 0xA5; + dev->wbuf[3] =3D (write_ap) ? 0x0 : 0x1; + dev->wbuf[4] =3D (end >> 16) & 0xFF; + dev->wbuf[5] =3D (end >> 8) & 0xFF; + dev->wbuf[6] =3D end & 0xFF; + dev->wbuf[7] =3D (checksum >> 16) & 0xFF; + dev->wbuf[8] =3D (checksum >> 8) & 0xFF; + dev->wbuf[9] =3D 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 =3D (struct ilitek_ts_device *)handle; + u8 busy; + + /* retry 2 times at least */ + int i =3D 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 =3D=3D 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 =3D (struct ilitek_ts_device *)handle; + int cnt =3D 0, retry =3D 15; + const u8 target_mode =3D (to_bl) ? BL_MODE : AP_MODE; + + do { + if (api_protocol_set_cmd(dev, GET_MCU_MOD, NULL) < 0) + continue; + + if (dev->ic[0].mode =3D=3D target_mode) + goto success_change_mode; + + if (to_bl) { + if (dev->protocol.flag =3D=3D PTL_V3 && + api_write_enable_v3(dev, true, false, 0, 0) < 0) + continue; + else if (dev->protocol.flag =3D=3D 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 =3D=3D PTL_V3 && + api_write_enable_v3(dev, true, false, 0, 0) < 0) + continue; + else if (dev->protocol.flag =3D=3D 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 =3D (struct ilitek_ts_device *)handle; + + _memset(dev->wbuf, 0, 64); + dev->wbuf[1] =3D (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 =3D (struct ilitek_ts_device *)handle; + int error; + bool get =3D false; + + _memset(dev->wbuf, 0, 64); + + switch (dev->protocol.flag) { + case PTL_V3: + dev->wbuf[1] =3D 0x55; + dev->wbuf[2] =3D 0xAA; + break; + case PTL_V6: + dev->wbuf[1] =3D 0x5A; + dev->wbuf[2] =3D 0xA5; + break; + default: + TP_ERR(dev->id, "unrecognized protocol: %x, flag: " PFMT_U8 "", + dev->protocol.ver, dev->protocol.flag); + return -EINVAL; + } + dev->wbuf[3] =3D mode; + + if (dev->protocol.ver < 0x30400) { + TP_ERR(dev->id, "protocol: 0x%x not support\n", + dev->protocol.ver); + return -EINVAL; + } + + error =3D api_protocol_set_cmd(dev, SET_FUNC_MOD, &get); + if (error < 0) + return error; + error =3D api_get_func_mode(dev); + if (error < 0) + return error; + + return (dev->func.mode =3D=3D mode) ? 0 : -EFAULT; +} + +int api_get_func_mode(void *handle) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + bool get =3D true; + + return api_protocol_set_cmd(dev, SET_FUNC_MOD, &get); +} + +int api_erase_data_v3(void *handle) +{ + struct ilitek_ts_device *dev =3D (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 !=3D AP_MODE) { + TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n", + dev->ic[0].mode); + return 0; + } + + error =3D api_write_enable_v3(dev, true, false, 0, 0); + if (error < 0) + return error; + + dev->cb.delay_ms(100); + + dev->wbuf[1] =3D 0x02; + error =3D 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 !=3D BL_MODE) { + TP_WARN(dev->id, "invalid mode: " PFMT_X8 " for data erase\n", + dev->ic[0].mode); + return 0; + } + + error =3D 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 =3D 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 =3D start + len, copied; + + for (addr =3D start, copied =3D 0; addr < end; + addr +=3D 32, copied +=3D 32) { + error =3D api_protocol_set_cmd(dev, SET_ADDR, &addr); + if (error < 0) + return error; + error =3D 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 =3D start + len, copied; + u16 data_len; + + if (dev->ic[0].mode !=3D BL_MODE) + return -EINVAL; + + for (addr =3D start, copied =3D 0; addr < end; + addr +=3D data_len, copied +=3D data_len) { + if (end - addr > 1024) + data_len =3D 2048; + else if (end - addr > 256) + data_len =3D 1024; + else if (end - addr > 64) + data_len =3D 256; + else + data_len =3D 64; + + error =3D api_set_data_len(dev, data_len); + if (error < 0) + return error; + error =3D api_protocol_set_cmd(dev, SET_ADDR, &addr); + if (error < 0) + return error; + + dev->wbuf[1] =3D 0x1; code =3D 1 << 16; + error =3D api_protocol_set_cmd(dev, READ_FLASH, &code); + if (error < 0) + return error; + + dev->wbuf[1] =3D 0x0; code =3D data_len & 0xFFFF; + error =3D api_protocol_set_cmd(dev, READ_FLASH, &code); + if (error < 0) + return error; + + if (dev->_interface =3D=3D 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 =3D (struct ilitek_ts_device *)handle; + + if (dev->protocol.flag =3D=3D 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 =3D (struct ilitek_ts_device *)handle; + struct tuning_para_settings tuning; + u8 *buf =3D (u8 *)(&dev->mp); + u32 addr =3D (is_29xx(dev)) ? 0x2e000 : 0x3e000; + + if (dev->ic[0].mode =3D=3D BL_MODE) + return api_read_flash_v6(dev, buf, addr, 1000); + + /* 1000 bytes data/ 2 bytes crc/ 1 bytes checksum */ + tuning.len =3D 1003; + tuning.buf =3D buf; + + tuning.func =3D 0x0; tuning.ctrl =3D 0x4; tuning.type =3D 0x10; + error =3D api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning); + if (error < 0) + return error; + + tuning.func =3D 0x1; tuning.ctrl =3D 0x4; tuning.type =3D 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 =3D _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 =3D (struct ilitek_ts_device *)handle; + struct tuning_para_settings tuning; + u8 *buf =3D (u8 *)mp; + u16 crc; + + crc =3D get_crc(0, 1000, buf, sizeof(struct mp_station)); + mp->crc =3D crc; + + tuning.func =3D 0x0; + tuning.ctrl =3D 0x5; + tuning.type =3D 0x10; + tuning.buf =3D buf; + tuning.len =3D 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 =3D _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 =3D=3D 1) + TP_INFO(NULL, PFMT_C8 " Result: PASS\n", item); + else if (data =3D=3D 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 =3D (struct ilitek_ts_device *)handle; + struct mp_station *mp =3D &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 =3D=3D 0xFFFFFFFF || + mp->info.mp_result_ver < 0x01000000) { + _mp =3D (struct mp_station_old *)mp; + + for (i =3D 0; i < 9; i++) { + TP_INFO(NULL, "***Station%d***\n", i + 1); + + if (!_mp->station[i].week || + _mp->station[i].week =3D=3D 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] =3D '\0'; + for (j =3D 0; j < (int)sizeof(_mp->station[i].module); + j++) { + if ((u8)module_name[j] =3D=3D 0xFF) { + module_name[j] =3D '\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] =3D '\0'; + for (j =3D 0; j < (int)sizeof(_mp->station[i].bar_code); + j++) { + if ((u8)bar_code[j] =3D=3D 0xFF) { + bar_code[j] =3D '\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 =3D 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 =3D=3D 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] =3D '\0'; + for (j =3D 0; j < (int)sizeof(mp->station[i].module); j++) { + if ((u8)module_name[j] =3D=3D 0xFF) { + module_name[j] =3D '\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] =3D '\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 =3D (struct ilitek_ts_device *)handle; + struct tuning_para_settings tuning; + int got, need; + + if (dev->ic[0].mode =3D=3D BL_MODE) + return -EINVAL; + + error =3D api_set_ctrl_mode(dev, mode_suspend, false, true); + if (error < 0) + return error; + + for (got =3D 0, need =3D rlen; need > 0; got +=3D 1024, need -=3D 1024) { + tuning.len =3D MIN(need, 1024); + tuning.buf =3D buf + got; + + tuning.func =3D 0x0; tuning.ctrl =3D 0x2; tuning.type =3D 0x0; + error =3D api_protocol_set_cmd(dev, TUNING_PARA_V6, &tuning); + if (error < 0) + return error; + + tuning.func =3D 0x1; tuning.ctrl =3D 0x2; tuning.type =3D 0x0; + error =3D 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 =3D (struct ilitek_ts_device *)handle; + int cnt =3D 0, retry =3D 15; + const u8 target_mode =3D (to_bl) ? BL_MODE : AP_MODE; + u8 mode; + + if (dev->_interface !=3D 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 =3D=3D 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 =3D=3D 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 " m= ode 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 =3D (struct ilitek_ts_device *)handle; + + error =3D api_set_ctrl_mode(dev, mode_suspend, false, true); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_AP_CRC, NULL); + if (error < 0) + goto err_set_normal; + + if (dev->ic[0].mode !=3D AP_MODE) + return 0; + + switch (dev->protocol.flag) { + case PTL_V3: + error =3D api_protocol_set_cmd(dev, GET_DF_CRC, NULL); + if (error < 0) + goto err_set_normal; + break; + case PTL_V6: + for (i =3D 1; i < dev->tp_info.block_num; i++) { + dev->ic[0].crc[i] =3D api_get_block_crc_by_num(dev, + CRC_CALCULATE, i); + } + break; + default: + error =3D -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 =3D (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 =3D=3D 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 !=3D AP_MODE) + return; + + for (i =3D 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 =3D=3D 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_resoluti= on); + TP_INFO(dev->id, "Y resolution : " PFMT_U16 "\n", dev->tp_info.y_resoluti= on); + 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_finger= s); + 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 =3D=3D 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 =3D=3D PTL_V6) { + TP_INFO(dev->id, "[FW CRC]\n"); + TP_INFO(dev->id, "Master : 0x%04X\n", dev->ic[0].crc[0]); + for (i =3D 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 =3D (struct ilitek_ts_device *)handle; + u8 m2v_mode; + u32 m2v_checksum; + u8 m2v_fw_ver[8]; + + if (dev->ic[0].mode !=3D 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 =3D=3D 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 =3D (struct ilitek_ts_device *)handle; + + /* set protocol default V6 initially for comms. afterwards */ + dev->protocol.flag =3D PTL_V6; + dev->tp_info.ic_num =3D 1; + + error =3D api_set_ctrl_mode(dev, mode_suspend, false, true); + if (error < 0) + goto err_set_normal; + error =3D 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 =3D=3D PTL_V3) { + error =3D api_set_ctrl_mode(dev, mode_suspend, false, true); + if (error < 0) + goto err_set_normal; + } + + error =3D api_protocol_set_cmd(dev, GET_MCU_MOD, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_MCU_VER, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_FW_VER, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_AP_CRC, NULL); + if (error < 0) + goto err_set_normal; + + if (dev->protocol.flag =3D=3D PTL_V6) { + error =3D api_protocol_set_cmd(dev, GET_PRODUCT_INFO, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_FWID, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_SENSOR_ID, NULL); + if (error < 0) + goto err_set_normal; + error =3D 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 !=3D AP_MODE) + return 0; + + /* V3 need to get DF CRC */ + if (dev->protocol.flag =3D=3D PTL_V3) { + error =3D api_protocol_set_cmd(dev, GET_DF_CRC, NULL); + if (error < 0) + goto err_set_normal; + } + + if (dev->protocol.flag =3D=3D PTL_V6) { + error =3D api_protocol_set_cmd(dev, GET_TUNING_VER, NULL); + if (error < 0) + goto err_set_normal; + } + + error =3D api_protocol_set_cmd(dev, GET_CORE_VER, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_SCRN_RES, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_protocol_set_cmd(dev, GET_TP_INFO, NULL); + if (error < 0) + goto err_set_normal; + error =3D api_get_func_mode(dev); + if (error < 0) + goto err_set_normal; + + if (dev->tp_info.ic_num > 1) { + error =3D api_protocol_set_cmd(dev, GET_AP_CRC, + &dev->tp_info.ic_num); + if (error < 0) + goto err_set_normal; + error =3D 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 =3D (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 =3D (struct ilitek_ts_device *)handle; + + if (!handle) + return; + + dev->quirks =3D quirks; +} + +void ilitek_dev_set_sys_info(void *handle, struct ilitek_sys_info *sys) +{ + struct ilitek_ts_device *dev =3D (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 =3D (struct ilitek_ts_device *)handle; + +#define X(_enum, _id, _size, _cnt) \ + touch_fmts[_id].size =3D _size; \ + touch_fmts[_id].max_cnt =3D _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 =3D touch_fmts[touch_fmt_0x0].size; + dev->fmt.touch_max_cnt =3D 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 *cal= lback) +{ + struct ilitek_ts_device *dev =3D (struct ilitek_ts_device *)handle; + + if (callback) { + _memcpy(&dev->cb, callback, sizeof(struct ilitek_ts_callback)); + if (dev->cb.msg) + g_msg =3D 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 =3D (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 =3D _interface; + dev->_private =3D _private; + + /* set protocol default V6 initially for comms. afterwards */ + dev->protocol.flag =3D PTL_V6; + dev->tp_info.ic_num =3D 1; + + dev->fw_mode =3D 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 =3D (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/i= nput/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 + * Copyright (c) 2022 Joe Hung + */ + +#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 =3D _cmd, +enum ilitek_cmds { ILITEK_CMD_MAP }; +#undef X + +enum ilitek_hw_interfaces { + interface_i2c =3D 0, + interface_hid_over_i2c, + interface_usb, +}; + +enum ilitek_fw_modes { + mode_unknown =3D -1, + mode_normal =3D 0, + mode_test, + mode_debug, + mode_suspend, +}; + +enum ilitek_key_modes { + key_disable =3D 0, + key_hw =3D 1, + key_hsw =3D 2, + key_vitual =3D 3, + key_fw_disable =3D 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 =3D _id, +enum ilitek_touch_fmts { + ILITEK_TOUCH_REPORT_FORMAT + touch_fmt_max =3D 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 =3D _id, +enum ilitek_pen_fmts { + ILITEK_PEN_REPORT_FORMAT + pen_fmt_max =3D 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 =3D 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 *s= ys); +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/inp= ut/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 + * Copyright (c) 2022 Joe Hung + */ + +#include "ilitek_report.h" +#include "ilitek_crypto.h" + +static bool is_debug_packet_id(u8 id, u8 _interface) +{ + return ((_interface =3D=3D interface_i2c && id =3D=3D 0xdb) || + (_interface =3D=3D interface_usb && id =3D=3D 0xaa)); +} + +static bool is_pen_packet_id(u8 id) +{ + return (id =3D=3D 0x0c || id =3D=3D 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 =3D dev->fmt.touch_size; + + offset =3D (dev->quirks & QUIRK_BRIDGE) ? 4 : 0; + + for (i =3D 0; i < cnt && i < dev->tp_info.max_fingers; i++) { + finger =3D &report->touch.finger[i]; + + do { + if (dev->protocol.flag =3D=3D PTL_V3 && + dev->_interface =3D=3D interface_i2c) { + finger->id =3D i; + finger->status =3D buf[1 + offset + i * 5] >> 7; + finger->x =3D ((buf[1 + offset + i * 5] & 0x3F) << 8) + + buf[2 + offset + i * 5]; + finger->y =3D ((buf[3 + offset + i * 5] & 0x3F) << 8) + + buf[4 + offset + i * 5]; + finger->pressure =3D buf[5 + offset + i * 5]; + finger->height =3D 128; + finger->width =3D 1; + + break; + } + + parser =3D (struct touch_fmt *) + (buf + 1 + i * size); + parser_iwb =3D (struct touch_iwb_fmt *) + (buf + 1 + i * size); + + finger->id =3D parser->id; + finger->status =3D parser->status; + finger->x =3D parser->x; + finger->y =3D parser->y; + + if (dev->setting.default_format_enabled) + break; + + switch (dev->tp_info.format) { + case touch_fmt_0x1: + finger->pressure =3D parser->pressure; + break; + case touch_fmt_0x2: + finger->width =3D parser->width; + finger->height =3D parser->height; + break; + case touch_fmt_0x3: + finger->pressure =3D parser->pressure; + finger->width =3D parser->width; + finger->height =3D parser->height; + break; + case touch_fmt_0x4: + finger->id =3D parser_iwb->id; + finger->status =3D + (parser_iwb->status =3D=3D 0x7) ? 1 : 0; + finger->x =3D parser_iwb->x; + finger->y =3D parser_iwb->y; + finger->width =3D parser_iwb->width; + finger->height =3D parser_iwb->height; + break; + case touch_fmt_0x10: + finger->width =3D le16(buf + i * size + 6); + finger->height =3D le16(buf + i * size + 8); + finger->algo =3D buf[i * size + 10]; + report->touch.algo =3D 0; + break; + case touch_fmt_0x11: + finger->id =3D + (!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 >=3D dev->tp_info.max_fingers) { + TP_ERR(dev->id, "invalid touch id: %hhu >=3D %hhu\n", + finger->id, dev->tp_info.max_fingers); + return; + } + + /* + * if x/y within key's range, skip touch range check. + */ + for (j =3D 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 =3D 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 =3D (struct pen_fmt *)(buf + 1); + struct pen_fmt *pen =3D &report->pen.pen; + + report->pen.cnt =3D buf[61]; + report->pen.algo =3D 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_til= t: %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_til= t: %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_til= t: %hd, y_tilt: %hd, battery: %hhu, barrel_pressure: %hu, idx: %hhu, color2= 4: %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] >=3D buf_size) + return; + + buf[buf[1]] =3D '\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 =3D (dev->quirks & QUIRK_BRIDGE) ? 4 : 0; + + UNUSED(buf_size); + + if (dev->_interface =3D=3D interface_i2c) { + if (dev->quirks & QUIRK_BRIDGE) { + error =3D read_interrupt_in(dev, buf, 64, 1000); + if (error < 0) + return error; + + if (buf[0] !=3D 0x03 || buf[1] !=3D 0xa3 || buf[2] !=3D 0x10) + return -EAGAIN; + + /* move algo byte to index 62 */ + buf[62] =3D buf[35]; + } else if (dev->quirks & QUIRK_DAEMON_I2C || + dev->quirks & QUIRK_BRIDGE || + dev->quirks & QUIRK_WIFI_ITS_I2C) { + error =3D read_interrupt_in(dev, buf, 64, 1000); + if (error < 0) + return error; + } else { + dev->wbuf[0] =3D 0x10; + error =3D write_then_read(dev, dev->wbuf, 1, buf, 32); + if (error < 0) + return error; + + report->touch.algo =3D buf[31]; + buf[31] =3D 0; + + if (buf[0] =3D=3D 2) { + error =3D write_then_read(dev, NULL, 0, + buf + 31, 20); + if (error < 0) + return error; + } + + /* move algo byte to index 62 */ + buf[62] =3D report->touch.algo; + } + + switch (dev->rbuf[idx]) { + default: + case 1: + cnt =3D 6; + break; + case 0: + cnt =3D 0; + break; + case 2: + cnt =3D 10; + break; + } + } else { + error =3D read_interrupt_in(dev, buf, 64, 1000); + if (error < 0) + return error; + + cnt =3D buf[55]; + + /* move algo byte to index 62 */ + buf[62] =3D buf[56]; + } + + report->touch.algo =3D buf[62]; + + report->touch.dbg_size =3D 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 =3D=3D 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 =3D=3D interface_usb || + dev->_interface =3D=3D interface_hid_over_i2c) && + packet_id =3D=3D 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 =3D dev->fmt.touch_size, max_cnt =3D dev->fmt.touch_max_cnt; + u8 tmp[64]; + + UNUSED(buf_size); + + error =3D read_interrupt_in(dev, tmp, 64, 1000); + if (error < 0) + return error; + + if (dev->tp_info.format =3D=3D 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] =3D 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 =3D tmp[62]; + report->touch.algo =3D tmp[62]; + report->pen.dbg_size =3D 64; + report->touch.dbg_size =3D 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 =3D is_iwb_fmt(dev) ? tmp[55] : tmp[61]; + for (i =3D 1; i < DIV_ROUND_UP(cnt, max_cnt); i++) { + report_buf(report, tmp, false); + + error =3D read_interrupt_in(dev, tmp, 64, 1000); + if (error < 0) + return error; + + if (dev->tp_info.format =3D=3D 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 =3D (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 =3D report_get_raw_v3(dev, report, dev->rbuf, + sizeof(dev->rbuf)); + if (cnt < 0) + return cnt; + + break; + + case PTL_V6: + cnt =3D 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/inp= ut/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 + * Copyright (c) 2022 Joe Hung + */ + +#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 + * + * 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 + +#include + +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, uint= 32_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, int= 32_t) +#define ILITEK_IOCTL_I2C_UPDATE _IOWR(ILITEK_IOCTL_BASE, 1= 1, int32_t) +#define ILITEK_IOCTL_STOP_READ_DATA _IOWR(ILITEK_IOCTL_BASE, 1= 2, int32_t) +#define ILITEK_IOCTL_START_READ_DATA _IOWR(ILITEK_IOCTL_BASE, 1= 3, 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, uint= 32_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 =3D true; + TP_MSG(NULL, "operation_protection =3D %d\n", ts->operation_protection); + return 0; +} + +static s32 ilitek_file_close(struct inode *inode, struct file *filp) +{ + ts->operation_protection =3D false; + TP_MSG(NULL, "operation_protection =3D %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 =3D 0, count =3D 0; + u8 buffer[512]; + u32 *data; + char *token =3D NULL, *cur =3D NULL; + + if (size > sizeof(buffer)) { + TP_ERR(NULL, "invalid buf len: %zu > %zu too large\n", + size, sizeof(buffer)); + return -EINVAL; + } + + ret =3D copy_from_user(buffer, buf, size); + if (ret < 0) { + TP_ERR(NULL, "copy data from user space, failed"); + return -1; + } + + token =3D cur =3D buffer; + + data =3D kcalloc(size, sizeof(u32), GFP_KERNEL); + + while ((token =3D strsep(&cur, ",")) !=3D NULL) { + //data[count] =3D str2hex(token); + sscanf(token, "%x", &data[count]); + TP_MSG(NULL, "data[%d] =3D %x\n", count, data[count]); + count++; + } + + if (buffer[size - 2] =3D=3D 'I' && (size =3D=3D 20 || size =3D=3D 52) && = buffer[0] =3D=3D 0x77 && buffer[1] =3D=3D 0x77) { + + TP_MSG(NULL, "IOCTL_WRITE CMD =3D %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 =3D %d.\n", buffer[3]); + if (buffer[3] =3D=3D 0) { + ilitek_debug_flag =3D false; + } else if (buffer[3] =3D=3D 1) { + ilitek_debug_flag =3D true; + } + break; +#endif + case 15: + if (buffer[3] =3D=3D 0) + ilitek_irq_disable(); + else + ilitek_irq_enable(); + + break; + case 16: + ts->operation_protection =3D buffer[3]; + TP_MSG(NULL, "ts->operation_protection =3D %d\n", ts->operation_protect= ion); + break; + case 18: + ilitek_irq_disable(); + mutex_lock(&ts->ilitek_mutex); + ret =3D 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] =3D=3D 'W') { + ilitek_irq_disable(); + mutex_lock(&ts->ilitek_mutex); + ret =3D 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 =3D !ts->unhandle_irq; + TP_MSG(NULL, "ts->unhandle_irq =3D %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 =3D %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 =3D %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 =3D %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 =3D %d.\n", log_level_err); + } else if (!strncmp(buffer, "dbg_num", strlen("dbg_num"))) { + TP_MSG(NULL, "ilitek_log_level_value =3D %d.\n", tp_log_level); + } +#ifdef ILITEK_TUNING_MESSAGE + else if (!strncmp(buffer, "truning_dbg_flag", strlen("truning_dbg_flag"))= ) { + ilitek_debug_flag =3D !ilitek_debug_flag; + TP_MSG(NULL, " %s debug_flag message(%X).\n", ilitek_debug_flag ? "Enabl= ed" : "Disabled", ilitek_debug_flag); + } +#endif + else if (!strncmp(buffer, "irq_status", strlen("irq_status"))) { + TP_MSG(NULL, "gpio_get_value(i2c.irq_gpio) =3D %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 =3D %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 =3D 0; + int tmp; + + buffer =3D 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 =3D -EINVAL; + break; + } + + if (copy_from_user(buffer, (u8 *)arg, len)) { + TP_ERR(NULL, "copy data from user space, failed\n"); + ret =3D -EFAULT; + break; + } + + mutex_lock(&ts->ilitek_mutex); + ret =3D 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 =3D -EINVAL; + break; + } + + mutex_lock(&ts->ilitek_mutex); + ret =3D 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 =3D -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 =3D arg; + break; + case ILITEK_IOCTL_DRIVER_INFORMATION: + memcpy(buffer, driver_ver, 7); + if (copy_to_user((u8 *)arg, buffer, 7)) + ret =3D -EFAULT; + break; + case ILITEK_IOCTL_I2C_UPDATE: + break; + case ILITEK_IOCTL_I2C_INT_FLAG: + buffer[0] =3D !(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 =3D -EFAULT; + break; + } + TP_DBG(NULL, "ILITEK_IOCTL_I2C_INT_FLAG =3D %d.\n", buffer[0]); + break; + case ILITEK_IOCTL_START_READ_DATA: + ilitek_irq_enable(); + ts->unhandle_irq =3D false; + TP_MSG(NULL, "enable_irq and ts->unhandle_irq =3D false.\n"); + break; + case ILITEK_IOCTL_STOP_READ_DATA: + ilitek_irq_disable(); + ts->unhandle_irq =3D true; + TP_MSG(NULL, "disable_irq and ts->unhandle_irq =3D 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 =3D -EFAULT; + break; +#ifdef ILITEK_TUNING_MESSAGE + case ILITEK_IOCTL_DEBUG_SWITCH: + if (copy_from_user(buffer, (u8 *)arg, 1)) { + ret =3D -EFAULT; + break; + } + TP_MSG(NULL, "ilitek The debug_flag =3D %d.\n", buffer[0]); + if (buffer[0] =3D=3D 0) + ilitek_debug_flag =3D false; + else if (buffer[0] =3D=3D 1) + ilitek_debug_flag =3D true; + break; +#endif + case ILITEK_IOCTL_I2C_SWITCH_IRQ: + if (copy_from_user(buffer, (u8 *)arg, 1)) { + ret =3D -EFAULT; + break; + } + + if (buffer[0] =3D=3D 0) + ilitek_irq_disable(); + else + ilitek_irq_enable(); + + break; + case ILITEK_IOCTL_UPDATE_FLAG: + ts->operation_protection =3D arg; + TP_MSG(NULL, "operation_protection =3D %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 =3D -EFAULT; + break; + } + + ilitek_irq_disable(); + mutex_lock(&ts->ilitek_mutex); + ret =3D 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 =3D 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 =3D -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 =3D (arg >> 16); + ts->irq_read_len =3D 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 =3D ilitek_netlink_init(arg & 0xFF); + else + ilitek_netlink_exit(); + + break; + default: + TP_ERR(NULL, "unrecognized ioctl cmd: 0x%04x\n", cmd); + ret =3D -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 =3D 8192; + + tmp =3D kmalloc(count, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + ilitek_irq_disable(); + mutex_lock(&ts->ilitek_mutex); + ret =3D 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 =3D -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 =3D { + .FOPS_IOCTL =3D ilitek_file_ioctl, + +#ifdef CONFIG_COMPAT + .compat_ioctl =3D ilitek_file_compat_ioctl, +#endif + + .read =3D ilitek_file_read, + .write =3D ilitek_file_write, + .open =3D ilitek_file_open, + .release =3D ilitek_file_close, +}; + +static struct PROC_FOPS_T ilitek_proc_fops =3D { + .PROC_IOCTL =3D ilitek_file_ioctl, + +#ifdef CONFIG_COMPAT + .PROC_COMPAT_IOCTL =3D ilitek_file_compat_ioctl, +#endif + + .PROC_READ =3D ilitek_file_read, + .PROC_WRITE =3D ilitek_file_write, + .PROC_OPEN =3D ilitek_file_open, + .PROC_RELEASE =3D ilitek_file_close, +}; + +static ssize_t ilitek_driver_version_read(struct file *fp, char __user *bu= f, + size_t size, loff_t *off) +{ + int cnt; + u8 str[256]; + + if (*off) + return 0; + + cnt =3D scnprintf(str, sizeof(str), "driver-version-tag: [%*phD]\n", + 7, driver_ver); + + if (copy_to_user(buf, str, cnt)) + return -EFAULT; + + *off +=3D cnt; + + return cnt; +} + +static struct PROC_FOPS_T ilitek_fops_drv_version =3D { + .PROC_READ =3D 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 =3D 0; + u8 str[256]; + + if (*off) + return 0; + + memset(str, 0, sizeof(str)); + error =3D ilitek_upgrade_firmware("ilitek.hex"); + if (error < 0) { + error =3D ilitek_upgrade_firmware("ilitek.bin"); + if (error < 0) + cnt +=3D scnprintf(str, sizeof(str), + "upgrade failed, err: %d\n", error); + } else { + cnt +=3D 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 +=3D cnt; +return cnt; +} + +static struct PROC_FOPS_T ilitek_fops_fwupdate =3D { + .PROC_READ =3D 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 =3D api_update_ts_info(ts->dev); + mutex_unlock(&ts->ilitek_mutex); + ilitek_irq_enable(); + + if (error < 0) + cnt =3D scnprintf(str, sizeof(str), "read failed, err: %d\n", + error); + else + cnt =3D scnprintf(str, sizeof(str), "fw-version-tag: [%*phD]\n", + 8, ts->dev->fw_ver); + + if (copy_to_user(buf, str, cnt)) + return -EFAULT; + + *off +=3D cnt; + + return cnt; +} + +static struct PROC_FOPS_T ilitek_fops_fwversion =3D { + .PROC_READ =3D 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 =3D str; + size_t wlen, rlen, _wlen =3D 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 =3D 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 =3D strsep(&cur, "-")) && _wlen < sizeof(cmd)) + sscanf(ptr, "%hhx", &cmd[_wlen++]); + + if (wlen > 0 && wlen !=3D _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 =3D 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 =3D { + .PROC_WRITE =3D 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 =3D 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 >=3D ts->dev->tp_info.support_modes) + return -EINVAL; + + ilitek_irq_disable(); + mutex_lock(&ts->ilitek_mutex); + error =3D api_set_ctrl_mode(ts->dev, mode_suspend, false, true); + error |=3D api_set_func_mode(ts->dev, func_mode); + error |=3D 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 =3D 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 +=3D scnprintf(str, sizeof(str), "function mode: "); + for (i =3D 0; i < ts->dev->tp_info.support_modes; i++) { + if (i =3D=3D ts->dev->func.mode) + cnt +=3D scnprintf(str + strlen(str), sizeof(str) - cnt, + "[%hhu] ", i); + else + cnt +=3D scnprintf(str + strlen(str), sizeof(str) - cnt, + "%hhu ", i); + } + cnt +=3D scnprintf(str + strlen(str), sizeof(str) - cnt, "\n"); + + if (copy_to_user(buf, str, cnt)) + return -EFAULT; + + *off +=3D cnt; + + return cnt; +} + +static struct PROC_FOPS_T ilitek_fops_func_mode =3D { + .PROC_READ =3D ilitek_func_mode_read, + .PROC_WRITE =3D 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 !=3D 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 =3D 0; + u8 str[256]; + + if (*off) + return 0; + + memset(str, 0, sizeof(str)); + cnt +=3D scnprintf(str, sizeof(str), "key: %*phD\n", + AES_KEY_LEN, crypto_key); + cnt +=3D scnprintf(str + strlen(str), sizeof(str), "iv: %*phD\n", + AES_KEY_LEN, crypto_iv); + + if (copy_to_user(buf, str, cnt)) + return -EFAULT; + + *off +=3D cnt; + + return cnt; +} + +static struct PROC_FOPS_T ilitek_fops_crypto_key =3D { + .PROC_READ =3D ilitek_crypto_key_read, + .PROC_WRITE =3D 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 =3D { + .PROC_READ =3D 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 =3D { + .PROC_READ =3D 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 =3D { + .PROC_READ =3D 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 =3D true; + } else if (!strncmp(buf, "disable", 7) && ts->esd_check) { + ilitek_remove_esd_check_workqueue(); + ts->esd_check =3D 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 =3D 0; + +#define X(_type, _id, _str) {.str =3D _str, .id =3D _id}, + struct { + char *str; + u8 id; + } modes[] =3D { ILITEK_LOW_POWER_TYPES }; +#undef X + + + for (i =3D 0; i < ARRAY_SIZE(modes); i++) { + if (modes[i].id =3D=3D ts->low_power_status) { + cnt +=3D scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, + "[%s] ", modes[i].str); + continue; + } + + cnt +=3D scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, + "%s ", modes[i].str); + } + + cnt +=3D 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 =3D _str, .id =3D _id}, + struct { + char *str; + u8 id; + } modes[] =3D { ILITEK_LOW_POWER_TYPES }; +#undef X + + TP_MSG(NULL, "set low power: %hhx to %s\n", ts->low_power_status, buf); + + for (i =3D 0; i < ARRAY_SIZE(modes); i++) { + if (strncmp(buf, modes[i].str, strlen(modes[i].str))) + continue; + ts->low_power_status =3D 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 =3D 0; + +#define X(_type, _id, _str) {.str =3D _str, .id =3D _id}, + struct { + char *str; + u8 id; + } modes[] =3D { ILITEK_GESTURE_TYPES }; +#undef X + + + for (i =3D 0; i < ARRAY_SIZE(modes); i++) { + if (modes[i].id =3D=3D ts->gesture_status) { + cnt +=3D scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, + "[%s] ", modes[i].str); + continue; + } + + cnt +=3D scnprintf(buf + strlen(buf), PAGE_SIZE - cnt, + "%s ", modes[i].str); + } + + cnt +=3D 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 =3D ts->gesture_status; + +#define X(_type, _id, _str) {.str =3D _str, .id =3D _id}, + struct { + char *str; + u8 id; + } modes[] =3D { ILITEK_GESTURE_TYPES }; +#undef X + + TP_MSG(NULL, "set gesture: %hhx to %s\n", ts->gesture_status, buf); + + for (i =3D 0; i < ARRAY_SIZE(modes); i++) { + if (strncmp(buf, modes[i].str, strlen(modes[i].str))) + continue; + type =3D 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 =3D type; + + return size; +} +static DEVICE_ATTR(gesture, 0664, ilitek_gesture_show, ilitek_gesture_stor= e); + +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 =3D 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, N= ULL); + +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 =3D api_set_ctrl_mode(ts->dev, mode_suspend, false, true); + if (error < 0) { + mutex_unlock(&ts->ilitek_mutex); + ilitek_irq_enable(); + goto out; + } + error =3D api_protocol_set_cmd(ts->dev, GET_MCU_VER, NULL); + if (error < 0) { + mutex_unlock(&ts->ilitek_mutex); + ilitek_irq_enable(); + goto out; + } + error =3D 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 =3D ilitek_upgrade_firmware("ilitek.hex"); + if (error < 0) + error =3D 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[] =3D { + &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[] =3D { + { .attrs =3D ilitek_sysfs_attrs_ctrl }, +}; + +int ilitek_create_sysfsnode(void) +{ + int error; + + error =3D 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 =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "ilitek_ctrl", + .fops =3D &ilitek_fops, + .mode =3D 0666, +}; + +int ilitek_create_tool_node(void) +{ +#ifdef ILITEK_TOOL + misc_register(&ilitek_misc); + + ilitek_proc =3D 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, NUL= L, &ilitek_fops) fail\n"); + + ilitek_proc_entry =3D 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 =3D 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 =3D NULL; + } +#endif + return 0; +} diff --git a/drivers/input/touchscreen/ilitek/ilitek_ts.h b/drivers/input/t= ouchscreen/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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#endif +//#if defined(CONFIG_FB) +#if 0 +#include +#include +#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 +#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 =3D=3D ILITEK_PLAT_QCOM +#ifdef CONFIG_QCOM_DRM +#include +#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 =3D _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 =3D _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 + (DOUBL= E_CLICK_ONE_CLICK_USED_TIME * 2)) + +//#define ILITEK_WAKELOCK_SUPPORT +#if defined(ILITEK_WAKELOCK_SUPPORT) +#include +#endif + +#define ILITEK_TS_NAME "ilitek_ts" + +#define ABSSUB(a, b) ((a > b) ? (a - b) : (b - a)) + +#if ILITEK_PLAT =3D=3D 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#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} //c= hange for you panel key info +#define TPD_KEYS_DIM {{key_1, 50, 30 }, {key_2, 50, 30 }, {key_3, 50, 3= 0 }, {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 +#include +#include "cust_gpio_usage.h" +#include +#include +#include +#include +#include + +#else +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTK_BOOT +#include "mt_boot_common.h" +#endif + +#endif /* MTK_UNDTS */ + +#include "tpd.h" +extern struct tpd_device *tpd; +#endif /* ILITEK_PLAT =3D=3D ILITEK_PLAT_MTK */ + +#if ILITEK_PLAT =3D=3D ILITEK_PLAT_ALLWIN +#include +#include +#include +#include +extern struct ctp_config_info config_info; +#endif + + +#ifndef ILITEK_GET_GPIO_NUM +#if ILITEK_PLAT =3D=3D 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 =3D=3D 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 =3D=3D ILITEK_PLAT_ALLWIN +extern int ilitek_suspend_allwin(struct i2c_client *client, pm_message_t m= esg); +extern int ilitek_resume_allwin(struct i2c_client *client); +#endif + +#endif diff --git a/drivers/input/touchscreen/ilitek/ilitek_update.c b/drivers/inp= ut/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 + * Copyright (c) 2022 Joe Hung + */ + +#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 =3D 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 =3D 0, lo =3D 0; + + while (count--) { + hi =3D hex_to_bin(*src++); + lo =3D 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++ =3D (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 =3D start; i <=3D 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[] =3D "ILITek AP CRC "; + const u8 blk_tag[] =3D "ILITek END TAG "; + + _memset(tag, 0xFF, sizeof(tag)); + _memcpy(tag + 16, (is_AP) ? ap_tag : blk_tag, 16); + + addr =3D 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 =3D (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 mat= ched\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 =3D 0xffff; + + fw->file.mm_addr =3D addr; + switch (addr) { + case 0x4038: + case 0x4020: + fw->file.mm_size =3D 128; + fw->file.fw_ver[0] =3D mapping->_lego.fw_ver[3]; + fw->file.fw_ver[1] =3D mapping->_lego.fw_ver[2]; + fw->file.fw_ver[2] =3D mapping->_lego.fw_ver[1]; + fw->file.fw_ver[3] =3D mapping->_lego.fw_ver[0]; + fw->file.fw_ver[4] =3D buf[0x2C007]; + fw->file.fw_ver[5] =3D buf[0x2C006]; + fw->file.fw_ver[6] =3D buf[0x2C005]; + fw->file.fw_ver[7] =3D buf[0x2C004]; + + fw->file.fwid =3D mapping->_lego.fwid; + break; + case 0x3020: + fw->file.mm_size =3D 128; + fw->file.fw_ver[0] =3D mapping->_lego.fw_ver[3]; + fw->file.fw_ver[1] =3D mapping->_lego.fw_ver[2]; + fw->file.fw_ver[2] =3D mapping->_lego.fw_ver[1]; + fw->file.fw_ver[3] =3D mapping->_lego.fw_ver[0]; + fw->file.fw_ver[4] =3D buf[0x3C007]; + fw->file.fw_ver[5] =3D buf[0x3C006]; + fw->file.fw_ver[6] =3D buf[0x3C005]; + fw->file.fw_ver[7] =3D buf[0x3C004]; + + fw->file.fwid =3D mapping->_lego.fwid; + break; + case 0x2020: + fw->file.mm_size =3D 132; + fw->file.fw_ver[0] =3D buf[0x2033]; + fw->file.fw_ver[1] =3D buf[0x2032]; + fw->file.fw_ver[2] =3D buf[0x2031]; + fw->file.fw_ver[3] =3D buf[0x2030]; + fw->file.fw_ver[4] =3D buf[0xF004]; + fw->file.fw_ver[5] =3D buf[0xF005]; + fw->file.fw_ver[6] =3D buf[0xF006]; + fw->file.fw_ver[7] =3D buf[0xF007]; + + /* for V3 251x IC, get AP crc and DF checksum */ + fw->file.blocks[0].check =3D 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 =3D get_checksum( + fw->file.blocks[1].start, + fw->file.blocks[1].end + 1, buf, buf_size); + } + break; + + case 0x500: + fw->file.mm_size =3D 132; + fw->file.fw_ver[0] =3D buf[0x52D]; + fw->file.fw_ver[1] =3D buf[0x52C]; + fw->file.fw_ver[2] =3D buf[0x52B]; + fw->file.fw_ver[3] =3D buf[0x52A]; + fw->file.fw_ver[4] =3D buf[0x1F404]; + fw->file.fw_ver[5] =3D buf[0x1F405]; + fw->file.fw_ver[6] =3D buf[0x1F406]; + fw->file.fw_ver[7] =3D buf[0x1F407]; + + /* for V3 231x IC, get AP checksum and DF checksum */ + fw->file.blocks[0].check =3D 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 =3D get_checksum( + fw->file.blocks[1].start, + fw->file.blocks[1].end + 1, buf, buf_size); + } + break; + default: + fw->file.mm_size =3D 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 =3D mapping->_lego.block_num; + + TP_MSG(NULL, "Total " PFMT_U8 " blocks\n", fw->file.block_num); + for (i =3D 0; i < fw->file.block_num; i++) { + fw->file.blocks[i].start =3D + le32(mapping->_lego.blocks[i].addr, 3); + fw->file.blocks[i].end =3D (i =3D=3D 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 =3D get_endaddr( + fw->file.blocks[i].start, fw->file.blocks[i].end, + buf, buf_size, i =3D=3D 0); + + fw->file.blocks[i].check =3D 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 =3D 0; + u32 mapping_info_addr =3D 0; + + /* m2v hex has another block at the end of hex file */ + u8 j =3D (fw->m2v) ? fw->file.block_num : 0; + + fw->file.blocks[j].start =3D (~0U); + fw->file.blocks[j].end =3D 0x0; + fw->file.blocks[j].check =3D 0x0; + fw->file.blocks[j + 1].start =3D (~0U); + fw->file.blocks[j + 1].end =3D 0x0; + fw->file.blocks[j + 1].check =3D 0x0; + + for (i =3D start; i < end; i++) { + /* filter out non-hexadecimal characters */ + if (hex_to_bin(hex[i]) < 0) + continue; + + error =3D hex2bin(info, hex + i, sizeof(info)); + if (error < 0) + return error; + + len =3D info[0]; + addr =3D be32(info + 1, 2); + type =3D info[3]; + + error =3D hex2bin(data, hex + i + 8, len); + if (error < 0) + return error; + + switch (type) { + case 0xAC: + mapping_info_addr =3D be32(data, len); + break; + + case 0xAD: + fw->file.blocks[1].start =3D be32(data, len); + _memset(buf + fw->file.blocks[1].start, 0, 0x1000); + break; + + case 0xBA: + if (be32(data, len) !=3D 2U) + break; + + TP_MSG(NULL, "start to decode M2V part of hex file\n"); + fw->m2v =3D true; + ////Reed Add : 20230721=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF= =BF=BD=EF=BF=BD=EF=BF=BD2326=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD= =EF=BF=BD=EF=BF=BD=EF=BF=BDDecode_mm,=EF=BF=BD=D9=BC=EF=BF=BD=EF=BF=BD=EF= =BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BDM2V=EF=BF=BD=EF=BF=BD=EF=BF=BD=D6= =A1=EF=BF=BD=EF=BF=BD=EF=BF=BD + 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 =3D be32(data, len) << 4; + break; + + case 0x04: + exaddr =3D 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 +=3D 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 =3D + MIN(fw->file.blocks[j].start, addr); + + if (addr + len < fw->file.blocks[j + 1].start) { + fw->file.blocks[j].end =3D + MAX(fw->file.blocks[j].end, + addr + len - 1); + fw->file.blocks[j].check +=3D get_checksum( + 0, len, data, sizeof(data)); + } else { + fw->file.blocks[j + 1].end =3D + MAX(fw->file.blocks[j + 1].end, + addr + len - 1); + fw->file.blocks[j + 1].check +=3D 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 =3D i + 10 + len * 2; + } + +success_return: + if (fw->m2v) + fw->m2v_checksum =3D 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 =3D 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 =3D api_protocol_set_cmd(dev, GET_PTL_VER, NULL); + if (error < 0) + return error; + error =3D api_protocol_set_cmd(dev, GET_MCU_VER, NULL); + if (error < 0) + return error; + + switch (dev->protocol.flag) { + case PTL_V6: + mapping_info_addr =3D 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 =3D 0x500; + + fw->file.blocks[1].start =3D 0x1f000; + fw->file.blocks[1].end =3D bin_size - 1; + fw->file.blocks[0].start =3D 0x0; + + /* +2 to get end addr. of last byte of 4 bytes checksum */ + fw->file.blocks[0].end =3D + get_endaddr(fw->file.blocks[0].start, + fw->file.blocks[1].start, + bin, bin_size, true) + 2; + + } else { + mapping_info_addr =3D 0x2020; + fw->file.blocks[1].start =3D 0xf000; + fw->file.blocks[1].end =3D bin_size - 1; + fw->file.blocks[0].start =3D 0x2000; + fw->file.blocks[0].end =3D + 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 =3D fw->dev; + u8 *ili_buf =3D CTPM_FW; + int size =3D 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 de= fault fw(id: 0x%x)\n", + dev->protocol.ver, dev->ic[0].mode, + ILITEK_BOOT_UPDATE_DEF_ID); + fw->file.id =3D ILITEK_BOOT_UPDATE_DEF_ID; + break; + } + + id =3D dev->sensor.id & dev->setting.sensor_id_mask; + if (id >=3D ARRAY_SIZE(ili_arr)) { + TP_ERR(dev->id, "invalid sensor id: " PFMT_U8 " >=3D %d\n", + dev->sensor.id, (int)ARRAY_SIZE(ili_arr)); + return -EINVAL; + } + + fw->file.id =3D id; + ili_buf =3D ili_arr[id].buf; + size =3D 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 =3D true; + fw->setting.fw_ver_policy =3D 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 =3D fw->dev; + u32 id; + +#if defined(ILITEK_BOOT_UPDATE) && defined(ILITEK_BOOT_UPDATE_ILI_VER) + if (fw->file.type !=3D fw_ili) + return false; + + switch (__ili_select_type__) { + case ili_by_sensor_id: + id =3D dev->sensor.id & dev->setting.sensor_id_mask; + if (id =3D=3D 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 =3D 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 =3D 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 =3D 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 =3D 2; + fw->file.blocks[0].start =3D (~0U); + fw->file.blocks[0].end =3D 0x0; + fw->file.blocks[0].check =3D 0x0; + fw->file.blocks[1].start =3D (~0U); + fw->file.blocks[1].end =3D 0x0; + fw->file.blocks[1].check =3D 0x0; + + TP_MSG(NULL, "start to read fw file: " PFMT_C16 "\n", filename); + + buf =3D (u8 *)CALLOC(ILITEK_FW_FILE_SIZE, 1); + if (!buf) + return -ENOMEM; + + file_ext =3D WCSRCHR(filename, '.'); + if (!file_ext) + return -ENOENT; + + /* no need to read .ili file */ + if (WCSCASECMP(file_ext, ".ili")) { + if (!fw->cb.read_fw) { + error =3D -EFAULT; + TP_ERR(NULL, "read fw callback not registered\n"); + goto err_free; + } + + size =3D fw->cb.read_fw(filename, buf, ILITEK_FW_FILE_SIZE, + fw->_private); + + error =3D 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 =3D fw_hex; + error =3D decode_hex(fw, buf, 0, size, fw->file.buf, + fw->file.buf_size); + } else if (!WCSCASECMP(file_ext, ".bin")) { + fw->file.type =3D fw_bin; + error =3D 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 =3D fw_ili; + error =3D decode_ili(fw, fw->file.buf, fw->file.buf_size); + } +#endif + else { + error =3D -EINVAL; + } + +err_free: + CFREE(buf); + + return error; +} + +static bool need_fw_update_v3(struct ilitek_fw_handle *fw) +{ + struct ilitek_ts_device *dev =3D fw->dev; + bool fw_file_has_data_flash =3D + (fw->file.blocks[1].start < fw->file.blocks[1].end); + bool need =3D false; + + TP_INFO(dev->id, "------------V3 AP/DF Info.------------\n"); + + fw->file.blocks[0].check_match =3D (fw->setting.force_update) ? + false : dev->ic[0].crc[0] =3D=3D 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 =3D true; + if (fw_file_has_data_flash) { + fw->file.blocks[1].check_match =3D (fw->setting.force_update) ? + false : dev->ic[0].crc[1] =3D=3D 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 =3D true; + } + + TP_INFO(dev->id, "--------------------------------------\n"); + + need |=3D (!fw->file.blocks[0].check_match || + !fw->file.blocks[1].check_match); + + if (dev->ic[0].mode =3D=3D BL_MODE) + return true; + + return need; +} + +static bool need_fw_update_v6(struct ilitek_fw_handle *fw) +{ + struct ilitek_ts_device *dev =3D fw->dev; + u8 i; + bool need =3D false; + + TP_INFO(dev->id, "------------Lego Block Info.------------\n"); + + for (i =3D 0; i < fw->file.block_num; i++) { + dev->ic[0].crc[i] =3D api_get_block_crc_by_addr(dev, + CRC_CALCULATE, fw->file.blocks[i].start, + fw->file.blocks[i].end); + } + + for (i =3D 0; i < fw->file.block_num; i++) { + fw->file.blocks[i].check_match =3D (fw->setting.force_update) ? + false : (dev->ic[0].crc[i] =3D=3D fw->file.blocks[i].check); + + need =3D (!fw->file.blocks[i].check_match) ? true : need; + + TP_INFO(dev->id, "Block[" PFMT_U8 "]: Start/End Addr.: 0x%x/0x%x, IC/Fil= e 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 =3D=3D BL_MODE) { + need =3D true; + goto force_return; + } + + for (i =3D 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 =3D=3D dev->ic[i].crc[0]) ? + "matched" : "not matched", + dev->ic[i].mode, dev->ic[i].mode_str); + + if (dev->ic[i].crc[0] =3D=3D fw->file.blocks[0].check && + dev->ic[i].mode =3D=3D AP_MODE) + continue; + need =3D 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 =3D + fw->file.blocks[fw->file.block_num].check =3D=3D + 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 =3D + !fw->file.blocks[fw->file.block_num].check_match || + fw->setting.force_update; + need |=3D 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 =3D fw->dev; + bool need =3D false; + + struct ilitek_fw_settings *set =3D &fw->setting; + u64 dev_fw_ver, file_fw_ver; + + if (dev->protocol.flag =3D=3D PTL_V3) + need =3D need_fw_update_v3(fw); + else if (dev->protocol.flag =3D=3D PTL_V6) + need =3D 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 =3D=3D 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-%0= 2x\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 =3D + 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 =3D + 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 =3D=3D 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 =3D 0; + struct ilitek_ts_device *dev =3D fw->dev; + unsigned int i; + u16 file_crc; + int retry =3D 3; + + TP_MSG(dev->id, "updating block[%d], data len: %u, start/end addr: 0x%x/0= x%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 =3D 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 =3D fw->file.blocks[idx].start; + i < fw->file.blocks[idx].end; i +=3D 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 =3D api_write_data_v6(dev, len + 1); + + if (error < 0) + goto err_retry; + + fw->progress_curr =3D MIN(i + len - fw->file.blocks[idx].offset, + fw->progress_max); + fw->progress =3D (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 =3D 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] =3D + 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 =3D=3D dev->ic[0].crc[idx]) ? + "matched" : "not matched"); + + if (file_crc !=3D dev->ic[0].crc[idx]) { + error =3D -EINVAL; + goto err_retry; + } + + return 0; +} + +static int update_slave(struct ilitek_fw_handle *fw) +{ + int error; + struct ilitek_ts_device *dev =3D fw->dev; + u8 i; + + for (i =3D 0; i < fw->file.block_num; i++) { + dev->ic[0].crc[i] =3D api_get_block_crc_by_addr(dev, + CRC_CALCULATE, fw->file.blocks[i].start, + fw->file.blocks[i].end); + } + + error =3D api_protocol_set_cmd(dev, GET_AP_CRC, &dev->tp_info.ic_num); + if (error < 0) + return error; + + for (i =3D 0; i < dev->tp_info.ic_num; i++) { + if (dev->ic[0].crc[0] =3D=3D 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 =3D api_access_slave(dev, 0x3, CMD_WRITE_DATA_V6, NULL); + if (error < 0) + return error; + error =3D 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 =3D api_protocol_set_cmd(dev, GET_MCU_MOD, &dev->tp_info.ic_num); + if (error < 0) + return error; + + for (i =3D 0; i < dev->tp_info.ic_num; i++) { + if (dev->ic[i].mode =3D=3D 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 =3D 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 =3D fw->dev; + u32 i; + u8 buf[6]; + u32 AdressRange, M2V_Buf_Len, CheckSumRange; + u16 AddDataCount; + + u8 j =3D fw->file.block_num; + + error =3D 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 =3D fw->file.blocks[j].end - fw->file.blocks[j].start; + M2V_Buf_Len =3D fw->file.blocks[j].end - fw->file.blocks[j].start + 1; + CheckSumRange =3D fw->file.blocks[j].check; + AddDataCount =3D len - M2V_Buf_Len % len; + AddDataCount =3D (AddDataCount >=3D 8) ? AddDataCount : AddDataCount + le= n; + AdressRange +=3D AddDataCount; + CheckSumRange +=3D AddDataCount * 0xFF; + + error =3D api_to_bl_mode_m2v(dev, true); + if (error < 0) + return error; + + dev->cb.delay_ms(100);//Reed Add : 20230927 + + error =3D 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] =3D (AdressRange >> 16) & 0xFF; + buf[1] =3D (AdressRange >> 8) & 0xFF; + buf[2] =3D AdressRange & 0xFF; + buf[3] =3D (CheckSumRange >> 16) & 0xFF; + buf[4] =3D (CheckSumRange >> 8) & 0xFF; + buf[5] =3D CheckSumRange & 0xFF; + dev->cb.delay_ms(100);//Reed Add : 20230927 + error =3D api_access_slave(dev, 0x80, CMD_WRITE_ENABLE, buf); + if (error < 0) + return error; + + _memset(dev->wbuf, 0xff, sizeof(dev->wbuf)); + for (i =3D fw->file.blocks[j].start; + i < fw->file.blocks[j].end; i +=3D len) { + _memcpy(dev->wbuf + 1, fw->m2v_buf + i, len); + + error =3D api_write_data_m2v(dev, len + 1); + if (error < 0) + return error; + + fw->progress_curr =3D MIN(fw->progress_curr + len, + fw->progress_max); + fw->progress =3D (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 =3D api_to_bl_mode_m2v(dev, false); + if (error < 0) + return error; + + dev->cb.delay_ms(100);//Reed Add : 20230927 + + error =3D 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 =3D fw->dev; + u8 i; + + error =3D api_set_data_len(dev, fw->update_len); + if (error < 0) + return error; + + for (i =3D 0; i < fw->file.block_num; i++) { + if (fw->file.blocks[i].check_match) + continue; + + error =3D 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 =3D api_to_bl_mode(dev, false, fw->file.blocks[0].start, + fw->file.blocks[0].end); + if (error < 0) + return error; + + error =3D api_set_ctrl_mode(dev, mode_suspend, false, true); + if (error < 0) + return error; + + /* get tp info. for updating ic_num */ + error =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 che= cksum: 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 =3D 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 =3D fw->file.blocks[1].start; + i <=3D fw->file.blocks[1].end; i +=3D 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 =3D api_write_data_v3(dev); + if (error < 0) + return error; + + dev->cb.delay_ms(2); + + error =3D api_check_busy(dev, 1000, 10); + if (error < 0) + return error; + + fw->progress_curr +=3D 32; + fw->progress_curr =3D MIN(fw->progress_curr, + fw->progress_max); + fw->progress =3D (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] =3D CMD_GET_AP_CRC; + error =3D write_then_read(dev, dev->wbuf, 1, NULL, 0); + if (error < 0) + return error; + error =3D api_check_busy(dev, 1000, 10); + if (error < 0) + return error; + + dev->wbuf[0] =3D CMD_GET_AP_CRC; + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4); + if (error < 0) + return error; + + dev->ic[0].crc[1] =3D (le16(dev->rbuf + 2) << 16) | le16(dev->rbuf); + + TP_INFO(dev->id, "DF block, start/end addr.: 0x%x/0x%x, ic/file checkksu= m: 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] =3D=3D fw->file.blocks[1].check) ? + "matched" : "not matched"); + + if (dev->ic[0].crc[1] !=3D 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 =3D 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 =3D fw->file.blocks[0].start; + i <=3D fw->file.blocks[0].end; i +=3D 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 =3D api_write_data_v3(dev); + if (error < 0) + return error; + + dev->cb.delay_ms(2); + + error =3D api_check_busy(dev, 1000, 10); + if (error < 0) + return error; + + fw->progress_curr +=3D 32; + fw->progress_curr =3D MIN(fw->progress_curr, + fw->progress_max); + fw->progress =3D (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] =3D CMD_GET_AP_CRC; + error =3D write_then_read(dev, dev->wbuf, 1, NULL, 0); + if (error < 0) + return error; + error =3D api_check_busy(dev, 1000, 10); + if (error < 0) + return error; + + dev->wbuf[0] =3D CMD_GET_AP_CRC; + error =3D write_then_read(dev, dev->wbuf, 1, dev->rbuf, 4); + if (error < 0) + return error; + + dev->ic[0].crc[0] =3D + (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] =3D=3D fw->file.blocks[0].check) ? + "matched" : "not matched"); + + if (dev->ic[0].crc[0] !=3D 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 =3D fw->dev; + struct ilitek_ts_callback *cb =3D &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 ch= ecksum: 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 =3D ((fw->file.blocks[1].end + 1) % 32) ? + 31 + 32 - ((fw->file.blocks[1].end + 1) % 32) : 31; + end =3D fw->file.blocks[1].end + bytes; + check =3D 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 =3D api_write_enable_v3(dev, false, false, end + 1, check); + if (error < 0) + return error; + + for (i =3D fw->file.blocks[1].start, j =3D 0; + i <=3D fw->file.blocks[1].end; i +=3D 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 =3D api_write_data_v3(dev); + if (error < 0) + return error; + + cb->delay_ms((j % 16) ? 1 : 5); + + fw->progress_curr =3D MIN(fw->progress_curr + 32, + fw->progress_max); + fw->progress =3D (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] =3D 0; + error =3D 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 cr= c: 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 =3D ((fw->file.blocks[0].end + 1) % 32) ? + 31 + 32 - ((fw->file.blocks[0].end + 1) % 32) : 31; + end =3D fw->file.blocks[0].end + bytes; + check =3D 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 =3D api_write_enable_v3(dev, false, true, end + 1, check); + if (error < 0) + return error; + + for (i =3D fw->file.blocks[0].start, j =3D 0; + i <=3D fw->file.blocks[0].end; i +=3D 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 =3D api_write_data_v3(dev); + if (error < 0) + return error; + + cb->delay_ms((j % 16) ? 1 : 5); + + fw->progress_curr =3D MIN(fw->progress_curr + 32, + fw->progress_max); + fw->progress =3D (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] =3D 0; + error =3D 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 =3D fw->dev; + u8 i; + unsigned int last_end =3D 0, last_offset =3D 0; + + fw->progress =3D 0; + fw->progress_max =3D 0; + fw->progress_curr =3D 0; + + switch (dev->protocol.flag) { + case PTL_V3: + fw->progress_max +=3D + (fw->file.blocks[1].end > fw->file.blocks[1].start) ? + fw->file.blocks[1].end - fw->file.blocks[1].start : 0; + fw->progress_max +=3D + (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 =3D 0; i < fw->file.block_num; i++) { + if (fw->file.blocks[i].check_match) + continue; + + fw->progress_max +=3D + fw->file.blocks[i].end - + fw->file.blocks[i].start; + last_offset +=3D fw->file.blocks[i].start - last_end; + fw->file.blocks[i].offset =3D last_offset; + + last_end =3D fw->file.blocks[i].end; + } + + if (fw->m2v_need_update) { + fw->progress_max +=3D + 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 =3D (struct ilitek_ts_device *)_dev; + + if (need_update_ts_info && dev && api_update_ts_info(dev) < 0) + return NULL; + + fw =3D (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 =3D (dev) ? dev : NULL; + + /* initial update-len to default UPDATE_LEN */ + fw->update_len =3D UPDATE_LEN; + + fw->dev =3D dev; + fw->_private =3D _private; + fw->file.buf_size =3D ILITEK_FW_BUF_SIZE; + fw->file.buf =3D (u8 *)CALLOC(fw->file.buf_size, 1); + if (!fw->file.buf) + goto err_free_fw; + + fw->m2v_buf =3D (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 =3D (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 =3D (struct ilitek_fw_handle *)handle; + + if (!fw) + return; + + fw->update_len =3D len; +} + +int ilitek_update_load_fw(void *handle, WCHAR *fw_name) +{ + int error; + struct ilitek_fw_handle *fw =3D (struct ilitek_fw_handle *)handle; + + u32 i; + + if (!handle) + return -EINVAL; + + error =3D 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 =3D=3D PTL_V6) { + for (i =3D 0; i < fw->file.block_num; i++) { + if (fw->dev->mcu_info.min_addr <=3D + 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 <=3D 0x%x/0x%x < 0x%x) o= r 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 =3D 0; + struct ilitek_fw_handle *fw =3D (struct ilitek_fw_handle *)handle; + struct ilitek_ts_device *dev; + + if (!handle) + return -EINVAL; + dev =3D 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 =3D 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 =3D 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 =3D 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 =3D ilitek_update_BL_v1_8(fw); + break; + case BL_PROTOCOL_V1_7: + error =3D ilitek_update_BL_v1_7(fw); + break; + case BL_PROTOCOL_V1_6: + error =3D 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 =3D 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 =3D api_erase_data_v3(dev); + if (error < 0) + continue; + } + +success_return: + error =3D api_update_ts_info(dev); + if (error < 0) + continue; + + error =3D 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 *settin= g) +{ + struct ilitek_fw_handle *fw =3D (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/inp= ut/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 + * Copyright (c) 2022 Joe Hung + */ + +#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 =3D 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 Flas= h. + * 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 =3D 1, + allow_fw_ver_same =3D 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\24= 0\201\346\217\220\344\272\244\346\265\201\347\250\213 .docx" "b/drivers/inp= ut/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..54af506a4b7c4546aaa12ec99f1= 36c9083b62a83 GIT binary patch literal 162 zcmZQCuPRR|FHUA439vJiGgJX_3J@1FfHc>ZFflPOG_>^}{sH8{XrBE*K_md