From a0a0b42641597ad1623e64e045ba6a986cc48d5a Mon Sep 17 00:00:00 2001 From: wingte49 Date: Tue, 8 Mar 2022 10:43:35 +0800 Subject: [PATCH] dlkm: chipone_tddi_fhd: add initial chipone fhd tddi driver chipone_tddi_fhd_mmi: Add initial fhd tddi touch driver. This driver is for icnl9922 and later fhd driver. Change-Id: I2cce6e8d624e56fbaae963c4da47ad0c063f7e65 Reviewed-on: https://gerrit.mot.com/2209608 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yeqing Wang Reviewed-by: Huosheng Liao Reviewed-by: Jun Weng Submit-Approved: panyq6 --- .../chipone_tddi_fhd_mmi/Android.mk | 17 + .../touchscreen/chipone_tddi_fhd_mmi/Kbuild | 26 + .../touchscreen/chipone_tddi_fhd_mmi/Makefile | 22 + .../cts_builtin_firmware.h | 24 + .../chipone_tddi_fhd_mmi/cts_charger_detect.c | 1104 ++++++ .../chipone_tddi_fhd_mmi/cts_charger_detect.h | 44 + .../chipone_tddi_fhd_mmi/cts_config.h | 143 + .../chipone_tddi_fhd_mmi/cts_core.c | 3263 +++++++++++++++++ .../chipone_tddi_fhd_mmi/cts_core.h | 890 +++++ .../chipone_tddi_fhd_mmi/cts_firmware.c | 1162 ++++++ .../chipone_tddi_fhd_mmi/cts_firmware.h | 74 + .../chipone_tddi_fhd_mmi/cts_hostcomm.c | 533 +++ .../chipone_tddi_fhd_mmi/cts_hostcomm.h | 9 + .../chipone_tddi_fhd_mmi/cts_i2c_driver.c | 960 +++++ .../cts_plat_qcom_config.h | 66 + .../chipone_tddi_fhd_mmi/cts_platform.c | 1259 +++++++ .../chipone_tddi_fhd_mmi/cts_platform.h | 262 ++ .../chipone_tddi_fhd_mmi/cts_sfctrl.h | 38 + .../chipone_tddi_fhd_mmi/cts_sfctrlv2.c | 354 ++ .../chipone_tddi_fhd_mmi/cts_spi_flash.c | 532 +++ .../chipone_tddi_fhd_mmi/cts_spi_flash.h | 73 + .../chipone_tddi_fhd_mmi/cts_sysfs.c | 2778 ++++++++++++++ .../chipone_tddi_fhd_mmi/cts_sysfs.h | 25 + .../chipone_tddi_fhd_mmi/cts_tcs.c | 1651 +++++++++ .../chipone_tddi_fhd_mmi/cts_tcs.h | 228 ++ .../chipone_tddi_fhd_mmi/cts_test.c | 1350 +++++++ .../chipone_tddi_fhd_mmi/cts_test.h | 17 + .../chipone_tddi_fhd_mmi/cts_tool.c | 803 ++++ 28 files changed, 17707 insertions(+) create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h create mode 100644 drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk new file mode 100644 index 000000000000..7f385e3e8c8e --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +CHIPONE_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +CHIPONE_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +ifeq ($(CHIPONE_ESD),true) + KERNEL_CFLAGS += CONFIG_CHIPONE_ESD=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := chipone_tddi_fhd_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(CHIPONE_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild new file mode 100644 index 000000000000..1a3f606cda13 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild @@ -0,0 +1,26 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/chipone_tddi_fhd_mmi +ifneq ($(filter m y,$(CONFIG_CHIPONE_ESD)),) +EXTRA_CFLAGS += -DCONFIG_CTS_ESD_PROTECTION +endif + +#EXTRA_CFLAGS += -DCONFIG_CTS_I2C_HOST + + +obj-m := chipone_tddi_fhd_mmi.o + +chipone_tddi_fhd_mmi-objs += cts_i2c_driver.o +chipone_tddi_fhd_mmi-objs += cts_core.o +chipone_tddi_fhd_mmi-objs += cts_platform.o +chipone_tddi_fhd_mmi-objs += cts_sfctrlv2.o +chipone_tddi_fhd_mmi-objs += cts_spi_flash.o +chipone_tddi_fhd_mmi-objs += cts_firmware.o +chipone_tddi_fhd_mmi-objs += cts_tool.o +chipone_tddi_fhd_mmi-objs += cts_sysfs.o +chipone_tddi_fhd_mmi-objs += cts_test.o +chipone_tddi_fhd_mmi-objs += cts_charger_detect.o +chipone_tddi_fhd_mmi-objs += cts_hostcomm.o +chipone_tddi_fhd_mmi-objs += cts_tcs.o + diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile new file mode 100644 index 000000000000..5f4305cd8e77 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile @@ -0,0 +1,22 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +obj-$(CONFIG_TOUCHSCREEN_CHIPONE) += chipone-tddi.o + +chipone-tddi-objs := cts_i2c_driver.o +chipone-tddi-objs += cts_core.o cts_sfctrlv2.o cts_spi_flash.o cts_firmware.o cts_test.o +chipone-tddi-objs += cts_charger_detect.o +chipone-tddi-objs += cts_hostcomm.o +chipone-tddi-objs += cts_tcs.o +chipone-tddi-objs += cts_platform.o +chipone-tddi-objs += cts_tool.o +chipone-tddi-objs += cts_sysfs.o + diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h new file mode 100644 index 000000000000..5ea7b7bc3166 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +static u8 icnl9911s_driver_builtin_firmware[] = { +}; + +static u8 icnl9911c_driver_builtin_firmware[] = { +}; + +const static struct cts_firmware cts_driver_builtin_firmwares[] = { + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9911S, + .fwid = CTS_DEV_FWID_ICNL9911S, + .data = icnl9911s_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9911s_driver_builtin_firmware), + }, + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9911C, + .fwid = CTS_DEV_FWID_ICNL9911C, + .data = icnl9911c_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9911c_driver_builtin_firmware), + }, + +}; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c new file mode 100644 index 000000000000..2baf4aacab26 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c @@ -0,0 +1,1104 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Charger" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" + +#ifdef CONFIG_CTS_CHARGER_DETECT + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/** + * match_string - matches given string in an array + * @array: array of strings + * @n: number of strings in the array or -1 for NULL terminated arrays + * @string: string to match with + * + * Return: + * index of a @string in the @array if matches, or %-EINVAL otherwise. + */ +int match_string(const char *const *array, size_t n, const char *string) +{ + int index; + const char *item; + + for (index = 0; index < n; index++) { + item = array[index]; + if (!item) + break; + if (!strcmp(item, string)) + return index; + } + + return -EINVAL; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#define CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#endif +#define CFG_CTS_CHARGER_DETECT_PSY_POLL + +#include +#include + +enum cts_charger_detect_type { + CTS_CHGR_DET_TYPE_NONE = 0, + CTS_CHGR_DET_TYPE_PSY_NOTIFY, + CTS_CHGR_DET_TYPE_POLL_PSP, + CTS_CHGR_DET_TYPE_MAX +}; + +/* Over-written setting for DTS */ +/* #define CFG_CTS_DEF_CHGR_DET_ENABLE */ + +/* Default settings */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_PSY_NOTIFY +#else /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_POLL_PSP +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#define CFG_CTS_DEF_CHGR_DET_PSY_NAME "usb" +#define CFG_CTS_DEF_CHGR_DET_PSY_PROP POWER_SUPPLY_PROP_ONLINE +#define CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL 2000u + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/* Lower version has no power_supply_get_property() */ +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + power_supply_get_property(psy, psp, val) +#else +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + psy->get_property(psy, psp, val) +#endif + +struct cts_charger_detect_data { + bool enable; + bool running; + bool state; + + /* Parameter */ + enum cts_charger_detect_type type; + const char *psy_name; + enum power_supply_property psp; + u32 psp_poll_interval; /* Unit is ms */ + + /* Power supply notify */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + bool notifier_registered; + struct notifier_block psy_notifier; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + /* Polling power supply property */ + struct delayed_work psp_poll_work; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + struct work_struct set_charger_state_work; + + /* Sysfs */ +#ifdef CONFIG_CTS_SYSFS + bool sysfs_created; +#endif /* CONFIG_CTS_SYSFS */ + + struct chipone_ts_data *cts_data; + +}; + +#define POWER_SUPPLY_PROP_PREFIX "POWER_SUPPLY_PROP_" +#define POWER_SUPPLY_PROP_TOKEN(property) \ + {.prop = POWER_SUPPLY_PROP_ ##property, \ + .name = POWER_SUPPLY_PROP_PREFIX #property} + +const static struct { + enum power_supply_property prop; + const char *name; +} power_supply_prop_token[] = { + POWER_SUPPLY_PROP_TOKEN(STATUS), + POWER_SUPPLY_PROP_TOKEN(PRESENT), + POWER_SUPPLY_PROP_TOKEN(ONLINE), +}; + +static const char *power_supply_prop_str(enum power_supply_property prop) +{ + int i; + + for (i = 0; i < sizeof(power_supply_prop_token); i++) { + if (prop == power_supply_prop_token[i].prop) + return power_supply_prop_token[i].name; + } + + return "Unknown"; +} + +static enum power_supply_property power_supply_prop_from_name(const char *name) +{ + size_t offset = strlen(POWER_SUPPLY_PROP_PREFIX); + int i; + + for (i = 0; i < ARRAY_SIZE(power_supply_prop_token); i++) { + if (strcasecmp(power_supply_prop_token[i].name + offset, name) + == 0 + || strcasecmp(power_supply_prop_token[i].name, name) == 0) { + return power_supply_prop_token[i].prop; + } + } + + return -ENOENT; +} + +static const char *charger_detect_type_text[] = { + "none", "notify", "poll", +}; + +static const char *charger_detect_type_str(enum cts_charger_detect_type type) +{ + return type < ARRAY_SIZE(charger_detect_type_text) ? + charger_detect_type_text[type] : "Unknown"; +} + +static int parse_charger_detect_dt(struct cts_charger_detect_data *cd_data, + struct device_node *np) +{ + const char *psy_name; + const char *type_str; + const char *psp_str; + int ret; + + cts_info("Parse dt"); + +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = + of_property_read_bool(np, "chipone,touch-charger-detect-enable"); +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-type", + &type_str); + if (ret) + cts_warn("Parse detect type failed %d", ret); + else { + int type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), + type_str); + if (type < 0) + cts_err("Parse detect type '%s' invalid", type_str); + else + cd_data->type = type; + } + + ret = of_property_read_string(np, + "chipone,touch-charger-detect-psy-name", + &psy_name); + if (ret) { + cts_warn("Parse detect psy name failed %d", ret); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } else { + if (power_supply_get_by_name(psy_name) == NULL) { + cts_warn("Power supply '%s' not found", psy_name); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } + } + cd_data->psy_name = kstrdup(psy_name, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Alloc mem for psy name failed"); + return -ENOMEM; + } + + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-psp", + &psp_str); + if (ret) { + cts_warn("Parse detect psp failed %d", ret); + } else { + struct power_supply *psy; + enum power_supply_property psp; + union power_supply_propval val; + + psp = power_supply_prop_from_name(psp_str); + if (psp < 0) { + cts_warn("Parse detect psp: '%s' invalid", psp_str); + } else { + psy = power_supply_get_by_name(cd_data->psy_name); + if (psy != NULL && + POWER_SUPPLY_GET_PROPERTY(psy, psp, &val) >= 0) { + cts_err("Parse detect psp invalid"); + } else { + cd_data->psp = psp; + } + } + } + + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + ret = of_property_read_u32(np, + "chipone,touch-charger-detect-psp-poll-interval", + &cd_data->psp_poll_interval); + if (ret) + cts_warn("Parse detect psp poll interval failed %d", ret); + + return 0; +} + +static int start_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->enable) { + cts_warn("Start detect while NOT enabled"); + return -EINVAL; + } + + if (cd_data->running) { + cts_warn("Start detect while already RUNNING"); + return 0; + } + + cts_info("Start detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if (cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + int ret = power_supply_reg_notifier(&cd_data->psy_notifier); + + if (ret) { + cts_err("Register notifier failed: %d", ret); + return ret; + } + cd_data->notifier_registered = true; + + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if (cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies + (cd_data->psp_poll_interval))) { + cts_warn + ("Queue detect work while already on the queue"); + } + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return -ENOTSUPP; +} + +static int stop_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->running) { + cts_warn("Stop detect while NOT running"); + return 0; + } + + cts_info("Stop detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if (cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + if (cd_data->notifier_registered) { + power_supply_unreg_notifier(&cd_data->psy_notifier); + cd_data->notifier_registered = false; + } + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if (cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!cancel_delayed_work_sync(&cd_data->psp_poll_work)) + cts_warn("Cancel poll psp work while NOT pending"); + + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return 0; +} + +static int enable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + cts_info("Enable detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + + cd_data->enable = true; + + return 0; +} + +static int disable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + + cts_info("Disable detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = stop_charger_detect(cd_data); + if (ret) + cts_err("Disable detect failed %d", ret); + + cd_data->enable = false; + + return 0; +} + +static int get_charger_state(struct cts_charger_detect_data *cd_data) +{ + struct power_supply *psy; + union power_supply_propval propval; + int ret; + + cts_dbg("Get state from psy: '%s' prop: %d(%s)", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + + psy = power_supply_get_by_name(cd_data->psy_name); + if (cd_data->psy_name == NULL || psy == NULL) { + cts_err("Get state from psy: '%s' not found", + cd_data->psy_name); + return -EINVAL; + } + + ret = POWER_SUPPLY_GET_PROPERTY(psy, cd_data->psp, &propval); + if (ret < 0) { + cts_err("Get state from psy: '%s' prop: %d(%s) failed", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + return -EINVAL; + } + + /* ONLY for bool type */ + cd_data->state = !!propval.intval; + + cts_dbg("State: %s", cd_data->state ? "ATTACHED" : "DETACHED"); + + return 0; +} + +static int switch_charger_detect_type(struct cts_charger_detect_data *cd_data, + enum cts_charger_detect_type type) +{ + bool running; + + cts_info("Switch detect type to %d(%s)", + type, charger_detect_type_str(type)); + + if (type >= CTS_CHGR_DET_TYPE_MAX) { + cts_err("Switch detect type %d invalid", type); + return -EINVAL; + } + + if (cd_data->type == type) { + cts_warn("Switch detect type equal"); + return 0; + } + + running = cd_data->running; + + if (running) { + int ret = stop_charger_detect(cd_data); + + if (ret) { + cts_err("Stop detect failed %d", ret); + return ret; + } + } + + cd_data->type = type; + + if (running) { + int ret = start_charger_detect(cd_data); + + if (ret) { + cts_err("Start detect failed %d", ret); + cd_data->type = CTS_CHGR_DET_TYPE_NONE; + return ret; + } + } + + return 0; +} + +static void set_dev_charger_state_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cd_data = container_of(work, struct cts_charger_detect_data, + set_charger_state_work); + + cts_lock_device(&cd_data->cts_data->cts_dev); + ret = cts_set_dev_charger_attached(&cd_data->cts_data->cts_dev, + cd_data->state); + cts_unlock_device(&cd_data->cts_data->cts_dev); + if (ret) { + cts_err("Set dev charger attached to %s failed %d", + cd_data->state ? "ATTACHED" : "DETATCHED", ret); + /* Set to previous state, try set again in next loop */ + cd_data->state = !cd_data->state; + } +} + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +static int psy_notify_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cts_charger_detect_data *cd_data; + struct power_supply *psy; + bool prev_state; + int ret; + + if (nb == NULL || data == NULL) { + cts_err + ("PSY notify callback with notifier %p or data %p = NULL", + nb, data); + return NOTIFY_DONE; + } + + if (action != PSY_EVENT_PROP_CHANGED) { + cts_dbg("Notify event %ld not care", action); + return NOTIFY_DONE; + } + + cd_data = + container_of(nb, struct cts_charger_detect_data, psy_notifier); + + psy = (struct power_supply *)data; + if (strcmp(psy->desc->name, cd_data->psy_name) != 0) { + cts_dbg("Notify from power supply '%s' not care", + psy->desc->name); + return NOTIFY_DONE; + } + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + return NOTIFY_DONE; + } + + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn("Set device charger state work is PENDING"); + } + + return NOTIFY_OK; +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL +static void poll_psp_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + bool prev_state; + int ret; + + cts_dbg("Poll psp work"); + + cd_data = container_of(to_delayed_work(work), + struct cts_charger_detect_data, psp_poll_work); + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + } else { + if (cd_data->state != prev_state) { + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn + ("Set device charger state work is PENDING"); + } + } + } + + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies(cd_data->psp_poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + +static int init_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + cd_data->psy_notifier.notifier_call = psy_notify_callback; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + INIT_DELAYED_WORK(&cd_data->psp_poll_work, poll_psp_work); +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + INIT_WORK(&cd_data->set_charger_state_work, set_dev_charger_state_work); + + ret = get_charger_state(cd_data); + if (ret) + cts_err("Get state failed %d", ret); + + return 0; +} + +/* Sysfs */ +#ifdef CONFIG_CTS_SYSFS +extern int argc; +extern char *argv[]; +extern int parse_arg(const char *buf, size_t count); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/* From lib/kstrtox.c */ +/** + * kstrtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0', or + * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value + * pointed to by res is updated upon finding a match. + */ +int kstrtobool(const char *s, bool *res) +{ + if (!s) + return -EINVAL; + + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + return 0; + case 'n': + case 'N': + case '0': + *res = false; + return 0; + case 'o': + case 'O': + switch (s[1]) { + case 'n': + case 'N': + *res = true; + return 0; + case 'f': + case 'F': + *res = false; + return 0; + default: + break; + } + default: + break; + } + + return -EINVAL; +} +#endif + +#define CHARGER_DET_SYSFS_GROUP_NAME "charger-det" + +static ssize_t charger_detect_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect: %s\n", + cd_data->enable ? "ENABLED" : "DISABLED"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > enable */ +static ssize_t charger_detect_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool enable; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &enable); + if (ret) { + cts_err("Invalid param of enable"); + return ret; + } + + if (enable) + ret = enable_charger_detect(cd_data); + else + ret = disable_charger_detect(cd_data); + + if (ret) { + cts_err("%s charger detect failed %d", + enable ? "Enable" : "Disable", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + charger_detect_enable_show, charger_detect_enable_store); + +static ssize_t charger_detect_running_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect: %sRunning\n", + cd_data->running ? "" : "Not-"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > runing */ +static ssize_t charger_detect_running_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool running; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &running); + if (ret) { + cts_err("Invalid param of running"); + return ret; + } + + if (running) + ret = start_charger_detect(cd_data); + else + ret = stop_charger_detect(cd_data); + + if (ret) { + cts_err("%s charger detect failed %d", + running ? "Start" : "Stop", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(running, S_IWUSR | S_IRUGO, + charger_detect_running_show, charger_detect_running_store); + +static ssize_t charger_detect_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect type: %d(%s)\n", + cd_data->type, charger_detect_type_str(cd_data->type)); +} + +/* echo charger_detect_type > type */ +static ssize_t charger_detect_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int type, ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err(""); + return -EINVAL; + } + + type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), argv[0]); + if (type < 0) { + cts_err("Invalid charger detect type: '%s'", argv[0]); + return -EINVAL; + } + + ret = switch_charger_detect_type(cd_data, type); + if (ret) { + cts_err("Switch charger detect type failed %d", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(type, S_IWUSR | S_IRUGO, + charger_detect_type_show, charger_detect_type_store); + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int ret; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + ret = get_charger_state(cd_data); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Get charge state failed %d\n", ret); + } + + return scnprintf(buf, PAGE_SIZE, + "Charger state: %s\n", + cd_data->state ? "ATTACHED" : "DETACHED"); +} + +static DEVICE_ATTR(state, S_IRUGO, charger_state_show, NULL); + +static ssize_t charger_detect_param_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + psy = power_supply_get_by_name(cd_data->psy_name); + + return scnprintf(buf, PAGE_SIZE, + "Power supply name: %s(%sExist)\n" + "Power supply prop: %d(%s)\n" + "Poll interval : %dms\n", + cd_data->psy_name, psy == NULL ? "Non-" : "", + cd_data->psp, power_supply_prop_str(cd_data->psp), + cd_data->psp_poll_interval); +} + +/* echo psy_name psy_property [interval] > param */ +static ssize_t charger_detect_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + union power_supply_propval propval; + enum power_supply_property psp; + u32 interval; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc < 2 || argc > 3) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + psy = power_supply_get_by_name(argv[0]); + if (psy == NULL) { + cts_err("Power supply '%s' not found", argv[0]); + return -EINVAL; + } + + psp = power_supply_prop_from_name(argv[1]); + if (psp < 0 || POWER_SUPPLY_GET_PROPERTY(psy, psp, &propval) < 0) { + cts_err("Power supply '%s' property '%s' not valid", + argv[0], power_supply_prop_str(psp)); + return -EINVAL; + } + + interval = cd_data->psp_poll_interval; + if (argc > 2) { + ret = kstrtou32(argv[2], 0, &interval); + if (ret) { + cts_err("Arg interval is invalid"); + return -EINVAL; + } + } + + if (cd_data->psy_name) + kfree(cd_data->psy_name); + + cd_data->psy_name = kstrdup(argv[0], GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + return -ENOMEM; + } + + cd_data->psp = psp; + cd_data->psp_poll_interval = interval; + + return count; +} + +static DEVICE_ATTR(param, S_IWUSR | S_IRUGO, + charger_detect_param_show, charger_detect_param_store); + +static struct attribute *charger_detect_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_running.attr, + &dev_attr_type.attr, + &dev_attr_state.attr, + &dev_attr_param.attr, + NULL +}; + +static const struct attribute_group charger_detect_attr_group = { + .name = CHARGER_DET_SYSFS_GROUP_NAME, + .attrs = charger_detect_attrs, +}; +#endif /* CONFIG_CTS_SYSFS */ + +int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret = 0; + + cts_info("Init detect"); + + if (cts_data == NULL) { + cts_err("Init detect while cts_data = NULL"); + return -EFAULT; + } + + cd_data = kzalloc(sizeof(*cd_data), GFP_KERNEL); + if (cd_data == NULL) { + cts_err("Alloc charger detect data failed"); + return -ENOMEM; + } + +#ifdef CONFIG_CTS_OF + ret = parse_charger_detect_dt(cd_data, cts_data->device->of_node); +#else /* CONFIG_CTS_OF */ +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = false; +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + cd_data->psy_name = kstrdup(CFG_CTS_DEF_CHGR_DET_PSY_NAME, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + ret = -ENOMEM; + } +#endif /* CONFIG_CTS_OF */ + if (ret) { + cts_err("Get detect param failed %d", ret); + goto free_cd_data; + } + + cts_info("Detect: %sABLED", cd_data->enable ? "EN" : "DIS"); + cts_info(" Type : %s", charger_detect_type_str(cd_data->type)); + cts_info(" PSY Name: %s, prop: %d(%s)", cd_data->psy_name, + cd_data->psp, power_supply_prop_str(cd_data->psp)); + cts_info(" Poll Int: %dms", cd_data->psp_poll_interval); + + ret = init_charger_detect(cd_data); + if (ret) { + cts_err("Init detect failed %d", ret); + goto free_psy_name; + } + +#ifdef CONFIG_CTS_SYSFS + cts_info("Create sysfs group '%s'", CHARGER_DET_SYSFS_GROUP_NAME); + ret = sysfs_create_group(&cts_data->device->kobj, + &charger_detect_attr_group); + if (ret) + cts_warn("Create sysfs group failed %d", ret); + else + cd_data->sysfs_created = true; + +#endif /* CONFIG_CTS_SYSFS */ + + cts_data->charger_detect_data = cd_data; + cd_data->cts_data = cts_data; + + return 0; + +free_psy_name: + kfree(cd_data->psy_name); +free_cd_data: + kfree(cd_data); + + return ret; +} + +int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cts_info("Deinit detect"); + + if (cts_data == NULL) { + cts_err("Deinit detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_warn("Deinit detect with charger_detect_data = NULL"); + return 0; + } + + if (cd_data->running) { + ret = stop_charger_detect(cd_data); + if (ret) + cts_err("Stop detect failed %d", ret); + } + +#ifdef CONFIG_CTS_SYSFS + if (cd_data->sysfs_created) { + cts_info("Remove sysfs group '%s'", + CHARGER_DET_SYSFS_GROUP_NAME); + sysfs_remove_group(&cts_data->device->kobj, + &charger_detect_attr_group); + cd_data->sysfs_created = false; + } +#endif /* CONFIG_CTS_SYSFS */ + + if (cd_data->psy_name) { + cts_info("Kfree power supply name"); + kfree(cd_data->psy_name); + cd_data->psy_name = NULL; + } + + kfree(cd_data); + cts_data->charger_detect_data = NULL; + + return 0; +} + +int cts_is_charger_attached(struct chipone_ts_data *cts_data, bool *attached) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + if (cts_data == NULL) { + cts_err("Get state with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Get state with charger_detect_data = NULL"); + return -ENODEV; + } + + cts_info("Get state using type %d(%s) param", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = get_charger_state(cd_data); + if (ret) { + cts_err("Get state failed %d", ret); + return ret; + } + + *attached = cd_data->state; + + return 0; +} + +int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Start detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Start detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return start_charger_detect(cd_data); +} + +int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Stop detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Stop detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return stop_charger_detect(cd_data); +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h new file mode 100644 index 000000000000..4665561fffc4 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_CHARGER_DETECT_H +#define CTS_CHARGER_DETECT_H + +#include "cts_config.h" + +struct chipone_ts_data; + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern int cts_charger_detect_init(struct chipone_ts_data *cts_data); +extern int cts_charger_detect_deinit(struct chipone_ts_data *cts_data); +extern int cts_start_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_stop_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} + +static inline int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} + +static inline int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} + +static inline int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} + +static inline int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached) +{ + return -ENODEV; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#endif /* CTS_CHARGER_DETECT_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h new file mode 100644 index 000000000000..0a8d953fc403 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_CONFIG_H +#define CTS_CONFIG_H + +/** Driver version */ +#define CFG_CTS_DRIVER_MAJOR_VERSION 3 +#define CFG_CTS_DRIVER_MINOR_VERSION 0 +#define CFG_CTS_DRIVER_PATCH_VERSION 0 + +#define CFG_CTS_DRIVER_VERSION "v3.0.9" + +/** Whether reset pin is used */ +#define CFG_CTS_HAS_RESET_PIN + +/* #define CONFIG_CTS_I2C_HOST */ +#ifndef CONFIG_CTS_I2C_HOST + +#ifndef CFG_CTS_HAS_RESET_PIN +#define CFG_CTS_HAS_RESET_PIN +#endif + +#define CFG_CTS_SPI_SPEED_KHZ 6000 + +#endif + +#define CFG_CTS_FORCE_UP +#define CONFIG_GENERIC_HARDIRQS +//#define CFG_CTS_FW_LOG_REDIRECT + +/** Whether force download firmware to chip */ +/* #define CFG_CTS_FIRMWARE_FORCE_UPDATE */ + +/** Use build in firmware or firmware file in fs*/ +/* #define CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +#define CFG_CTS_FIRMWARE_IN_FS +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* Add FW custom process. */ +/* #define CFG_CTS_FW_UPDATE_SYS */ + +/* Load config fw bin as default. */ +/* #define CFG_CTS_FW_UPDATE_FILE_LOAD */ +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD +#define CFG_CTS_FW_FILE_NAME_MAX_LEN 128 +#define CFG_CTS_FW_FILE_PATH "/vendor/firmware/" +#define CFG_CTS_FW_FILE_NAME_VENDOR "chipone" +#endif /*CFG_CTS_FW_UPDATE_FILE_LOAD */ + +#define CFG_CTS_FIRMWARE_FILENAME_9911 "ICNL9911.bin" +#define CFG_CTS_FIRMWARE_FILENAME_9916 "ICNL9916.bin" +#define CFG_CTS_FIRMWARE_FILENAME_9922 "ICNL9922.bin" + +#define CFG_CTS_FIRMWARE_FILEPATH_9911 "/vendor/firmware/ICNL9911.bin" +#define CFG_CTS_FIRMWARE_FILEPATH_9916 "/vendor/firmware/ICNL9916.bin" +#define CFG_CTS_FIRMWARE_FILEPATH_9922 "/vendor/firmware/ICNL9922.bin" +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +/* IC type support */ +#define CFG_CTS_CHIP_NAME "ICNL9911S" + +#ifdef CONFIG_PROC_FS + /* Proc FS for backward compatibility for APK tool com.ICN85xx */ +#define CONFIG_CTS_LEGACY_TOOL +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_SYSFS + /* Sys FS for gesture report, debug feature etc. */ +#define CONFIG_CTS_SYSFS +#endif /* CONFIG_SYSFS */ + +#define CFG_CTS_MAX_TOUCH_NUM (10) // 5 + +/* Virtual key support */ +/* #define CONFIG_CTS_VIRTUALKEY */ +#ifdef CONFIG_CTS_VIRTUALKEY +#define CFG_CTS_MAX_VKEY_NUM (4) +#define CFG_CTS_NUM_VKEY (3) +#define CFG_CTS_VKEY_KEYCODES {KEY_BACK, KEY_HOME, KEY_MENU} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +/* Gesture wakeup */ +/* #define CFG_CTS_GESTURE */ +#ifdef CFG_CTS_GESTURE +#define GESTURE_UP 0x11 +#define GESTURE_C 0x12 +#define GESTURE_O 0x13 +#define GESTURE_M 0x14 +#define GESTURE_W 0x15 +#define GESTURE_E 0x16 +#define GESTURE_S 0x17 +#define GESTURE_Z 0x1d +#define GESTURE_V 0x1e +#define GESTURE_D_TAP 0x50 +#define GESTURE_DOWN 0x22 +#define GESTURE_LEFT 0x23 +#define GESTURE_RIGHT 0x24 + +#define CFG_CTS_NUM_GESTURE (13u) +#define CFG_CTS_GESTURE_REPORT_KEY +#define CFG_CTS_GESTURE_KEYMAP \ +({ +{GESTURE_C, KEY_C,}, +{GESTURE_W, KEY_W,}, +{GESTURE_V, KEY_V,}, +{GESTURE_D_TAP, KEY_F1,}, +{GESTURE_Z, KEY_Z,}, +{GESTURE_M, KEY_M,}, +{GESTURE_O, KEY_O,}, +{GESTURE_E, KEY_E,}, +{GESTURE_S, KEY_S,}, +{GESTURE_UP, KEY_UP,}, +{GESTURE_DOWN, KEY_DOWN,}, +{GESTURE_LEFT, KEY_LEFT,}, +{GESTURE_RIGHT, KEY_RIGHT,}, +}) +#define CFG_CTS_GESTURE_REPORT_TRACE 0 +#endif /* CFG_CTS_GESTURE */ + +/* #define CONFIG_CTS_GLOVE */ + +#define CONFIG_CTS_CHARGER_DETECT + +/* ESD protection */ +/* #define CONFIG_CTS_ESD_PROTECTION */ +#ifdef CONFIG_CTS_ESD_PROTECTION +#define CFG_CTS_ESD_PROTECTION_CHECK_PERIOD (2 * HZ) +#define CFG_CTS_ESD_FAILED_CONFIRM_CNT 3 +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +/* Use slot protocol (protocol B), comment it if use protocol A. */ +#define CONFIG_CTS_SLOTPROTOCOL + +#ifdef CONFIG_CTS_LEGACY_TOOL +#define CFG_CTS_TOOL_PROC_FILENAME "icn85xx_tool" +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#define CFG_CTS_UPDATE_CRCCHECK +/**************************************************************************** + * Platform configurations + ****************************************************************************/ + +#include "cts_plat_qcom_config.h" + +#endif /* CTS_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c new file mode 100644 index 000000000000..36519999bdbb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c @@ -0,0 +1,3263 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Core" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_sysfs.h" +#include "cts_firmware.h" +#include "cts_charger_detect.h" +#include "cts_tcs.h" + +static DEFINE_RT_MUTEX(dev_lock); + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_i2c_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%02x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, b); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writeb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, + delay); +} + +static int cts_i2c_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%04x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, w); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writew invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, + delay); +} + +static int cts_i2c_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%08x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, l); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writel invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, + delay); +} + +static int cts_i2c_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cts_plat_get_i2c_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t)(max_xfer_size - cts_dev->rtdata.addr_width), + len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, data); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, data); + else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = cts_plat_i2c_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + + return 0; +} + +static int cts_i2c_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + cts_dbg("Readb from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, + retry, delay); +} + +static int cts_i2c_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + cts_dbg("Readw from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readw invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, + retry, delay); + if (ret == 0) + *w = get_unaligned_le16(buff); + + return ret; +} + +static int cts_i2c_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + cts_dbg("Readl from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readl invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, + retry, delay); + if (ret == 0) + *l = get_unaligned_le32(buff); + + return ret; +} + +static int cts_i2c_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + cts_dbg("Readsb from slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + + return 0; +} +#else +static int cts_spi_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writeb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, + delay); + return 0; +} + +static int cts_spi_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writew invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, + delay); + return 0; +} + +static int cts_spi_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writel invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, + delay); + return 0; +} + +static int cts_spi_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + data = cts_plat_get_spi_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t)(max_xfer_size - cts_dev->rtdata.addr_width), + len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, data); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, data); + else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = + cts_plat_spi_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + return 0; +} + +static int cts_spi_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, + retry, delay); +} + +static int cts_spi_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readw invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, + retry, delay); + if (ret == 0) + *w = get_unaligned_le16(buff); + + return ret; +} + +static int cts_spi_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readl invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, + retry, delay); + if (ret == 0) + *l = get_unaligned_le32(buff); + + return ret; +} + +static int cts_spi_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +static int cts_spi_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay, int idle) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read_delay_idle(cts_dev->pdata, + cts_dev->rtdata.slave_addr, + addr_buf, + cts_dev->rtdata.addr_width, + dst, xfer_len, retry, delay, + idle); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +#endif /* CONFIG_CTS_I2C_HOST */ + +int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writeb(cts_dev, addr, b, retry, delay); +#else + return cts_spi_writeb(cts_dev, addr, b, retry, delay); +#endif +} + +static inline int cts_dev_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writew(cts_dev, addr, w, retry, delay); +#else + return cts_spi_writew(cts_dev, addr, w, retry, delay); +#endif +} + +static inline int cts_dev_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writel(cts_dev, addr, l, retry, delay); +#else + return cts_spi_writel(cts_dev, addr, l, retry, delay); +#endif +} + +static inline int cts_dev_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, + int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writesb(cts_dev, addr, src, len, retry, delay); +#else + return cts_spi_writesb(cts_dev, addr, src, len, retry, delay); +#endif +} + +int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readb(cts_dev, addr, b, retry, delay); +#else + return cts_spi_readb(cts_dev, addr, b, retry, delay); +#endif +} + +static inline int cts_dev_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readw(cts_dev, addr, w, retry, delay); +#else + return cts_spi_readw(cts_dev, addr, w, retry, delay); +#endif +} + +static inline int cts_dev_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readl(cts_dev, addr, l, retry, delay); +#else + return cts_spi_readl(cts_dev, addr, l, retry, delay); +#endif +} + +static inline int cts_dev_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else + return cts_spi_readsb(cts_dev, addr, dst, len, retry, delay); +#endif +} + +static inline int cts_dev_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, + int retry, int delay, int idle) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else + return cts_spi_readsb_delay_idle(cts_dev, addr, dst, len, retry, delay, + idle); +#endif +} + +#ifdef CFG_CTS_UPDATE_CRCCHECK +int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry) +{ + int ret = 0, retries; + u32 addr[3]; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S + || cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) { + addr[0] = 0x015ff0; + addr[1] = 0x08fffc; + addr[2] = 0x08fff8; + } + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916 + || cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922) { + addr[0] = 0x01fff0; + addr[1] = 0x01fff8; + addr[2] = 0x01fffc; + } + + retries = 0; + do { + ret = cts_dev_writel(cts_dev, addr[0], 0xCC33555A, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[1], crc, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[2], len, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + break; + } while (retries++ < retry); + + return ret; +} +#endif + +static int cts_write_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + int retry, int delay) +{ + int i, ret; + u8 buff[5]; + + for (i = 0; i < len; i++) { + put_unaligned_le32(addr, buff); + buff[4] = *(u8 *) src; + + addr++; + src++; + + ret = cts_dev_writesb(cts_dev, CTS_DEVICE_FW_REG_DEBUG_INTF, + buff, 5, retry, delay); + if (ret) { + cts_err("Write rDEBUG_INTF len=5B failed %d", ret); + return ret; + } + } + + return 0; +} + +int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_writeb(cts_dev, addr, b, retry, delay); + else + return cts_write_sram_normal_mode(cts_dev, addr, &b, 1, retry, + delay); +} + +int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_writew(cts_dev, addr, w, retry, delay); + else { + put_unaligned_le16(w, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 2, retry, + delay); + } +} + +int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_writel(cts_dev, addr, l, retry, delay); + else { + put_unaligned_le32(l, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 4, retry, + delay); + } +} + +int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, int retry, + int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_writesb(cts_dev, addr, src, len, retry, delay); + else + return cts_write_sram_normal_mode(cts_dev, addr, src, len, + retry, delay); +} + +static int cts_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + cts_info("Calc crc from sram 0x%06x size %zu", sram_addr, size); + + return cts_dev->hwdata->sfctrl->ops->calc_sram_crc(cts_dev, + sram_addr, size, + crc); +} + +int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + u32 crc, int retry) +{ + int ret, retries; + u32 fw_crc; + u8 tdata[16]; + u8 magic_num[] = { 0x5A, 0x55, 0x33, 0xCC, 0x00, 0x00, 0x00, 0x00 }; + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = cts_sram_writesb(cts_dev, 0, src, len); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + fw_crc = cts_crc32(src, len); + cts_info("fw crc: 0x%04x", fw_crc); + memcpy(tdata, magic_num, 8); + memcpy(tdata + 8, &fw_crc, 4); + memcpy(tdata + 12, &len, 4); + + ret = + cts_hw_reg_writesb_retry(cts_dev, 0x01FFF0, tdata, + sizeof(tdata), 3, 1); + if (ret != 0) { + cts_err("cts_hw_reg_writesb_retry %d", ret); + continue; + } + + ret = cts_calc_sram_crc(cts_dev, 0, len, &crc_sram); + if (ret != 0) { + cts_err("Get CRC for sram writesb failed %d retries %d", + ret, retries); + continue; + } + + if (crc == crc_sram) + return 0; + + cts_err + ("Check CRC for sram writesb mismatch %x != %x retries %d", + crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = cts_dev_writel(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF, addr, retry, + delay); + if (ret) { + cts_err("Write addr to rDEBUG_INTF failed %d", ret); + return ret; + } + + ret = cts_dev_readb(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF + 4, + (u8 *) dst, retry, delay); + if (ret) { + cts_err("Read value from rDEBUG_INTF + 4 failed %d", + ret); + return ret; + } + + addr++; + dst++; + } + + return 0; +} + +int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_readb(cts_dev, addr, b, retry, delay); + else + return cts_read_sram_normal_mode(cts_dev, addr, b, 1, retry, + delay); +} + +int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_readw(cts_dev, addr, w, retry, delay); + else { + ret = + cts_read_sram_normal_mode(cts_dev, addr, buff, 2, retry, + delay); + if (ret) { + cts_err("SRAM readw in normal mode failed %d", ret); + return ret; + } + + *w = get_unaligned_le16(buff); + return 0; + } +} + +int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_readl(cts_dev, addr, l, retry, delay); + else { + ret = + cts_read_sram_normal_mode(cts_dev, addr, buff, 4, retry, + delay); + if (ret) { + cts_err("SRAM readl in normal mode failed %d", ret); + return ret; + } + + *l = get_unaligned_le32(buff); + + return 0; + } +} + +int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_readsb(cts_dev, addr, dst, len, retry, delay); + else + return cts_read_sram_normal_mode(cts_dev, addr, dst, len, retry, + delay); +} + +int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writeb to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writeb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writew to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writew(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writel to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writel(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writesb to fw reg 0x%04x under program mode", + reg_addr); + return -ENODEV; + } + + return cts_dev_writesb(cts_dev, reg_addr, src, len, retry, delay); +} + +int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readw from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readw(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readl from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readl(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, + int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb(cts_dev, reg_addr, dst, len, retry, delay); +} + +int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay, int idle) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb_delay_idle(cts_dev, reg_addr, dst, len, retry, + delay, idle); +} + +int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + return cts_sram_writeb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + return cts_sram_writew_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + return cts_sram_writel_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay) +{ + return cts_sram_writesb_retry(cts_dev, reg_addr, src, len, retry, + delay); +} + +int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + return cts_sram_readb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + return cts_sram_readw_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + return cts_sram_readl_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, + int delay) +{ + return cts_sram_readsb_retry(cts_dev, reg_addr, dst, len, retry, delay); +} + +#define CTS_DEV_HW_REG_DDI_REG_CTRL (0x3002Cu) + +static int icnl9911s_set_access_ddi_reg(struct cts_device *cts_dev, bool enable) +{ + int ret; + u8 access_flag; + + cts_info("ICNL9911S %s access ddi reg", enable ? "enable" : "disable"); + + ret = + cts_hw_reg_readb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + &access_flag); + if (ret) { + cts_err("Read HW_REG_DDI_REG_CTRL failed %d", ret); + return ret; + } + + access_flag = enable ? (access_flag | 0x01) : (access_flag & (~0x01)); + ret = + cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + access_flag); + if (ret) { + cts_err("Write HW_REG_DDI_REG_CTRL %02x failed %d", access_flag, + ret); + return ret; + } + + ret = cts_hw_reg_writeb(cts_dev, 0x30074, enable ? 1 : 0); + if (ret) { + cts_err("Write 0x30074 failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writew(cts_dev, 0x3DFF0, enable ? 0x595A : 0x5A5A); + if (ret) { + cts_err("Write password to F0 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writew(cts_dev, 0x3DFF4, enable ? 0xA6A5 : 0x5A5A); + if (ret) { + cts_err("Write password to F1 failed %d", ret); + return ret; + } + + return 0; +} + +static int icnl9916_set_access_ddi_reg(struct cts_device *cts_dev, bool enable) +{ + int ret; + u8 access_flag; + + cts_info("ICNL9916 %s access ddi reg", enable ? "enable" : "disable"); + + ret = + cts_hw_reg_readb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + &access_flag); + if (ret) { + cts_err("Read HW_REG_DDI_REG_CTRL failed %d", ret); + return ret; + } + + access_flag = enable ? (access_flag | 0x01) : (access_flag & (~0x01)); + ret = + cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + access_flag); + if (ret) { + cts_err("Write HW_REG_DDI_REG_CTRL %02x failed %d", access_flag, + ret); + return ret; + } + + ret = cts_hw_reg_writeb(cts_dev, 0x30074, enable ? 1 : 0); + if (ret) { + cts_err("Write 0x30074 failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writew(cts_dev, 0x3DFF0, enable ? 0x595A : 0x5A5A); + if (ret) { + cts_err("Write password to F0 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writew(cts_dev, 0x3DFF4, enable ? 0xA6A5 : 0x5A5A); + if (ret) { + cts_err("Write password to F1 failed %d", ret); + return ret; + } + + return 0; +} + +const static struct cts_sfctrl icnl9911_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (80 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9911s_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (64 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9911c_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (64 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9916_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9922_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_device_hwdata cts_device_hwdatas[] = { + { + .name = "ICNL9911", + .hwid = CTS_DEV_HWID_ICNL9911, + .fwid = CTS_DEV_FWID_ICNL9911, + .num_row = 32, + .num_col = 18, + .sram_size = 80 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911_sfctrl, + }, + { + .name = "ICNL9911S", + .hwid = CTS_DEV_HWID_ICNL9911S, + .fwid = CTS_DEV_FWID_ICNL9911S, + .num_row = 32, + .num_col = 18, + .sram_size = 64 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911s_sfctrl, + }, + { + .name = "ICNL9911C", + .hwid = CTS_DEV_HWID_ICNL9911C, + .fwid = CTS_DEV_FWID_ICNL9911C, + .num_row = 32, + .num_col = 18, + .sram_size = 64 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911c_sfctrl, + .enable_access_ddi_reg = icnl9911s_set_access_ddi_reg, + }, + { + .name = "ICNL9916", + .hwid = CTS_DEV_HWID_ICNL9916, + .fwid = CTS_DEV_FWID_ICNL9916, + .num_row = 32, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9916_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + { + .name = "ICNL9922", + .hwid = CTS_DEV_HWID_ICNL9922, + .fwid = CTS_DEV_FWID_ICNL9922, + .num_row = 36, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9922_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + } +}; + +static int cts_init_device_hwdata(struct cts_device *cts_dev, + u32 hwid, u16 fwid) +{ + int i; + + cts_info("Init hardware data hwid: %06x fwid: %04x", hwid, fwid); + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (hwid == cts_device_hwdatas[i].hwid || + fwid == cts_device_hwdatas[i].fwid) { + cts_dev->hwdata = &cts_device_hwdatas[i]; + return 0; + } + } + + return -EINVAL; +} + +void cts_lock_device(const struct cts_device *cts_dev) +{ + cts_dbg("*** Lock ***"); + + rt_mutex_lock(&dev_lock); +} + +void cts_unlock_device(const struct cts_device *cts_dev) +{ + cts_dbg("### Un-Lock ###"); + + rt_mutex_unlock(&dev_lock); +} + +int cts_set_work_mode(const struct cts_device *cts_dev, u8 mode) +{ + cts_info("Set work mode to %u", mode); + + return cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); +} + +int cts_get_work_mode(const struct cts_device *cts_dev, u8 *mode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); +} + +int cts_get_firmware_version(const struct cts_device *cts_dev, u16 *version) +{ + int ret = cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_VERSION, version); + + if (ret) + *version = 0; + else + *version = be16_to_cpup(version); + + return ret; +} + +int cts_get_ddi_version(const struct cts_device *cts_dev, u8 *version) +{ + int ret = + cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_DDI_VERSION, version); + + if (ret) + *version = 0; + + return ret; +} + +int cts_get_lib_version(const struct cts_device *cts_dev, u16 *lib_version) +{ + u8 main_version, sub_version; + int ret; + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION, + &main_version); + if (ret) { + cts_err("Get fw lib main version failed %d", ret); + return ret; + } + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION, + &sub_version); + if (ret) { + cts_err("Get fw lib sub version failed %d", ret); + return ret; + } + + *lib_version = (main_version << 8) + sub_version; + return 0; +} + +int cts_get_data_ready_flag(const struct cts_device *cts_dev, u8 *flag) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_DATA_READY, flag); +} + +int cts_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_DATA_READY, 0); +} + +int cts_send_command(const struct cts_device *cts_dev, u8 cmd) +{ + cts_info("Send command 0x%02x", cmd); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Send command %u while chip in program mode", cmd); + return -ENODEV; + } + + return cts_fw_reg_writeb_retry(cts_dev, CTS_DEVICE_FW_REG_CMD, cmd, 3, + 0); +} + +static int cts_get_touchinfo(const struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + cts_dbg("Get touch info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get touch info in program mode"); + return -ENODEV; + } + + if (cts_dev->rtdata.suspended) { + cts_warn("Get touch info while is suspended"); + return -ENODEV; + } + + return cts_dev->ops->get_touchinfo(cts_dev, touch_info); +} + +int cts_get_panel_param(const struct cts_device *cts_dev, + void *param, size_t size) +{ + cts_info("Get panel parameter"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get panel parameter in program mode"); + return -ENODEV; + } + + return cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM, + param, size); +} + +int cts_set_panel_param(const struct cts_device *cts_dev, + const void *param, size_t size) +{ + cts_info("Set panel parameter"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Set panel parameter in program mode"); + return -ENODEV; + } + return cts_fw_reg_writesb(cts_dev, + CTS_DEVICE_FW_REG_PANEL_PARAM, param, size); +} + +int cts_get_x_resolution(const struct cts_device *cts_dev, u16 *resolution) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_X_RESOLUTION, + resolution); +} + +int cts_get_y_resolution(const struct cts_device *cts_dev, u16 *resolution) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_Y_RESOLUTION, + resolution); +} + +int cts_get_num_rows(const struct cts_device *cts_dev, u8 *num_rows) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_NUM_TX, num_rows); +} + +int cts_get_num_cols(const struct cts_device *cts_dev, u8 *num_cols) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_NUM_RX, num_cols); +} + +#ifdef CONFIG_CTS_LEGACY_TOOL +int cts_enable_get_rawdata(const struct cts_device *cts_dev) +{ + cts_info("Enable get raw/diff data"); + return cts_send_command(cts_dev, CTS_CMD_ENABLE_READ_RAWDATA); +} + +int cts_disable_get_rawdata(const struct cts_device *cts_dev) +{ + cts_info("Disable get raw/diff data"); + return cts_send_command(cts_dev, CTS_CMD_DISABLE_READ_RAWDATA); +} + +static void tsdata_flip_x(void *tsdata, u8 fw_rows, u8 fw_cols) +{ + u8 r, c; + u16 *data; + + data = (u16 *) tsdata; + for (r = 0; r < fw_rows; r++) { + for (c = 0; c < fw_cols / 2; c++) { + swap(data[r * fw_cols + c], + data[r * fw_cols + wrap(fw_cols, c)]); + } + } +} + +static void tsdata_flip_y(void *tsdata, u8 fw_rows, u8 fw_cols) +{ + u8 r, c; + u16 *data; + + data = (u16 *) tsdata; + for (r = 0; r < fw_rows / 2; r++) { + for (c = 0; c < fw_cols; c++) { + swap(data[r * fw_cols + c], + data[wrap(fw_rows, r) * fw_cols + c]); + } + } +} + +int cts_polling_data(const struct cts_device *cts_dev, void *buf, size_t size) +{ + if (cts_dev->ops->polling_data) + return cts_dev->ops->polling_data(cts_dev, buf, size); + + return -EIO; +} + +int cts_polling_test_data(const struct cts_device *cts_dev, void *buf, + size_t size) +{ + if (cts_dev->ops->polling_test_data) + return cts_dev->ops->polling_test_data(cts_dev, buf, size); + + return -EIO; +} + +int cts_get_rawdata(const struct cts_device *cts_dev, void *buf) +{ + int i, ret; + u8 ready; + u8 retries = 5; + + cts_info("Get rawdata"); + + /** - Wait data ready flag set */ + for (i = 0; i < 100; i++) { + mdelay(10); + ret = cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + continue; + } + if (ready) + goto read_rawdata; + } + + cts_err("Wait data ready timeout"); + return -ETIMEDOUT; + + /* Read rawdata */ +read_rawdata: + do { + ret = cts_dev->ops->get_rawdata(cts_dev, buf); + if (ret) + cts_err("Read rawdata failed %d", ret); + else + break; + } while (--retries > 0); + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->clr_data_ready_flag(cts_dev); + + if (r) + cts_err("Clear data ready flag failed %d", r); + else + break; + } + + if (cts_dev->fwdata.flip_x) + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + if (cts_dev->fwdata.flip_y) + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + + return ret; +} + +int cts_get_diffdata(const struct cts_device *cts_dev, void *buf) +{ + int i, j, ret; + u8 ready; + u8 retries = 5; + u8 *cache_buf; + + cts_info("Get diffdata"); + cache_buf = kzalloc((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + + 2) * 2, GFP_KERNEL); + if (cache_buf == NULL) { + cts_err("Get diffdata: malloc error"); + ret = -ENOMEM; + goto get_diff_exit; + } + /** - Wait data ready flag set */ + for (i = 0; i < 1000; i++) { + mdelay(1); + ret = cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + goto get_diff_free_buf; + } + if (ready) + break; + } + if (i == 1000) { + ret = -ETIMEDOUT; + goto get_diff_free_buf; + } + do { + ret = cts_dev->ops->get_diffdata(cts_dev, cache_buf); + if (ret) + cts_err("Read diffdata failed %d", ret); + else + break; + } while (--retries > 0); + + if (ret == 0) { + for (i = 0; i < cts_dev->fwdata.rows; i++) { + for (j = 0; j < cts_dev->fwdata.cols; j++) { + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j)] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1)]; + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j) + + 1] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1) + 1]; + } + } + } + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->clr_data_ready_flag(cts_dev); + + if (r) + cts_err("Clear data ready flag failed %d", r); + else + break; + } + + if (cts_dev->fwdata.flip_x) + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + if (cts_dev->fwdata.flip_y) + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); +get_diff_free_buf: + kfree(cache_buf); +get_diff_exit: + return ret; +} + +int cts_get_basedata(const struct cts_device *cts_dev, void *buf) +{ + int i, j, ret; + u8 ready; + u8 retries = 5; + u8 *cache_buf; + + cts_info("Get basedata"); + cache_buf = kzalloc((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + + 2) * 2, GFP_KERNEL); + if (cache_buf == NULL) { + cts_err("Get basedata: malloc error"); + ret = -ENOMEM; + goto get_diff_exit; + } + /** - Wait data ready flag set */ + for (i = 0; i < 1000; i++) { + mdelay(1); + ret = cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + goto get_diff_free_buf; + } + if (ready) + break; + } + if (i == 1000) { + ret = -ETIMEDOUT; + goto get_diff_free_buf; + } + do { + ret = cts_dev->ops->get_basedata(cts_dev, cache_buf); + if (ret) + cts_err("Read basedata failed %d", ret); + else + break; + } while (--retries > 0); + if (ret == 0) { + for (i = 0; i < cts_dev->fwdata.rows; i++) { + for (j = 0; j < cts_dev->fwdata.cols; j++) { + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j)] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1)]; + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j) + + 1] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1) + 1]; + } + } + } + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->clr_data_ready_flag(cts_dev); + + if (r) + cts_err("Clear data ready flag failed %d", r); + else + break; + } + + if (cts_dev->fwdata.flip_x) + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + if (cts_dev->fwdata.flip_y) + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); +get_diff_free_buf: + kfree(cache_buf); +get_diff_exit: + return ret; +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +static int cts_get_dev_boot_mode(const struct cts_device *cts_dev, + u8 *boot_mode) +{ + int ret; + + if (cts_dev->rtdata.program_mode) + ret = cts_hw_reg_readb_retry(cts_dev, + CTS_DEV_HW_REG_CURRENT_MODE, + boot_mode, 5, 10); + else + ret = cts_dev->ops->read_hw_reg(cts_dev, + CTS_DEV_HW_REG_CURRENT_MODE, + boot_mode, 1); + + if (ret) { + cts_err("Read boot mode failed %d", ret); + return ret; + } + + *boot_mode &= CTS_DEV_BOOT_MODE_MASK; + + cts_info("Curr dev boot mode: %u(%s)", *boot_mode, + cts_dev_boot_mode2str(*boot_mode)); + return 0; +} + +static int cts_set_dev_boot_mode(const struct cts_device *cts_dev, u8 boot_mode) +{ + int ret; + + cts_info("Set dev boot mode to %u(%s)", boot_mode, + cts_dev_boot_mode2str(boot_mode)); + + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_BOOT_MODE, + boot_mode, 5, 5); + if (ret) { + cts_err("Write hw register BOOT_MODE failed %d", ret); + return ret; + } + + cts_info("%s exit", __func__); + + return 0; +} + +static int cts_init_fwdata(struct cts_device *cts_dev) +{ + + struct cts_device_fwdata *fwdata = &cts_dev->fwdata; + int ret; + + cts_info("Init firmware data"); + + if (cts_dev->rtdata.program_mode) { + cts_err("Init firmware data while in program mode"); + return -EINVAL; + } + + memset(fwdata, 0, sizeof(*fwdata)); + + ret = cts_dev->ops->get_fw_ver(cts_dev, &fwdata->version); + if (ret < 0) { + cts_err("get_fw_ver failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_lib_ver(cts_dev, &fwdata->lib_version); + if (ret < 0) { + cts_err("get_lib_ver failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_ddi_ver(cts_dev, &fwdata->ddi_version); + if (ret < 0) { + cts_err("get_ddi_ver failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_res_x(cts_dev, &fwdata->res_x); + if (ret < 0) { + cts_err("get_res_x failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_res_y(cts_dev, &fwdata->res_y); + if (ret < 0) { + cts_err("get_res_y failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_rows(cts_dev, &fwdata->rows); + if (ret < 0) { + cts_err("get_rows failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_cols(cts_dev, &fwdata->cols); + if (ret < 0) { + cts_err("get_cols failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_flip_x(cts_dev, &fwdata->flip_x); + if (ret < 0) { + cts_err("get_flip_x failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_flip_y(cts_dev, &fwdata->flip_y); + if (ret < 0) { + cts_err("get_flip_y failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_swap_axes(cts_dev, &fwdata->swap_axes); + if (ret < 0) { + cts_err("get_swap_axes failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_int_mode(cts_dev, &fwdata->int_mode); + if (ret < 0) { + cts_err("get_int_mode failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_int_keep_time(cts_dev, &fwdata->int_keep_time); + if (ret < 0) { + cts_err("get_int_keep_time failed"); + return -EINVAL; + } + fwdata->rawdata_target = 2000; + + ret = cts_dev->ops->get_esd_method(cts_dev, &fwdata->esd_method); + if (ret < 0) { + cts_err("get_esd_method failed"); + return -EINVAL; + } + + if (cts_dev->ops->get_has_int_data) { + ret = + cts_dev->ops->get_has_int_data(cts_dev, + &fwdata->has_int_data); + if (ret < 0) { + cts_err("get_has_int_data failed: %d", ret); + return -EINVAL; + } + + if (fwdata->has_int_data) { + if (cts_dev->ops->get_int_data_method) { + ret = + cts_dev->ops->get_int_data_method(cts_dev, + &fwdata->int_data_method); + if (ret < 0) { + cts_err + ("get_int_data_method failed: %d", + ret); + return -EINVAL; + } + if (fwdata->int_data_method > + INT_DATA_METHOD_POLLING) { + cts_err("Invalid int data method: %d", + fwdata->int_data_method); + return -EINVAL; + } + + } + + if (cts_dev->ops->get_int_data_types) { + ret = + cts_dev->ops->get_int_data_types(cts_dev, + &fwdata->int_data_types); + if (ret < 0) { + cts_err("get_int_data_types failed: %d", + ret); + return -EINVAL; + } + fwdata->int_data_types &= INT_DATA_TYPE_MASK; + } + + if (cts_dev->ops->calc_int_data_size) { + ret = cts_dev->ops->calc_int_data_size(cts_dev); + if (ret < 0) { + cts_err("calc_int_data_size failed: %d", + ret); + return -EINVAL; + } + } + + if (cts_dev->ops->init_int_data) { + ret = cts_dev->ops->init_int_data(cts_dev); + if (ret < 0) { + cts_err("init_int_data failed: %d", + ret); + return -EINVAL; + } + } + } + } + + cts_err("fwver: %04x", fwdata->version); + cts_err("libver: %04x", fwdata->lib_version); + cts_err("ddi_version: %02x", fwdata->ddi_version); + cts_err("res_x: %d", fwdata->res_x); + cts_err("res_y: %d", fwdata->res_y); + cts_err("rows: %d", fwdata->rows); + cts_err("cols: %d", fwdata->cols); + cts_err("flip_x: %d", fwdata->flip_x); + cts_err("flip_y: %d", fwdata->flip_y); + cts_err("swap_axes: %d", fwdata->swap_axes); + cts_err("int_mode: %d", fwdata->int_mode); + cts_err("int_keep_time: %d", fwdata->int_keep_time); + cts_err("rawdata_target: %d", fwdata->rawdata_target); + cts_err("esd_method: %d", fwdata->esd_method); + + if (cts_dev->ops->get_has_int_data) { + cts_err("has_int_data: %d", fwdata->has_int_data); + cts_err("int_data_method: %d", fwdata->int_data_method); + cts_err("int_data_types: %d", fwdata->int_data_types); + cts_err("int_data_size: %ld", fwdata->int_data_size); + } + + return 0; +} + +#ifdef CFG_CTS_FW_LOG_REDIRECT +void cts_show_fw_log(struct cts_device *cts_dev) +{ + u8 len, max_len; + int ret; + u8 *data; + + max_len = cts_plat_get_max_fw_log_size(cts_dev->pdata); + data = cts_plat_get_fw_log_buf(cts_dev->pdata, max_len); + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 1, &len, + 1); + if (ret) { + cts_err("Get i2c print buf len error"); + return; + } + if (len >= max_len) + len = max_len - 1; + + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 2, data, + len); + if (ret) { + cts_err("Get i2c print buf error"); + return; + } + data[len] = '\0'; + printk("CTS-FW_LOG %s", data); + cts_fw_log_show_finish(cts_dev); +} +#endif + +int cts_irq_handler(struct cts_device *cts_dev) +{ + int ret; + + cts_dbg("Enter IRQ handler"); + + if (cts_dev->rtdata.program_mode) { + cts_err("IRQ triggered in program mode"); + return -EINVAL; + } + + if (unlikely(cts_dev->rtdata.suspended)) { +#ifdef CFG_CTS_GESTURE + if (cts_dev->rtdata.gesture_wakeup_enabled) { + struct cts_device_gesture_info gesture_info; + + ret = cts_get_gesture_info(cts_dev, + &gesture_info, + CFG_CTS_GESTURE_REPORT_TRACE); + if (ret) + cts_warn("Get gesture info failed %d", ret); + /* return ret; */ + +/** - Issure another suspend with gesture wakeup command to device + * after get gesture info. + */ + cts_send_command(cts_dev, CTS_CMD_SUSPEND_WITH_GESTURE); + + ret = + cts_plat_process_gesture_info(cts_dev->pdata, + &gesture_info); + if (ret) { + cts_err("Process gesture info failed %d", ret); + return ret; + } + } else + cts_warn("IRQ triggered while device suspended " + "without gesture wakeup enable"); +#endif /* CFG_CTS_GESTURE */ + } else { + struct cts_device_touch_info *touch_info; + + touch_info = &cts_dev->rtdata.touch_info; +#ifdef CFG_CTS_FW_LOG_REDIRECT + ret = cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO, + touch_info, 1); + if (ret) { + cts_err("Get vkey_state failed %d", ret); + return ret; + } + + if (touch_info->vkey_state == CTS_FW_LOG_REDIRECT_SIGN) { + if (cts_is_fw_log_redirect(cts_dev)) + cts_show_fw_log(cts_dev); + + return 0; + } +#endif + ret = cts_get_touchinfo(cts_dev, touch_info); + if (ret) { + cts_err("Get touch info failed %d", ret); + return ret; + } + + cts_dbg("Touch info: vkey_state %x, num_msg %u", + touch_info->vkey_state, touch_info->num_msg); + + ret = cts_plat_process_touch_msg(cts_dev->pdata, + touch_info->msgs, + touch_info->num_msg); + if (ret) { + cts_err("Process touch msg failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_process_vkey(cts_dev->pdata, + touch_info->vkey_state); + if (ret) { + cts_err("Process vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + } + + return 0; +} + +bool cts_is_device_suspended(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.suspended; +} + +int cts_suspend_device(struct cts_device *cts_dev) +{ + int ret; + u8 buf; + + cts_info("Suspend device"); + + if (cts_dev->rtdata.suspended) { + cts_warn("Suspend device while already suspended"); + return 0; + } + if (cts_dev->rtdata.program_mode) { + cts_info("Quit programming mode before suspend"); + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Failed to exit program mode before suspend:%d", + ret); + return ret; + } + } + + buf = cts_dev->rtdata.gesture_wakeup_enabled ? 2 : 3; + ret = cts_dev->ops->set_pwr_mode(cts_dev, buf); + if (ret) { + cts_err("Suspend device failed %d", ret); + + return ret; + } + +#ifdef CFG_CTS_HAS_RESET_PIN + /* cts_plat_set_reset(cts_dev->pdata, 0); */ +#endif + + cts_info("Device suspended ..."); + cts_dev->rtdata.suspended = true; + + return 0; +} + +int cts_resume_device(struct cts_device *cts_dev) +{ + int ret = 0; + int retries = 3; + + cts_info("Resume device"); + + /* Check whether device is in normal mode */ + while (--retries >= 0) { +#ifdef CFG_CTS_HAS_RESET_PIN + cts_reset_device(cts_dev); +#endif + cts_set_normal_addr(cts_dev); +#ifdef CONFIG_CTS_I2C_HOST + if (cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) +#else + if (cts_plat_is_normal_mode(cts_dev->pdata)) +#endif + { + break; + } + } + + if (retries < 0) { + const struct cts_firmware *firmware = NULL; + + cts_info("Need update firmware when resume"); + +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + if (cts_dev->config_fw_name[0] != '\0') { + firmware = cts_request_firmware_from_fs(cts_dev, + cts_dev->config_fw_name); + } +#else + firmware = cts_request_firmware(cts_dev, + cts_dev->hwdata->hwid, + cts_dev->hwdata->fwid, 0); +#endif + if (firmware) { + ret = cts_update_firmware(cts_dev, firmware, false); + cts_release_firmware(firmware); + + if (ret) { + cts_err("Update default firmware failed %d", + ret); + goto err_set_program_mode; + } + } else { + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + goto err_set_program_mode; + } + } +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + cts_dev->rtdata.suspended = false; + return 0; + +err_set_program_mode: + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; + + return ret; +} + +bool cts_is_device_program_mode(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.program_mode; +} + +static inline void cts_init_rtdata_with_normal_mode(struct cts_device *cts_dev) +{ + memset(&cts_dev->rtdata, 0, sizeof(cts_dev->rtdata)); + + cts_set_normal_addr(cts_dev); + cts_dev->rtdata.suspended = false; + cts_dev->rtdata.updating = false; + cts_dev->rtdata.testing = false; + cts_dev->rtdata.fw_log_redirect_enabled = false; + cts_dev->rtdata.glove_mode_enabled = false; + cts_dev->rtdata.int_data = NULL; +} + +int cts_enter_program_mode(struct cts_device *cts_dev) +{ + const static u8 magic_num[] = { 0xCC, 0x33, 0x55, 0x5A }; + u8 boot_mode; + int ret; + + cts_info("Enter program mode"); + + if (cts_dev->rtdata.program_mode) + cts_warn("Enter program mode while alredy in"); + /* return 0; */ +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_plat_i2c_write(cts_dev->pdata, + CTS_DEV_PROGRAM_MODE_I2CADDR, magic_num, 4, 5, + 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_I2CADDR, ret); + return ret; + } + + cts_set_program_addr(cts_dev); + /* Write i2c deglitch register */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x37001, 0x0F, 5, 1); + if (ret) + cts_err("Write i2c deglitch register failed\n"); + +#else + cts_set_program_addr(cts_dev); + ret = cts_plat_spi_write(cts_dev->pdata, 0xcc, &magic_num[1], 3, 5, 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_SPIADDR, ret); + return ret; + } +#endif /* CONFIG_CTS_I2C_HOST */ + cts_dev->rtdata.program_mode = true; + mdelay(5); + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) { + cts_err("Read BOOT_MODE failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_I2C_HOST + if ((boot_mode == CTS_DEV_BOOT_MODE_TCH_PRG_9916) || + (boot_mode == CTS_DEV_BOOT_MODE_I2C_PRG_9911C)) +#else + if ((boot_mode == CTS_DEV_BOOT_MODE_TCH_PRG_9916) || + (boot_mode == CTS_DEV_BOOT_MODE_SPI_PRG_9911C)) +#endif + { + return 0; + } + cts_err("BOOT_MODE readback %u != I2C/SPI PROMGRAM mode", boot_mode); + return -EFAULT; +} + +const char *cts_dev_boot_mode2str(u8 boot_mode) +{ + switch (boot_mode) { + case CTS_DEV_BOOT_MODE_IDLE: + return "IDLE-BOOT"; + case CTS_DEV_BOOT_MODE_FLASH: + return "FLASH-BOOT"; + case CTS_DEV_BOOT_MODE_SRAM: + return "SRAM-BOOT"; + /* case CTS_DEV_BOOT_MODE_I2C_PRG_9911C: */ + case CTS_DEV_BOOT_MODE_TCH_PRG_9916: + return "I2C-PRG-BOOT/TCH-PRG-BOOT"; + case CTS_DEV_BOOT_MODE_DDI_PRG: + return "DDI-PRG-BOOT"; + case CTS_DEV_BOOT_MODE_SPI_PRG_9911C: + return "SPI-PROG-BOOT/INVALID-BOOT"; + default: + return "INVALID"; + } +} + +int cts_enter_normal_mode(struct cts_device *cts_dev) +{ + int ret = 0; + u8 boot_mode; + int retries; + u16 fwid = CTS_DEV_FWID_INVALID; + u8 auto_boot = 0; + u8 first_boot = 1; + + cts_info("Enter normal mode"); + + if (!cts_dev->rtdata.program_mode) { + cts_warn("Enter normal mode while already in"); + return 0; + } + + if (cts_dev->rtdata.has_flash) + auto_boot = 1; + +#ifdef CFG_CTS_UPDATE_CRCCHECK + auto_boot = 1; +#endif + for (retries = 5; retries >= 0; retries--) { + if (first_boot == 1 || auto_boot == 0) { + cts_set_program_addr(cts_dev); + ret = + cts_set_dev_boot_mode(cts_dev, + CTS_DEV_BOOT_MODE_SRAM); + if (ret) + cts_err("Set BOOT_MODE to SRAM failed %d," + "try to reset device", ret); + + mdelay(30); + } + first_boot = 0; +#ifdef CONFIG_CTS_I2C_HOST + if (cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) + cts_set_normal_addr(cts_dev); +#else + cts_set_normal_addr(cts_dev); +#endif + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) + cts_err("Get BOOT_MODE failed %d", ret); + if (boot_mode != CTS_DEV_BOOT_MODE_SRAM) + cts_err("Curr boot mode %u(%s) != SRAM_BOOT", + boot_mode, cts_dev_boot_mode2str(boot_mode)); + else + break; + + ret = cts_dev->ops->get_fw_id(cts_dev, &fwid); + if (ret) + cts_err("Get firmware id failed %d, retries %d", ret, + retries); + else { + if (fwid == CTS_DEV_FWID_ICNL9916 + || fwid == CTS_DEV_FWID_ICNL9911 + || fwid == CTS_DEV_FWID_ICNL9911S + || fwid == CTS_DEV_FWID_ICNL9911C + || fwid == CTS_DEV_FWID_ICNL9922) { + cts_info("Get firmware id successful 0x%02x", + fwid); + break; + } + } + cts_reset_device(cts_dev); + } + if (retries >= 0) { + ret = cts_init_fwdata(cts_dev); + if (ret) { + cts_err("Device init firmware data failed %d", ret); + return ret; + } + return 0; + } + cts_set_program_addr(cts_dev); + return ret; +} + +bool cts_is_device_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->enabled; +} + +int cts_start_device(struct cts_device *cts_dev) +{ +#if defined(CONFIG_CTS_ESD_PROTECTION) || defined(CONFIG_CTS_CHARGER_DETECT) + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); +#endif + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_enable_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_start_charger_detect(cts_data); +#endif /* CONFIG_CTS_CHARGER_DETECT */ + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn + ("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_disable_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_stop_charger_detect(cts_data); +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&cts_data->pdata->ts_irq_work)) { + cts_warn("IRQ work is pending .... flush it"); + flush_work(&cts_data->pdata->ts_irq_work); + } else + cts_info("None IRQ work is pending"); +#endif + cts_info("Flush workqueue..."); + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +int cts_start_device_esdrecover(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device_esdrecover(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn + ("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} +#endif + +bool cts_is_fwid_valid(u16 fwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].fwid == fwid) + return true; + } + + return false; +} + +static bool cts_is_hwid_valid(u32 hwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].hwid == hwid) + return true; + } + + return false; +} + +int cts_get_fwid(struct cts_device *cts_dev, u16 *fwid) +{ + int ret; + + cts_info("Get device firmware id"); + + if (cts_dev->hwdata) { + *fwid = cts_dev->hwdata->fwid; + return 0; + } + + if (cts_dev->rtdata.program_mode) { + cts_err("Get device firmware id while in program mode"); + ret = -ENODEV; + goto err_out; + } + + ret = cts_fw_reg_readw_retry(cts_dev, + CTS_DEVICE_FW_REG_CHIP_TYPE, fwid, 5, 1); + if (ret) + goto err_out; + + *fwid = be16_to_cpu(*fwid); + + cts_info("Device firmware id: %04x", *fwid); + + if (!cts_is_fwid_valid(*fwid)) { + cts_warn("Get invalid firmware id %04x", *fwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *fwid = CTS_DEV_FWID_INVALID; + return ret; +} + +int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid) +{ + int ret; + + cts_info("Get device hardware id"); + + if (cts_dev->hwdata) { + *hwid = cts_dev->hwdata->hwid; + return 0; + } + + cts_info + ("Device hardware data not initialized, try to read from register"); + + if (!cts_dev->rtdata.program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_out; + } + } + + ret = + cts_hw_reg_readl_retry(cts_dev, CTS_DEV_HW_REG_HARDWARE_ID, hwid, 5, + 0); + if (ret) + goto err_out; + + *hwid = le32_to_cpu(*hwid); + *hwid &= 0XFFFFFFF0; + cts_info("Device hardware id: %04x", *hwid); + + if (!cts_is_hwid_valid(*hwid)) { + cts_warn("Device hardware id %04x invalid", *hwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *hwid = CTS_DEV_HWID_INVALID; + return ret; +} + +extern struct cts_dev_ops tcs_ops; +extern struct cts_dev_ops hostcomm_ops; + +int cts_probe_device(struct cts_device *cts_dev) +{ + int ret, retries = 0; + u16 fwid = CTS_DEV_FWID_INVALID; + u32 hwid = CTS_DEV_HWID_INVALID; + u16 device_fw_ver = 0; + const struct cts_firmware *firmware; + + cts_info("Probe device"); + + cts_dev->tx = kzalloc(INT_DATA_MAX_SIZ, GFP_KERNEL); + if (cts_dev->tx == NULL) { + cts_err("Alloc tx failed"); + return -ENOMEM; + } + cts_dev->rx = kzalloc(INT_DATA_MAX_SIZ, GFP_KERNEL); + if (cts_dev->rx == NULL) { + cts_err("Alloc rx failed"); + ret = -ENOMEM; + goto alloc_rx_exit; + } + + cts_dev->ops = &tcs_ops; + +read_hwid: +/** - Try to read hardware id, + *it will enter program mode as normal + */ + ret = cts_get_hwid(cts_dev, &hwid); + if (ret || hwid == CTS_DEV_HWID_INVALID) { + retries++; + + cts_err("Get hardware id failed %d retries %d", ret, retries); + + if (retries < 3) { + cts_reset_device(cts_dev); + goto read_hwid; + } else + return -ENODEV; + } + + if (hwid == CTS_DEV_HWID_ICNL9916 || hwid == CTS_DEV_HWID_ICNL9922) + cts_dev->ops = &tcs_ops; + else if ((hwid == CTS_DEV_HWID_ICNL9911C) || + (hwid == CTS_DEV_HWID_ICNL9911S) || + (hwid == CTS_DEV_HWID_ICNL9911)) + cts_dev->ops = &hostcomm_ops; + + //read_fwid: +#ifdef CONFIG_CTS_I2C_HOST + if (!cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) + cts_warn("Normal mode i2c addr is offline"); + else +#else + if (!cts_plat_is_normal_mode(cts_dev->pdata)) + cts_warn("Normal mode spi addr is offline"); + else +#endif + { + ret = cts_dev->ops->get_fw_id(cts_dev, &fwid); + if (ret) + cts_err("Get firmware id failed %d, retries %d", ret, + retries); + else { + ret = + cts_fw_reg_readw_retry(cts_dev, + CTS_DEVICE_FW_REG_VERSION, + &device_fw_ver, 5, 0); + if (ret) { + cts_err("Read firmware version failed %d", ret); + device_fw_ver = 0; + } else { + device_fw_ver = be16_to_cpu(device_fw_ver); + cts_info("Device firmware version: %04x", + device_fw_ver); + } + goto init_hwdata; + } + } + +init_hwdata: + ret = cts_init_device_hwdata(cts_dev, hwid, fwid); + if (ret) { + cts_err("Device hwid: %06x fwid: %04x not found", hwid, fwid); + return -ENODEV; + } + +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + cts_warn("Force update firmware"); + firmware = cts_request_firmware(cts_dev, + CTS_DEV_HWID_ANY, CTS_DEV_FWID_ANY, 0); +#else /* CFG_CTS_FIRMWARE_FORCE_UPDATE */ + firmware = cts_request_firmware(cts_dev, hwid, fwid, device_fw_ver); +#endif /* CFG_CTS_FIRMWARE_FORCE_UPDATE */ + + retries = 0; +update_firmware: + if (firmware) { + ++retries; + ret = cts_update_firmware(cts_dev, firmware, false); + if (ret) { + cts_err("Update firmware failed %d retries %d", ret, + retries); + + if (retries < 3) { + cts_reset_device(cts_dev); + goto update_firmware; + } + } + cts_release_firmware(firmware); + } else { + if (fwid != CTS_DEV_FWID_INVALID) { + ret = cts_init_fwdata(cts_dev); + if (ret) + cts_err("Device init firmware data failed %d", + ret); + } + } + return 0; + +alloc_rx_exit: + if (cts_dev->tx) { + kfree(cts_dev->tx); + cts_dev->tx = NULL; + } + return ret; +} + +#ifdef CFG_CTS_GESTURE +void cts_enable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Enable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = true; +} + +void cts_disable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Disable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = false; +} + +bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.gesture_wakeup_enabled; +} + +int cts_get_gesture_info(const struct cts_device *cts_dev, + void *gesture_info, bool trace_point) +{ + int ret; + + cts_info("Get gesture info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get gesture info in program mode"); + return -ENODEV; + } + + if (!cts_dev->rtdata.suspended) { + cts_warn("Get gesture info while not suspended"); + return -ENODEV; + } + + if (!cts_dev->rtdata.gesture_wakeup_enabled) { + cts_warn("Get gesture info while gesture wakeup not enabled"); + return -ENODEV; + } + + ret = cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_GESTURE_INFO, + gesture_info, 2); + if (ret) { + cts_err("Read gesture info header failed %d", ret); + return ret; + } + + if (trace_point) { + ret = + cts_fw_reg_readsb(cts_dev, + CTS_DEVICE_FW_REG_GESTURE_INFO + 2, + gesture_info + 2, + (((u8 *) gesture_info))[1] * + sizeof(struct cts_device_gesture_point)); + if (ret) { + cts_err("Read gesture trace points failed %d", ret); + return ret; + } + } + + return 0; +} +#endif /* CFG_CTS_GESTURE */ + +int cts_set_int_data_types(struct cts_device *cts_dev, u16 types) +{ + int ret; + u8 old_method = cts_dev->fwdata.int_data_method; + u16 realtypes = types & INT_DATA_TYPE_MASK; + + cts_info("Set int data types: %#06x, mask to %#06x", types, realtypes); + + if (old_method != INT_DATA_METHOD_NONE) { + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set int data method failed: %d", ret); + return -EIO; + } + } + + if (cts_dev->ops->set_int_data_types) { + ret = cts_dev->ops->set_int_data_types(cts_dev, realtypes); + if (ret) { + cts_err("Set int data type failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_types = realtypes; + ret = 0; + } + + if (old_method != INT_DATA_METHOD_NONE) { + ret = cts_set_int_data_method(cts_dev, old_method); + if (ret) { + cts_err("Set int data method failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_method = old_method; + } + + return ret; +} + +int cts_set_int_data_method(struct cts_device *cts_dev, u8 method) +{ + int ret = 0; + + cts_info("Set int data method: %d", method); + + if (method > INT_DATA_METHOD_POLLING) { + cts_err("Invalid int data method"); + return -EINVAL; + } + + if (cts_dev->ops->set_int_data_method) { + ret = cts_dev->ops->set_int_data_method(cts_dev, method); + if (ret) { + cts_err("Set int data method failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_method = method; + ret = 0; + } + + if (cts_dev->ops->calc_int_data_size) + cts_dev->ops->calc_int_data_size(cts_dev); + + return ret; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +static void cts_esd_protection_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + int ret; + + cts_info("ESD protection work"); + cts_data = container_of(work, struct chipone_ts_data, esd_work.work); + cts_lock_device(&cts_data->cts_dev); +#ifdef CONFIG_CTS_I2C_HOST + if (!cts_plat_is_i2c_online + (cts_data->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) +#else + if (!cts_plat_is_normal_mode(cts_data->pdata)) +#endif + { + cts_data->esd_check_fail_cnt++; + /*reset chip next time */ + if ((cts_data->esd_check_fail_cnt % 2) == 0) { + cts_err + ("ESD protection read normal mode failed, reset chip!"); + ret = cts_reset_device(cts_data); + if (ret) + cts_err("ESD protection reset chip failed %d", + ret); + + } + } else + cts_data->esd_check_fail_cnt = 0; + + if (cts_data->esd_check_fail_cnt >= CFG_CTS_ESD_FAILED_CONFIRM_CNT) { + const struct cts_firmware *firmware; + + cts_warn("ESD protection check failed, update firmware!!!"); + cts_stop_device_esdrecover(&cts_data->cts_dev); + firmware = cts_request_firmware(&cts_data->cts_dev, + cts_data->cts_dev.hwdata->hwid, + cts_data->cts_dev.hwdata->fwid, + 0); + if (firmware) { + ret = + cts_update_firmware(&cts_data->cts_dev, firmware, + false); + cts_release_firmware(firmware); + + if (ret) + cts_err("Update default firmware failed %d", + ret); + } else + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + cts_start_device_esdrecover(&cts_data->cts_dev); + cts_data->esd_check_fail_cnt = 0; + } + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + + cts_unlock_device(&cts_data->cts_dev); +} + +void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && !cts_data->esd_enabled) { + cts_info("ESD protection enable"); + + cts_data->esd_enabled = true; + cts_data->esd_check_fail_cnt = 0; + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + } +} + +void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_info("ESD protection disable"); + + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + flush_workqueue(cts_data->esd_workqueue); + } +} + +void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("Init ESD protection"); + + INIT_DELAYED_WORK(&cts_data->esd_work, cts_esd_protection_work); + + cts_data->esd_enabled = false; + cts_data->esd_check_fail_cnt = 0; +} + +void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("De-Init ESD protection"); + + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + } +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_GLOVE +int cts_enter_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Enter glove mode"); + + ret = cts_fw_reg_writeb(cts_dev, 0x8000 + 149, 1); + if (ret) + cts_err("Enable Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = true; + + return ret; +} + +int cts_exit_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Exit glove mode"); + + ret = cts_fw_reg_writeb(cts_dev, 0x8000 + 149, 0); + if (ret) + cts_err("Exit Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = false; + + return ret; +} + +int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.glove_mode_enabled; +} + +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CONFIG_CTS_CHARGER_DETECT +bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data; + bool attached = false; + int ret; + + cts_data = container_of(cts_dev, struct chipone_ts_data, cts_dev); + + ret = cts_is_charger_attached(cts_data, &attached); + if (ret) + cts_err("Get charger state failed %d", ret); + + cts_dev->rtdata.charger_exist = attached; + + return attached; +} + +int cts_set_dev_charger_attached(struct cts_device *cts_dev, bool attached) +{ + int ret; + + cts_info("Set dev charger %s", attached ? "ATTACHED" : "DETATCHED"); + ret = cts_send_command(cts_dev, + attached ? CTS_CMD_CHARGER_ATTACHED : + CTS_CMD_CHARGER_DETACHED); + if (ret) { + if (ret) + cts_err("Send CMD_CHARGER_%s failed %d", + attached ? "ATTACHED" : "DETACHED", ret); + } + + return ret; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +int cts_enable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect enable"); + ret = cts_send_command(cts_dev, CTS_CMD_ENABLE_FW_LOG_REDIRECT); + if (ret) + cts_err("Send CTS_CMD_ENABLE_FW_LOG_REDIRECT failed %d", ret); + else + cts_dev->rtdata.fw_log_redirect_enabled = true; + + return 0; +} + +int cts_disable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect disable"); + ret = cts_send_command(cts_dev, CTS_CMD_DISABLE_FW_LOG_REDIRECT); + if (ret) + cts_err("Send CTS_CMD_DISABLE_FW_LOG_REDIRECT failed %d", ret); + else + cts_dev->rtdata.fw_log_redirect_enabled = false; + + return 0; +} + +bool cts_is_fw_log_redirect(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.fw_log_redirect_enabled; +} + +int cts_fw_log_show_finish(struct cts_device *cts_dev) +{ + int ret; + + ret = cts_send_command(cts_dev, CTS_CMD_FW_LOG_SHOW_FINISH); + if (ret) + cts_err("Send CTS_CMD_FW_LOG_SHOW_FINISH failed %d", ret); + + return ret; +} + +int cts_get_compensate_cap(struct cts_device *cts_dev, u8 *cap) +{ + int i, ret; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911 && + cts_dev->fwdata.lib_version < 0x0500) { + cts_err("ICNL9911 lib version 0x%04x < v5.0 " + "NOT supported get compensate cap", + cts_dev->fwdata.lib_version); + return -ENOTSUPP; + } + + cts_info("Get compensate cap"); + + ret = cts_dev->ops->enable_get_cneg(cts_dev); + if (ret) { + cts_err("Enable read compensate cap failed %d", ret); + return ret; + } + + mdelay(10); + + for (i = 0; i < 100; i++) { + u8 ready; + + ret = cts_dev->ops->is_cneg_ready(cts_dev, &ready); + if (ret) + cts_err("Read compensate cap ready flag failed %d", + ret); + else { + if (ready) + goto read_compensate_cap; + } + mdelay(5); + } + + cts_err("Wait compensate cap ready timeout"); + return -ETIMEDOUT; + +read_compensate_cap: + ret = cts_dev->ops->get_cneg(cts_dev, cap, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col); + if (ret) + cts_err("Read compensate cap failed %d", ret); + /*Fall through to disable read compensate cap */ + + for (i = 0; i < 100; i++) { + int r; + u8 ready; + + /* r = cts_send_command(cts_dev,CTS_CMD_DISABLE_READ_CNEG); */ + r = cts_dev->ops->disable_get_cneg(cts_dev); + if (r) { + cts_err("Send cmd DISABLE_READ_CNEG failed %d", r); + continue; + } + + mdelay(1); + + r = cts_dev->ops->is_cneg_ready(cts_dev, &ready); + if (r) { + cts_err("Read compensate cap ready flag failed %d", r); + continue; + } + if (ready) + continue; + else + return ret; + } + + cts_warn("Compensate cap ready flag cannot clear, try to do reset"); + + /* Try to do hardware reset */ + cts_reset_device(cts_dev); + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + int r = cts_enter_glove_mode(cts_dev); + + if (r) + cts_err("Enter dev glove mode failed %d", r); + } +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + int r = cts_enable_fw_log_redirect(cts_dev); + + if (r) + cts_err("Enable fw log redirect failed %d", r); + } +#endif /* CONFIG_CTS_GLOVE */ + + return ret; +} + +void cts_firmware_upgrade_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + const struct cts_firmware *firmware; + int retries; + int ret; + + cts_info("Firmware upgrade work"); + + cts_data = + container_of(work, struct chipone_ts_data, fw_upgrade_work.work); + cts_dev = &cts_data->cts_dev; + + firmware = cts_request_firmware(cts_dev, cts_dev->hwdata->hwid, + CTS_DEV_FWID_ANY, + cts_dev->fwdata.version); + if (firmware == NULL) { + cts_err("Request firmware failed"); + return; + } + + retries = 0; + do { + ret = cts_update_firmware(cts_dev, firmware, false); + if (ret) { + cts_err("Update firmware failed %d retries %d", ret, + retries); + + cts_reset_device(cts_dev); + } else + break; + } while (++retries < 3); + + cts_release_firmware(firmware); + + if (ret == 0) + cts_start_device(cts_dev); +} + +void cts_deinit_rtdata(struct cts_device *cts_dev) +{ + if (cts_dev->ops->deinit_int_data) + cts_dev->ops->deinit_int_data(cts_dev); +} + +int cts_reset_device(struct cts_device *cts_dev) +{ + cts_info("Reset device"); + + if (!cts_dev->ops) + return cts_enter_program_mode(cts_dev); + + if (cts_dev->ops->reset_device) + return cts_dev->ops->reset_device(cts_dev); + + cts_err("BUG! reset_device should NOT be null!"); + return -EIO; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h new file mode 100644 index 000000000000..1dcb59d2f679 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h @@ -0,0 +1,890 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_CORE_H +#define CTS_CORE_H + +#include "cts_config.h" + +enum cts_dev_hw_reg { + CTS_DEV_HW_REG_HARDWARE_ID = 0x30000u, + CTS_DEV_HW_REG_CLOCK_GATING = 0x30004u, + CTS_DEV_HW_REG_RESET_CONFIG = 0x30008u, + CTS_DEV_HW_REG_BOOT_MODE = 0x30010u, + CTS_DEV_HW_REG_CURRENT_MODE = 0x30011u, +}; + +enum cts_dev_boot_mode { + CTS_DEV_BOOT_MODE_IDLE = 0, + CTS_DEV_BOOT_MODE_FLASH = 1, + CTS_DEV_BOOT_MODE_I2C_PRG_9911C = 2, + CTS_DEV_BOOT_MODE_TCH_PRG_9916 = 2, + CTS_DEV_BOOT_MODE_SRAM = 3, + CTS_DEV_BOOT_MODE_DDI_PRG = 4, + CTS_DEV_BOOT_MODE_SPI_PRG_9911C = 5, + CTS_DEV_BOOT_MODE_MASK = 7, +}; + +/** I2C addresses(7bits), transfer size and bitrate */ +#define CTS_DEV_NORMAL_MODE_I2CADDR (0x48) +#define CTS_DEV_PROGRAM_MODE_I2CADDR (0x30) +#define CTS_DEV_NORMAL_MODE_ADDR_WIDTH (2) +#define CTS_DEV_PROGRAM_MODE_ADDR_WIDTH (3) +#define CTS_DEV_NORMAL_MODE_SPIADDR (0xF0) +#define CTS_DEV_PROGRAM_MODE_SPIADDR (0x60) + +/** Chipone firmware register addresses under normal mode */ +enum cts_device_fw_reg { + CTS_DEVICE_FW_REG_WORK_MODE = 0x0000, + CTS_DEVICE_FW_REG_SYS_BUSY = 0x0001, + CTS_DEVICE_FW_REG_DATA_READY = 0x0002, + CTS_DEVICE_FW_REG_CMD = 0x0004, + CTS_DEVICE_FW_REG_POWER_MODE = 0x0005, + CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION = 0x0009, + CTS_DEVICE_FW_REG_CHIP_TYPE = 0x000A, + CTS_DEVICE_FW_REG_VERSION = 0x000C, + CTS_DEVICE_FW_REG_DDI_VERSION = 0x0010, + CTS_DEVICE_FW_REG_GET_WORK_MODE = 0x003F, + CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION = 0x0047, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY = 0x004E, + + CTS_DEVICE_FW_REG_TOUCH_INFO = 0x1000, + CTS_DEVICE_FW_REG_RAW_DATA = 0x2000, + CTS_DEVICE_FW_REG_DIFF_DATA = 0x3000, + CTS_DEVICE_FW_REG_GESTURE_INFO = 0x7000, + + CTS_DEVICE_FW_REG_PANEL_PARAM = 0x8000, + CTS_DEVICE_FW_REG_NUM_TX = 0x8007, + CTS_DEVICE_FW_REG_NUM_RX = 0x8008, + CTS_DEVICE_FW_REG_INT_KEEP_TIME = 0x8047, /* Unit us */ + CTS_DEVICE_FW_REG_RAWDATA_TARGET = 0x8049, + CTS_DEVICE_FW_REG_X_RESOLUTION = 0x8090, + CTS_DEVICE_FW_REG_Y_RESOLUTION = 0x8092, + CTS_DEVICE_FW_REG_SWAP_AXES = 0x8094, + CTS_DEVICE_FW_REG_GLOVE_MODE = 0x8095, + CTS_DEVICE_FW_REG_TEST_WITH_DISPLAY_ON = 0x80A3, + CTS_DEVICE_FW_REG_INT_MODE = 0x80D8, + CTS_DEVICE_FW_REG_EARJACK_DETECT_SUPP = 0x8113, + CTS_DEVICE_FW_REG_AUTO_COMPENSATE_EN = 0x8114, + CTS_DEVICE_FW_REG_ESD_PROTECTION = 0x8156, /* RW */ + CTS_DEVICE_FW_REG_FLAG_BITS = 0x8158, + + CTS_DEVICE_FW_REG_COMPENSATE_CAP = 0xA000, + CTS_DEVICE_FW_REG_DEBUG_INTF = 0xF000, +}; + +/** Hardware IDs, read from hardware id register */ +enum cts_dev_hwid { + CTS_DEV_HWID_ICNL9911 = 0x990100u, + CTS_DEV_HWID_ICNL9911S = 0x990110u, + CTS_DEV_HWID_ICNL9911C = 0x991110u, + CTS_DEV_HWID_ICNL9916 = 0x990160u, + CTS_DEV_HWID_ICNL9922 = 0x990220u, + + CTS_DEV_HWID_ANY = 0, + CTS_DEV_HWID_INVALID = 0xFFFFFFFFu, +}; + +/* Firmware IDs, read from firmware register @ref CTS_DEV_FW_REG_CHIP_TYPE + * under normal mode + */ +enum cts_dev_fwid { + CTS_DEV_FWID_ICNL9911 = 0x9911u, + CTS_DEV_FWID_ICNL9911S = 0x9964u, + CTS_DEV_FWID_ICNL9911C = 0x9954u, + CTS_DEV_FWID_ICNL9916 = 0x9916u, + CTS_DEV_FWID_ICNL9922 = 0x9922u, + + CTS_DEV_FWID_ANY = 0u, + CTS_DEV_FWID_INVALID = 0xFFFFu +}; + +/** Commands written to firmware register @ref CTS_DEVICE_FW_REG_CMD under normal mode */ +enum cts_firmware_cmd { + CTS_CMD_RESET = 1, + CTS_CMD_SUSPEND = 2, + CTS_CMD_ENTER_WRITE_PARA_TO_FLASH_MODE = 3, + CTS_CMD_WRITE_PARA_TO_FLASH = 4, + CTS_CMD_WRTITE_INT_HIGH = 5, + CTS_CMD_WRTITE_INT_LOW = 6, + CTS_CMD_RELASE_INT_TEST = 7, + CTS_CMD_RECOVERY_TX_VOL = 0x10, + CTS_CMD_DEC_TX_VOL_1 = 0x11, + CTS_CMD_DEC_TX_VOL_2 = 0x12, + CTS_CMD_DEC_TX_VOL_3 = 0x13, + CTS_CMD_DEC_TX_VOL_4 = 0x14, + CTS_CMD_DEC_TX_VOL_5 = 0x15, + CTS_CMD_DEC_TX_VOL_6 = 0x16, + CTS_CMD_ENABLE_READ_RAWDATA = 0x20, + CTS_CMD_DISABLE_READ_RAWDATA = 0x21, + CTS_CMD_SUSPEND_WITH_GESTURE = 0x40, + CTS_CMD_QUIT_GESTURE_MONITOR = 0x41, + CTS_CMD_CHARGER_ATTACHED = 0x55, + CTS_CMD_EARJACK_ATTACHED = 0x57, + CTS_CMD_EARJACK_DETACHED = 0x58, + CTS_CMD_CHARGER_DETACHED = 0x66, + CTS_CMD_ENABLE_FW_LOG_REDIRECT = 0x86, + CTS_CMD_DISABLE_FW_LOG_REDIRECT = 0x87, + CTS_CMD_ENABLE_READ_CNEG = 0x88, + CTS_CMD_DISABLE_READ_CNEG = 0x89, + CTS_CMD_FW_LOG_SHOW_FINISH = 0xE0, + +}; + +#pragma pack(1) +/** Touch message read back from chip */ +struct cts_device_touch_msg { + u8 id; + __le16 x; + __le16 y; + u8 pressure; + u8 event; + +#define CTS_DEVICE_TOUCH_EVENT_NONE (0) +#define CTS_DEVICE_TOUCH_EVENT_DOWN (1) +#define CTS_DEVICE_TOUCH_EVENT_MOVE (2) +#define CTS_DEVICE_TOUCH_EVENT_STAY (3) +#define CTS_DEVICE_TOUCH_EVENT_UP (4) + +}; + +/** Touch information read back from chip */ +struct cts_device_touch_info { + u8 vkey_state; + u8 num_msg; + + struct cts_device_touch_msg msgs[CFG_CTS_MAX_TOUCH_NUM]; +}; + +/** Gesture trace point read back from chip */ +struct cts_device_gesture_point { + __le16 x; + __le16 y; + u8 pressure; + u8 event; +}; + +/** Gesture information read back from chip */ +struct cts_device_gesture_info { + u8 gesture_id; +#define CTS_GESTURE_UP (0x11) +#define CTS_GESTURE_C (0x12) +#define CTS_GESTURE_O (0x13) +#define CTS_GESTURE_M (0x14) +#define CTS_GESTURE_W (0x15) +#define CTS_GESTURE_E (0x16) +#define CTS_GESTURE_S (0x17) +#define CTS_GESTURE_B (0x18) +#define CTS_GESTURE_T (0x19) +#define CTS_GESTURE_H (0x1A) +#define CTS_GESTURE_F (0x1B) +#define CTS_GESTURE_X (0x1C) +#define CTS_GESTURE_Z (0x1D) +#define CTS_GESTURE_V (0x1E) +#define CTS_GESTURE_D_TAP (0x50) + + u8 num_points; + +#define CTS_CHIP_MAX_GESTURE_TRACE_POINT (64u) + struct cts_device_gesture_point points[CTS_CHIP_MAX_GESTURE_TRACE_POINT]; + +}; +#pragma pack() + +struct cts_device; + +enum cts_crc_type { + CTS_CRC16 = 1, + CTS_CRC32 = 2, +}; + +/** Chip hardware data, will never change */ +struct cts_device_hwdata { + const char *name; + u32 hwid; + u16 fwid; + u8 num_row; + u8 num_col; + u32 sram_size; + + /* Address width under program mode */ + u8 program_addr_width; + + const struct cts_sfctrl *sfctrl; + int (*enable_access_ddi_reg)(struct cts_device *cts_dev, bool enable); +}; + +enum int_data_type { + INT_DATA_TYPE_NONE = 0, + INT_DATA_TYPE_RAWDATA = BIT(0), + INT_DATA_TYPE_MANUAL_DIFF = BIT(1), + INT_DATA_TYPE_REAL_DIFF = BIT(2), + INT_DATA_TYPE_NOISE_DIFF = BIT(3), + INT_DATA_TYPE_BASEDATA = BIT(4), + INT_DATA_TYPE_CNEGDATA = BIT(5), + INT_DATA_TYPE_MASK = 0x3F, +}; + +enum int_data_method { + INT_DATA_METHOD_NONE, + INT_DATA_METHOD_HOST, + INT_DATA_METHOD_POLLING, +}; + +/** Chip firmware data */ +struct cts_device_fwdata { + u16 version; + u16 res_x; + u16 res_y; + u8 rows; + u8 cols; + bool flip_x; + bool flip_y; + bool swap_axes; + u8 ddi_version; + u8 int_mode; + u8 esd_method; + u16 lib_version; + u16 int_keep_time; + u16 rawdata_target; + bool has_int_data; + u8 int_data_method; + u16 int_data_types; + size_t int_data_size; +}; + +/** Chip runtime data */ +struct cts_device_rtdata { + u8 slave_addr; + int addr_width; + bool program_mode; + bool has_flash; + + bool suspended; + bool updating; + bool testing; + + bool gesture_wakeup_enabled; + bool charger_exist; + bool fw_log_redirect_enabled; + bool glove_mode_enabled; + + struct cts_device_touch_info touch_info; + + u8 *int_data; +}; + +struct cts_dev_ops { + int (*get_fw_ver)(const struct cts_device *cts_dev, u16 *fwver); + int (*get_lib_ver)(const struct cts_device *cts_dev, u16 *libver); + int (*get_ddi_ver)(const struct cts_device *cts_dev, u8 *ddiver); + int (*get_res_x)(const struct cts_device *cts_dev, u16 *res_x); + int (*get_res_y)(const struct cts_device *cts_dev, u16 *res_y); + int (*get_rows)(const struct cts_device *cts_dev, u8 *rows); + int (*get_cols)(const struct cts_device *cts_dev, u8 *cols); + int (*get_flip_x)(const struct cts_device *cts_dev, bool *flip_x); + int (*get_flip_y)(const struct cts_device *cts_dev, bool *flip_y); + int (*get_swap_axes)(const struct cts_device *cts_dev, + bool *swap_axes); + int (*get_int_mode)(const struct cts_device *cts_dev, u8 *int_mode); + int (*get_int_keep_time)(const struct cts_device *cts_dev, + u16 *int_keep_time); + int (*get_esd_method)(const struct cts_device *cts_dev, + u8 *esd_method); + int (*get_touchinfo)(const struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info); + int (*get_esd_protection)(const struct cts_device *cts_dev, + u8 *esd_protection); + int (*get_data_ready_flag)(const struct cts_device *cts_dev, + u8 *ready); + int (*clr_data_ready_flag)(const struct cts_device *cts_dev); + int (*enable_get_rawdata)(const struct cts_device *cts_dev); + int (*is_enabled_get_rawdata)(const struct cts_device *cts_dev, + u8 *enabled); + int (*disable_get_rawdata)(const struct cts_device *cts_dev); + int (*enable_get_cneg)(const struct cts_device *cts_dev); + int (*disable_get_cneg)(const struct cts_device *cts_dev); + int (*is_cneg_ready)(const struct cts_device *cts_dev, u8 *ready); + int (*quit_guesture_mode)(const struct cts_device *cts_dev); + int (*get_rawdata)(const struct cts_device *cts_dev, u8 *buf); + int (*get_diffdata)(const struct cts_device *cts_dev, u8 *buf); + int (*get_basedata)(const struct cts_device *cts_dev, u8 *buf); + int (*get_cneg)(const struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*read_hw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*write_hw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*read_ddi_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); + int (*write_ddi_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); + int (*read_fw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*write_fw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*read_reg)(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen); + int (*write_reg)(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen); + int (*get_fw_id)(const struct cts_device *cts_dev, u16 *fwid); + int (*get_workmode)(const struct cts_device *cts_dev, u8 *workmode); + int (*set_workmode)(const struct cts_device *cts_dev, u8 workmode); + int (*set_openshort_mode)(const struct cts_device *cts_dev, u8 mode); + int (*set_tx_vol)(const struct cts_device *cts_dev, u8 txvol); + int (*set_short_test_type)(const struct cts_device *cts_dev, + u8 short_type); + int (*set_openshort_enable)(const struct cts_device *cts_dev, + u8 enable); + int (*is_openshort_enabled)(const struct cts_device *cts_dev, + u8 *enabled); + int (*set_esd_enable)(const struct cts_device *cts_dev, u8 enable); + int (*set_cneg_enable)(const struct cts_device *cts_dev, u8 enable); + int (*set_mnt_enable)(const struct cts_device *cts_dev, u8 enable); + int (*is_display_on)(const struct cts_device *cts_dev, + u8 *display_on); + int (*set_display_on)(const struct cts_device *cts_dev, u8 display_on); + int (*is_cneg_enabled)(const struct cts_device *cts_dev, u8 *enabled); + int (*is_mnt_enabled)(const struct cts_device *cts_dev, u8 *enabled); + int (*set_pwr_mode)(const struct cts_device *cts_dev, u8 pwr_mode); + int (*read_sram_normal_mode)(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + int (*init_int_data)(struct cts_device *cts_dev); + int (*deinit_int_data)(struct cts_device *cts_dev); + int (*get_has_int_data)(const struct cts_device *cts_dev, + bool *has_int_data); + int (*get_int_data_method)(const struct cts_device *cts_dev, + u8 *int_data_method); + int (*set_int_data_method)(const struct cts_device *cts_dev, + u8 int_data_method); + int (*get_int_data_types)(const struct cts_device *cts_dev, + u16 *int_data_types); + int (*set_int_data_types)(const struct cts_device *cts_dev, + u16 int_data_types); + int (*calc_int_data_size)(struct cts_device *cts_dev); + int (*polling_data)(const struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*polling_test_data)(const struct cts_device *cts_dev, u8 *buf, + size_t size); + + int (*top_get_rawdata)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_manual_diff)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_real_diff)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_noise_diff)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_basedata)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_cnegdata)(struct cts_device *cts_dev, u8 *buf, + size_t size); + + int (*reset_device)(const struct cts_device *cts_dev); +}; + +struct cts_device { + struct cts_platform_data *pdata; + + const struct cts_device_hwdata *hwdata; + struct cts_device_fwdata fwdata; + struct cts_device_rtdata rtdata; + const struct cts_flash *flash; + bool enabled; +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + char config_fw_name[CFG_CTS_FW_FILE_NAME_MAX_LEN + 1]; +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + + struct cts_dev_ops *ops; + u8 *tx; + u8 *rx; +}; + +struct cts_platform_data; + +struct chipone_ts_data { +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; +#else + struct spi_device *spi_client; +#endif /* CONFIG_CTS_I2C_HOST */ + struct device *device; + struct cts_device cts_dev; + struct cts_platform_data *pdata; + struct workqueue_struct *workqueue; + struct delayed_work fw_upgrade_work; + struct work_struct ts_resume_work; +#ifdef CONFIG_CTS_CHARGER_DETECT + void *charger_detect_data; +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_ESD_PROTECTION + struct workqueue_struct *esd_workqueue; + struct delayed_work esd_work; + bool esd_enabled; + int esd_check_fail_cnt; +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_LEGACY_TOOL + struct proc_dir_entry *procfs_entry; +#endif /* CONFIG_CTS_LEGACY_TOOL */ + + bool force_reflash; + +}; + +static inline u32 get_unaligned_le24(const void *p) +{ + const u8 *puc = (const u8 *)p; + + return (puc[0] | (puc[1] << 8) | (puc[2] << 16)); +} + +static inline u32 get_unaligned_be24(const void *p) +{ + const u8 *puc = (const u8 *)p; + + return (puc[2] | (puc[1] << 8) | (puc[0] << 16)); +} + +static inline void put_unaligned_be24(u32 v, void *p) +{ + u8 *puc = (u8 *) p; + + puc[0] = (v >> 16) & 0xFF; + puc[1] = (v >> 8) & 0xFF; + puc[2] = (v >> 0) & 0xFF; +} + +#define wrap(max, x) ((max) - 1 - (x)) + +extern void cts_lock_device(const struct cts_device *cts_dev); +extern void cts_unlock_device(const struct cts_device *cts_dev); + +extern int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); + +extern int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +extern int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay); +extern int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay); +extern int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + int retry, int delay); +extern int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, + size_t len, u32 crc, int retry); + +extern int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay); +extern int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay); +extern int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +extern int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay); + +extern int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); +extern int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, + size_t len, int retry, int delay, + int idle); + +extern int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay); + +extern int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); + +static inline int cts_hw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_fw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_fw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writel(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_fw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_fw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_fw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_fw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_readw(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_fw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_readl(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_fw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_fw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_fw_reg_readsb_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, + size_t len, int idle) +{ + return cts_fw_reg_readsb_retry_delay_idle(cts_dev, reg_addr, dst, len, + 1, 0, idle); +} + +static inline int cts_hw_reg_writeb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_writel_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_hw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_hw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_hw_reg_readb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_readw_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_hw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_readl_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_hw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_hw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_sram_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b) +{ + return cts_sram_writeb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_writew(const struct cts_device *cts_dev, + u32 addr, u16 w) +{ + return cts_sram_writew_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_writel(const struct cts_device *cts_dev, + u32 addr, u32 l) +{ + return cts_sram_writel_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_writesb(const struct cts_device *cts_dev, u32 addr, + const void *src, size_t len) +{ + return cts_sram_writesb_retry(cts_dev, addr, src, len, 1, 0); +} + +static inline int cts_sram_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b) +{ + return cts_sram_readb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w) +{ + return cts_sram_readw_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l) +{ + return cts_sram_readl_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len) +{ + return cts_sram_readsb_retry(cts_dev, addr, dst, len, 1, 0); +} + +#ifdef CONFIG_CTS_I2C_HOST +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_I2CADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#else +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_SPIADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_SPIADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#endif + +extern int cts_irq_handler(struct cts_device *cts_dev); +extern void cts_firmware_upgrade_work(struct work_struct *work); + +extern bool cts_is_device_suspended(const struct cts_device *cts_dev); +extern int cts_suspend_device(struct cts_device *cts_dev); +extern int cts_resume_device(struct cts_device *cts_dev); + +extern bool cts_is_device_program_mode(const struct cts_device *cts_dev); +extern int cts_enter_program_mode(struct cts_device *cts_dev); +extern int cts_enter_normal_mode(struct cts_device *cts_dev); + +extern int cts_probe_device(struct cts_device *cts_dev); +extern int cts_set_work_mode(const struct cts_device *cts_dev, u8 mode); +extern int cts_get_work_mode(const struct cts_device *cts_dev, u8 *mode); +extern int cts_get_firmware_version(const struct cts_device *cts_dev, + u16 *version); +extern int cts_get_ddi_version(const struct cts_device *cts_dev, u8 *version); +extern int cts_get_lib_version(const struct cts_device *cts_dev, + u16 *lib_version); +extern int cts_get_data_ready_flag(const struct cts_device *cts_dev, u8 *flag); +extern int cts_clr_data_ready_flag(const struct cts_device *cts_dev); +extern int cts_send_command(const struct cts_device *cts_dev, u8 cmd); +extern int cts_get_panel_param(const struct cts_device *cts_dev, + void *param, size_t size); +extern int cts_set_panel_param(const struct cts_device *cts_dev, + const void *param, size_t size); +extern int cts_get_x_resolution(const struct cts_device *cts_dev, + u16 *resolution); +extern int cts_get_y_resolution(const struct cts_device *cts_dev, + u16 *resolution); +extern int cts_get_num_rows(const struct cts_device *cts_dev, u8 *num_rows); +extern int cts_get_num_cols(const struct cts_device *cts_dev, u8 *num_cols); +extern int cts_enable_get_rawdata(const struct cts_device *cts_dev); +extern int cts_disable_get_rawdata(const struct cts_device *cts_dev); +extern int cts_polling_data(const struct cts_device *cts_dev, void *buf, + size_t size); +extern int cts_polling_test_data(const struct cts_device *cts_dev, void *buf, + size_t size); +extern int cts_get_rawdata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_diffdata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_basedata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_compensate_cap(struct cts_device *cts_dev, u8 *cap); +extern int cts_get_fwid(struct cts_device *cts_dev, u16 *fwid); +extern int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid); +extern int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +#ifdef CFG_CTS_GESTURE +extern void cts_enable_gesture_wakeup(struct cts_device *cts_dev); +extern void cts_disable_gesture_wakeup(struct cts_device *cts_dev); +extern bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev); +extern int cts_get_gesture_info(const struct cts_device *cts_dev, + void *gesture_info, bool trace_point); +#endif /* CFG_CTS_GESTURE */ + +extern int cts_set_int_data_types(struct cts_device *cts_dev, u16 types); +extern int cts_set_int_data_method(struct cts_device *cts_dev, u8 method); + +#ifdef CONFIG_CTS_ESD_PROTECTION +extern void cts_init_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_enable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_disable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_deinit_esd_protection(struct chipone_ts_data *cts_data); +#else /* CONFIG_CTS_ESD_PROTECTION */ +static inline void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_GLOVE +extern int cts_enter_glove_mode(struct cts_device *cts_dev); +extern int cts_exit_glove_mode(struct cts_device *cts_dev); +int cts_is_glove_enabled(const struct cts_device *cts_dev); +#else +static inline int cts_enter_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_exit_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return 0; +} +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern bool cts_is_charger_exist(struct cts_device *cts_dev); +extern int cts_set_dev_charger_attached(struct cts_device *cts_dev, + bool attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + return false; +} + +static inline int cts_set_dev_charger_attached(struct cts_device *cts_dev, + bool attached) +{ + return 0; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_LEGACY_TOOL +extern int cts_tool_init(struct chipone_ts_data *cts_data); +extern void cts_tool_deinit(struct chipone_ts_data *data); +#else /* CONFIG_CTS_LEGACY_TOOL */ +static inline int cts_tool_init(struct chipone_ts_data *cts_data) +{ + return 0; +} + +static inline void cts_tool_deinit(struct chipone_ts_data *data) +{ +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +extern bool cts_is_device_enabled(const struct cts_device *cts_dev); +extern int cts_start_device(struct cts_device *cts_dev); +extern int cts_stop_device(struct cts_device *cts_dev); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern int cts_enable_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_disable_fw_log_redirect(struct cts_device *cts_dev); +extern bool cts_is_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_fw_log_show_finish(struct cts_device *cts_dev); +#endif + +#ifdef CFG_CTS_UPDATE_CRCCHECK +extern int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry); +#endif + +extern const char *cts_dev_boot_mode2str(u8 boot_mode); +extern bool cts_is_fwid_valid(u16 fwid); +extern void cts_deinit_rtdata(struct cts_device *cts_dev); + +extern int cts_reset_device(struct cts_device *cts_dev); +#endif /* CTS_CORE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c new file mode 100644 index 000000000000..c132cdca6c8d --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Firmware" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include +#include +#include +#include + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#include "cts_builtin_firmware.h" +#define NUM_DRIVER_BUILTIN_FIRMWARE ARRAY_SIZE(cts_driver_builtin_firmwares) +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#define CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE (0x20000) +#define CTS_SECTION_ENABLE_FLAG (0x0000C35A) + +enum cts_firmware_section_offset { + CTS_FIRMWARE_SECTION_OFFSET = 0x00000000, + CTS_FIRMWARE_CRC_SECTION_OFFSET = 0x17000, + CTS_DDIPARAM_SECTION_OFFSET = 0x00019000, + CTS_DDIPARAM_CRC_SECTION_OFFSET = 0x1B000, + +}; + +struct cts_firmware_sect_info { + const u8 *firmware_sect; + size_t firmware_sect_size; + const u8 *firmware_crc_sect; + size_t firmware_crc_sect_size; + u32 firmware_sect_crc; + + const u8 *ddiparam_sect; + size_t ddiparam_sect_size; + const u8 *ddiparam_crc_sect; + size_t ddiparam_crc_sect_size; + u32 ddiparam_sect_crc; + +}; + +#define FIRMWARE_SECTION(firmware) \ + ((firmware)->data) +#define FIRMWARE_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_FIRMWARE_CRC_SECTION_OFFSET) +#define DDIPARAM_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_SECTION_OFFSET) +#define DDIPARAM_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_CRC_SECTION_OFFSET) + +#define FIRMWARE_SECTION_CRC(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware))) +#define FIRMWARE_SECTION_SIZE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 4)) +#define FIRMWARE_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 8)) +#define FIRMWARE_CRC_SECTION_SIZE (12) + +#define DDIPARAM_SECTION_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware))) +#define DDIPARAM_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 4)) +#define DDIPARAM_SECTION_CRC(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 8)) +#define DDIPARAM_SECTION_SIZE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 12)) +#define DDIPARAM_CRC_SECTION_SIZE (17) + +u16 cts_crc16(const u8 *data, size_t len) +{ + u16 crc = 0; + + const static u16 crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202, + }; + + while (len) { + crc = (crc << 8) ^ crc16_table[((crc >> 8) ^ *data) & 0xFF]; + data++; + len--; + } + + return crc; +} + +u32 cts_crc32(const u8 *data, size_t len) +{ + const static u32 crc32_table[] = { + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, + 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, + 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, + 0x48D0C6C7, + 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, + 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, + 0x7DDC5DA3, + 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, + 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, + 0xBAEA46EF, + 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, + 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, + 0xC3F706FB, + 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, + 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, + 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, + 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, + 0x054BF6A4, + 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, + 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, + 0x5A5E5B08, + 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, + 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, + 0xBB60ADFC, + 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, + 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, + 0xE4750050, + 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, + 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, + 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, + 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, + 0x4BC510E1, + 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, + 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, + 0x32D850F5, + 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, + 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, + 0xF5EE4BB9, + 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, + 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, + 0xC0E2D0DD, + 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, + 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, + 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, + 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, + 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, + 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, + 0x155A565E, + 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, + 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, + 0x2056CD3A, + 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, + 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, + 0xE760D676, + 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, + 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, + 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, + 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + }; + + u32 crc = 0; + + while (len) { + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *data) & 0xFF]; + + data++; + len--; + } + + return crc; +} + +static int calc_crc_in_flash(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + return cts_dev->hwdata->sfctrl->ops->calc_flash_crc(cts_dev, + flash_addr, size, + crc); +} + +static bool is_multi_section_firmware(const struct cts_firmware *firmware) +{ + return (firmware->size == CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_single_section_firmware(const struct cts_firmware *firmware) +{ + return !(is_multi_section_firmware(firmware)); +} + +static bool is_firmware_size_valid(const struct cts_firmware *firmware) +{ + return (firmware->size > 0x102 && + firmware->size <= CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_multi_section_firmware_valid(const struct cts_firmware *firmware) +{ + u32 crc; + + crc = + cts_crc32(FIRMWARE_SECTION(firmware), + FIRMWARE_SECTION_SIZE(firmware)); + if (crc != FIRMWARE_SECTION_CRC(firmware)) { + cts_err("Firmware-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + FIRMWARE_SECTION_CRC(firmware)); + return false; + } + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + crc = cts_crc32(DDIPARAM_SECTION(firmware), + DDIPARAM_SECTION_SIZE(firmware)); + if (crc != DDIPARAM_SECTION_CRC(firmware)) { + cts_err + ("DDIParam-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + DDIPARAM_SECTION_CRC(firmware)); + return false; + } + } else + cts_info("DDIParam-section is NOT enabled"); + + return true; +} + +static bool is_firmware_valid(const struct cts_firmware *firmware) +{ + if (firmware && firmware->data && is_firmware_size_valid(firmware)) { + if (is_single_section_firmware(firmware) || + is_multi_section_firmware_valid(firmware)) + return true; + } + + return false; +} + +static void parse_single_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + static u8 crc_sect[12]; + + info->firmware_sect = firmware->data; + info->firmware_sect_size = firmware->size; + info->firmware_sect_crc = cts_crc32(firmware->data, firmware->size); + + put_unaligned_le32(info->firmware_sect_crc, crc_sect); + put_unaligned_le32(info->firmware_sect_size, crc_sect + 4); + put_unaligned_le32(~0x0000C35A, crc_sect + 8); /* Enable CRC check */ + info->firmware_crc_sect = crc_sect; + info->firmware_crc_sect_size = sizeof(crc_sect); +} + +static void parse_multi_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + info->firmware_sect = FIRMWARE_SECTION(firmware); + info->firmware_sect_size = FIRMWARE_SECTION_SIZE(firmware); + info->firmware_crc_sect = FIRMWARE_CRC_SECTION(firmware); + info->firmware_crc_sect_size = FIRMWARE_CRC_SECTION_SIZE; + info->firmware_sect_crc = FIRMWARE_SECTION_CRC(firmware); + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + info->ddiparam_sect = DDIPARAM_SECTION(firmware); + info->ddiparam_sect_size = DDIPARAM_SECTION_SIZE(firmware); + info->ddiparam_crc_sect = DDIPARAM_CRC_SECTION(firmware); + info->ddiparam_crc_sect_size = DDIPARAM_CRC_SECTION_SIZE; + info->ddiparam_sect_crc = DDIPARAM_SECTION_CRC(firmware); + } +} + +static int parse_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + memset(info, 0, sizeof(*info)); + + if (is_multi_section_firmware(firmware)) + parse_multi_section_firmware(firmware, info); + else + parse_single_section_firmware(firmware, info); + + cts_info(" Firmware section size: %zu", info->firmware_sect_size); + if (info->ddiparam_crc_sect) + cts_info(" DDIParam section size: %zu", + info->ddiparam_sect_size); + + return 0; +} + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#ifdef CONFIG_CTS_SYSFS +int cts_get_num_driver_builtin_firmware(void) +{ + return NUM_DRIVER_BUILTIN_FIRMWARE; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_name(const + char + *name) +{ + const struct cts_firmware *firmware; + int i; + + cts_info("Request driver builtin by name '%s'", name); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < NUM_DRIVER_BUILTIN_FIRMWARE; i++, firmware++) { + if (strcmp(firmware->name, name) == 0) { + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware)); + return firmware; + } + + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu invalid", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } + } + + return NULL; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_index(u32 + index) +{ + const struct cts_firmware *firmware; + + cts_info("Request driver builtin by index %u", index); + + if (index < NUM_DRIVER_BUILTIN_FIRMWARE) { + firmware = cts_driver_builtin_firmwares + index; + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + return firmware; + } + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu INVALID", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } else + cts_warn("Request driver builtin by index %u too large >= %zu", + index, NUM_DRIVER_BUILTIN_FIRMWARE); + + return NULL; +} +#endif /* CONFIG_CTS_SYSFS */ + +static const struct cts_firmware *cts_request_newer_driver_builtin_firmware(u32 + hwid, + u16 + fwid, + u16 + device_fw_ver) +{ +#define MATCH_HWID(firmware, hwid) \ + ((hwid) == CTS_DEV_HWID_ANY || (firmware)->hwid == (hwid)) +#define MATCH_FWID(firmware, fwid) \ + ((fwid) == CTS_DEV_FWID_ANY || (firmware)->fwid == (fwid)) + + const struct cts_firmware *firmware = NULL; + int i; + + cts_info + ("Request driver builtin if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, device_fw_ver); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < ARRAY_SIZE(cts_driver_builtin_firmwares); + i++, firmware++) { + if (MATCH_HWID(firmware, hwid) && MATCH_FWID(firmware, fwid)) { + if (!is_firmware_valid(firmware)) { + cts_err("Found driver builtin '%s' " + "hwid: %06x fwid: %04x data: %p size: %zu INVALID", + firmware->name, firmware->hwid, + firmware->fwid, firmware->data, + firmware->size); + continue; + } + + cts_info("Found matched driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + + /* if(FIRMWARE_VERSION(firmware) > device_fw_ver) { */ + if (1) { + cts_info("Found newer driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x > %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware), + device_fw_ver); + return firmware; + } + } + } + + cts_info("No newer driver builtin found"); + + return NULL; + +#undef MATCH_HWID +#undef MATCH_FWID +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +bool is_filesystem_mounted(const char *filepath) +{ + struct path root_path; + struct path path; + int ret; + + ret = kern_path("/", LOOKUP_FOLLOW, &root_path); + if (ret) + return false; + + ret = kern_path(filepath, LOOKUP_FOLLOW, &path); + if (ret) + goto err_put_root_path; + + if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) + /* not mounted */ + ret = false; + else + ret = true; + + path_put(&path); +err_put_root_path: + path_put(&root_path); + + return !!ret; +} + +static int cts_request_firmware_full_filepath(struct cts_firmware *firmware, + const char *filepath, + int curr_version) +{ + struct file *file; + u16 version; + u8 buff[2]; + loff_t pos = 0; + int read_size; + int ret = 0; + + file = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + firmware->size = file_inode(file)->i_size; + if (!is_firmware_size_valid(firmware)) { + cts_info("File '%s' size: %zu invalid", filepath, + firmware->size); + ret = -EINVAL; + goto err_close_file; + } + + pos = FIRMWARE_VERSION_OFFSET; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, buff, 2, &pos); +#else + read_size = kernel_read(file, pos, buff, 2); +#endif + if (read_size < 0) { + cts_err("Read version from offset 0x100 failed"); + ret = -EIO; + goto err_close_file; + } + version = get_unaligned_le16(buff); + + if (version <= curr_version) { + cts_info("File '%s' size: %zu version: %04x <= %04x", + filepath, firmware->size, version, curr_version); + ret = -EINVAL; + goto err_close_file; + } + + cts_info("File '%s' size: %zu version: %04x", + filepath, firmware->size, version); + + firmware->data = (u8 *) vmalloc(firmware->size); + if (firmware->data == NULL) { + cts_err("Request form fs alloc firmware data failed"); + ret = -ENOMEM; + goto err_close_file; + } + + pos = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, firmware->data, firmware->size, &pos); +#else + read_size = kernel_read(file, pos, firmware->data, firmware->size); +#endif + if (read_size < 0 || read_size != firmware->size) { + cts_err("Request from fs read whole file failed %d", read_size); + ret = -EIO; + goto err_free_firmware_data; + } + + { + int r = filp_close(file, NULL); + + if (r) + cts_warn("Close file '%s' failed %d", filepath, r); + } + + return 0; + +err_free_firmware_data: + vfree(firmware->data); + firmware->data = NULL; + +err_close_file:{ + int r = filp_close(file, NULL); + + if (r) + cts_warn("Close file '%s' failed %d", filepath, r); + } + + return ret; +} + +static int cts_wrap_request_firmware(struct cts_firmware *firmware, + const char *name, struct device *device, + int curr_version) +{ + int ret; + + ret = request_firmware(&firmware->fw, name, device); + if (ret) { + cts_err("Could not load firmware from %s: %d", name, ret); + return ret; + } + + /* Map firmware structure to cts_firmware */ + firmware->data = (u8 *) firmware->fw->data; + firmware->size = firmware->fw->size; + + /* Check firmware */ + if (!is_firmware_valid(firmware)) { + cts_err("Request firmware file INVALID, size %zu", + firmware->size); + ret = -EINVAL; + goto err_release_firmware; + } + + /* Check firmware version */ + if (curr_version > 0 && curr_version >= FIRMWARE_VERSION(firmware)) { + cts_info("Request firmware file with version 0x%04x < 0x%04x", + FIRMWARE_VERSION(firmware), curr_version); + ret = -EINVAL; + goto err_release_firmware; + } + + return 0; + +err_release_firmware: + release_firmware(firmware->fw); + + return ret; +} + +const struct cts_firmware *cts_request_newer_firmware_from_fs(const struct + cts_device + *cts_dev, + const char + *filepath, + u16 curr_version) +{ + struct cts_firmware *firmware = NULL; + int ret = 0; + + cts_info("Request from file '%s' if version > %04x", + filepath, curr_version); + + if (filepath == NULL || filepath[0] == '\0') { + cts_err("Request from file path INVALID %p", filepath); + return NULL; + } + + firmware = + (struct cts_firmware *)kzalloc(sizeof(*firmware), GFP_KERNEL); + if (firmware == NULL) { + cts_err("Request from file alloc struct cts_firmware failed"); + return NULL; + } + + if (strchr(filepath, '/') != NULL) { /* Full path */ + cts_info("Filepath is fullpath, direct read it out"); + ret = cts_request_firmware_full_filepath(firmware, + filepath, + curr_version); + } else { /* ONLY file name is givin */ + cts_info("Filepath is only filename, use request_firmware()"); + ret = cts_wrap_request_firmware(firmware, filepath, + &cts_dev->pdata->ts_input_dev->dev, + curr_version); + } + + if (ret) { + cts_err("Request from file '%s' failed %d", filepath, ret); + kfree(firmware); + return NULL; + } else { + return firmware; + } +} + +const struct cts_firmware *cts_request_firmware_from_fs(const struct cts_device + *cts_dev, + const char *filepath) +{ + cts_info("Request from file '%s'", filepath); + + return cts_request_newer_firmware_from_fs(cts_dev, filepath, 0); +} + +int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash) +{ + const struct cts_firmware *firmware; + int ret; + + cts_info("Update from file '%s' to %s", filepath, + to_flash ? "flash" : "sram"); + + firmware = cts_request_firmware_from_fs(cts_dev, filepath); + if (firmware == NULL) { + cts_err("Request from file '%s' failed", filepath); + return -EFAULT; + } + + ret = cts_update_firmware(cts_dev, firmware, to_flash); + if (ret) { + cts_err("Update to %s from file failed %d", + to_flash ? "flash" : "sram", ret); + goto err_release_firmware; + } + + cts_info("Update from file success"); + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} +#endif /*CFG_CTS_FIRMWARE_IN_FS */ + +const struct cts_firmware *cts_request_firmware(const struct cts_device + *cts_dev, u32 hwid, u16 fwid, + u16 curr_firmware_ver) +{ + const struct cts_firmware *firmware_builtin = NULL; + const struct cts_firmware *firmware_from_file = NULL; + + if (hwid == CTS_DEV_HWID_INVALID) + hwid = CTS_DEV_HWID_ANY; + if (fwid == CTS_DEV_FWID_INVALID) + fwid = CTS_DEV_FWID_ANY; + + cts_info("Request newer if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, curr_firmware_ver); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + firmware_builtin = + cts_request_newer_driver_builtin_firmware(hwid, fwid, + curr_firmware_ver); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS + /* Check firmware in file system when probe only when build to .ko */ + if (hwid == CTS_DEV_HWID_ICNL9911C) { + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH_9911)) { + firmware_from_file = + cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] + ? cts_dev->config_fw_name : + CFG_CTS_FIRMWARE_FILENAME_9911, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + CFG_CTS_FIRMWARE_FILENAME_9911, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION(firmware_builtin) + : + curr_firmware_ver); + } + } else if (hwid == CTS_DEV_HWID_ICNL9916) { + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH_9916)) { + firmware_from_file = + cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] + ? cts_dev->config_fw_name : + CFG_CTS_FIRMWARE_FILENAME_9916, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + CFG_CTS_FIRMWARE_FILENAME_9916, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION(firmware_builtin) + : + curr_firmware_ver); + } + } else if (hwid == CTS_DEV_HWID_ICNL9922) { + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH_9922)) { + firmware_from_file = + cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] + ? cts_dev->config_fw_name : + CFG_CTS_FIRMWARE_FILENAME_9922, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + CFG_CTS_FIRMWARE_FILENAME_9922, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION(firmware_builtin) + : + curr_firmware_ver); + } + } +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + + return firmware_from_file ? firmware_from_file : firmware_builtin; +} + +void cts_release_firmware(const struct cts_firmware *firmware) +{ + cts_info("Release firmware %p", firmware); + + if (firmware) { + if (firmware->fw) { + /* Use request_firmware() get struct firmware * */ + cts_info("Release firmware from request_firmware"); + release_firmware(firmware->fw); + kfree(firmware); + } else if (firmware->name == NULL) { + /* Direct read from file */ + cts_info("Release firmware from direct-load"); + vfree(firmware->data); + kfree(firmware); + } else + /* Builtin firmware with non-NULL name, no need to free */ + cts_info("Release firmware from driver built-in"); + } +} + +static int validate_flash_data(const struct cts_device *cts_dev, + u32 flash_addr, const u8 *data, size_t size, + u8 *buf, bool calc_crc, u32 crc) +{ + int ret, i; + bool free_data = false; + u32 crc_flash; + + cts_info("Validate flash data from 0x%06x size %zu by %s", + flash_addr, size, calc_crc ? "check-crc" : "direct-readback"); + + if (calc_crc) { + ret = calc_crc_in_flash(cts_dev, flash_addr, size, &crc_flash); + if (ret) { + cts_err + ("Calc data in flash from 0x%06x size %zu crc failed %d, " + "try to validate by direct readback", flash_addr, + size, ret); + /* FALL through by direct compare data read back */ + } else { + if (crc_flash != crc) { + cts_err + ("Crc in flash from 0x%06x size %zu mismatch 0x%08x != 0x%08x", + flash_addr, size, crc_flash, crc); + /* FALL through by direct compare data read back */ + } else { + cts_info("Flash data crc correct"); + return 0; + } + } + } + + if (buf == NULL) { + buf = (u8 *) kmalloc(size, GFP_KERNEL); + if (buf == NULL) { + cts_err("Validate flash data allocate mem failed"); + return -ENOMEM; + } + + free_data = true; + } + + ret = cts_read_flash(cts_dev, flash_addr, buf, size); + if (ret) { + cts_err("Read flash from 0x%06x size %zu failed %d", flash_addr, + size, ret); + goto err_free_buf; + } + + for (i = 0; i < size; i++) { + if (buf[i] != data[i]) { + if (ret == 0) { + cts_err + ("Flash data from 0x%06x size %zu first bytes diff:", + flash_addr, size); + } + + if (ret < 100) { + cts_err(" 0x%06x: %02x %02x", i, buf[i], + data[i]); + } else if (ret == 100) { + cts_err(" ..."); + } + ret++; + } + } + +err_free_buf: + if (free_data) + kfree(buf); + + return ret; +} + +static int cts_program_firmware(const struct cts_device *cts_dev, + const struct cts_firmware_sect_info + *firmware_info) +{ + int ret; + u8 crc_sect_buf[FIRMWARE_CRC_SECTION_SIZE]; + + cts_info("Program firmware size %zu", + firmware_info->firmware_sect_size); + + /* Write firmware to SRAM for easy enter normal mode after update */ + cts_info("Write firmware section to sram"); + ret = cts_sram_writesb_check_crc_retry(cts_dev, 0, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, + firmware_info->firmware_sect_crc, + 3); + if (ret) { + cts_err("Write firmware section to sram failed %d", ret); + return ret; + } + + ret = cts_program_flash_from_sram(cts_dev, + 4, 4, + firmware_info->firmware_sect_size - + 4); + if (ret) { + cts_err("Program firmware section from sram failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, + CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size); + if (ret) { + cts_err("Program firmware crc section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate Firmware-CRC section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, 0, firmware_info->firmware_sect, 4); + if (ret) { + cts_err("Program firmware section fist 4bytes failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_FIRMWARE_SECTION_OFFSET, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, NULL, true, + firmware_info->firmware_sect_crc); + if (ret) { + cts_err("Validate firmware section failed %d", ret); + return ret; + } + + return 0; +} + +static int cts_program_ddiparam(const struct cts_device *cts_dev, + const struct cts_firmware_sect_info + *firmware_info) +{ + int ret; + u8 crc_sect_buf[DDIPARAM_CRC_SECTION_SIZE]; + + cts_info("Program DDIParam size: %zu", + firmware_info->ddiparam_sect_size); + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size); + if (ret) { + cts_err("Program DDIParam section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size, NULL, true, + firmware_info->ddiparam_sect_crc); + if (ret) { + cts_err("Validate DDIParam section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size); + if (ret) { + cts_err("Program DDIParam-CRC section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate DDIParam-CRC section failed %d", ret); + return ret; + } + + return 0; +} + +int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, bool to_flash) +{ + struct cts_firmware_sect_info firmware_info; + int ret, retries; + + to_flash = false; + cts_info("Update firmware to %s ver: %04x size: %zu", + to_flash ? "flash" : "sram", + FIRMWARE_VERSION(firmware), firmware->size); + + if (parse_firmware(firmware, &firmware_info)) { + cts_err("Parse firmware failed"); + return -EINVAL; + } + + cts_dev->rtdata.updating = true; + + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Device enter program mode failed %d", ret); + goto out; + } + + if (!to_flash || !cts_dev->rtdata.has_flash) { + cts_info("Write firmware section to sram size %zu", + firmware_info.firmware_sect_size); + ret = cts_sram_writesb_check_crc_retry(cts_dev, + 0, + firmware_info.firmware_sect, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, 3); + if (ret) + cts_err("Write firmware section to sram failed %d", + ret); +#ifdef CFG_CTS_UPDATE_CRCCHECK + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916 || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922) + cts_sram_writesb_boot_crc_retry(cts_dev, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, + 3); +#endif + goto out; + } + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) + cts_warn("Prepare flash operation failed %d", ret); + /* Go through and try */ + + retries = 0; + do { + retries++; + + ret = cts_erase_flash(cts_dev, CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info.firmware_crc_sect_size); + if (ret) { + cts_err + ("Erase firmware crc section failed %d retries %d", + ret, retries); + continue; + } + + ret = + cts_erase_flash(cts_dev, 0, + firmware_info.firmware_sect_size); + if (ret) { + cts_err("Erase firmware section failed %d retries %d", + ret, retries); + continue; + } + + ret = cts_program_firmware(cts_dev, &firmware_info); + if (ret) { + cts_err + ("Program firmware & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + + if (ret == 0 && firmware_info.ddiparam_sect_size != 0) { + retries = 0; + do { + retries++; + + ret = + cts_erase_flash(cts_dev, + CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info.ddiparam_crc_sect_size); + if (ret) { + cts_err + ("Erase DDIParam crc secction failed %d, retries %d", + ret, retries); + continue; + } + + ret = + cts_erase_flash(cts_dev, + CTS_DDIPARAM_SECTION_OFFSET, + firmware_info.ddiparam_sect_size); + if (ret) { + cts_err + ("Erase DDIParam section failed %d, retries %d", + ret, retries); + continue; + } + + ret = cts_program_ddiparam(cts_dev, &firmware_info); + if (ret) { + cts_err + ("Program DDIParam & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + } + + cts_post_flash_operation(cts_dev); + +out: + cts_dev->rtdata.updating = false; + + if (ret == 0) { + if (firmware_info.firmware_sect_size <= + cts_dev->hwdata->sfctrl->xchg_sram_base) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + /* return ret; */ + } + } + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + return ret; +} + +bool cts_is_firmware_updating(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.updating; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h new file mode 100644 index 000000000000..6d057eae8367 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_FIRMWARE_H +#define CTS_FIRMWARE_H + +#include "cts_config.h" +#include + +struct cts_firmware { + const char *name; /* MUST set to non-NULL if driver builtin firmware */ + u32 hwid; + u16 fwid; + + u8 *data; + size_t size; + const struct firmware *fw; +}; + +#define FIRMWARE_VERSION_OFFSET 0x100 +#define FIRMWARE_VERSION(firmware) \ + get_unaligned_le16((firmware)->data + FIRMWARE_VERSION_OFFSET) + +struct cts_device; + +extern const struct cts_firmware *cts_request_firmware(const struct cts_device + *cts_dev, u32 hwid, + u16 fwid, + u16 device_fw_ver); +extern void cts_release_firmware(const struct cts_firmware *firmware); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +extern int cts_get_num_driver_builtin_firmware(void); +extern const struct cts_firmware +*cts_request_driver_builtin_firmware_by_name(const char *name); +extern const struct cts_firmware +*cts_request_driver_builtin_firmware_by_index(u32 index); +#else /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +static inline int cts_get_num_driver_builtin_firmware(void) +{ + return 0; +} + +static inline const struct cts_firmware +*cts_request_driver_builtin_firmware_by_name(const char *name) +{ + return NULL; +} + +static inline const struct cts_firmware +*cts_request_driver_builtin_firmware_by_index(u32 index) +{ + return NULL; +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +extern bool cts_is_firmware_updating(const struct cts_device *cts_dev); + +extern int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, + bool to_flash); + +#ifdef CFG_CTS_FIRMWARE_IN_FS +extern const struct cts_firmware *cts_request_firmware_from_fs(const struct + cts_device + *cts_dev, + const char + *filepath); +extern int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash); + +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +extern u16 cts_crc16(const u8 *data, size_t len); +extern u32 cts_crc32(const u8 *data, size_t len); + +#endif /* CTS_FIRMWARE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c new file mode 100644 index 000000000000..1adac1e316b8 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "cts_config.h" +#include "cts_firmware.h" +#include "cts_platform.h" + +#include "cts_hostcomm.h" + +extern bool set_short_test_type(const struct cts_device *cts_dev, u8 type); +extern int disable_fw_monitor_mode(const struct cts_device *cts_dev); +extern int disable_fw_auto_compensate(const struct cts_device *cts_dev); +extern int disable_fw_esd_protection(const struct cts_device *cts_dev); +extern int set_fw_work_mode(const struct cts_device *cts_dev, u8 mode); +extern int set_display_state(const struct cts_device *cts_dev, bool active); + +int cts_hostcomm_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver) +{ + return cts_get_firmware_version(cts_dev, fwver); +} + +int cts_hostcomm_get_lib_ver(const struct cts_device *cts_dev, u16 *libver) +{ + return cts_get_lib_version(cts_dev, libver); +} + +int cts_hostcomm_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver) +{ + return cts_get_ddi_version(cts_dev, ddiver); +} + +int cts_hostcomm_get_res_x(const struct cts_device *cts_dev, u16 *res_x) +{ + return cts_get_x_resolution(cts_dev, res_x); +} + +int cts_hostcomm_get_res_y(const struct cts_device *cts_dev, u16 *res_y) +{ + return cts_get_y_resolution(cts_dev, res_y); +} + +int cts_hostcomm_get_rows(const struct cts_device *cts_dev, u8 *rows) +{ + return cts_get_num_rows(cts_dev, rows); +} + +int cts_hostcomm_get_cols(const struct cts_device *cts_dev, u8 *cols) +{ + return cts_get_num_cols(cts_dev, cols); +} + +int cts_hostcomm_get_flip_x(const struct cts_device *cts_dev, bool *flip_x) +{ + u8 val; + int ret; + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FLAG_BITS, &val); + if (ret < 0) + return ret; + + *flip_x = !!(val & BIT(2)); + return 0; +} + +int cts_hostcomm_get_flip_y(const struct cts_device *cts_dev, bool *flip_y) +{ + u8 val; + int ret; + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FLAG_BITS, &val); + if (ret < 0) + return ret; + + *flip_y = !!(val & BIT(3)); + return 0; +} + +int cts_hostcomm_get_swap_axes(const struct cts_device *cts_dev, + bool *swap_axes) +{ + u8 val; + int ret; + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_SWAP_AXES, &val); + if (ret < 0) + return ret; + + *swap_axes = !!val; + return 0; +} + +int cts_hostcomm_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_INT_MODE, int_mode); +} + +int cts_hostcomm_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_INT_KEEP_TIME, + int_keep_time); +} + +int cts_hostcomm_get_esd_method(const struct cts_device *cts_dev, + u8 *esd_method) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_ESD_PROTECTION, + esd_method); +} + +int cts_hostcomm_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 342, esd_protection); +} + +int cts_hostcomm_get_data_ready_flag(const struct cts_device *cts_dev, + u8 *ready) +{ + return cts_get_data_ready_flag(cts_dev, ready); +} + +int cts_hostcomm_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + return cts_clr_data_ready_flag(cts_dev); +} + +int cts_hostcomm_enable_get_rawdata(const struct cts_device *cts_dev) +{ + return cts_enable_get_rawdata(cts_dev); +} + +int cts_hostcomm_disable_get_rawdata(const struct cts_device *cts_dev) +{ + return cts_disable_get_rawdata(cts_dev); +} + +int cts_hostcomm_enable_get_cneg(const struct cts_device *cts_dev) +{ + return cts_send_command(cts_dev, CTS_CMD_ENABLE_READ_CNEG); +} + +int cts_hostcomm_disable_get_cneg(const struct cts_device *cts_dev) +{ + return cts_send_command(cts_dev, CTS_CMD_DISABLE_READ_CNEG); +} + +int cts_hostcomm_is_cneg_ready(const struct cts_device *cts_dev, u8 *ready) +{ + return cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY, ready); +} + +int cts_hostcomm_quit_guesture_mode(const struct cts_device *cts_dev) +{ + //TODO: + return 0; +} + +int cts_hostcomm_get_rawdata(const struct cts_device *cts_dev, u8 *buf) +{ + return cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_RAW_DATA, + buf, + cts_dev->fwdata.rows * + cts_dev->fwdata.cols * 2, 500); +} + +int cts_hostcomm_get_diffdata(const struct cts_device *cts_dev, u8 *buf) +{ + return cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_DIFF_DATA, + buf, + (cts_dev->fwdata.rows + 2) * + (cts_dev->fwdata.cols + 2) * 2, + 500); +} + +int cts_hostcomm_get_basedata(const struct cts_device *cts_dev, u8 *buf) +{ + //TODO: + return 0; +} + +int cts_hostcomm_get_cneg(const struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP, + buf, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col, 500); +} + +int cts_hostcomm_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + + return cts_hw_reg_readsb_retry(cts_dev, addr, regbuf, size, 3, 100); +} + +int cts_hostcomm_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_hw_reg_writesb_retry(cts_dev, addr, regbuf, size, 3, 100); +} + +int cts_hostcomm_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_read_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_write_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_read_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_fw_reg_readsb(cts_dev, addr, regbuf, size); +} + +int cts_hostcomm_write_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_fw_reg_writesb(cts_dev, addr, regbuf, size); +} + +int cts_hostcomm_get_touchinfo(const struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + memset(touch_info, 0, sizeof(*touch_info)); + return cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO, + touch_info, sizeof(*touch_info)); +} + +int cts_hostcomm_get_fw_id(const struct cts_device *cts_dev, u16 *fwid) +{ + u16 tmp = 0xffff; + int ret; + + ret = cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_CHIP_TYPE, &tmp); + tmp = be16_to_cpu(tmp); + *fwid = tmp; + cts_info("%s:%04X", __func__, tmp); + + return ret; +} + +int cts_hostcomm_get_workmode(const struct cts_device *cts_dev, u8 *workmode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_GET_WORK_MODE, + workmode); +} + +int cts_hostcomm_set_workmode(const struct cts_device *cts_dev, u8 workmode) +{ + return set_fw_work_mode(cts_dev, workmode); +} + +int cts_hostcomm_set_openshort_mode(const struct cts_device *cts_dev, u8 type) +{ + int i, ret; + + cts_info("Set test type %d", type); + + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = cts_fw_reg_writeb(cts_dev, 0x34, type); + if (ret) { + cts_err("Write test type register to failed %d", ret); + continue; + } + + ret = cts_fw_reg_readb(cts_dev, 0x34, &type_readback); + if (ret) { + cts_err("Read test type register failed %d", ret); + continue; + } + + if (type != type_readback) { + cts_err("Set test type %u != readback %u", type, + type_readback); + ret = -EFAULT; + continue; + } + } + + return ret; +} + +int cts_hostcomm_set_tx_vol(const struct cts_device *cts_dev, u8 txvol) +{ + return cts_send_command(cts_dev, CTS_CMD_RECOVERY_TX_VOL); +} + +int cts_hostcomm_is_enabled_get_rawdata(const struct cts_device *cts_dev, + u8 *enabled) +{ + return cts_fw_reg_readb(cts_dev, 0x12, enabled); +} + +int cts_hostcomm_set_short_test_type(const struct cts_device *cts_dev, + u8 short_type) +{ + return set_short_test_type(cts_dev, short_type); +} + +int cts_hostcomm_is_openshort_enabled(const struct cts_device *cts_dev, + u8 *enabled) +{ + int ret = -1; + + cts_err("BUG! Should not be here!"); + return ret; +} + +int cts_hostcomm_set_openshort_enable(const struct cts_device *cts_dev, + u8 enable) +{ + int ret = -1; + + cts_err("BUG! Should not be here!"); + return ret; +} + +int cts_hostcomm_set_esd_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + if (!enable) + return disable_fw_esd_protection(cts_dev); + + return ret; +} + +int cts_hostcomm_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 276, enabled); +} + +int cts_hostcomm_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 344, enabled); +} + +int cts_hostcomm_set_cneg_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + if (!enable) + return disable_fw_auto_compensate(cts_dev); + + return ret; +} + +int cts_hostcomm_set_mnt_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + if (!enable) + return disable_fw_monitor_mode(cts_dev); + + return ret; +} + +int cts_hostcomm_is_display_on(const struct cts_device *cts_dev, + u8 *display_on) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 163, display_on); +} + +int cts_hostcomm_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode) +{ + //TODO: + return 0; +} + +int cts_hostcomm_set_display_on(const struct cts_device *cts_dev, u8 display_on) +{ + return set_display_state(cts_dev, false); +} + +int cts_hostcomm_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: moved from sysfs:rawdata_show + //TODO: + return 0; +} + +int cts_hostcomm_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_basedata(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return cts_get_compensate_cap(cts_dev, buf); +} + +int cts_hostcomm_reset_device(const struct cts_device *cts_dev) +{ + return cts_plat_reset_device(cts_dev->pdata); +} + +struct cts_dev_ops hostcomm_ops = { + .get_fw_ver = cts_hostcomm_get_fw_ver, + .get_lib_ver = cts_hostcomm_get_lib_ver, + .get_ddi_ver = cts_hostcomm_get_ddi_ver, + .get_res_x = cts_hostcomm_get_res_x, + .get_res_y = cts_hostcomm_get_res_y, + .get_rows = cts_hostcomm_get_rows, + .get_cols = cts_hostcomm_get_cols, + .get_flip_x = cts_hostcomm_get_flip_x, + .get_flip_y = cts_hostcomm_get_flip_y, + .get_swap_axes = cts_hostcomm_get_swap_axes, + .get_int_mode = cts_hostcomm_get_int_mode, + .get_int_keep_time = cts_hostcomm_get_int_keep_time, + .get_esd_method = cts_hostcomm_get_esd_method, + .get_touchinfo = cts_hostcomm_get_touchinfo, + .get_esd_protection = cts_hostcomm_get_esd_protection, + .get_data_ready_flag = cts_hostcomm_get_data_ready_flag, + .clr_data_ready_flag = cts_hostcomm_clr_data_ready_flag, + .enable_get_rawdata = cts_hostcomm_enable_get_rawdata, + .is_enabled_get_rawdata = cts_hostcomm_is_enabled_get_rawdata, + .disable_get_rawdata = cts_hostcomm_disable_get_rawdata, + .enable_get_cneg = cts_hostcomm_enable_get_cneg, + .disable_get_cneg = cts_hostcomm_disable_get_cneg, + .is_cneg_ready = cts_hostcomm_is_cneg_ready, + .quit_guesture_mode = cts_hostcomm_quit_guesture_mode, + .get_rawdata = cts_hostcomm_get_rawdata, + .get_diffdata = cts_hostcomm_get_diffdata, + .get_basedata = cts_hostcomm_get_basedata, + .get_cneg = cts_hostcomm_get_cneg, + .read_hw_reg = cts_hostcomm_read_hw_reg, + .write_hw_reg = cts_hostcomm_write_hw_reg, + .read_ddi_reg = cts_hostcomm_read_ddi_reg, + .write_ddi_reg = cts_hostcomm_write_ddi_reg, + .read_fw_reg = cts_hostcomm_read_fw_reg, + .write_fw_reg = cts_hostcomm_write_fw_reg, + .read_reg = cts_hostcomm_read_reg, + .write_reg = cts_hostcomm_write_reg, + .get_fw_id = cts_hostcomm_get_fw_id, + .get_workmode = cts_hostcomm_get_workmode, + .set_workmode = cts_hostcomm_set_workmode, + .set_openshort_mode = cts_hostcomm_set_openshort_mode, + .set_tx_vol = cts_hostcomm_set_tx_vol, + .set_short_test_type = cts_hostcomm_set_short_test_type, + .set_openshort_enable = cts_hostcomm_set_openshort_enable, + .is_openshort_enabled = cts_hostcomm_is_openshort_enabled, + .set_esd_enable = cts_hostcomm_set_esd_enable, + .set_cneg_enable = cts_hostcomm_set_cneg_enable, + .set_mnt_enable = cts_hostcomm_set_mnt_enable, + .is_display_on = cts_hostcomm_is_display_on, + .set_display_on = cts_hostcomm_set_display_on, + .is_cneg_enabled = cts_hostcomm_is_cneg_enabled, + .is_mnt_enabled = cts_hostcomm_is_mnt_enabled, + .set_pwr_mode = cts_hostcomm_set_pwr_mode, + .read_sram_normal_mode = cts_read_sram_normal_mode, + .init_int_data = NULL, //TODO + .deinit_int_data = NULL, //TODO + .get_has_int_data = NULL, + .get_int_data_types = NULL, + .set_int_data_types = NULL, + .get_int_data_method = NULL, + .set_int_data_method = NULL, + .calc_int_data_size = NULL, + .polling_data = NULL, + .polling_test_data = NULL, + + .top_get_rawdata = cts_hostcomm_top_get_rawdata, + .top_get_manual_diff = cts_hostcomm_top_get_manual_diff, + .top_get_real_diff = cts_hostcomm_top_get_real_diff, + .top_get_noise_diff = cts_hostcomm_top_get_noise_diff, + .top_get_basedata = cts_hostcomm_top_get_basedata, + .top_get_cnegdata = cts_hostcomm_top_get_cnegdata, + + .reset_device = cts_hostcomm_reset_device, +}; + +//TODO: +//fwdata->rawdata_target; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h new file mode 100644 index 000000000000..f28968798b6e --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_HOSTCOMM_H +#define CTS_HOSTCOMM_H + +#include "cts_config.h" +#include "cts_core.h" +extern struct cts_dev_ops hostcomm_ops; + +#endif /* CTS_HOSTCOMM_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c new file mode 100644 index 000000000000..16f45637831f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "I2CDrv" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_charger_detect.h" + +static void cts_resume_work_func(struct work_struct *work); +#ifdef CFG_CTS_DRM_NOTIFIER +#include +static struct drm_panel *active_panel; +static int check_dt(struct device_node *np); +#endif +bool cts_show_debug_log; +#ifdef CTS_MTK_GET_PANEL +static char *active_panel_name; +#endif + +module_param_named(debug_log, cts_show_debug_log, bool, 0660); +MODULE_PARM_DESC(debug_log, "Show debug log control"); + +static int cts_suspend(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Suspend"); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_suspend_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) + cts_err("Suspend device failed %d", ret); + + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } +#ifdef CFG_CTS_GESTURE + /* Enable IRQ wake if gesture wakeup enabled */ + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_enable_irq_wake(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ wake failed %d", ret); + return ret; + } + ret = cts_plat_enable_irq(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + } +#endif /* CFG_CTS_GESTURE */ + +/** - To avoid waking up while not sleeping, + *delay 20ms to ensure reliability + */ + msleep(20); + + return 0; +} + +static int cts_resume(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Resume"); + +#ifdef CFG_CTS_GESTURE + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_disable_irq_wake(cts_data->pdata); + if (ret) + cts_warn("Disable IRQ wake failed %d", ret); + ret = cts_plat_disable_irq(cts_data->pdata); + if (ret < 0) + cts_err("Disable IRQ failed %d", ret); + } +#endif /* CFG_CTS_GESTURE */ + + cts_lock_device(&cts_data->cts_dev); + ret = cts_resume_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + if (ret) { + cts_warn("Resume device failed %d", ret); + return ret; + } + + ret = cts_start_device(&cts_data->cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + + return 0; +} + +static void cts_resume_work_func(struct work_struct *work) +{ + struct chipone_ts_data *cts_data = + container_of(work, struct chipone_ts_data, ts_resume_work); + cts_info("%s", __func__); + cts_resume(cts_data); +} + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CFG_CTS_DRM_NOTIFIER +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct drm_panel_notifier *evdata = data; + + cts_info("FB notifier callback"); + if (!evdata || !cts_data) + return 0; + + blank = *(int *)evdata->data; + cts_info("action=%lu, blank=%d\n", action, blank); + + if (action == DRM_PANEL_EARLY_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_POWERDOWN) + cts_suspend(cts_data); + } else if (evdata->data) { + blank = *(int *)evdata->data; + if (action == DRM_PANEL_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_UNBLANK) + /* cts_resume(cts_data); */ + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + } + } + + return 0; +} +#else +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct fb_event *evdata = data; + + cts_info("FB notifier callback"); + + if (evdata && evdata->data) { + if (action == FB_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_UNBLANK) { + /* cts_resume(cts_data); */ + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + return NOTIFY_OK; + } + } else if (action == FB_EARLY_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_POWERDOWN) { + cts_suspend(cts_data); + return NOTIFY_OK; + } + } + } + + return NOTIFY_DONE; +} +#endif + +static int cts_init_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Init FB notifier"); + + cts_data->pdata->fb_notifier.notifier_call = fb_notifier_callback; + +#ifdef CFG_CTS_DRM_NOTIFIER + { + int ret = -ENODEV; + + if (active_panel) { + ret = + drm_panel_notifier_register(active_panel, + &cts_data->pdata->fb_notifier); + if (ret) + cts_err + ("register drm_notifier failed. ret=%d\n", + ret); + } + return ret; + } +#else + return fb_register_client(&cts_data->pdata->fb_notifier); +#endif +} + +static int cts_deinit_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Deinit FB notifier"); +#ifdef CFG_CTS_DRM_NOTIFIER + { + int ret = 0; + + if (active_panel) { + ret = + drm_panel_notifier_unregister(active_panel, + &cts_data->pdata->fb_notifier); + if (ret) + cts_err + ("Error occurred while unregistering drm_notifier.\n"); + } + return ret; + } +#else + return fb_unregister_client(&cts_data->pdata->fb_notifier); +#endif +} +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +#ifdef CFG_CTS_DRM_NOTIFIER +static int check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + cts_info("check active_panel"); + active_panel = panel; + return 0; + } + } + if (node) + pr_err("%s: %s not actived\n", __func__, node->name); + return -ENODEV; +} + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + cts_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + cts_err(" %s:fail to read %s %d\n", __func__, "compatible", + ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + cts_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} +#endif + +#ifdef CTS_MTK_GET_PANEL +char panel_name[50] = { 0 }; + +static int cts_get_panel(void) +{ + int ret = -1; + + cts_info("enter"); + if (saved_command_line) { + char *sub; + char key_prefix[] = "mipi_mot_vid_"; + char ic_prefix[] = "icnl"; + + cts_info("saved_command_line is %s", saved_command_line); + sub = strstr(saved_command_line, key_prefix); + if (sub) { + char *d; + int n, len, len_max = 50; + + d = strstr(sub, " "); + if (d) + n = strlen(sub) - strlen(d); + else + n = strlen(sub); + + if (n > len_max) + len = len_max; + else + len = n; + + strncpy(panel_name, sub, len); + active_panel_name = panel_name; + if (strstr(active_panel_name, ic_prefix)) + cts_info("active_panel_name=%s", + active_panel_name); + else { + cts_info("Not chipone panel!"); + return ret; + } + + } else { + cts_info("chipone active panel not found!"); + return ret; + } + } else { + cts_info("saved_command_line null!"); + return ret; + } + + return 0; +} +#endif +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int cts_driver_probe(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data = NULL; + int ret = 0; + +#ifdef CTS_MTK_GET_PANEL + ret = cts_get_panel(); + if (ret) { + cts_info("MTK get chipone panel error"); + return ret; + } +#endif + +#ifdef CFG_CTS_DRM_NOTIFIER + { + struct device_node *dp = client->dev.of_node; + + if (check_dt(dp)) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + cts_err("%s: %s not actived\n", __func__, dp->name); + return ret; + } + } +#endif + +#ifdef CONFIG_CTS_I2C_HOST + cts_info("Probe i2c client: name='%s' addr=0x%02x flags=0x%02x irq=%d", + client->name, client->addr, client->flags, client->irq); + +#if !defined(CONFIG_MTK_PLATFORM) + if (client->addr != CTS_DEV_NORMAL_MODE_I2CADDR) { + cts_err("Probe i2c addr 0x%02x != driver config addr 0x%02x", + client->addr, CTS_DEV_NORMAL_MODE_I2CADDR); + return -ENODEV; + }; +#endif + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + cts_err("Check functionality failed"); + return -ENODEV; + } +#endif + + cts_data = + (struct chipone_ts_data *)kzalloc(sizeof(*cts_data), GFP_KERNEL); + if (cts_data == NULL) { + cts_err("Allocate chipone_ts_data failed"); + return -ENOMEM; + } + cts_data->pdata = (struct cts_platform_data *) + kzalloc(sizeof(struct cts_platform_data), GFP_KERNEL); + if (cts_data->pdata == NULL) { + cts_err("Allocate cts_platform_data failed"); + ret = -ENOMEM; + goto err_free_cts_data; + } +#ifdef CONFIG_CTS_I2C_HOST + i2c_set_clientdata(client, cts_data); + cts_data->i2c_client = client; + cts_data->device = &client->dev; +#else + spi_set_drvdata(client, cts_data); + cts_data->spi_client = client; + cts_data->device = &client->dev; +#endif + + cts_init_platform_data(cts_data->pdata, client); + + cts_data->cts_dev.pdata = cts_data->pdata; + cts_data->pdata->cts_dev = &cts_data->cts_dev; + + cts_data->workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-workqueue"); + if (cts_data->workqueue == NULL) { + cts_err("Create workqueue failed"); + ret = -ENOMEM; + goto err_deinit_platform_data; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_data->esd_workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-esd_workqueue"); + if (cts_data->esd_workqueue == NULL) { + cts_err("Create esd workqueue failed"); + ret = -ENOMEM; + goto err_destroy_workqueue; + } +#endif + ret = cts_plat_request_resource(cts_data->pdata); + if (ret < 0) { + cts_err("Request resource failed %d", ret); + goto err_destroy_esd_workqueue; + } + + ret = cts_reset_device(&cts_data->cts_dev); + if (ret < 0) { + cts_err("Reset device failed %d", ret); + goto err_free_resource; + } + + ret = cts_probe_device(&cts_data->cts_dev); + if (ret) { + cts_err("Probe device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_touch_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init touch device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_vkey_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init vkey device failed %d", ret); + goto err_deinit_touch_device; + } + + ret = cts_plat_init_gesture(cts_data->pdata); + if (ret < 0) { + cts_err("Init gesture failed %d", ret); + goto err_deinit_vkey_device; + } + + cts_init_esd_protection(cts_data); + + ret = cts_tool_init(cts_data); + if (ret < 0) + cts_warn("Init tool node failed %d", ret); + + ret = cts_sysfs_add_device(&client->dev); + if (ret < 0) + cts_warn("Add sysfs entry for device failed %d", ret); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + ret = cts_init_pm_fb_notifier(cts_data); + if (ret) { + cts_err("Init FB notifier failed %d", ret); + goto err_deinit_sysfs; + } +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + + ret = cts_plat_request_irq(cts_data->pdata); + if (ret < 0) { + cts_err("Request IRQ failed %d", ret); + goto err_register_fb; + } + +#ifdef CONFIG_CTS_CHARGE_DETECT + ret = cts_charger_detect_init(cts_data); + if (ret) + cts_err("Init charger detect failed %d", ret); + /* Ignore this error */ +#endif + + /* Init firmware upgrade work and schedule */ + INIT_DELAYED_WORK(&cts_data->fw_upgrade_work, + cts_firmware_upgrade_work); + queue_delayed_work(cts_data->workqueue, &cts_data->fw_upgrade_work, + msecs_to_jiffies(15 * 1000)); + + INIT_WORK(&cts_data->ts_resume_work, cts_resume_work_func); + + return 0; + +err_register_fb: +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +err_deinit_sysfs: +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + cts_sysfs_remove_device(&client->dev); +#ifdef CONFIG_CTS_LEGACY_TOOL + cts_tool_deinit(cts_data); +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_deinit_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CFG_CTS_GESTURE + cts_plat_deinit_gesture(cts_data->pdata); +#endif /* CFG_CTS_GESTURE */ + +err_deinit_vkey_device: +#ifdef CONFIG_CTS_VIRTUALKEY + cts_plat_deinit_vkey_device(cts_data->pdata); +#endif /* CONFIG_CTS_VIRTUALKEY */ + +err_deinit_touch_device: + cts_plat_deinit_touch_device(cts_data->pdata); + +err_free_resource: + cts_plat_free_resource(cts_data->pdata); +err_destroy_esd_workqueue: +#ifdef CONFIG_CTS_ESD_PROTECTION + destroy_workqueue(cts_data->esd_workqueue); +err_destroy_workqueue: +#endif + destroy_workqueue(cts_data->workqueue); +err_deinit_platform_data: + cts_deinit_platform_data(cts_data->pdata); + kfree(cts_data->pdata); +err_free_cts_data: + kfree(cts_data); + + cts_err("Probe failed %d", ret); + + return ret; +} + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_remove(struct i2c_client *client) +#else +static int cts_driver_remove(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data; + int ret = 0; + + cts_info("Remove"); + +#ifdef CONFIG_CTS_I2C_HOST + cts_data = (struct chipone_ts_data *)i2c_get_clientdata(client); +#else + cts_data = (struct chipone_ts_data *)spi_get_drvdata(client); +#endif + if (cts_data) { + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) + cts_warn("Stop device failed %d", ret); + +#ifdef CONFIG_CTS_CHARGE_DETECT + cts_charger_detect_deinit(cts_data); +#endif + + cts_plat_free_irq(cts_data->pdata); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + + cts_tool_deinit(cts_data); + + cts_sysfs_remove_device(&client->dev); + + cts_deinit_esd_protection(cts_data); + + cts_plat_deinit_touch_device(cts_data->pdata); + + cts_plat_deinit_vkey_device(cts_data->pdata); + + cts_plat_deinit_gesture(cts_data->pdata); + + cts_plat_free_resource(cts_data->pdata); + +#ifdef CONFIG_CTS_ESD_PROTECTION + if (cts_data->esd_workqueue) + destroy_workqueue(cts_data->esd_workqueue); +#endif + + if (cts_data->workqueue) + destroy_workqueue(cts_data->workqueue); + + cts_deinit_platform_data(cts_data->pdata); + + cts_deinit_rtdata(&cts_data->cts_dev); + + if (cts_data->pdata) + kfree(cts_data->pdata); + kfree(cts_data); + } else { + cts_warn + ("Chipone i2c driver remove while NULL chipone_ts_data"); + return -EINVAL; + } + + return ret; +} + +#ifdef CONFIG_CTS_PM_LEGACY +static int cts_i2c_driver_suspend(struct device *dev, pm_message_t state) +{ + cts_info("Suspend by legacy power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_resume(struct device *dev) +{ + cts_info("Resume by legacy power management"); + return cts_resume(dev_get_drvdata(dev)); +} +#endif /* CONFIG_CTS_PM_LEGACY */ + +#ifdef CONFIG_CTS_PM_GENERIC +static int cts_i2c_driver_pm_suspend(struct device *dev) +{ + cts_info("Suspend by bus power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_pm_resume(struct device *dev) +{ + cts_info("Resume by bus power management"); + return cts_resume(dev_get_drvdata(dev)); +} + +/* bus control the suspend/resume procedure */ +static const struct dev_pm_ops cts_i2c_driver_pm_ops = { + .suspend = cts_i2c_driver_pm_suspend, + .resume = cts_i2c_driver_pm_resume, +}; +#endif /* CONFIG_CTS_PM_GENERIC */ + +#ifdef CONFIG_CTS_SYSFS +static ssize_t reset_pin_show(struct device_driver *driver, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_HAS_RESET_PIN + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(reset_pin, S_IRUGO, reset_pin_show, NULL); +#else +static DRIVER_ATTR_RO(reset_pin); +#endif + +static ssize_t swap_xy_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_SWAP_XY: %c\n", +#ifdef CFG_CTS_SWAP_XY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(swap_xy, S_IRUGO, swap_xy_show, NULL); +#else +static DRIVER_ATTR_RO(swap_xy); +#endif + +static ssize_t wrap_x_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_X: %c\n", +#ifdef CFG_CTS_WRAP_X + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(wrap_x, S_IRUGO, wrap_x_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_x); +#endif + +static ssize_t wrap_y_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_Y: %c\n", +#ifdef CFG_CTS_WRAP_Y + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(wrap_y, S_IRUGO, wrap_y_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_y); +#endif + +static ssize_t force_update_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(force_update, S_IRUGO, force_update_show, NULL); +#else +static DRIVER_ATTR_RO(force_update); +#endif + +static ssize_t max_touch_num_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_TOUCH_NUM: %d\n", + CFG_CTS_MAX_TOUCH_NUM); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(max_touch_num, S_IRUGO, max_touch_num_show, NULL); +#else +static DRIVER_ATTR_RO(max_touch_num); +#endif + +static ssize_t vkey_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_VIRTUALKEY: %c\n", +#ifdef CONFIG_CTS_VIRTUALKEY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(vkey, S_IRUGO, vkey_show, NULL); +#else +static DRIVER_ATTR_RO(vkey); +#endif + +static ssize_t gesture_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_GESTURE: %c\n", +#ifdef CFG_CTS_GESTURE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(gesture, S_IRUGO, gesture_show, NULL); +#else +static DRIVER_ATTR_RO(gesture); +#endif + +static ssize_t esd_protection_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_ESD_PROTECTION: %c\n", +#ifdef CONFIG_CTS_ESD_PROTECTION + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); +#else +static DRIVER_ATTR_RO(esd_protection); +#endif + +static ssize_t slot_protocol_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_SLOTPROTOCOL: %c\n", +#ifdef CONFIG_CTS_SLOTPROTOCOL + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(slot_protocol, S_IRUGO, slot_protocol_show, NULL); +#else +static DRIVER_ATTR_RO(slot_protocol); +#endif + +static ssize_t max_xfer_size_show(struct device_driver *dev, char *buf) +{ +#ifdef CONFIG_CTS_I2C_HOST + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_I2C_XFER_SIZE: %d\n", + CFG_CTS_MAX_I2C_XFER_SIZE); +#else + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_SPI_XFER_SIZE: %d\n", + CFG_CTS_MAX_SPI_XFER_SIZE); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(max_xfer_size, S_IRUGO, max_xfer_size_show, NULL); +#else +static DRIVER_ATTR_RO(max_xfer_size); +#endif + +static ssize_t driver_info_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "Driver version: %s\n", + CFG_CTS_DRIVER_VERSION); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(driver_info, S_IRUGO, driver_info_show, NULL); +#else +static DRIVER_ATTR_RO(driver_info); +#endif + +static struct attribute *cts_i2c_driver_config_attrs[] = { + &driver_attr_reset_pin.attr, + &driver_attr_swap_xy.attr, + &driver_attr_wrap_x.attr, + &driver_attr_wrap_y.attr, + &driver_attr_force_update.attr, + &driver_attr_max_touch_num.attr, + &driver_attr_vkey.attr, + &driver_attr_gesture.attr, + &driver_attr_esd_protection.attr, + &driver_attr_slot_protocol.attr, + &driver_attr_max_xfer_size.attr, + &driver_attr_driver_info.attr, + NULL +}; + +static const struct attribute_group cts_i2c_driver_config_group = { + .name = "config", + .attrs = cts_i2c_driver_config_attrs, +}; + +static const struct attribute_group *cts_i2c_driver_config_groups[] = { + &cts_i2c_driver_config_group, + NULL, +}; +#endif /* CONFIG_CTS_SYSFS */ + +#ifdef CONFIG_CTS_OF +static const struct of_device_id cts_i2c_of_match_table[] = { + {.compatible = CFG_CTS_OF_DEVICE_ID_NAME, }, + { }, +}; + +MODULE_DEVICE_TABLE(of, cts_i2c_of_match_table); +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST +static const struct i2c_device_id cts_device_id_table[] = { + { CFG_CTS_DEVICE_NAME, 0 }, + { } +}; +#else +static const struct spi_device_id cts_device_id_table[] = { + { CFG_CTS_DEVICE_NAME, 0 }, + { } +}; +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static struct i2c_driver cts_i2c_driver = { +#else +static struct spi_driver cts_spi_driver = { +#endif + .probe = cts_driver_probe, + .remove = cts_driver_remove, + .driver = { + .name = CFG_CTS_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_CTS_OF + .of_match_table = of_match_ptr(cts_i2c_of_match_table), +#endif /* CONFIG_CTS_OF */ +#ifdef CONFIG_CTS_SYSFS + .groups = cts_i2c_driver_config_groups, +#endif /* CONFIG_CTS_SYSFS */ +#ifdef CONFIG_CTS_PM_LEGACY + .suspend = cts_i2c_driver_suspend, + .resume = cts_i2c_driver_resume, +#endif /* CONFIG_CTS_PM_LEGACY */ +#ifdef CONFIG_CTS_PM_GENERIC + .pm = &cts_i2c_driver_pm_ops, +#endif /* CONFIG_CTS_PM_GENERIC */ + + }, + .id_table = cts_device_id_table, +}; + +static int __init cts_driver_init(void) +{ + cts_info("Init"); + +#ifdef CONFIG_CTS_I2C_HOST + return i2c_add_driver(&cts_i2c_driver); +#else + pr_err("%s spi", __func__); + return spi_register_driver(&cts_spi_driver); +#endif +} + +static void __exit cts_driver_exit(void) +{ + cts_info("Exit"); + +#ifdef CONFIG_CTS_I2C_HOST + i2c_del_driver(&cts_i2c_driver); +#else + spi_unregister_driver(&cts_spi_driver); +#endif +} + +module_init(cts_driver_init); +module_exit(cts_driver_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Chipone TDDI touchscreen Driver for QualComm platform"); +MODULE_VERSION(CFG_CTS_DRIVER_VERSION); +MODULE_AUTHOR("Miao Defang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h new file mode 100644 index 000000000000..e4e07fe22469 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_PLAT_QCOM_CONFIG_H +#define CTS_PLAT_QCOM_CONFIG_H + +#define CONFIG_CTS_PM_FB_NOTIFIER + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +/* + * #ifdef CONFIG_DRM + * #define CFG_CTS_DRM_NOTIFIER + * #endif + */ +#ifdef CONFIG_DRM +#define CFG_CTS_DRM_NOTIFIER +#endif /*CONFIG_DRM_MSM */ +#else /*CONFIG_CTS_PM_FB_NOTIFIER */ +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_SUSPEND) + /* #define CONFIG_CTS_PM_GENERIC */ +#endif /* CONFIG_PM_SLEEP */ + +#if !defined(CONFIG_CTS_PM_GENERIC) +#define CONFIG_CTS_PM_LEGACY +#endif /*CONFIG_CTS_PM_GENERIC */ +#endif /*CONFIG_CTS_PM_FB_NOTIFIER */ + +#define CFG_CTS_MAX_I2C_XFER_SIZE (48u) +#define CFG_CTS_MAX_SPI_XFER_SIZE (8192u) + +#define CTS_FW_LOG_REDIRECT_SIGN 0x60 +#define CTS_FW_LOG_BUF_LEN 128 + +/** + * #define CFG_CTS_SWAP_XY + * #define CFG_CTS_WRAP_X + * #define CFG_CTS_WRAP_Y + */ + +#define CFG_CTS_DEVICE_NAME "chipone-tddi" +#define CFG_CTS_DRIVER_NAME "chipone-tddi" + +#if CFG_CTS_MAX_I2C_XFER_SIZE < 8 +#error "I2C transfer size should large than 8" +#endif + +#ifdef CONFIG_OF +#define CONFIG_CTS_OF +#endif +#ifdef CONFIG_CTS_OF +#define CFG_CTS_OF_DEVICE_ID_NAME "chipone-tddi" + +#define CFG_CTS_OF_INT_GPIO_NAME "chipone,irq-gpio" +#define CFG_CTS_OF_RST_GPIO_NAME "chipone,rst-gpio" + +#ifdef CFG_CTS_MANUAL_CS +#define CFG_CTS_OF_CS_GPIO_NAME "chipone,cs-gpio" +#endif + +#define CFG_CTS_OF_X_RESOLUTION_NAME "chipone,x-res" +#define CFG_CTS_OF_Y_RESOLUTION_NAME "chipone,y-res" + +#ifdef CFG_CTS_FW_UPDATE_SYS +#define CFG_CTS_OF_PANEL_SUPPLIER "chipone,panel-supplier" +#endif +#endif /* CONFIG_CTS_OF */ + +#endif /* CTS_PLAT_QCOM_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c new file mode 100644 index 000000000000..640d92cfe81f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c @@ -0,0 +1,1259 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Plat" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_sysfs.h" + +#ifdef CFG_CTS_FW_LOG_REDIRECT +size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata) +{ + return CTS_FW_LOG_BUF_LEN; +} + +u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, size_t size) +{ + return pdata->fw_log_buf; +} +#endif + +size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_I2C_XFER_SIZE; +} + +#ifdef CONFIG_CTS_I2C_HOST +u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ + return pdata->i2c_fifo_buf; +} + +int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + + struct i2c_msg msg = { + .flags = 0, + .addr = i2c_addr, + .buf = (u8 *) src, + .len = len, + }; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, &msg, 1); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + + if (delay) + mdelay(delay); + continue; + } else + return 0; + } while (++retries < retry); + + return ret; +} + +int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int num_msg, ret = 0, retries = 0; + + struct i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = 0, + .buf = (u8 *) wbuf, + .len = wlen }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = (u8 *) rbuf, + .len = rlen } + }; + + if (wbuf == NULL || wlen == 0) + num_msg = 1; + else + num_msg = 2; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, + msgs + ARRAY_SIZE(msgs) - num_msg, num_msg); + + if (ret != num_msg) { + if (ret >= 0) + ret = -EIO; + + if (delay) + mdelay(delay); + continue; + } else + return 0; + } while (++retries < retry); + + return ret; +} + +int cts_plat_is_i2c_online(struct cts_platform_data *pdata, u8 i2c_addr) +{ + u8 dummy_bytes[2] = { 0x00, 0x00 }; + int ret; + + ret = cts_plat_i2c_write(pdata, i2c_addr, dummy_bytes, + sizeof(dummy_bytes), 5, 2); + if (ret) { + cts_err("!!! I2C addr 0x%02x is offline !!!", i2c_addr); + return false; + } + + cts_dbg("I2C addr 0x%02x is online", i2c_addr); + return true; +} +#else +void dump_spi_common(const char *prefix, u8 *data, size_t datalen) +{ + u8 str[1024]; + int offset = 0; + int i; + + offset += snprintf(str + offset, sizeof(str) - offset, "%s", prefix); + for (i = 0; i < datalen; i++) { + offset += + snprintf(str + offset, sizeof(str) - offset, " %02x", + data[i]); + } + cts_err("%s", str); +} + +#ifdef CFG_CTS_MANUAL_CS +int cts_plat_set_cs(struct cts_platform_data *pdata, int val) +{ + if (val) + gpio_set_value(pdata->cs_gpio, 1); + else + gpio_set_value(pdata->cs_gpio, 0); + + return 0; +} +#endif + +int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer) +{ + struct chipone_ts_data *cts_data; + struct spi_message msg; + struct spi_transfer cmd = { + .cs_change = 0, + .delay_usecs = 0, + .speed_hz = pdata->spi_speed * 1000u, + .tx_buf = tx_buffer, + .rx_buf = rx_buffer, + .len = len, + /* .tx_dma = 0, */ + /* .rx_dma = 0, */ + .bits_per_word = 8, + }; + int ret = 0; + + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 0); +#endif + cts_data->spi_client->chip_select = 0; + spi_message_init(&msg); + spi_message_add_tail(&cmd, &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync failed %d", ret); + + udelay(100); + +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 1); +#endif + return ret; +} + +size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_SPI_XFER_SIZE; +} + +u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ + return pdata->spi_cache_buf; +} + +int cts_plat_spi_write(struct cts_platform_data *pdata, u8 dev_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc; + size_t data_len; + + if (len > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write too much data:wlen=%zu\n", len); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr; + memcpy(&pdata->spi_tx_buf[1], src, len); + + do { + ret = + cts_spi_send_recv(pdata, len + 1, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); + } else { + data_len = len - 2; + pdata->spi_tx_buf[0] = dev_addr; + pdata->spi_tx_buf[1] = *((u8 *) src + 1); + pdata->spi_tx_buf[2] = *((u8 *) src); + put_unaligned_le16(data_len, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + memcpy(&pdata->spi_tx_buf[7], (char *)src + 2, data_len); + crc = (u16) cts_crc32((char *)src + 2, data_len); + put_unaligned_le16(crc, &pdata->spi_tx_buf[7 + data_len]); + do { + ret = + cts_spi_send_recv(pdata, len + 7, pdata->spi_tx_buf, + pdata->spi_rx_buf); + udelay(10 * data_len); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); + } + return ret; +} + +int cts_plat_spi_read(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE + || rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zd, rlen=%zd", wlen, + rlen); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = cts_spi_send_recv(pdata, rlen + 5, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = cts_spi_send_recv(pdata, 7, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(100); + ret = cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + get_unaligned_le16(&pdata->spi_rx_buf[rlen]), + crc); + continue; + } + return 0; + } while (++retries < retry); + } + if (retries >= retry) + cts_err("SPI read too much retry"); + + return -EIO; +} + +int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay, int idle) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE || + rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zu, rlen=%zu", wlen, + rlen); + return -E2BIG; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = cts_spi_send_recv(pdata, rlen + 5, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = + cts_spi_send_recv(pdata, 7, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(idle); + ret = cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) + continue; + return 0; + } while (++retries < retry); + } + if (retries >= retry) + cts_err("cts_plat_spi_read error"); + + return -EIO; +} + +int cts_plat_is_normal_mode(struct cts_platform_data *pdata) +{ + struct chipone_ts_data *cts_data; + u16 fwid; + int ret; + /* u8 tx_buf[4] = { 0 }; */ + /* u32 addr; */ + + cts_set_normal_addr(pdata->cts_dev); + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + /* + * addr = CTS_DEVICE_FW_REG_CHIP_TYPE; + * put_unaligned_be16(addr, tx_buf); + * ret = cts_plat_spi_read(pdata, CTS_DEV_NORMAL_MODE_SPIADDR, + * tx_buf, 2, + * &fwid, 2, 3, 10); + * fwid = be16_to_cpu(fwid); + */ + ret = pdata->cts_dev->ops->get_fw_id(pdata->cts_dev, &fwid); + + if (ret || !cts_is_fwid_valid(fwid)) + return false; + + return true; +} +#endif + +static void cts_plat_handle_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_dbg("Handle IRQ"); + + cts_lock_device(pdata->cts_dev); + ret = cts_irq_handler(pdata->cts_dev); + if (ret) + cts_err("Device handle IRQ failed %d", ret); + cts_unlock_device(pdata->cts_dev); +} + +static irqreturn_t cts_plat_irq_handler(int irq, void *dev_id) +{ + struct cts_platform_data *pdata; +#ifndef CONFIG_GENERIC_HARDIRQS + struct chipone_ts_data *cts_data; +#endif /* CONFIG_GENERIC_HARDIRQS */ + + cts_dbg("IRQ handler"); + + pdata = (struct cts_platform_data *)dev_id; + if (pdata == NULL) { + cts_err("IRQ handler with NULL dev_id"); + return IRQ_NONE; + } +#ifdef CONFIG_GENERIC_HARDIRQS + cts_plat_handle_irq(pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (queue_work(cts_data->workqueue, &pdata->ts_irq_work)) { + cts_dbg("IRQ queue work"); + cts_plat_disable_irq(pdata); + } else + cts_warn + ("IRQ handler queue work failed as already on the queue"); +#endif /* CONFIG_GENERIC_HARDIRQS */ + + return IRQ_HANDLED; +} + +#ifndef CONFIG_GENERIC_HARDIRQS +static void cts_plat_touch_dev_irq_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = + container_of(work, struct cts_platform_data, ts_irq_work); + + cts_dbg("IRQ work"); + + cts_plat_handle_irq(pdata); + + cts_plat_enable_irq(pdata); +} +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_OF +static int cts_plat_parse_dt(struct cts_platform_data *pdata, + struct device_node *dev_node) +{ + int ret = 0; + + cts_info("Parse device tree"); + + pdata->int_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_INT_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->int_gpio)) { + cts_err("Parse INT GPIO from dt failed %d", pdata->int_gpio); + pdata->int_gpio = -1; + } + cts_info(" %-12s: %d", "int gpio", pdata->int_gpio); + + pdata->irq = gpio_to_irq(pdata->int_gpio); + if (pdata->irq < 0) { + cts_err("Parse irq failed %d", ret); + return pdata->irq; + } + cts_info(" %-12s: %d", "irq num", pdata->irq); + +#ifdef CFG_CTS_HAS_RESET_PIN + pdata->rst_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_RST_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->rst_gpio)) { + cts_err("Parse RST GPIO from dt failed %d", pdata->rst_gpio); + pdata->rst_gpio = -1; + } + cts_info(" %-12s: %d", "rst gpio", pdata->rst_gpio); +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + pdata->cs_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_CS_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->cs_gpio)) { + cts_err("Parse CS GPIO from dt failed %d", pdata->cs_gpio); + pdata->cs_gpio = -1; + } + cts_info(" %-12s: %d", "cs gpio", pdata->cs_gpio); +#endif + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_X_RESOLUTION_NAME, + &pdata->res_x); + if (ret) + cts_warn("Parse X resolution from dt failed %d", ret); + + cts_info(" %-12s: %d", "X resolution", pdata->res_x); + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_Y_RESOLUTION_NAME, + &pdata->res_y); + if (ret) + cts_warn("Parse Y resolution from dt failed %d", ret); + + cts_info(" %-12s: %d", "Y resolution", pdata->res_y); + + if (of_property_read_u32 + (dev_node, "chipone,def-build-id", &pdata->build_id)) { + pdata->build_id = 0; + cts_info("chipone,build_id undefined.\n"); + } else + cts_info("chipone,build_id=0x%04X\n", pdata->build_id); + + if (of_property_read_u32 + (dev_node, "chipone,def-config-id", &pdata->config_id)) { + pdata->config_id = 0; + cts_info("chipone,config_id undefined.\n"); + } else + cts_info("chipone,config_id=0x%04X\n", pdata->config_id); + +#ifdef CFG_CTS_FW_UPDATE_SYS + ret = of_property_read_string(dev_node, CFG_CTS_OF_PANEL_SUPPLIER, + &pdata->panel_supplier); + if (ret) { + pdata->panel_supplier = NULL; + cts_warn("read panel supplier failed, ret=%d\n", ret); + } else + cts_info("panel supplier=%s", (char *)pdata->panel_supplier); +#endif + + return 0; +} +#endif /* CONFIG_CTS_OF */ + +#ifdef CFG_CTS_FORCE_UP +static void cts_plat_touch_event_timeout_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = container_of + (work, struct cts_platform_data, touch_event_timeout_work.work); + + cts_warn("Touch event timeout work"); + + cts_plat_release_all_touch(pdata); +} +#endif + +int cts_plat_spi_setup(struct cts_platform_data *pdata) +{ + int ret; + + pdata->spi_client->mode = SPI_MODE_3; + pdata->spi_client->bits_per_word = 8; + if (pdata->spi_client->max_speed_hz > 6000000) + pdata->spi_client->max_speed_hz = 6000000; + + ret = spi_setup(pdata->spi_client); + if (ret) + cts_err("spi_setup err!"); + return 0; +} + +#ifdef CONFIG_CTS_I2C_HOST +int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client) +#else +int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi) +#endif +{ + struct input_dev *input_dev; + int ret = 0; + + cts_info("cts_init_platform_data Init"); + +#ifdef CONFIG_CTS_OF + { + struct device *dev; + +#ifdef CONFIG_CTS_I2C_HOST + dev = &i2c_client->dev; +#else + dev = &spi->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + ret = cts_plat_parse_dt(pdata, dev->of_node); + if (ret) { + cts_err("Parse dt failed %d", ret); + return ret; + } + } +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST + pdata->i2c_client = i2c_client; + pdata->i2c_client->irq = pdata->irq; +#else + pdata->spi_client = spi; + pdata->spi_client->irq = pdata->irq; +#endif /* CONFIG_CTS_I2C_HOST */ + spin_lock_init(&pdata->irq_lock); + + input_dev = input_allocate_device(); + if (input_dev == NULL) { + cts_err("Failed to allocate input device."); + return -ENOMEM; + } + + /** - Init input device */ + input_dev->name = CFG_CTS_DEVICE_NAME; + input_dev->name = CFG_CTS_DEVICE_NAME; +#ifdef CONFIG_CTS_I2C_HOST + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &pdata->i2c_client->dev; +#else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = &pdata->spi_client->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + input_dev->evbit[0] = BIT_MASK(EV_SYN) | + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +#ifdef CFG_CTS_SWAP_XY + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_y, 0, + 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_x, 0, + 0); +#else /* CFG_CTS_SWAP_XY */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_x, 0, + 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_y, 0, + 0); +#endif /* CFG_CTS_SWAP_XY */ + + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + CFG_CTS_MAX_TOUCH_NUM * 2, 0, 0); + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + input_mt_init_slots(input_dev, CFG_CTS_MAX_TOUCH_NUM, 0); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + __set_bit(EV_ABS, input_dev->evbit); + input_set_drvdata(input_dev, pdata); + ret = input_register_device(input_dev); + if (ret) { + cts_err("Failed to register input device"); + return ret; + } + + pdata->ts_input_dev = input_dev; + +#if !defined(CONFIG_GENERIC_HARDIRQS) + INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_VIRTUALKEY + { + u8 vkey_keymap[CFG_CTS_NUM_VKEY] = CFG_CTS_VKEY_KEYCODES; + + memcpy(pdata->vkey_keycodes, vkey_keymap, sizeof(vkey_keymap)); + pdata->vkey_num = CFG_CTS_NUM_VKEY; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE + { + u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2] = + CFG_CTS_GESTURE_KEYMAP; + + memcpy(pdata->gesture_keymap, gesture_keymap, + sizeof(gesture_keymap)); + pdata->gesture_num = CFG_CTS_NUM_GESTURE; + } +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FORCE_UP + INIT_DELAYED_WORK(&pdata->touch_event_timeout_work, + cts_plat_touch_event_timeout_work); +#endif + +#ifndef CONFIG_CTS_I2C_HOST + pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ; + cts_plat_spi_setup(pdata); +#endif + return 0; +} + +int cts_deinit_platform_data(struct cts_platform_data *pdata) +{ + cts_info("De-Init platform_data"); + input_unregister_device(pdata->ts_input_dev); + return 0; +} + +int cts_plat_request_resource(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request resource"); + + ret = gpio_request_one(pdata->int_gpio, GPIOF_IN, + CFG_CTS_DEVICE_NAME "-int"); + if (ret) { + cts_err("Request INT gpio (%d) failed %d", pdata->int_gpio, + ret); + goto err_out; + } +#ifdef CFG_CTS_HAS_RESET_PIN + ret = gpio_request_one(pdata->rst_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-rst"); + if (ret) { + cts_err("Request RST gpio (%d) failed %d", pdata->rst_gpio, + ret); + goto err_free_int; + } +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + ret = gpio_request_one(pdata->cs_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-cs"); + if (ret) { + cts_err("Request CS gpio (%d) failed %d", pdata->cs_gpio, ret); + goto err_request_cs_gpio; + } +#endif + + return 0; + +#ifdef CFG_CTS_MANUAL_CS +err_request_cs_gpio: +#endif + +#ifdef CONFIG_CTS_REGULATOR +err_free_rst: +#endif /* CONFIG_CTS_REGULATOR */ +#ifdef CFG_CTS_HAS_RESET_PIN + gpio_free(pdata->rst_gpio); +err_free_int: +#endif /* CFG_CTS_HAS_RESET_PIN */ + gpio_free(pdata->int_gpio); +err_out: + return ret; +} + +void cts_plat_free_resource(struct cts_platform_data *pdata) +{ + cts_info("Free resource"); + + if (gpio_is_valid(pdata->int_gpio)) + gpio_free(pdata->int_gpio); + +#ifdef CFG_CTS_HAS_RESET_PIN + if (gpio_is_valid(pdata->rst_gpio)) + gpio_free(pdata->rst_gpio); + +#endif /* CFG_CTS_HAS_RESET_PIN */ +#ifdef CFG_CTS_MANUAL_CS + if (gpio_is_valid(pdata->cs_gpio)) + gpio_free(pdata->cs_gpio); + +#endif +} + +int cts_plat_request_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request IRQ"); + +#ifdef CONFIG_GENERIC_HARDIRQS + ret = request_threaded_irq(pdata->irq, NULL, + cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + CFG_CTS_DRIVER_NAME, pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + ret = request_irq(pdata->irq, + cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + CFG_CTS_DRIVER_NAME, pdata); +#endif /* CONFIG_GENERIC_HARDIRQS */ + if (ret) { + cts_err("Request IRQ failed %d", ret); + return ret; + } + + cts_plat_disable_irq(pdata); + + return 0; +} + +void cts_plat_free_irq(struct cts_platform_data *pdata) +{ + free_irq(pdata->irq, pdata); +} + +int cts_plat_enable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Enable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (pdata->irq_is_disable) { /* && !cts_is_device_suspended(pdata->chip)) */ + cts_dbg("Real enable IRQ"); + enable_irq(pdata->irq); + pdata->irq_is_disable = false; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +int cts_plat_disable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Disable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (!pdata->irq_is_disable) { + cts_dbg("Real disable IRQ"); + disable_irq_nosync(pdata->irq); + pdata->irq_is_disable = true; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + /* !!!can not be modified */ + /* !!!can not be modified */ + /* !!!can not be modified */ + cts_info("Reset device"); + + gpio_set_value(pdata->rst_gpio, 1); + mdelay(1); + gpio_set_value(pdata->rst_gpio, 0); + mdelay(10); + gpio_set_value(pdata->rst_gpio, 1); + mdelay(40); + + return 0; +} + +int cts_plat_set_reset(struct cts_platform_data *pdata, int val) +{ + cts_info("Set Reset to %s", val ? "HIGH" : "LOW"); + if (val) + gpio_set_value(pdata->rst_gpio, 1); + else + gpio_set_value(pdata->rst_gpio, 0); + + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +int cts_plat_get_int_pin(struct cts_platform_data *pdata) +{ + return gpio_get_value(pdata->int_gpio); +} + +int cts_plat_power_up_device(struct cts_platform_data *pdata) +{ + cts_info("Power up device"); + + return 0; +} + +int cts_plat_power_down_device(struct cts_platform_data *pdata) +{ + cts_info("Power down device"); + + return 0; +} + +int cts_plat_init_touch_device(struct cts_platform_data *pdata) +{ + cts_info("Init touch device"); + + return 0; +} + +void cts_plat_deinit_touch_device(struct cts_platform_data *pdata) +{ + cts_info("De-init touch device"); + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&pdata->ts_irq_work)) + cancel_work_sync(&pdata->ts_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ +} + +int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + int i; + int contact = 0; +#ifdef CONFIG_CTS_SLOTPROTOCOL + static unsigned char finger_last[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; + unsigned char finger_current[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; +#endif + + cts_dbg("Process touch %d msgs", num); + if (num == 0 || num > CFG_CTS_MAX_TOUCH_NUM) + return 0; + + for (i = 0; i < num; i++) { + u16 x, y; + + x = le16_to_cpu(msgs[i].x); + y = le16_to_cpu(msgs[i].y); + +#ifdef CFG_CTS_SWAP_XY + swap(x, y); +#endif /* CFG_CTS_SWAP_XY */ +#ifdef CFG_CTS_WRAP_X + x = wrap(pdata->res_x, x); +#endif /* CFG_CTS_WRAP_X */ +#ifdef CFG_CTS_WRAP_Y + y = wrap(pdata->res_y, y); +#endif /* CFG_CTS_WRAP_Y */ + cts_dbg(" Process touch msg[%d]: id[%u] ev=%u x=%u y=%u p=%u", + i, msgs[i].id, msgs[i].event, x, y, msgs[i].pressure); + if (msgs[i].event == CTS_DEVICE_TOUCH_EVENT_DOWN + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_MOVE + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_STAY) { + if (msgs[i].id < CFG_CTS_MAX_TOUCH_NUM) + finger_current[msgs[i].id] = 1; + } +#ifdef CONFIG_CTS_SLOTPROTOCOL + /* input_mt_slot(input_dev, msgs[i].id); */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_mt_slot(input_dev, msgs[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + true); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_PRESSURE, + msgs[i].pressure); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + /* input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); */ + break; + + default: + cts_warn + ("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#else /* CONFIG_CTS_SLOTPROTOCOL */ + /** + * If the driver reports one of BTN_TOUCH or ABS_PRESSURE + * in addition to the ABS_MT events, the last SYN_MT_REPORT event + * may be omitted. Otherwise, the last SYN_REPORT will be dropped + * by the input core, resulting in no zero-contact event + * reaching userland. + */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_report_abs(input_dev, ABS_MT_PRESSURE, + msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + msgs[i].pressure); + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_mt_sync(input_dev); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + break; + default: + cts_warn + ("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + } + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (i = 0; i < CFG_CTS_MAX_TOUCH_NUM; i++) { + if (finger_last[i] != 0 && finger_current[i] == 0) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + false); + } + finger_last[i] = finger_current[i]; + } + input_report_key(input_dev, BTN_TOUCH, contact > 0); +#else + if (contact == 0) { + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } +#endif + input_sync(input_dev); +#ifdef CFG_CTS_FORCE_UP + if (contact) { + struct chipone_ts_data *cts_data; + + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, + cts_dev); + + if (delayed_work_pending(&pdata->touch_event_timeout_work)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, + msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, + msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_event_timeout_work); + } +#endif + return 0; +} + +int cts_plat_release_all_touch(struct cts_platform_data *pdata) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + +#if defined(CONFIG_CTS_SLOTPROTOCOL) + int id; +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + + cts_info("Release all touch"); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (id = 0; id < CFG_CTS_MAX_TOUCH_NUM; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + input_report_key(input_dev, BTN_TOUCH, 0); +#else + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + input_sync(input_dev); + + return 0; +} + +#ifdef CONFIG_CTS_VIRTUALKEY +int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init VKey"); + + pdata->vkey_state = 0; + + for (i = 0; i < pdata->vkey_num; i++) { + input_set_capability(pdata->ts_input_dev, + EV_KEY, pdata->vkey_keycodes[i]); + } + + return 0; +} + +void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ + cts_info("De-init VKey"); + + pdata->vkey_state = 0; +} + +int cts_plat_process_vkey(struct cts_platform_data *pdata, u8 vkey_state) +{ + u8 event; + int i; + + event = pdata->vkey_state ^ vkey_state; + + cts_dbg("Process vkey state=0x%02x, event=0x%02x", vkey_state, event); + + for (i = 0; i < pdata->vkey_num; i++) { + input_report_key(pdata->ts_input_dev, + pdata->vkey_keycodes[i], + vkey_state & BIT(i) ? 1 : 0); + } + + pdata->vkey_state = vkey_state; + + return 0; +} + +int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Release all vkeys"); + + for (i = 0; i < pdata->vkey_num; i++) { + if (pdata->vkey_state & BIT(i)) { + input_report_key(pdata->ts_input_dev, + pdata->vkey_keycodes[i], 0); + } + } + + pdata->vkey_state = 0; + + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +int cts_plat_enable_irq_wake(struct cts_platform_data *pdata) +{ + cts_info("Enable IRQ wake"); + + if (pdata->irq > 0) { + if (!pdata->irq_wake_enabled) { + pdata->irq_wake_enabled = true; + return enable_irq_wake(pdata->irq); + } + + cts_warn("Enable irq wake while already disabled"); + return -EINVAL; + } + + cts_warn("Enable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_disable_irq_wake(struct cts_platform_data *pdata) +{ + cts_info("Disable IRQ wake"); + + if (pdata->irq > 0) { + if (pdata->irq_wake_enabled) { + pdata->irq_wake_enabled = false; + return disable_irq_wake(pdata->irq); + } + + cts_warn("Disable irq wake while already disabled"); + return -EINVAL; + } + + cts_warn("Disable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init gesture"); + + /* TODO: If system will issure enable/disable command, comment following line. */ + /* cts_enable_gesture_wakeup(pdata->cts_dev); */ + + for (i = 0; i < pdata->gesture_num; i++) { + input_set_capability(pdata->ts_input_dev, EV_KEY, + pdata->gesture_keymap[i][1]); + } + + return 0; +} + +void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ + cts_info("De-init gesture"); +} + +int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info *gesture_info) +{ + int i; + + cts_info("Process gesture, id=0x%02x", gesture_info->gesture_id); + +#if defined(CFG_CTS_GESTURE_REPORT_KEY) + for (i = 0; i < CFG_CTS_NUM_GESTURE; i++) { + if (gesture_info->gesture_id == pdata->gesture_keymap[i][0]) { + cts_info("Report key[%u]", pdata->gesture_keymap[i][1]); + input_report_key(pdata->ts_input_dev, + pdata->gesture_keymap[i][1], 1); + input_sync(pdata->ts_input_dev); + + input_report_key(pdata->ts_input_dev, + pdata->gesture_keymap[i][1], 0); + input_sync(pdata->ts_input_dev); + + return 0; + } + } +#endif /* CFG_CTS_GESTURE_REPORT_KEY */ + + cts_warn("Process unrecognized gesture id=%u", + gesture_info->gesture_id); + + return -EINVAL; +} + +#endif /* CFG_CTS_GESTURE */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h new file mode 100644 index 000000000000..59e9dcd7041e --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_PLATFORM_H +#define CTS_PLATFORM_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 + +#ifdef CONFIG_OF +#include +#include +#include +#endif /* CONFIG_OF */ + +#include +#include + +#include +#include + +#include "cts_config.h" +#include "cts_core.h" + +extern bool cts_show_debug_log; + +#ifndef LOG_TAG +#define LOG_TAG "" +#endif /* LOG_TAG */ + +#define cts_err(fmt, ...) \ + printk("CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_warn(fmt, ...) \ + printk("CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_info(fmt, ...) \ + printk("CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_dbg(fmt, ...) \ + do { \ + if (cts_show_debug_log) \ + printk("CTS-" LOG_TAG " "fmt"\n", ##__VA_ARGS__); \ + } while (0) + +struct cts_device; +struct cts_device_touch_msg; +struct cts_device_gesture_info; + +struct cts_platform_data { + int irq; + int int_gpio; +#ifdef CFG_CTS_HAS_RESET_PIN + int rst_gpio; +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + int cs_gpio; +#endif + + u32 res_x; + u32 res_y; + +#ifdef CONFIG_CTS_VIRTUALKEY + u8 vkey_num; + u8 vkey_state; + u8 vkey_keycodes[CFG_CTS_MAX_VKEY_NUM]; +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_FW_UPDATE_SYS + const char *panel_supplier; +#endif + u32 build_id; + u32 config_id; + + struct cts_device *cts_dev; + + struct input_dev *ts_input_dev; + +#ifndef CONFIG_GENERIC_HARDIRQS + struct work_struct ts_irq_work; +#endif /* CONFIG_GENERIC_HARDIRQS */ + + struct spinlock irq_lock; + bool irq_is_disable; + +#ifdef CFG_CTS_GESTURE + u8 gesture_num; + u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2]; + bool irq_wake_enabled; +#endif /* CFG_CTS_GESTURE */ + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + struct notifier_block fb_notifier; +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +#ifdef CFG_CTS_FORCE_UP + struct delayed_work touch_event_timeout_work; +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + u8 fw_log_buf[CTS_FW_LOG_BUF_LEN]; +#endif + +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; + u8 i2c_fifo_buf[CFG_CTS_MAX_I2C_XFER_SIZE]; +#else + struct spi_device *spi_client; + u8 spi_cache_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_rx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_tx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u32 spi_speed; +#endif /* CONFIG_CTS_I2C_HOST */ +}; + +#ifdef CONFIG_CTS_I2C_HOST +extern size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, + int delay); +extern int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_is_i2c_online(struct cts_platform_data *pdata, u8 i2c_addr); +#else /* CONFIG_CTS_I2C_HOST */ +extern size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_spi_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, + int delay); +extern int cts_plat_spi_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, + u8 dev_addr, const u8 *wbuf, + size_t wlen, void *rbuf, size_t rlen, + int retry, int delay, int idle); +extern int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer); +#endif /* CONFIG_CTS_I2C_HOST */ + +#ifdef CONFIG_CTS_I2C_HOST +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client); +#else +extern int cts_plat_is_normal_mode(struct cts_platform_data *pdata); +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi); +#endif + +extern int cts_deinit_platform_data(struct cts_platform_data *pdata); +extern int cts_plat_request_resource(struct cts_platform_data *pdata); +extern void cts_plat_free_resource(struct cts_platform_data *pdata); + +extern int cts_plat_request_irq(struct cts_platform_data *pdata); +extern void cts_plat_free_irq(struct cts_platform_data *pdata); +extern int cts_plat_enable_irq(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_HAS_RESET_PIN +extern int cts_plat_reset_device(struct cts_platform_data *pdata); +#else /* CFG_CTS_HAS_RESET_PIN */ +static inline int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +extern int cts_plat_init_touch_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_touch_device(struct cts_platform_data *pdata); +extern int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, + int num); +extern int cts_plat_release_all_touch(struct cts_platform_data *pdata); + +#ifdef CONFIG_CTS_VIRTUALKEY +extern int cts_plat_init_vkey_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata); +extern int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state); +extern int cts_plat_release_all_vkey(struct cts_platform_data *pdata); +#else /* CONFIG_CTS_VIRTUALKEY */ +static inline int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ +} + +static inline int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state) +{ + return 0; +} + +static inline int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +extern int cts_plat_enable_irq_wake(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq_wake(struct cts_platform_data *pdata); + +extern int cts_plat_init_gesture(struct cts_platform_data *pdata); +extern void cts_plat_deinit_gesture(struct cts_platform_data *pdata); +extern int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info + *gesture_info); +#else /* CFG_CTS_GESTURE */ +static inline int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ +} +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, + size_t size); +#endif + +extern int cts_plat_set_reset(struct cts_platform_data *pdata, int val); +extern int cts_plat_get_int_pin(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_MANUAL_CS +extern int cts_plat_set_cs(struct cts_platform_data *pdata, int val); +#endif +#endif /* CTS_PLATFORM_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h new file mode 100644 index 000000000000..3e296fe20124 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_SFCTRL_H +#define CTS_SFCTRL_H + +#include "cts_core.h" +#include "cts_spi_flash.h" + +struct cts_device; + +struct cts_sfctrl_ops { + int (*rdid)(const struct cts_device *cts_dev, u32 *id); + int (*se)(const struct cts_device *cts_dev, u32 sector_addr); + int (*be)(const struct cts_device *cts_dev, u32 sector_addr); + int (*read)(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t len); + int (*read_to_sram)(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*program)(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t len); + int (*program_from_sram)(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*calc_sram_crc)(const struct cts_device *cts_dev, + u32 sram_addr, size_t len, u32 *crc); + int (*calc_flash_crc)(const struct cts_device *cts_dev, + u32 flash_addr, size_t len, u32 *crc); + +}; + +struct cts_sfctrl { + u32 reg_base; + u32 xchg_sram_base; + size_t xchg_sram_size; + const struct cts_sfctrl_ops *ops; +}; + +extern const struct cts_sfctrl_ops cts_sfctrlv2_ops; + +#endif /* CTS_SFCTRL_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c new file mode 100644 index 000000000000..019c1ceb42ec --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "SFCtrl" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" + +#define rSFCTRLv2_CMD_SEL (0x0000) +#define rSFCTRLv2_FLASH_ADDR (0x0004) +#define rSFCTRLv2_SRAM_ADDR (0x0008) +#define rSFCTRLv2_DATA_LENGTH (0x000C) +#define rSFCTRLv2_START_DEXC (0x0010) +#define rSFCTRLv2_RELEASE_FLASH (0x0014) +#define rSFCTRLv2_HW_STATE (0x0018) +#define rSFCTRLv2_CRC_RESULT (0x001C) +#define rSFCTRLv2_SRAM_CRC_START (0x0020) +#define rSFCTRLv2_FLASH_CRC_START (0x0022) +#define rSFCTRLv2_SF_BUSY (0x0024) + +/** Constants for register @ref rSFCTRLv2_CMD_SEL */ +enum sfctrlv2_cmd { + SFCTRLv2_CMD_FAST_READ = 0x01u, + SFCTRLv2_CMD_SE = 0x02u, + SFCTRLv2_CMD_BE = 0x03u, + SFCTRLv2_CMD_PP = 0x04u, + SFCTRLv2_CMD_RDSR = 0x05u, + SFCTRLv2_CMD_RDID = 0x06u +}; + +/** SPI flash controller v2 operation flags. */ +enum sfctrlv2_opflags { + SFCTRLv2_OPFLAG_READ = BIT(0), + SFCTRLv2_OPFLAG_SET_FLASH_ADDR = BIT(1), + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG = BIT(2), + SFCTRLv2_OPFLAG_SET_DATA_LENGTH = BIT(3), + SFCTRLv2_OPFLAG_WAIT_WIP_CLR = BIT(4), +}; + +#define SFCTRLv2_CMD_RDID_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_RDSR_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_SE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_BE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_PP_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_FAST_READ_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH) + +#define sfctrl_reg_addr(cts_dev, offset) \ + ((cts_dev)->hwdata->sfctrl->reg_base + offset) + +#define DEFINE_SFCTRL_REG_ACCESS_FUNC(access_type, data_type) \ + static inline int sfctrl_reg_ ## access_type(const struct cts_device *cts_dev, \ + u32 reg, data_type data) { \ + return cts_hw_reg_ ## access_type(cts_dev, sfctrl_reg_addr(cts_dev, reg), data); \ + } + +DEFINE_SFCTRL_REG_ACCESS_FUNC(writeb_relaxed, u8) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writew_relaxed, u16) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writel_relaxed, u32) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readb_relaxed, u8 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readw_relaxed, u16 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readl_relaxed, u32 *) +#define sfctrl_write_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Write " #reg " to 0x%x", val); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Write " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +#define sfctrl_read_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Read " #reg ""); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Read " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +static int wait_sfctrl_xfer_comp(const struct cts_device *cts_dev) +{ + int retries = 0; + u8 status; + + do { + sfctrl_read_reg_check_ret(readb_relaxed, rSFCTRLv2_SF_BUSY, + &status); + if (status == 0) + return 0; + mdelay(20); + } while (status && retries++ < 50); + + return -ETIMEDOUT; +} + +static int sfctrlv2_rdsr(const struct cts_device *cts_dev, u8 *status) +{ +#define RDSR_XCHG_SRAM_ADDR (cts_dev->hwdata->sram_size - 1) + + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, + SFCTRLv2_CMD_RDSR); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + RDSR_XCHG_SRAM_ADDR); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = cts_sram_readb(cts_dev, RDSR_XCHG_SRAM_ADDR, status); + if (ret) { + cts_err("Read exchange sram failed %d", ret); + return ret; + } +#undef RDSR_XCHG_SRAM_ADDR + + return 0; +} + +static int wait_flash_wip_clear(const struct cts_device *cts_dev) +{ +#define FLASH_SR_WIP BIT(0) /*!< Write in progress */ + + int ret, retries = 0; + u8 status; + + do { + ret = sfctrlv2_rdsr(cts_dev, &status); + if (ret) { + cts_err("Read flash status register failed %d", ret); + return ret; + } + + if (status & FLASH_SR_WIP) + mdelay(1); + else + return 0; + } while (status & FLASH_SR_WIP && retries++ < 1000); + + return -ETIMEDOUT; +#undef FLASH_SR_WIP +} + +static int sfctrlv2_transfer(const struct cts_device *cts_dev, + u8 cmd, void *data, u32 flash_addr, u32 sram_addr, + size_t size, u32 flags) +{ + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, cmd); + + if (flags & SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + sram_addr); + +/** - Write data to exchange SRAM if operation is write and + * data != NULL(data not in SRAM) + */ + if ((!(flags & SFCTRLv2_OPFLAG_READ)) && data) { + ret = cts_sram_writesb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err + ("Write data to exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + } + + if (flags & SFCTRLv2_OPFLAG_SET_FLASH_ADDR) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, + flash_addr); + } + if (flags & SFCTRLv2_OPFLAG_SET_DATA_LENGTH) { + sfctrl_write_reg_check_ret(writel_relaxed, + rSFCTRLv2_DATA_LENGTH, (u32) size); + } + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = wait_flash_wip_clear(cts_dev); + if (flags & SFCTRLv2_OPFLAG_WAIT_WIP_CLR && ret != 0) { + cts_err("Wait WIP clear failed %d", ret); + return ret; + } + + if ((flags & SFCTRLv2_OPFLAG_READ) && data) { + ret = cts_sram_readsb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err + ("Read data from exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + + return 0; +} + +static int sfctrlv2_rdid(const struct cts_device *cts_dev, u32 *id) +{ + int ret; + u8 buf[4]; + + ret = sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_RDID, buf, + 0, cts_dev->hwdata->sfctrl->xchg_sram_base, 3, + SFCTRLv2_CMD_RDID_FLAGS); + *id = ret ? 0 : get_unaligned_be24(buf); + + return ret; +} + +static int sfctrlv2_se(const struct cts_device *cts_dev, u32 sector_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_SE, NULL, + sector_addr, 0, 0, SFCTRLv2_CMD_SE_FLAGS); +} + +static int sfctrlv2_be(const struct cts_device *cts_dev, u32 block_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_BE, NULL, + block_addr, 0, 0, SFCTRLv2_CMD_BE_FLAGS); +} + +static int sfctrlv2_pp(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, (void *)src, + flash_addr, + cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t len) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, NULL, + flash_addr, sram_addr, len, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_read(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, dst, + flash_addr, + cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +static int sfctrlv2_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, NULL, + flash_addr, sram_addr, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +int sfctrlv2_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + int ret; + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + sram_addr); + mdelay(1); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, + (u32) size); + mdelay(1); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_SRAM_CRC_START, 1); + mdelay(10); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +int sfctrlv2_calc_flash_crc(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + int ret; + + cts_info("Calc crc from flash 0x%06x size %zu", flash_addr, size); + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, + flash_addr); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, + (u32) size); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_FLASH_CRC_START, + 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +const struct cts_sfctrl_ops cts_sfctrlv2_ops = { + .rdid = sfctrlv2_rdid, + .se = sfctrlv2_se, + .be = sfctrlv2_be, + .read = sfctrlv2_read, + .read_to_sram = sfctrlv2_read_flash_to_sram, + .program = sfctrlv2_pp, + .program_from_sram = sfctrlv2_program_flash_from_sram, + .calc_sram_crc = sfctrlv2_calc_sram_crc, + .calc_flash_crc = sfctrlv2_calc_flash_crc, +}; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c new file mode 100644 index 000000000000..0b291eef2f60 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Flash" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +static int probe_flash(struct cts_device *cts_dev) +{ + /* for no-flash project */ + return -ENODEV; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static int erase_sector_retry(const struct cts_device *cts_dev, + u32 sector_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase sector 0x%06x", sector_addr); + + retries = 0; + do { + retries++; + ret = cts_dev->hwdata->sfctrl->ops->se(cts_dev, sector_addr); + if (ret) { + cts_err("Erase sector 0x%06x failed %d retries %d", + sector_addr, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static inline int erase_sector(const struct cts_device *cts_dev, + u32 sector_addr) +{ + return erase_sector_retry(cts_dev, sector_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +/** Make sure block addr is block aligned && < flash total size */ +static int erase_block_retry(const struct cts_device *cts_dev, + u32 block_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase block 0x%06x", block_addr); + + retries = 0; + do { + retries++; + + ret = cts_dev->hwdata->sfctrl->ops->be(cts_dev, block_addr); + if (ret) { + cts_err("Erase block 0x%06x failed %d retries %d", + block_addr, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure block addr is block aligned && < flash total size */ +static inline int erase_block(const struct cts_device *cts_dev, u32 block_addr) +{ + return erase_block_retry(cts_dev, block_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +int cts_prepare_flash_operation(struct cts_device *cts_dev) +{ + int ret; + u8 diva = 0; + bool program_mode = cts_is_device_program_mode(cts_dev); + bool enabled = cts_is_device_enabled(cts_dev); + + cts_info("Prepare for flash operation"); + + if (enabled) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + } + + if (!program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_start_device; + } + } + + ret = cts_hw_reg_readb_retry(cts_dev, 0x30033, &diva, 5, 0); + if (ret) + cts_warn("Read DIVA failed %d", ret); + else + cts_dbg("Device DIVA = %d", diva); + + if (ret == 0 && diva == 0x0C) + cts_info("DIVA is ready already"); + else { + int retries; + + /* Set HCLK to 10MHz */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x30033, 0x0C, 5, 0); + if (ret) { + cts_err("Write DIVA failed %d", ret); + goto err_enter_normal_mode; + } + + /* Reset SFCTL */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x30008, 0xFB, 5, 0); + if (ret) { + cts_err("Reset sfctl failed %d", ret); + goto err_enter_normal_mode; + } + + retries = 0; + do { + u8 state; + + ret = + cts_hw_reg_readb_relaxed(cts_dev, 0x3401B, &state); + if (ret == 0 && (state & 0x40) != 0) + goto init_flash; + + mdelay(2); + } while (++retries < 1000); + + cts_warn("Wait SFCTRL ready failed %d", ret); + } + +init_flash: + if (cts_dev->flash == NULL) { + cts_info("Flash is not initialized, try to probe..."); + ret = probe_flash(cts_dev); + if (ret != 0) { + cts_dev->rtdata.has_flash = false; + cts_warn("Probe flash failed %d", ret); + return 0; + } + } + cts_dev->rtdata.has_flash = true; + return 0; + +err_enter_normal_mode: + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + cts_err("Enter normal mode failed %d", r); + } +err_start_device: + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + + return ret; +} + +int cts_post_flash_operation(struct cts_device *cts_dev) +{ + cts_info("Post flash operation"); + + return cts_hw_reg_writeb_retry(cts_dev, 0x30033, 4, 5, 0); +} + +int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Read from 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->read == NULL) { + cts_err("Read not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read actually from 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l; + + l = min(sfctrl->xchg_sram_size, size); + + ret = sfctrl->ops->read(cts_dev, flash_addr, dst, l); + if (ret < 0) { + cts_err("Read from 0x%06x size %zu failed %d", + flash_addr, size, ret); + return ret; + } + + dst += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size, + int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->read_to_sram == NULL) { + cts_err("Read to sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu", flash_addr, + size); + + retries = 0; + do { + retries++; + ret = + sfctrl->ops->read_to_sram(cts_dev, flash_addr, sram_addr, + size); + if (ret) { + cts_err + ("Read from 0x%06x to sram 0x%06x size %zu failed %d retries %d", + flash_addr, sram_addr, size, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +int cts_read_flash_to_sram_check_crc_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size, u32 crc, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu with crc check", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->read_to_sram == NULL || + sfctrl->ops->calc_sram_crc == NULL) { + cts_err("Read to sram check crc not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu with crc check", + flash_addr, size); + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = + sfctrl->ops->read_to_sram(cts_dev, flash_addr, sram_addr, + size); + if (ret) { + cts_err + ("Read from 0x%06x to sram 0x%06x size %zu failed %d retries %d", + flash_addr, sram_addr, size, ret, retries); + continue; + } + + ret = + sfctrl->ops->calc_sram_crc(cts_dev, sram_addr, size, + &crc_sram); + if (ret) { + cts_err + ("Get crc for read from 0x%06x to sram 0x%06x size %zu " + "failed %d retries %d", flash_addr, sram_addr, + size, ret, retries); + continue; + } + + if (crc == crc_sram) + return 0; + + cts_err + ("Check crc for read from 0x%06x to sram 0x%06x size %zu " + "mismatch %x != %x, retries %d", flash_addr, sram_addr, + size, crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->program == NULL) { + cts_err("Program not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) + l = min(flash->page_size - offset, l); + + ret = sfctrl->ops->program(cts_dev, flash_addr, src, l); + if (ret) { + cts_err("Program to 0x%06x size %zu failed %d", + flash_addr, l, ret); + return ret; + } + + src += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->program_from_sram == NULL) { + cts_err("Program from sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) + l = min(flash->page_size - offset, l); + + ret = + sfctrl->ops->program_from_sram(cts_dev, flash_addr, + sram_addr, l); + if (ret) { + cts_err + ("Program to 0x%06x from sram 0x%06x size %zu failed %d", + flash_addr, sram_addr, l, ret); + return ret; + } + + size -= l; + flash_addr += l; + sram_addr += l; + } + + return 0; +} + +int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Erase from 0x%06x size %zu", addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || sfctrl->ops == NULL || + sfctrl->ops->se == NULL || sfctrl->ops->be == NULL || + flash == NULL) { + cts_err("Oops"); + return -EINVAL; + } + + /* Addr and size MUST sector aligned */ + addr = rounddown(addr, flash->sector_size); + size = roundup(size, flash->sector_size); + + if (addr > flash->total_size) { + cts_err("Erase from 0x%06x > flash size 0x%06zx", + addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - addr); + + cts_info("Erase actually from 0x%06x size %zu", addr, size); + + if (flash->block_size) { + while (addr != ALIGN(addr, flash->block_size) && + size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err + ("Erase sector 0x%06x size 0x%04zx failed %d", + addr, flash->sector_size, ret); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + while (size >= flash->block_size) { + ret = erase_block(cts_dev, addr); + if (ret) { + cts_err + ("Erase block 0x%06x size 0x%04zx failed %d", + addr, flash->block_size, ret); + return ret; + } + addr += flash->block_size; + size -= flash->block_size; + } + } + + while (size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err("Erase sector 0x%06x size 0x%04zx failed %d", + addr, flash->sector_size, ret); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + return 0; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h new file mode 100644 index 000000000000..963abcd2676e --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_SPI_FLASH_H +#define CTS_SPI_FLASH_H + +struct cts_flash { + const char *name; + u32 jedec_id; /* Device ID by command 0x9F */ + size_t page_size; /* Page size by command 0x02 */ + size_t sector_size; /* Sector size by command 0x20 */ + +/* Block size by command 0x52, + * if 0 means block erase NOT supported + */ + size_t block_size; + size_t total_size; + +}; + +#define CTS_FLASH_READ_DEFAULT_RETRY (3) +#define CTS_FLASH_ERASE_DEFAULT_RETRY (3) + +struct cts_device; + +extern int cts_prepare_flash_operation(struct cts_device *cts_dev); +extern int cts_post_flash_operation(struct cts_device *cts_dev); + +extern int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, + int retry); +static inline int cts_read_flash(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return cts_read_flash_retry(cts_dev, flash_addr, dst, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size, int retry); +static inline int cts_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size) +{ + return cts_read_flash_to_sram_retry(cts_dev, + flash_addr, sram_addr, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_check_crc_retry(const struct cts_device + *cts_dev, u32 flash_addr, + u32 sram_addr, size_t size, + u32 crc, int retry); +static inline int cts_read_flash_to_sram_check_crc(const struct cts_device + *cts_dev, u32 flash_addr, + u32 sram_addr, size_t size, + u32 crc) +{ + return cts_read_flash_to_sram_check_crc_retry(cts_dev, + flash_addr, sram_addr, + size, crc, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, + size_t size); + +extern int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size); +extern int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size); + +#endif /* CTS_SPI_FLASH_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c new file mode 100644 index 000000000000..d7963cdb1526 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c @@ -0,0 +1,2778 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Sysfs" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_test.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" + +#ifdef CONFIG_CTS_SYSFS + +#define SPLIT_LINE_STR \ + "-----------------------------------------------"\ + "------------------------------------------------\n" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%4u " +#define DATA_FORMAT_STR "%5d" + +#define DIFFDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +#define MAX_ARG_NUM (100) +#define MAX_ARG_LENGTH (1024) +#define HW_STUB_ADDR (0XF000) + +static char cmdline_param[MAX_ARG_LENGTH + 1]; +int argc; +char *argv[MAX_ARG_NUM]; + +static int jitter_test_frame = 10; +static s16 *manualdiff_base; +static int manualdiff_base_updated; + +int parse_arg(const char *buf, size_t count) +{ + char *p; + + memcpy(cmdline_param, buf, min((size_t)MAX_ARG_LENGTH, count)); + cmdline_param[count] = '\0'; + + argc = 0; + p = strim(cmdline_param); + if (p == NULL || p[0] == '\0') + return 0; + + while (p && p[0] != '\0' && argc < MAX_ARG_NUM) + argv[argc++] = strsep(&p, " ,"); + + return argc; +} + +/* echo addr value1 value2 value3 ... valueN > write_reg */ +static ssize_t write_firmware_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write firmware register '%.*s'", (int)count, buf); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + ret = cts_dev->ops->write_reg(cts_dev, addr, data, argc - 1); + if (ret) { + cts_err("Write firmware register addr: 0x%04x size: %d failed", + addr, argc - 1); + goto free_data; + } + +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +#ifdef CFG_CTS_FW_UPDATE_SYS + +static ssize_t cts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + + if (data->pdata && data->pdata->panel_supplier) { + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->pdata->panel_supplier); + } + return 0; +} + +static ssize_t buildid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0000-%04x\n", + cts_data->cts_dev.fwdata.version); +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 0) + return -EINVAL; + + cts_data->force_reflash = (input == 0) ? false : true; + + cts_info("%s force_reflash=%d, count=%zu", __func__, + (cts_data->force_reflash ? 1 : 0), count); + return count; +} + +static ssize_t flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_is_firmware_updating(&cts_data->cts_dev) ? 1 : 0); +} + +static bool is_reflash_filename_valid(const struct chipone_ts_data *cts_data, + const char *filename) +{ + char prefix[CFG_CTS_FW_FILE_NAME_MAX_LEN]; + + if (cts_data->pdata->panel_supplier != NULL) { + snprintf(prefix, sizeof(prefix), "%s-%s-%s-", + CFG_CTS_FW_FILE_NAME_VENDOR, + cts_data->pdata->panel_supplier, + cts_data->cts_dev.hwdata->name); + } else { + /* panel supplier not set, just check vendor. */ + snprintf(prefix, sizeof(prefix), "%s", + CFG_CTS_FW_FILE_NAME_VENDOR); + } + + cts_info("%s: prefix=%s", __func__, prefix); + if (strncmp(filename, prefix, strlen(prefix))) { + cts_err("%s: invalid FW file.", __func__); + return false; + } + + return true; +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + cts_info("doreflash FW filename: %s len: %zu", buf, count); + + if (count > CFG_CTS_FW_FILE_NAME_MAX_LEN) { + cts_err("doreflash FW filename is too long %zu > %d", + count, CFG_CTS_FW_FILE_NAME_MAX_LEN); + return -EINVAL; + } + + if (cts_is_device_suspended(&cts_data->cts_dev)) { + cts_err("In suspend state, try again later"); + return -EAGAIN; + } + + if (cts_is_firmware_updating(&cts_data->cts_dev)) { + cts_err("In FW flashing state, try again later"); + return -EAGAIN; + } + + if (!cts_data->force_reflash) { + /* Check filename if force_reflash is false */ + if (!is_reflash_filename_valid(cts_data, buf)) { + cts_err("Invalid firmware filename '%*.s'", + (int)count, buf); + return -EINVAL; + } + } + + strncpy(cts_data->cts_dev.config_fw_name, buf, count); + + /* If use echo xxx > doreflash, 0x0A will append to the string, + * if use echo -n xxx > doreflash, nothing will append. + */ + if (cts_data->cts_dev.config_fw_name[count - 1] == '\n') + cts_data->cts_dev.config_fw_name[count - 1] = '\0'; + else + cts_data->cts_dev.config_fw_name[count] = '\0'; + + cts_stop_device(&cts_data->cts_dev); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_update_firmware_from_file(&cts_data->cts_dev, + cts_data->cts_dev.config_fw_name, + false); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) + cts_err("Update firmware from file '%s' failed %d", + cts_data->cts_dev.config_fw_name, ret); + + cts_start_device(&cts_data->cts_dev); + + cts_data->force_reflash = false; + + cts_info("%s: end", __func__); + + return ret ? ret : count; +} + +static ssize_t cts_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + bool val; + + /* TBD: check if cts is power to ready for flash. + * set "1" if power on ready. + */ + val = cts_is_device_suspended(&cts_data->cts_dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); +} + +static ssize_t cts_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + /* set chip IC type to productinfo */ + return scnprintf(buf, PAGE_SIZE, "%s\n", + cts_data->cts_dev.hwdata->name); +} + +/* add sys entries for FW update */ +/* static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); */ +/* static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); */ +static DEVICE_ATTR(panel_supplier, 0444, cts_panel_supplier_show, NULL); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); +static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); +static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); +static DEVICE_ATTR(poweron, S_IRUGO, cts_poweron_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, cts_productinfo_show, NULL); +#endif + +static DEVICE_ATTR(write_reg, S_IWUSR, NULL, write_firmware_register_store); + +static ssize_t read_firmware_register_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read firmware register "); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_reg\n" + " 2. cat read_reg\n", argc); + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", + argv[0]); + } + ret = kstrtou16(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) { + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + } + + cts_info("Read firmware register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + /* ret = cts_fw_reg_readsb(cts_dev, addr, data, (size_t) size); */ + ret = cts_dev->ops->read_reg(cts_dev, addr, data, (size_t)size); + cts_unlock_device(cts_dev); + if (ret) { + count = snprintf(buf, PAGE_SIZE, + "Read firmware register from 0x%04x size %u failed %d\n", + addr, size, ret); + goto err_free_data; + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += + snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_firmware_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_reg, S_IWUSR | S_IRUSR, + read_firmware_register_show, read_firmware_register_store); + +static ssize_t read_hw_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read hw register"); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_hw_reg\n" + " 2. cat read_hw_reg\n", argc); + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", + argv[0]); + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + + cts_info("Read hw register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911)) { + for (i = 0; i < size; i++) { + ret = + cts_fw_reg_writel_retry(cts_dev, HW_STUB_ADDR, + addr + i, 3, 10); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Write hw register error\n"); + goto err_free_data; + } + + ret = + cts_fw_reg_readsb(cts_dev, HW_STUB_ADDR + 4, + data + i, 1); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Read hw register error\n"); + goto err_free_data; + } + } + } + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < size; i++) { + ret = + cts_dev_readb(cts_dev, addr + i, data + i, + 3, 10); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Write hw register error\n"); + goto err_free_data; + } + } + } else { + ret = + cts_dev->ops->read_hw_reg(cts_dev, addr, data, + size); + if (ret < 0) { + count = + snprintf(buf, PAGE_SIZE, + "Read hw register error\n"); + goto err_free_data; + } + } + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += + snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_hw_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_hw_reg, S_IRUSR | S_IWUSR, read_hw_reg_show, + read_hw_reg_store); + +static ssize_t write_hw_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write hw register"); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + cts_lock_device(cts_dev); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911)) { + for (i = 0; i < argc - 1; i++) { + ret = + cts_fw_reg_writel_retry(cts_dev, HW_STUB_ADDR, + addr + i, 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + ret = + cts_fw_reg_writeb_retry(cts_dev, HW_STUB_ADDR + 4, + data[i], 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + } + } + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < argc - 1; i++) { + ret = + cts_dev_writeb(cts_dev, addr + i, data[i], + 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + } + } else { + ret = + cts_dev->ops->write_hw_reg(cts_dev, addr, data, + argc - 1); + if (ret < 0) + cts_err("Write hw register error"); + } + } + + cts_unlock_device(cts_dev); +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(write_hw_reg, S_IWUSR, NULL, write_hw_reg_store); + +static ssize_t curr_firmware_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current firmware version: %04x\n", + cts_data->cts_dev.fwdata.version); +} + +static DEVICE_ATTR(curr_version, S_IRUGO, curr_firmware_version_show, NULL); + +static ssize_t curr_ddi_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current ddi version: %02x\n", + cts_data->cts_dev.fwdata.ddi_version); +} + +static DEVICE_ATTR(curr_ddi_version, S_IRUGO, curr_ddi_version_show, NULL); + +static ssize_t rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num rows: %u\n", + cts_data->cts_dev.fwdata.rows); +} + +static DEVICE_ATTR(rows, S_IRUGO, rows_show, NULL); + +static ssize_t cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num cols: %u\n", + cts_data->cts_dev.fwdata.cols); +} + +static DEVICE_ATTR(cols, S_IRUGO, cols_show, NULL); + +static ssize_t res_x_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "X Resolution: %u\n", + cts_data->cts_dev.fwdata.res_x); +} + +static DEVICE_ATTR(res_x, S_IRUGO, res_x_show, NULL); + +static ssize_t res_y_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Y Resolution: %u\n", + cts_data->cts_dev.fwdata.res_y); +} + +static DEVICE_ATTR(res_y, S_IRUGO, res_y_show, NULL); + +static ssize_t esd_protection_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 esd_protection; + + cts_lock_device(cts_dev); + ret = cts_dev->ops->get_esd_protection(cts_dev, &esd_protection); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read firmware ESD protection register failed %d\n", + ret); + + return snprintf(buf, PAGE_SIZE, "ESD protection: %u\n", esd_protection); +} + +static DEVICE_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); + +static ssize_t monitor_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_dev->ops->is_mnt_enabled(cts_dev, &value); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read firmware monitor enable register failed %d\n", + ret); + + return snprintf(buf, PAGE_SIZE, "Monitor mode: %s\n", + value & BIT(0) ? "Enable" : "Disable"); +} + +static ssize_t monitor_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value, enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + + cts_info("Write firmware monitor mode to '%c', %s", + buf[0], enable ? "Enable" : "Disable"); + + cts_lock_device(cts_dev); + ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 344, &value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Write firmware monitor enable register failed %d", + ret); + return -EIO; + } + + if ((value & BIT(0)) && enable) + cts_info("Monitor mode already enabled"); + else if ((value & BIT(0)) == 0 && enable == 0) + cts_info("Monitor mode already disabled"); + else { + if (enable) + value |= BIT(0); + else + value &= ~BIT(0); + + cts_lock_device(cts_dev); + ret = + cts_fw_reg_writeb(&cts_data->cts_dev, 0x8000 + 344, value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err + ("Write firmware monitor enable register failed %d", + ret); + return -EIO; + } + } + + return count; +} + +static DEVICE_ATTR(monitor_mode, S_IRUGO, monitor_mode_show, + monitor_mode_store); + +static ssize_t auto_compensate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_dev->ops->is_cneg_enabled(&cts_data->cts_dev, &value); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read auto compensate enable register failed %d\n", + ret); + + return snprintf(buf, PAGE_SIZE, "Auto compensate: %s\n", + value ? "Enable" : "Disable"); +} + +static DEVICE_ATTR(auto_compensate, S_IRUGO, auto_compensate_show, NULL); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +static ssize_t driver_builtin_firmware_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, count = 0; + + count += snprintf(buf + count, PAGE_SIZE - count, + "Total %d builtin firmware:\n", + cts_get_num_driver_builtin_firmware()); + + for (i = 0; i < cts_get_num_driver_builtin_firmware(); i++) { + const struct cts_firmware *firmware = + cts_request_driver_builtin_firmware_by_index(i); + if (firmware) + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: hwid: %04x fwid: %04x ver: %04x size: %6zu desc: %s\n", + i, firmware->hwid, firmware->fwid, + FIRMWARE_VERSION(firmware), + firmware->size, firmware->name); + else + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: INVALID\n", i); + } + + return count; +} + +/* echo index/name [flash/sram] > driver_builtin_firmware */ +static ssize_t driver_builtin_firmware_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret, index = -1; + + parse_arg(buf, count); + + if (argc != 1 && argc != 2) { + cts_err("Invalid num args %d\n" + " echo index/name [flash/sram] > driver_builtin_firmware\n", + argc); + return -EFAULT; + } + + if (isdigit(*argv[0])) + index = simple_strtoul(argv[0], NULL, 0); + + if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err + ("Invalid location '%s', must be 'flash' or 'sram'", + argv[1]); + return -EINVAL; + } + } + + cts_info("Update driver builtin firmware '%s' to %s", + argv[1], to_flash ? "flash" : "sram"); + + if (index >= 0 && index < cts_get_num_driver_builtin_firmware()) + firmware = cts_request_driver_builtin_firmware_by_index(index); + else + firmware = cts_request_driver_builtin_firmware_by_name(argv[0]); + + if (firmware) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_start_device; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + } else { + cts_err("Firmware '%s' NOT found", argv[0]); + return -ENOENT; + } + + return count; + +err_start_device: + cts_start_device(cts_dev); + + return ret; +} + +static DEVICE_ATTR(driver_builtin_firmware, S_IWUSR | S_IRUGO, + driver_builtin_firmware_show, driver_builtin_firmware_store); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* echo filepath [flash/sram] > update_firmware_from_file */ +static ssize_t update_firmware_from_file_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret; + + parse_arg(buf, count); + + if (argc > 2) { + cts_err("Invalid num args %d\n" + " echo filepath [flash/sram] > update_from_file\n", + argc); + return -EFAULT; + } else if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err + ("Invalid location '%s', must be 'flash' or 'sram'", + argv[1]); + return -EINVAL; + } + } + + cts_info("Update firmware from file '%s'", argv[0]); + + firmware = cts_request_firmware_from_fs(cts_dev, argv[0]); + if (firmware == NULL) { + cts_err("Request firmware from file '%s' failed", argv[0]); + return -ENOENT; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_release_firmware; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_release_firmware; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + goto err_release_firmware; + } + + return count; + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} + +static DEVICE_ATTR(update_from_file, S_IWUSR, NULL, + update_firmware_from_file_store); +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +static ssize_t updating_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Updating: %s\n", + cts_data->cts_dev.rtdata.updating ? "Y" : "N"); +} + +static DEVICE_ATTR(updating, S_IRUGO, updating_show, NULL); + +static struct attribute *cts_dev_firmware_atts[] = { + &dev_attr_curr_version.attr, + &dev_attr_curr_ddi_version.attr, + &dev_attr_rows.attr, + &dev_attr_cols.attr, + &dev_attr_res_x.attr, + &dev_attr_res_y.attr, + &dev_attr_esd_protection.attr, + &dev_attr_monitor_mode.attr, + &dev_attr_auto_compensate.attr, +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + &dev_attr_driver_builtin_firmware.attr, +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +#ifdef CFG_CTS_FIRMWARE_IN_FS + &dev_attr_update_from_file.attr, +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + &dev_attr_updating.attr, + NULL +}; + +static const struct attribute_group cts_dev_firmware_attr_group = { + .name = "cts_firmware", + .attrs = cts_dev_firmware_atts, +}; + +static ssize_t flash_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_flash *flash; + + if (cts_dev->flash == NULL) { + bool program_mode; + bool enabled; + int ret; + + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", + ret); + + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", + ret); + } + + if (enabled) { + ret = cts_start_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", ret); + } + + if (cts_dev->flash == NULL) + return snprintf(buf, PAGE_SIZE, "Flash not found\n"); + } + + flash = cts_dev->flash; + return snprintf(buf, PAGE_SIZE, + "%s:\n" + " JEDEC ID : %06X\n" + " Page size : 0x%zx\n" + " Sector size: 0x%zx\n" + " Block size : 0x%zx\n" + " Total size : 0x%zx\n", + flash->name, flash->jedec_id, flash->page_size, + flash->sector_size, flash->block_size, + flash->total_size); +} + +static DEVICE_ATTR(info, S_IRUGO, flash_info_show, NULL); + +static ssize_t read_flash_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + bool program_mode; + bool enabled; + loff_t pos = 0; + + if (argc != 2 && argc != 3) + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid flash addr: %s\n", + argv[0]); + ret = kstrtou32(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + + cts_info("Read flash from 0x%06x size %u%s%s", + flash_addr, size, argc == 3 ? " to file " : "", + argc == 3 ? argv[2] : ""); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", ret); + goto err_free_data; + } + + ret = cts_read_flash(cts_dev, flash_addr, data, size); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, "Read flash data failed %d\n", + ret); + goto err_post_flash_operation; + } + + if (argc == 3) { + struct file *file; + + cts_info("Write flash data to file '%s'", argv[2]); + + file = filp_open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + count += + snprintf(buf, PAGE_SIZE, + "Open file '%s' failed %ld", argv[2], + PTR_ERR(file)); + goto err_post_flash_operation; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, data, size, &pos); +#else + ret = kernel_write(file, data, size, pos); +#endif + if (ret != size) + count += + snprintf(buf, PAGE_SIZE, + "Write flash data to file '%s' failed %d", + argv[2], ret); + + ret = filp_close(file, NULL); + if (ret) + count += + snprintf(buf, PAGE_SIZE, + "Close file '%s' failed %d", argv[2], ret); + } else { +#define PRINT_ROW_SIZE (16) + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = + min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += snprintf(buf + count, PAGE_SIZE - count - 1, + "%04x-%04x: ", flash_addr >> 16, + flash_addr & 0xFFFF); + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count - 1, + true); + count += strlen(buf + count); + buf[count++] = '\n'; + flash_addr += linelen; +#undef PRINT_ROW_SIZE + } + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + count += + snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", r); + } + + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) { + cts_unlock_device(cts_dev); + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", r); + } + } +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return (ret < 0 ? ret : count); +} + +/* echo start_addr size [filepath] > read */ +static ssize_t read_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(read, S_IWUSR | S_IRUGO, read_flash_show, read_flash_store); + +/* echo addr size > erase */ +static ssize_t erase_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size; + int ret; + bool program_mode; + bool enabled; + + parse_arg(buf, count); + + if (argc != 2) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) { + cts_err("Invalid flash addr: %s", argv[0]); + return -EINVAL; + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) { + cts_err("Invalid size: %s", argv[1]); + return -EINVAL; + } + + cts_info("Erase flash from 0x%06x size %u", flash_addr, size); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_err("Prepare flash operation failed %d", ret); + cts_unlock_device(cts_dev); + return ret; + } + + ret = cts_erase_flash(cts_dev, flash_addr, size); + if (ret) { + cts_err("Erase flash from 0x%06x size %u failed %d", + flash_addr, size, ret); + goto err_post_flash_operation; + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + cts_err("Enter normal mode failed %d", r); + } + + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + cts_unlock_device(cts_dev); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(erase, S_IWUSR, NULL, erase_flash_store); + +static struct attribute *cts_dev_flash_attrs[] = { + &dev_attr_info.attr, + &dev_attr_read.attr, + &dev_attr_erase.attr, + + NULL +}; + +static const struct attribute_group cts_dev_flash_attr_group = { + .name = "flash", + .attrs = cts_dev_flash_attrs, +}; + +static ssize_t open_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 threshold; + int ret; + + if (argc != 1) + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtou16(argv[0], 0, &threshold); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid threshold: %s\n", + argv[0]); + + cts_info("Open test, threshold = %u", threshold); + + ret = cts_open_test(cts_dev, threshold); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Open test FAILED %d, threshold = %u\n", ret, + threshold); + else + return snprintf(buf, PAGE_SIZE, + "Open test PASSED, threshold = %u\n", + threshold); +} + +/* echo threshod > open_test */ +static ssize_t open_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(open_test, S_IWUSR | S_IRUGO, open_test_show, + open_test_store); + +static ssize_t short_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 threshold; + int ret; + + if (argc != 1) + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtou16(argv[0], 0, &threshold); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid threshold: %s\n", + argv[0]); + + cts_info("Short test, threshold = %u", threshold); + + ret = cts_short_test(cts_dev, threshold); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Short test FAILED %d, threshold = %u\n", ret, + threshold); + else + return snprintf(buf, PAGE_SIZE, + "Short test PASSED, threshold = %u\n", + threshold); +} + +/* echo threshod > short_test */ +static ssize_t short_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(short_test, S_IWUSR | S_IRUGO, + short_test_show, short_test_store); + +static ssize_t testing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Testting: %s\n", + cts_data->cts_dev.rtdata.testing ? "Y" : "N"); +} + +static DEVICE_ATTR(testing, S_IRUGO, testing_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + int count; + + ret = cts_reset_test(cts_dev); + if (ret == 0) + count = snprintf(buf, PAGE_SIZE, "reset pin test sucessful\n"); + else + count = snprintf(buf, PAGE_SIZE, "reset pin test failed\n"); + + return count; +} + +static DEVICE_ATTR(reset_test, S_IRUGO, reset_test_show, NULL); +#endif + +static ssize_t compensate_cap_test_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 min_thres, max_thres; + int ret; + + if (argc != 2) + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > compensate_cap_test\n" + " 2. cat compensate_cap_test\n"); + + ret = kstrtou8(argv[0], 0, &min_thres); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid min_thres: %s\n", + argv[0]); + + ret = kstrtou8(argv[1], 0, &max_thres); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid max_thres: %s\n", + argv[1]); + + cts_info("Compensate cap test, min_thres = %u, max_thres = %u", + min_thres, max_thres); + + ret = cts_compensate_cap_test(cts_dev, min_thres, max_thres); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Compensate cap test FAILED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); + else + return snprintf(buf, PAGE_SIZE, + "Compensate cap test PASSED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); +} + +/* echo threshod > short_test */ +static ssize_t compensate_cap_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(compensate_cap_test, S_IWUSR | S_IRUGO, + compensate_cap_test_show, compensate_cap_test_store); + +static ssize_t rawdata_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 min_thres, max_thres; + int ret; + + if (argc != 2) + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > rawdata_test\n" + " 2. cat rawdata_test\n"); + + ret = kstrtou16(argv[0], 0, &min_thres); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid min_thres: %s\n", + argv[0]); + + ret = kstrtou16(argv[1], 0, &max_thres); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid max_thres: %s\n", + argv[1]); + + cts_info("Rawdata test, min_thres = %u, max_thres = %u", min_thres, + max_thres); + + ret = cts_rawdata_test(cts_dev, min_thres, max_thres); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Rawdata test FAILED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); + else + return snprintf(buf, PAGE_SIZE, + "Rawdata test PASSED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); +} + +/* echo threshod > short_test */ +static ssize_t rawdata_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(rawdata_test, S_IWUSR | S_IRUGO, + rawdata_test_show, rawdata_test_store); + +static struct attribute *cts_dev_test_atts[] = { + &dev_attr_open_test.attr, + &dev_attr_short_test.attr, + &dev_attr_testing.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_test.attr, +#endif + &dev_attr_compensate_cap_test.attr, + &dev_attr_rawdata_test.attr, + NULL +}; + +static const struct attribute_group cts_dev_test_attr_group = { + .name = "test", + .attrs = cts_dev_test_atts, +}; + +static ssize_t ic_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "IC Type : %s\n", + cts_data->cts_dev.hwdata->name); +} + +static DEVICE_ATTR(ic_type, S_IRUGO, ic_type_show, NULL); + +static ssize_t program_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Program mode: %s\n", + cts_data->cts_dev.rtdata.program_mode ? "Y" : "N"); +} + +static ssize_t program_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + if (*argv[0] == '1' || tolower(*argv[0]) == 'y') { + ret = cts_enter_program_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + return ret; + } + } else if (*argv[0] == '0' || tolower(*argv[0]) == 'n') { + ret = cts_enter_normal_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Exit program mode failed %d", ret); + return ret; + } + } else + cts_err("Invalid args"); + + return count; +} + +static DEVICE_ATTR(program_mode, S_IWUSR | S_IRUGO, + program_mode_show, program_mode_store); + +static ssize_t rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 *rawdata = NULL; + int ret, r, c, count = 0; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show rawdata"); + + rawdata = (u16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate memory for rawdata failed\n"); + + cts_lock_device(cts_dev); + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get rawdata failed"); + goto err_free_rawdata; + } + data_valid = true; + + if (data_valid) { + max = min = rawdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = + rawdata[r * cts_dev->hwdata->num_col + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Raw data MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows && count < PAGE_SIZE; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; + c < cts_dev->fwdata.cols && count < PAGE_SIZE; + c++) + count += + snprintf(buf + count, PAGE_SIZE - count - 1, + DATA_FORMAT_STR, + rawdata[r * + cts_dev->hwdata->num_col + + c]); + + buf[count++] = '\n'; + } + } + +err_free_rawdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; + +#undef RAWDATA_BUFFER_SIZE +} + +static DEVICE_ATTR(rawdata, S_IRUGO, rawdata_show, NULL); + +static ssize_t diffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *diffdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show diffdata"); + + diffdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (diffdata == NULL) { + cts_err("Allocate memory for diffdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = + cts_dev->ops->top_get_real_diff(cts_dev, (u8 *) diffdata, + DIFFDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get diffdata failed"); + goto err_free_diffdata; + } + data_valid = true; + + if (data_valid) { + max = min = diffdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val = + diffdata[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Diff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, + diffdata[r * + cts_dev->hwdata->num_col + + c]); + + buf[count++] = '\n'; + } + } + +err_free_diffdata: + cts_unlock_device(cts_dev); + kfree(diffdata); + + return data_valid ? count : ret; +} + +static DEVICE_ATTR(diffdata, S_IRUGO, diffdata_show, NULL); + +static ssize_t manualdiffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + int frame; + struct file *file = NULL; + int i; + loff_t pos = 0; + + cts_info("Show manualdiff"); + + if (argc != 1 && argc != 2) { + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo updatebase > manualdiff\n" + " 2. cat manualdiff\n" + " or\n" + " 1. echo updatebase > manualdiff\n" + " 2. echo frame file > manualdiff\n" + " 3. cat manualdiff\n"); + } + + if (argc == 2) { + ret = kstrtou32(argv[0], 0, &frame); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid frame num\n"); + file = filp_open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) + return snprintf(buf, PAGE_SIZE, "Can't open file:%s", + argv[1]); + } else + frame = 1; + + rawdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + filp_close(file, NULL); + return -ENOMEM; + } + + if (!manualdiff_base && (DIFFDATA_BUFFER_SIZE(cts_dev) > 0)) { + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + filp_close(file, NULL); + kfree(rawdata); + goto err_free_diffdata; + } + } + cts_lock_device(cts_dev); + + cts_info("frame %d, file:%s", frame, argv[1]); + + if (!manualdiff_base_updated) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, + (u8 *) manualdiff_base, + DIFFDATA_BUFFER_SIZE + (cts_dev)); + if (ret) + cts_err("Get raw data failed %d", ret); + } + for (i = 0; i < frame; i++) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + DIFFDATA_BUFFER_SIZE + (cts_dev)); + if (ret) { + cts_err("Get raw data failed %d", ret); + data_valid = false; + } else + data_valid = true; + msleep(5); + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + + rawdata[r * cts_dev->hwdata->num_col + + c] -= + manualdiff_base[r * + cts_dev->hwdata->num_col + c]; + val = + rawdata[r * + cts_dev->hwdata->num_col + + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = + sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + count += + snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Manualdiff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR " | ", min_r, min_c, min, + max_r, max_c, max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, + PAGE_SIZE - count, + DATA_FORMAT_STR, + rawdata[r * + cts_dev->hwdata->num_col + c]); + + buf[count++] = '\n'; + } + /*cts_info("manualdiffdata_show:%d, %d", i, frame); */ + if (argc == 2) { + pos = file->f_pos; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, buf, count, &pos); +#else + ret = kernel_write(file, buf, count, pos); +#endif + if (ret != count) + cts_err + ("Write data to file '%s' failed %d", + argv[1], ret); + + file->f_pos += count; + count = 0; + } + } + } + if (argc == 2) + filp_close(file, NULL); + +err_free_diffdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; +} + +static ssize_t manualdiffdata_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + + parse_arg(buf, count); + + cts_lock_device(cts_dev); + if (strncasecmp("updatebase", argv[0], 10) == 0) { + ret = cts_dev->ops->enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read raw data failed %d", ret); + goto err_manual_diff_store; + } + + if (!manualdiff_base && (DIFFDATA_BUFFER_SIZE(cts_dev) > 0)) { + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), + GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + goto err_manual_diff_store; + } + } + if (manualdiff_base != NULL) { + ret = cts_get_rawdata(cts_dev, manualdiff_base); + if (ret) + cts_err("Get raw data failed %d", ret); + manualdiff_base_updated = 1; + } + /* cts_info("update base successful"); */ + ret = cts_dev->ops->disable_get_rawdata(cts_dev); + if (ret) + cts_err("Disable read raw data failed %d", ret); + } +err_manual_diff_store: + cts_unlock_device(cts_dev); + return count; +} + +static DEVICE_ATTR(manualdiff, S_IRUSR | S_IWUSR, manualdiffdata_show, + manualdiffdata_store); + +static ssize_t jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u16 cnt; + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &cnt); + if (ret == 0) { + if (cnt > 2 && cnt < 10000) + jitter_test_frame = cnt; + } + + cts_info("jitter test frame: %d", jitter_test_frame); + return count; +} + +static ssize_t jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + s16 *rawdata_min = NULL; + s16 *rawdata_max = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = false; + int i; + + cts_info("Show jitter"); + cts_lock_device(cts_dev); + rawdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_jitter_show_exit; + } + rawdata_min = + (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_min == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata; + } + rawdata_max = + (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_max == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata_min; + } + + for (i = 0; i < DIFFDATA_BUFFER_SIZE(cts_dev) / 2; i++) { + rawdata_min[i] = 32767; + rawdata_max[i] = -32768; + } + + for (i = 0; i < jitter_test_frame; i++) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + DIFFDATA_BUFFER_SIZE + (cts_dev)); + if (ret) { + cts_err("Get raw data failed %d", ret); + msleep(5); + continue; + } + for (r = 0; r < cts_dev->hwdata->num_row; r++) { + for (c = 0; c < cts_dev->hwdata->num_col; c++) { + int index; + + index = r * cts_dev->hwdata->num_col + c; + if (rawdata_min[index] > rawdata[index]) + rawdata_min[index] = rawdata[index]; + else if (rawdata_max[index] < rawdata[index]) + rawdata_max[index] = rawdata[index]; + } + } + } + data_valid = true; + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + int index = r * cts_dev->hwdata->num_col + c; + + val = rawdata_max[index] - rawdata_min[index]; + rawdata[index] = val; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Jitter data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d, TOTAL FRAME=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average, jitter_test_frame); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, + rawdata[r * + cts_dev->hwdata->num_col + + c]); + + buf[count++] = '\n'; + } + } + + //err_free_rawdata_max: + kfree(rawdata_max); +err_free_rawdata_min: + kfree(rawdata_min); +err_free_rawdata: + kfree(rawdata); +err_jitter_show_exit: + cts_unlock_device(cts_dev); + return data_valid ? count : ret; +} + +static DEVICE_ATTR(jitter, S_IRUSR | S_IWUSR, jitter_show, jitter_store); + +static ssize_t compensate_cap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 *cap = NULL; + int ret; + ssize_t count = 0; + int r, c, min, max, max_r, max_c, min_r, min_c, sum, average; + + cts_info("Read '%s'", attr->attr.name); + + cap = kzalloc(cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, + GFP_KERNEL); + if (cap == NULL) + return scnprintf(buf, PAGE_SIZE, + "Allocate mem for compensate cap failed\n"); + + cts_lock_device(cts_dev); + ret = cts_dev->ops->top_get_cnegdata(cts_dev, cap, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col); + cts_unlock_device(cts_dev); + if (ret) { + kfree(cap); + return scnprintf(buf, PAGE_SIZE, + "Get compensate cap failed %d\n", ret); + } + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = cap[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "--------------------------------------"\ + "--------------------------------------\n" + " Compensatete Cap MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + "---+----------------------------------"\ + "--------------------------------------\n" + " |", min_r, min_c, min, max_r, max_c, max, + average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += scnprintf(buf + count, PAGE_SIZE - count, " %3u", c); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "\n" + "---+----------------------------------"\ + "--------------------------------------\n"); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += scnprintf(buf + count, PAGE_SIZE - count, "%2u |", r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(buf + count, PAGE_SIZE - count, + " %3u", + cap[r * cts_dev->hwdata->num_col + + c]); + } + buf[count++] = '\n'; + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "---+----------------------------------"\ + "--------------------------------------\n"); + + kfree(cap); + + return count; +} + +static DEVICE_ATTR(compensate_cap, S_IRUGO, compensate_cap_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read RESET-PIN"); + + return snprintf(buf, PAGE_SIZE, + "Reset pin: %d, status: %d\n", + cts_data->pdata->rst_gpio, + gpio_get_value(cts_data->pdata->rst_gpio)); +} + +static ssize_t reset_pin_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + cts_info("Write RESET-PIN"); + cts_info("Chip staus maybe changed"); + + cts_plat_set_reset(cts_dev->pdata, (buf[0] == '1') ? 1 : 0); + return count; +} + +static DEVICE_ATTR(reset_pin, S_IRUSR | S_IWUSR, reset_pin_show, + reset_pin_store); +#endif + +static ssize_t irq_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read IRQ-PIN"); + + return snprintf(buf, PAGE_SIZE, + "IRQ pin: %d, status: %d\n", + cts_data->pdata->int_gpio, + gpio_get_value(cts_data->pdata->int_gpio)); +} + +static DEVICE_ATTR(irq_pin, S_IRUGO, irq_pin_show, NULL); + +static ssize_t irq_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct irq_desc *desc; + + cts_info("Read IRQ-INFO"); + + desc = irq_to_desc(cts_data->pdata->irq); + if (desc == NULL) { + return snprintf(buf, PAGE_SIZE, + "IRQ: %d descriptor not found\n", + cts_data->pdata->irq); + } + + return snprintf(buf, PAGE_SIZE, + "IRQ num: %d, depth: %u, " + "count: %u, unhandled: %u, last unhandled eslape: %lu\n", + cts_data->pdata->irq, desc->depth, + desc->irq_count, desc->irqs_unhandled, + desc->last_unhandled); +} + +static DEVICE_ATTR(irq_info, S_IRUGO, irq_info_show, NULL); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +static ssize_t fw_log_redirect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "Fw log redirect is %s\n", + cts_is_fw_log_redirect(cts_dev) ? "enable" : "disable"); +} + +static ssize_t fw_log_redirect_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + if (enable) + cts_enable_fw_log_redirect(cts_dev); + else + cts_disable_fw_log_redirect(cts_dev); + + return count; +} + +static DEVICE_ATTR(fw_log_redirect, S_IRUSR | S_IWUSR, fw_log_redirect_show, + fw_log_redirect_store); +#endif + +static ssize_t debug_spi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "spi_speed=%d\n", + cts_dev->pdata->spi_speed); +} + +static ssize_t debug_spi_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 s = 0; + int ret = 0; + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &s); + if (ret) { + cts_err("Invalid spi speed: %s", argv[0]); + return -EINVAL; + } + + cts_dev->pdata->spi_speed = s; + + return count; +} + +static DEVICE_ATTR(debug_spi, S_IRUSR | S_IWUSR, debug_spi_show, + debug_spi_store); + +#ifdef CFG_CTS_GESTURE +static ssize_t gesture_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return sprintf(buf, "Gesture wakup is %s\n", + cts_is_gesture_wakeup_enabled(cts_dev) ? "enable" : + "disable"); +} + +static ssize_t gesture_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + + if (enable) + cts_enable_gesture_wakeup(cts_dev); + else + cts_disable_gesture_wakeup(cts_dev); + + return count; +} + +static DEVICE_ATTR(gesture_en, S_IRUSR | S_IWUSR, gesture_en_show, + gesture_en_store); +#endif + +static ssize_t int_data_types_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%#04x\n", + cts_data->cts_dev.fwdata.int_data_types); +} + +static ssize_t int_data_types_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u16 type = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &type); + if (ret) { + cts_err("Invalid int data types: %s", argv[0]); + return -EINVAL; + } + + ret = cts_set_int_data_types(&cts_data->cts_dev, type); + if (ret) + return -EIO; + return count; +} + +static DEVICE_ATTR(int_data_types, S_IWUSR | S_IRUGO, + int_data_types_show, int_data_types_store); + +static ssize_t int_data_method_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_data->cts_dev.fwdata.int_data_method); +} + +static ssize_t int_data_method_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u8 method = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou8(argv[0], 0, &method); + if (ret) { + cts_err("Invalid int data method: %s", argv[0]); + return -EINVAL; + } else if (method > INT_DATA_METHOD_POLLING) { + cts_err("Invalid int data method: %s", argv[0]); + return -EINVAL; + } + + ret = cts_set_int_data_method(&cts_data->cts_dev, method); + if (ret) + return -EIO; + return count; +} + +static DEVICE_ATTR(int_data_method, S_IWUSR | S_IRUGO, + int_data_method_show, int_data_method_store); + +static struct attribute *cts_dev_misc_atts[] = { + &dev_attr_ic_type.attr, + &dev_attr_program_mode.attr, + &dev_attr_rawdata.attr, + &dev_attr_diffdata.attr, + &dev_attr_manualdiff.attr, + &dev_attr_jitter.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin.attr, +#endif + &dev_attr_irq_pin.attr, + &dev_attr_irq_info.attr, +#ifdef CFG_CTS_FW_LOG_REDIRECT + &dev_attr_fw_log_redirect.attr, +#endif + &dev_attr_compensate_cap.attr, + &dev_attr_read_reg.attr, + &dev_attr_write_reg.attr, + &dev_attr_read_hw_reg.attr, + &dev_attr_write_hw_reg.attr, + &dev_attr_debug_spi.attr, +#ifdef CFG_CTS_GESTURE + &dev_attr_gesture_en.attr, +#endif /* CFG_CTS_GESTURE */ + &dev_attr_int_data_types.attr, + &dev_attr_int_data_method.attr, + NULL +}; + +static const struct attribute_group cts_dev_misc_attr_group = { + .name = "misc", + .attrs = cts_dev_misc_atts, +}; + +#ifdef CFG_CTS_FW_UPDATE_SYS +static struct attribute *cts_dev_fw_up_atts[] = { + /** + * &dev_attr_drv_irq.attr, + * &dev_attr_reset.attr, + */ + &dev_attr_buildid.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static const struct attribute_group cts_dev_fw_up_attr_group = { + .attrs = cts_dev_fw_up_atts, +}; +#endif + +static const struct attribute_group *cts_dev_attr_groups[] = { + &cts_dev_firmware_attr_group, +#ifdef CFG_CTS_FW_UPDATE_SYS + &cts_dev_fw_up_attr_group, +#endif + &cts_dev_flash_attr_group, + &cts_dev_test_attr_group, + &cts_dev_misc_attr_group, + NULL +}; + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + cts_err("Read 'path' with chipone_ts_data NULL"); + return (ssize_t) 0; + } +#ifdef CONFIG_CTS_I2C_HOST + path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); +#else + path = kobject_get_path(&data->spi_client->dev.kobj, GFP_KERNEL); +#endif + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "chipone"); +} + +/* Attribute: vendor (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *ts = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->cts_dev.hwdata->name, + "Build ID: ", ts->cts_dev.fwdata.version + ? ts->cts_dev.fwdata.version + : ts->pdata->build_id, + "Config ID: ", ts->cts_dev.fwdata.ddi_version + ? ts->cts_dev.fwdata.ddi_version + : ts->pdata->config_id); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* + *Description: + * Chipone touchscreen FW function class. file node + * initial function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + *******************************************************/ +static int cts_fw_class_init(void *_data, bool create) +{ + struct chipone_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + dev_t devno; + + cts_info("%s touchscreen class files", create ? "Add" : "Remove"); + + if (create) { + if (data->cts_dev.hwdata->name != NULL) + error = + alloc_chrdev_region(&devno, 0, 1, + data->cts_dev.hwdata->name); + else + error = + alloc_chrdev_region(&devno, 0, 1, + CFG_CTS_CHIP_NAME); + + if (error) { + cts_info("Alloc input devno failed %d", error); + return error; + } + + cts_info("Create class 'touchscreen'"); + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + cts_err("Create class 'touchscreen' failed %ld", + PTR_ERR(touchscreen_class)); + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + if (data->cts_dev.hwdata->name != NULL) { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, data, "%s", + data->cts_dev.hwdata->name); + cts_info("Create device for IC: %s", + data->cts_dev.hwdata->name); + } else { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, data, "%s", + CFG_CTS_CHIP_NAME); + cts_info("Create device '" CFG_CTS_CHIP_NAME "'"); + } + if (IS_ERR(ts_class_dev)) { + cts_err("Create device '" CFG_CTS_CHIP_NAME + "'failed %ld", PTR_ERR(ts_class_dev)); + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + cts_info("Create attr files"); + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info(" Create attr file '%s'", attrs[i].attr.name); + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) { + cts_err("Create attr file '%s' failed %d", + attrs[i].attr.name, error); + break; + } + } + + if (error) + goto device_destroy; + else + cts_info("Create /sys/class/touchscreen/ Succeeded"); + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info("Remove device file '%s'", attrs[i].attr.name); + device_remove_file(ts_class_dev, &attrs[i]); + } + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, devno); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + cts_err("Creating touchscreen class failed %d", error); + + return -ENODEV; +} + +int cts_sysfs_add_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret, i; + + cts_info("Add device attr groups"); + + /*Low version kernel NOT support sysfs_create_groups() */ + for (i = 0; cts_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, cts_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) + sysfs_remove_group(&dev->kobj, + cts_dev_attr_groups[i]); + break; + } + } + + if (ret) { + cts_err("Add device attr failed %d", ret); + return ret; + } + + if (DIFFDATA_BUFFER_SIZE(cts_dev) > 0) { + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + return -ENOMEM; + } + } + + ret = sysfs_create_link(NULL, &dev->kobj, "chipone-tddi"); + if (ret) + cts_err("Create sysfs link error:%d", ret); + + ret = cts_fw_class_init(cts_data, true); + if (ret) { + cts_err("Create touchscreen class failed. ret=%d\n", ret); + return ret; + } + + return 0; +} + +void cts_sysfs_remove_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int i; + + cts_info("Remove device attr groups"); + + if (manualdiff_base != NULL) { + kfree(manualdiff_base); + manualdiff_base = NULL; + } + + sysfs_remove_link(NULL, "chipone-tddi"); + /*Low version kernel NOT support sysfs_remove_groups() */ + for (i = 0; cts_dev_attr_groups[i]; i++) + sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); + + cts_fw_class_init(cts_data, false); +} + +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR + +#endif /* CONFIG_CTS_SYSFS */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h new file mode 100644 index 000000000000..ea9ec24267be --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_SYSFS_H +#define CTS_SYSFS_H + +#include "cts_config.h" + +struct device; + +extern u16 speed; + +#ifdef CONFIG_CTS_SYSFS +extern int cts_sysfs_add_device(struct device *dev); +extern void cts_sysfs_remove_device(struct device *dev); +#else /* CONFIG_CTS_SYSFS */ +static inline int cts_sysfs_add_device(struct device *dev) +{ + return -ENOTSUPP; +} + +static inline void cts_sysfs_remove_device(struct device *dev) +{ +} +#endif /* CONFIG_CTS_SYSFS */ + +#endif /* CTS_SYSFS_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c new file mode 100644 index 000000000000..8a94b8578a7c --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c @@ -0,0 +1,1651 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#define LOG_TAG "TCS" + +#include "cts_config.h" +#include "cts_firmware.h" +#include "cts_platform.h" + +#define _CTS_TCS_C_ +#include "cts_tcs.h" +#undef _CTS_TCS_C_ + +#define TEST_RESULT_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +#define DIFFDATA_BUFFER_SIZE(cts_dev) \ + ((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + 2) * 2) + +/* raw touch info without data */ +#define TOUCH_INFO_SIZ (112) +/* tcs reply tail: (errcode + cmd + crc) */ +#define TCS_REPLY_TAIL_SIZ (sizeof(u8) + sizeof(u16) + sizeof(u16)) + +#define INT_DATA_INFO_SIZ (64) +#define INT_DATA_TYPE_LEN_SIZ (4) + +static u8 dump_flag = 1; + +void dump_spi(const char *prefix, u8 *data, size_t datalen) +{ + u8 str[1024]; + int offset = 0; + int i; + + if (!dump_flag) + return; + + offset += snprintf(str + offset, sizeof(str) - offset, "%s", prefix); + for (i = 0; i < datalen; i++) { + offset += + snprintf(str + offset, sizeof(str) - offset, " %02x", + data[i]); + } + cts_err("%s", str); +} + +static int cts_tcs_spi_xtrans(const struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + int ret; + struct spi_message msg; + struct spi_transfer xfer[2]; + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, + cts_dev); + u16 crc16_recv, crc16_calc; + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0; + xfer[0].delay_usecs = 0; + xfer[0].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[0].tx_buf = tx; + xfer[0].rx_buf = NULL; + xfer[0].len = txlen; + xfer[0].bits_per_word = 8; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[0] failed: %d", ret); + return ret; + } + udelay(100); + + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + xfer[1].cs_change = 0; + xfer[1].delay_usecs = 0; + xfer[1].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[1].tx_buf = NULL; + xfer[1].rx_buf = rx; + xfer[1].len = rxlen; + xfer[1].bits_per_word = 8; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[1] failed: %d", ret); + return ret; + } + + crc16_recv = (rx[rxlen - 1] << 8) | rx[rxlen - 2]; + crc16_calc = cts_crc16(rx, rxlen - 2); + if (crc16_recv != crc16_calc) { + cts_err("xtrans crc error: recv %04x != %04x calc", crc16_recv, + crc16_calc); + return -EIO; + } + udelay(100); + + return 0; +} + +static int cts_tcs_spi_xtrans_1_cs(const struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + int ret; + struct spi_message msg; + struct spi_transfer xfer[1]; + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, + cts_dev); + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0; + xfer[0].delay_usecs = 0; + xfer[0].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[0].tx_buf = tx; + xfer[0].rx_buf = rx; + xfer[0].len = txlen > rxlen ? txlen : rxlen; + xfer[0].bits_per_word = 8; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[0] failed: %d", ret); + return ret; + } + + return 0; +} + +static int cts_tcs_spi_read_pack(u8 *tx, TcsCmdValue_t *tcv, u16 rdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_RD_ADDR; + txhdr->cmd = + (tcv->baseFlag << 15) | + (tcv->isRead << 14) | (tcv->classID << 8) | (tcv->cmdID << 0); + txhdr->datlen = rdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + return packlen; +} + +static int cts_tcs_spi_write_pack(u8 *tx, TcsCmdValue_t *tcv, + u8 *wdata, u16 wdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_WR_ADDR; + txhdr->cmd = + (tcv->baseFlag << 15) | + (tcv->isWrite << 13) | (tcv->classID << 8) | (tcv->cmdID << 0); + txhdr->datlen = wdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + if (wdatalen > 0) { + memcpy(tx + sizeof(tcs_tx_head), wdata, wdatalen); + crc16 = cts_crc16(wdata, wdatalen); + *(tx + sizeof(tcs_tx_head) + wdatalen) = ((crc16 >> 0) & 0xFF); + *(tx + sizeof(tcs_tx_head) + wdatalen + 1) = + ((crc16 >> 8) & 0xFF); + packlen += wdatalen + sizeof(crc16); + } + + return packlen; +} + +int cts_tcs_spi_read(const struct cts_device *cts_dev, + enum TcsCmdIndex cmdIdx, u8 *rdata, size_t rdatalen) +{ + // static u8 tx[INT_DATA_MAX_SIZ]; + // static u8 rx[INT_DATA_MAX_SIZ]; + int txlen; + + txlen = + cts_tcs_spi_read_pack(cts_dev->tx, TcsCmdValue + cmdIdx, rdatalen); + /* dump_spi(">> ", tx, txlen); */ + cts_tcs_spi_xtrans(cts_dev, cts_dev->tx, txlen, cts_dev->rx, + rdatalen + sizeof(tcs_rx_tail)); + /* dump_spi("<< ", rx, rdatalen + sizeof(tcs_rx_tail)); */ + /** + * if (rx[rdatalen] != 0) { + * cts_err("spi_sync failed: err=%d", rx[rdatalen]); + * } + */ + memcpy(rdata, cts_dev->rx, rdatalen); + + return 0; +} + +int cts_tcs_spi_read_1_cs(const struct cts_device *cts_dev, + enum TcsCmdIndex cmdIdx, u8 *rdata, size_t rdatalen) +{ + // static u8 tx[INT_DATA_MAX_SIZ]; + // static u8 rx[INT_DATA_MAX_SIZ]; + int txlen; + u16 crc16_recv, crc16_calc; + + txlen = + cts_tcs_spi_read_pack(cts_dev->tx, TcsCmdValue + cmdIdx, rdatalen); + /* dump_spi(">> ", tx, txlen); */ + cts_tcs_spi_xtrans_1_cs(cts_dev, cts_dev->tx, txlen, cts_dev->rx, + rdatalen); + /* dump_spi("<< ", rx, rdatalen + sizeof(tcs_rx_tail)); */ + /** + * if (rx[rdatalen] != 0) { + * cts_err("spi_sync failed: err=%d", rx[rdatalen]); + * } + */ + crc16_recv = + (cts_dev->rx[rdatalen - 1] << 8) | cts_dev->rx[rdatalen - 2]; + crc16_calc = cts_crc16(cts_dev->rx, rdatalen - 2); + if (crc16_recv != crc16_calc) { + cts_err("Get int data crc error: recv %04x != %04x calc", + crc16_recv, crc16_calc); + return -EIO; + } + + memcpy(rdata, cts_dev->rx, rdatalen); + + return 0; +} + +int cts_tcs_spi_write(const struct cts_device *cts_dev, + enum TcsCmdIndex cmdIdx, u8 *wdata, size_t wdatalen) +{ + // static u8 tx[2048]; + // static u8 rx[2048]; + int txlen; + + txlen = + cts_tcs_spi_write_pack(cts_dev->tx, TcsCmdValue + cmdIdx, wdata, + wdatalen); + /* dump_spi(">> ", tx, txlen); */ + cts_tcs_spi_xtrans(cts_dev, cts_dev->tx, txlen, cts_dev->rx, + sizeof(tcs_rx_tail)); + /* dump_spi("<< ", rx, wdatalen + sizeof(tcs_rx_tail)); */ + return cts_dev->rx[0]; +} + +int cts_tcs_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver) +{ + int ret = -1; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_FW_VER_RO, + buf, sizeof(buf)); + if (!ret) { + *fwver = buf[0] | (buf[1] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_lib_ver(const struct cts_device *cts_dev, u16 *libver) +{ + int ret = -1; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_FW_VER_RO, buf, + sizeof(buf)); + if (!ret) { + *libver = buf[2] | (buf[3] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_fw_id(const struct cts_device *cts_dev, u16 *fwid) +{ + int ret = -1; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_CHIP_FW_ID_RO, + buf, sizeof(buf)); + if (!ret) { + *fwid = buf[0] | (buf[1] << 8); + return 0; + } + + return ret; +} + +int cts_tcs_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DDI_CODE_VER_RO, + buf, sizeof(buf)); + if (!ret) { + *ddiver = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_res_x(const struct cts_device *cts_dev, u16 *res_x) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *res_x = buf[0] | (buf[1] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_res_y(const struct cts_device *cts_dev, u16 *res_y) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *res_y = buf[2] | (buf[3] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_rows(const struct cts_device *cts_dev, u8 *rows) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *rows = buf[5]; + return 0; + } + return ret; +} + +int cts_tcs_get_cols(const struct cts_device *cts_dev, u8 *cols) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *cols = buf[4]; + return 0; + } + return ret; +} + +int cts_tcs_get_flip_x(const struct cts_device *cts_dev, bool *flip_x) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_COORD_FLIP_X_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *flip_x = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_flip_y(const struct cts_device *cts_dev, bool *flip_y) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_COORD_FLIP_Y_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *flip_y = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_swap_axes(const struct cts_device *cts_dev, bool *swap_axes) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_COORD_SWAP_AXES_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *swap_axes = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_INT_MODE_RW, + buf, sizeof(buf)); + if (!ret) { + *int_mode = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time) +{ + int ret = -1; + u8 buf[2]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_INT_KEEP_TIME_RW, + buf, sizeof(buf)); + if (!ret) { + *int_keep_time = (buf[0] | (buf[1] << 8)); + return 0; + } + return ret; + +} + +int cts_tcs_get_esd_method(const struct cts_device *cts_dev, u8 *esd_method) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_DDI_ESD_OPTIONS_RW, + buf, sizeof(buf)); + if (!ret) { + *esd_method = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection) +{ + int ret = -1; + u8 buf[4]; + + buf[0] = 0x01; + buf[1] = 0x56; + buf[2] = 0x81; + buf[3] = 0x00; + + ret = cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_spi_read(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + esd_protection, sizeof(u8)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_get_data_ready_flag(const struct cts_device *cts_dev, u8 *ready) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW, + buf, sizeof(buf)); + if (!ret) { + *ready = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + int ret; + u8 ready = 0; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW, + &ready, sizeof(ready)); + return ret; +} + +int cts_tcs_enable_get_rawdata(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x01; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_disable_get_rawdata(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x00; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_enable_get_cneg(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x01; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_disable_get_cneg(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x00; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_is_cneg_ready(const struct cts_device *cts_dev, u8 *ready) +{ + int ret; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_CNEG_RDY_FLAG_RW, + buf, sizeof(buf)); + if (!ret) { + *ready = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_quit_guesture_mode(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x00; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_MNT_FORCE_EXIT_MNT_WO, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_get_rawdata(const struct cts_device *cts_dev, u8 *buf) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_RAW_RO, buf, + RAWDATA_BUFFER_SIZE(cts_dev)); + + return ret; +} + +int cts_tcs_get_diffdata(const struct cts_device *cts_dev, u8 *buf) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_DIFF_RO, buf, + DIFFDATA_BUFFER_SIZE(cts_dev)); + + return ret; +} + +int cts_tcs_get_basedata(const struct cts_device *cts_dev, u8 *buf) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_BASE_RO, buf, + DIFFDATA_BUFFER_SIZE(cts_dev)); + + return ret; +} + +int cts_tcs_get_cneg(const struct cts_device *cts_dev, u8 *buf, size_t size) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_CNEG_RO, buf, size); + + return ret; + +} + +int cts_tcs_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 buf[4]; + + buf[0] = 1; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_READ_START_RO, + regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 *buf; + + buf = kmalloc(size + 6, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = ((size >> 0) & 0xFF); + buf[1] = ((size >> 8) & 0xFF); + buf[2] = ((addr >> 0) & 0xFF); + buf[3] = ((addr >> 8) & 0xFF); + buf[4] = ((addr >> 16) & 0xFF); + buf[5] = 0x00; + memcpy(buf + 6, regbuf, size); + + ret = cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; + +} + +int cts_tcs_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 buf[2]; + + buf[0] = 2; + buf[1] = addr; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_READ_START_RO, + regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 *buf; + + buf = kmalloc(size + 6, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = ((size >> 0) & 0xFF); + buf[1] = ((size >> 8) & 0xFF); + buf[2] = addr; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + memcpy(buf + 6, regbuf, size); + + ret = cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; + +} + +static int cts_tcs_reg_read_pack(u8 *tx, uint16_t cmd, u16 rdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_RD_ADDR; + txhdr->cmd = cmd; + txhdr->datlen = rdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + return packlen; +} + +int cts_tcs_read_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen) +{ + static u8 tx[2048]; + int txlen = cts_tcs_reg_read_pack(tx, cmd, rlen); + + cts_tcs_spi_xtrans(cts_dev, tx, txlen, rbuf, + rlen + sizeof(tcs_rx_tail)); + + return 0; +} + +static int cts_tcs_reg_write_pack(u8 *tx, uint16_t cmd, + u8 *wdata, u16 wdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_WR_ADDR; + txhdr->cmd = cmd; + txhdr->datlen = wdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + if (wdatalen > 0) { + memcpy(tx + sizeof(tcs_tx_head), wdata, wdatalen); + crc16 = cts_crc16(wdata, wdatalen); + *(tx + sizeof(tcs_tx_head) + wdatalen) = ((crc16 >> 0) & 0xFF); + *(tx + sizeof(tcs_tx_head) + wdatalen + 1) = + ((crc16 >> 8) & 0xFF); + packlen += wdatalen + sizeof(crc16); + } + + return packlen; +} + +int cts_tcs_write_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen) +{ + static u8 rx[2048]; + static u8 tx[2048]; + int txlen = cts_tcs_reg_write_pack(tx, cmd, wbuf, wlen); + + cts_tcs_spi_xtrans(cts_dev, tx, txlen + wlen, rx, + wlen + sizeof(tcs_rx_tail)); + return 0; +} + +int cts_tcs_read_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_READ_RO, regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 buf[4]; + + buf[0] = 0x01; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, regbuf, + sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_calc_int_data_size(struct cts_device *cts_dev) +{ +#define INT_DATA_TYPE_U8_SIZ \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * sizeof(u8)) +#define INT_DATA_TYPE_U16_SIZ \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * sizeof(u16)) + + int data_size = TOUCH_INFO_SIZ + TCS_REPLY_TAIL_SIZ; + u16 data_types = cts_dev->fwdata.int_data_types; + u8 data_method = cts_dev->fwdata.int_data_method; + + if (data_method == INT_DATA_METHOD_NONE) { + cts_dev->fwdata.int_data_size = data_size; + return 0; + } + + cts_info("data_method:%d, data_type:%d", data_method, data_types); + if (data_types != INT_DATA_TYPE_NONE) { + data_size += INT_DATA_INFO_SIZ; + if ((data_types & INT_DATA_TYPE_RAWDATA)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_MANUAL_DIFF)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_REAL_DIFF)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_NOISE_DIFF)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_BASEDATA)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_CNEGDATA)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U8_SIZ); + } + } + + cts_info("data_size: %d", data_size); + cts_dev->fwdata.int_data_size = data_size; + return 0; +} + +static int cts_tcs_polling_data(const struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + int ret = -1; + int retries = 100; + u8 ready = 0; + size_t data_size = cts_dev->fwdata.int_data_size; + + do { + ret = cts_tcs_get_data_ready_flag(cts_dev, &ready); + if (!ret && ready) + break; + mdelay(10); + } while (!ready && --retries); + cts_info("get data rdy, retries left %d", retries); + + if (!ready) { + cts_err("time out wait for data rdy"); + return -EIO; + } + + retries = 3; + do { + ret = + cts_tcs_spi_read_1_cs(cts_dev, + TP_STD_CMD_GET_DATA_BY_POLLING_RO, + cts_dev->rtdata.int_data, data_size); + mdelay(1); + if (cts_tcs_clr_data_ready_flag(cts_dev)) + cts_err("Clear data ready flag failed"); + } while (ret && --retries); + + return ret; +} + +static int cts_tcs_polling_test_data(const struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + int ret = -EIO, retries = 5; + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + + while (retries--) { + ret = cts_polling_data(cts_dev, buf, size); + if (!ret) { + memcpy(buf, cts_dev->rtdata.int_data + offset, size); + break; + } + } + + return ret; +} + +static int polling_data(struct cts_device *cts_dev, u8 *buf, size_t size, + enum int_data_type type) +{ + u8 old_int_data_method; + u16 old_int_data_types; + int ret = -EIO, retries = 5; + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + + old_int_data_types = cts_dev->fwdata.int_data_types; + old_int_data_method = cts_dev->fwdata.int_data_method; + + cts_set_int_data_types(cts_dev, type); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + while (retries--) { + ret = cts_polling_data(cts_dev, buf, size); + if (!ret) { + memcpy(buf, cts_dev->rtdata.int_data + offset, size); + break; + } + } + + cts_set_int_data_types(cts_dev, old_int_data_types); + cts_set_int_data_method(cts_dev, old_int_data_method); + + return ret; +} + +int cts_tcs_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_RAWDATA); +} + +int cts_tcs_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_MANUAL_DIFF); +} + +int cts_tcs_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_REAL_DIFF); +} + +int cts_tcs_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_NOISE_DIFF); +} + +int cts_tcs_top_get_basedata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_BASEDATA); +} + +int cts_tcs_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_CNEGDATA); +} + +static int cts_tcs_tp_reset(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[] = { 0x01, 0xfe }; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_RESET_WO, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_reset_device(const struct cts_device *cts_dev) +{ + bool use_soft_tp_reset; + int ret; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) + use_soft_tp_reset = false; + else if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922) + use_soft_tp_reset = true; + else + use_soft_tp_reset = true; + + cts_info("use_soft_tp_reset=%d", use_soft_tp_reset); + if (use_soft_tp_reset) { + /* normal */ + cts_info("tp reset in normal mode"); + ret = cts_tcs_tp_reset(cts_dev); + if (!ret) { + mdelay(40); + return 0; + } + /* program */ + cts_info("tp reset in program mode"); + ret = + cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_RESET_CONFIG, + 0xfd); + if (!ret) { + mdelay(40); + return 0; + } + return ret; + } else + return cts_plat_reset_device(cts_dev->pdata); +} + +int cts_tcs_get_touchinfo(const struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + int ret = -1; + size_t size = cts_dev->fwdata.int_data_size; + + memset(touch_info, 0, sizeof(*touch_info)); + + if (cts_dev->fwdata.int_data_method == INT_DATA_METHOD_NONE) { + dump_flag = 0; + ret = + cts_tcs_spi_read_1_cs(cts_dev, + TP_STD_CMD_TP_DATA_COORDINATES_RO, + cts_dev->rtdata.int_data, size); + dump_flag = 1; + if (!ret) { + memcpy(touch_info, cts_dev->rtdata.int_data, + sizeof(*touch_info)); + } + + return ret; + } else if (cts_dev->fwdata.int_data_method == INT_DATA_METHOD_HOST || + cts_dev->fwdata.int_data_method == INT_DATA_METHOD_POLLING) { + dump_flag = 0; + ret = + cts_tcs_spi_read_1_cs(cts_dev, + TP_STD_CMD_TP_DATA_COORDINATES_RO, + cts_dev->rtdata.int_data, size); + dump_flag = 1; + mdelay(1); + if (cts_tcs_clr_data_ready_flag(cts_dev)) + cts_err("Clear data ready flag failed"); + if (!ret) + memcpy(touch_info, cts_dev->rtdata.int_data, + sizeof(*touch_info)); + return ret; + } + + return ret; +} + +int cts_tcs_get_workmode(const struct cts_device *cts_dev, u8 *workmode) +{ + int ret = -1; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_CURRENT_WORKMODE_RO, + buf, sizeof(buf)); + if (!ret) { + *workmode = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_workmode(const struct cts_device *cts_dev, u8 workmode) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_WORK_MODE_RW, + &workmode, sizeof(workmode)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_openshort_mode(const struct cts_device *cts_dev, u8 mode) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_MODE_SEL_RW, &mode, + sizeof(mode)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_tx_vol(const struct cts_device *cts_dev, u8 txvol) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_VSTIM_LVL_RW, &txvol, + sizeof(txvol)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_enabled_get_rawdata(const struct cts_device *cts_dev, + u8 *enabled) +{ + int ret; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, + buf, sizeof(buf)); + if (!ret) { + *enabled = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_short_test_type(const struct cts_device *cts_dev, u8 short_type) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_SHORT_SEL_RW, + &short_type, sizeof(short_type)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_openshort_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + int ret = -1; + u8 buf[1]; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_EN_RW, buf, + sizeof(buf)); + if (!ret) { + *enabled = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_openshort_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_esd_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_DDI_ESD_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + int ret = -1; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_CNEG_EN_RW, enabled, + sizeof(*enabled)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + int ret = -1; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_MNT_EN_RW, enabled, + sizeof(*enabled)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_cneg_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_CNEG_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_mnt_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_MNT_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_display_on(const struct cts_device *cts_dev, u8 *display_on) +{ + int ret; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *display_on = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode) +{ + int ret; + u8 buf[0]; + + buf[0] = pwr_mode; + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_PWR_STATE_RW, buf, 1); + return ret; +} + +int cts_tcs_set_display_on(const struct cts_device *cts_dev, u8 display_on) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + &display_on, sizeof(display_on)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ + struct spi_message msg; + struct spi_transfer xfer[2]; + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, + cts_dev); + u16 crc; + int ret; + u8 tx[ALIGN(128, 4)]; + u8 rx[ALIGN(128, 4)]; + u8 tx1[ALIGN(128, 4)]; + + /* Write add */ + tx[0] = CTS_DEV_NORMAL_MODE_SPIADDR; + tx[1] = 0x01; + tx[2] = 0x21; + tx[3] = 0x04; + tx[4] = 0x00; + crc = cts_crc16(tx, 5); + tx[5] = ((crc >> 0) & 0xFF); + tx[6] = ((crc >> 8) & 0xFF); + tx[7] = 0x01; + tx[8] = ((addr >> 0) & 0xFF); + tx[9] = ((addr >> 8) & 0xFF); + tx[10] = ((addr >> 16) & 0xFF); + crc = cts_crc16(&tx[7], 4); + tx[11] = ((crc >> 0) & 0xFF); + tx[12] = ((crc >> 8) & 0xFF); + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0, + xfer[0].delay_usecs = 0, + xfer[0].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[0].tx_buf = tx, xfer[0].rx_buf = rx, xfer[0].len = 13, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[0].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret) + cts_err("spi sync 1 failed %d", ret); + udelay(100); + + memset(rx, 0, sizeof(rx)); + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + xfer[1].cs_change = 0, + xfer[1].delay_usecs = 0, + xfer[1].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[1].tx_buf = tx1, xfer[1].rx_buf = rx, xfer[1].len = 5, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[1].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret) + cts_err("spi sync 2 failed %d", ret); + udelay(100); + + memset(tx, 0, sizeof(tx)); + memset(tx1, 0, sizeof(tx1)); + memset(rx, 0, sizeof(rx)); + /* Read data */ + tx[0] = CTS_DEV_NORMAL_MODE_SPIADDR | 0x01; + tx[1] = 0x02; + tx[2] = 0x41; + tx[3] = ((len >> 0) & 0xFF); + tx[4] = ((len >> 8) & 0xFF); + crc = cts_crc16(tx, 5); + tx[5] = ((crc >> 0) & 0xFF); + tx[6] = ((crc >> 8) & 0xFF); + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0, + xfer[0].delay_usecs = 0, + xfer[0].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[0].tx_buf = tx, xfer[0].rx_buf = rx, xfer[0].len = 7, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[0].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync 3 failed %d", ret); + udelay(100); + + memset(rx, 0, sizeof(rx)); + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + xfer[1].cs_change = 0, + xfer[1].delay_usecs = 0, + xfer[1].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[1].tx_buf = tx1, xfer[1].rx_buf = rx, xfer[1].len = len + 5, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[1].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync 4 failed %d", ret); + udelay(100); + + memcpy(dst, rx, len); + + return ret; +} + +int cts_tcs_init_int_data(struct cts_device *cts_dev) +{ + if (!cts_dev->rtdata.int_data) { + cts_dev->rtdata.int_data = + kmalloc(INT_DATA_MAX_SIZ, GFP_KERNEL); + if (!cts_dev->rtdata.int_data) { + cts_err("Malloc for int_data failed"); + return -ENOMEM; + } + return 0; + } + + return 0; +} + +int cts_tcs_deinit_int_data(struct cts_device *cts_dev) +{ + if (cts_dev->rtdata.int_data) + cts_dev->rtdata.int_data = NULL; + + return 0; +} + +int cts_tcs_get_has_int_data(const struct cts_device *cts_dev, + bool *has_int_data) +{ + int ret = -1; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO, buf, + sizeof(buf)); + if (!ret) { + *has_int_data = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_int_data_types(const struct cts_device *cts_dev, + u16 *int_data_types) +{ + int ret = -1; + u8 buf[4]; + + ret = + cts_tcs_spi_read(cts_dev, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, buf, + sizeof(buf)); + if (!ret) { + *int_data_types = buf[0] | (buf[1] << 8); + return 0; + } + return ret; +} + +int cts_tcs_set_int_data_types(const struct cts_device *cts_dev, + u16 int_data_types) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + (u8 *) &int_data_types, sizeof(int_data_types)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_get_int_data_method(const struct cts_device *cts_dev, + u8 *int_data_method) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *int_data_method = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_set_int_data_method(const struct cts_device *cts_dev, + u8 int_data_method) +{ + int ret = -1; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW, + &int_data_method, sizeof(int_data_method)); + if (!ret) + return 0; + + return ret; +} + +struct cts_dev_ops tcs_ops = { + .get_fw_ver = cts_tcs_get_fw_ver, + .get_lib_ver = cts_tcs_get_lib_ver, + .get_ddi_ver = cts_tcs_get_ddi_ver, + .get_res_x = cts_tcs_get_res_x, + .get_res_y = cts_tcs_get_res_y, + .get_rows = cts_tcs_get_rows, + .get_cols = cts_tcs_get_cols, + .get_flip_x = cts_tcs_get_flip_x, + .get_flip_y = cts_tcs_get_flip_y, + .get_swap_axes = cts_tcs_get_swap_axes, + .get_int_mode = cts_tcs_get_int_mode, + .get_int_keep_time = cts_tcs_get_int_keep_time, + .get_esd_method = cts_tcs_get_esd_method, + .get_touchinfo = cts_tcs_get_touchinfo, + .get_esd_protection = cts_tcs_get_esd_protection, + .get_data_ready_flag = cts_tcs_get_data_ready_flag, + .clr_data_ready_flag = cts_tcs_clr_data_ready_flag, + .enable_get_rawdata = cts_tcs_enable_get_rawdata, + .is_enabled_get_rawdata = cts_tcs_is_enabled_get_rawdata, + .disable_get_rawdata = cts_tcs_disable_get_rawdata, + .enable_get_cneg = cts_tcs_enable_get_cneg, + .disable_get_cneg = cts_tcs_disable_get_cneg, + .is_cneg_ready = cts_tcs_is_cneg_ready, + .quit_guesture_mode = cts_tcs_quit_guesture_mode, + .get_rawdata = cts_tcs_get_rawdata, + .get_diffdata = cts_tcs_get_diffdata, + .get_basedata = cts_tcs_get_basedata, + .get_cneg = cts_tcs_get_cneg, + .read_hw_reg = cts_tcs_read_hw_reg, + .write_hw_reg = cts_tcs_write_hw_reg, + .read_ddi_reg = cts_tcs_read_ddi_reg, + .write_ddi_reg = cts_tcs_write_ddi_reg, + .read_fw_reg = cts_tcs_read_fw_reg, + .write_fw_reg = cts_tcs_write_fw_reg, + .read_reg = cts_tcs_read_reg, + .write_reg = cts_tcs_write_reg, + .get_fw_id = cts_tcs_get_fw_id, + .get_workmode = cts_tcs_get_workmode, + .set_workmode = cts_tcs_set_workmode, + .set_openshort_mode = cts_tcs_set_openshort_mode, + .set_tx_vol = cts_tcs_set_tx_vol, + .set_short_test_type = cts_tcs_set_short_test_type, + .set_openshort_enable = cts_tcs_set_openshort_enable, + .is_openshort_enabled = cts_tcs_is_openshort_enabled, + .set_esd_enable = cts_tcs_set_esd_enable, + .set_cneg_enable = cts_tcs_set_cneg_enable, + .set_mnt_enable = cts_tcs_set_mnt_enable, + .is_display_on = cts_tcs_is_display_on, + .set_display_on = cts_tcs_set_display_on, + .is_cneg_enabled = cts_tcs_is_cneg_enabled, + .is_mnt_enabled = cts_tcs_is_mnt_enabled, + .set_pwr_mode = cts_tcs_set_pwr_mode, + .read_sram_normal_mode = cts_tcs_read_sram_normal_mode, + .init_int_data = cts_tcs_init_int_data, + .deinit_int_data = cts_tcs_deinit_int_data, + .get_has_int_data = cts_tcs_get_has_int_data, + .get_int_data_types = cts_tcs_get_int_data_types, + .set_int_data_types = cts_tcs_set_int_data_types, + .get_int_data_method = cts_tcs_get_int_data_method, + .set_int_data_method = cts_tcs_set_int_data_method, + .calc_int_data_size = cts_tcs_calc_int_data_size, + .polling_data = cts_tcs_polling_data, + .polling_test_data = cts_tcs_polling_test_data, + + .top_get_rawdata = cts_tcs_top_get_rawdata, + .top_get_manual_diff = cts_tcs_top_get_manual_diff, + .top_get_real_diff = cts_tcs_top_get_real_diff, + .top_get_noise_diff = cts_tcs_top_get_noise_diff, + .top_get_basedata = cts_tcs_top_get_basedata, + .top_get_cnegdata = cts_tcs_top_get_cnegdata, + + .reset_device = cts_tcs_reset_device, +}; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h new file mode 100644 index 000000000000..f369626da411 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _CTS_TCS_H_ +#define _CTS_TCS_H_ + +#include "cts_config.h" + +#define TCS_RD_ADDR 0xF1 +#define TCS_WR_ADDR 0xF0 + +#define INT_DATA_MAX_SIZ 8192 + +#pragma pack(1) +typedef struct { + u8 addr; + u16 cmd; + u16 datlen; + u16 crc16; +} tcs_tx_head; + +typedef struct { + u8 ecode; + u16 cmd; + u16 crc16; +} tcs_rx_tail; + +typedef struct { + u8 baseFlag; + u8 classID; + u8 cmdID; + u8 isRead; + u8 isWrite; + u8 isData; +} TcsCmdValue_t; +#pragma pack(0) + +int cts_tcs_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver); +int cts_tcs_get_lib_ver(const struct cts_device *cts_dev, u16 *libver); +int cts_tcs_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver); +int cts_tcs_get_res_x(const struct cts_device *cts_dev, u16 *res_x); +int cts_tcs_get_res_y(const struct cts_device *cts_dev, u16 *res_y); +int cts_tcs_get_rows(const struct cts_device *cts_dev, u8 *rows); +int cts_tcs_get_cols(const struct cts_device *cts_dev, u8 *cols); +int cts_tcs_get_flip_x(const struct cts_device *cts_dev, bool *flip_x); +int cts_tcs_get_flip_y(const struct cts_device *cts_dev, bool *flip_y); +int cts_tcs_get_swap_axes(const struct cts_device *cts_dev, bool *swap_axes); + +int cts_tcs_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode); +int cts_tcs_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time); +int cts_tcs_get_esd_method(const struct cts_device *cts_dev, u8 *esd_method); + +int cts_tcs_get_touchinfo(const struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info); +int cts_tcs_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection); + +int cts_tcs_get_data_ready_flag(const struct cts_device *cts_dev, u8 *ready); +int cts_tcs_clr_data_ready_flag(const struct cts_device *cts_dev); +int cts_tcs_enable_get_rawdata(const struct cts_device *cts_dev); +int cts_tcs_is_enabled_get_rawdata(const struct cts_device *cts_dev, + u8 *enabled); +int cts_tcs_disable_get_rawdata(const struct cts_device *cts_dev); +int cts_tcs_enable_get_cneg(const struct cts_device *cts_dev); +int cts_tcs_disable_get_cneg(const struct cts_device *cts_dev); +int cts_tcs_is_cneg_ready(const struct cts_device *cts_dev, u8 *ready); + +int cts_tcs_quit_guesture_mode(const struct cts_device *cts_dev); + +int cts_tcs_get_rawdata(const struct cts_device *cts_dev, u8 *buf); +int cts_tcs_get_diffdata(const struct cts_device *cts_dev, u8 *buf); +int cts_tcs_get_basedata(const struct cts_device *cts_dev, u8 *buf); +int cts_tcs_get_cneg(const struct cts_device *cts_dev, u8 *buf, size_t size); + +int cts_tcs_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_read_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_read_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen); +int cts_tcs_write_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen); + +int cts_tcs_get_fw_id(const struct cts_device *cts_dev, u16 *fwid); +int cts_tcs_get_workmode(const struct cts_device *cts_dev, u8 *workmode); +int cts_tcs_set_workmode(const struct cts_device *cts_dev, u8 workmode); +int cts_tcs_set_openshort_mode(const struct cts_device *cts_dev, u8 mode); +int cts_tcs_set_tx_vol(const struct cts_device *cts_dev, u8 txvol); + +int cts_tcs_set_short_test_type(const struct cts_device *cts_dev, + u8 short_type); +int cts_tcs_set_openshort_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_openshort_enabled(const struct cts_device *cts_dev, + u8 *enabled); + +int cts_tcs_set_esd_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_cneg_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_mnt_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_display_on(const struct cts_device *cts_dev, u8 *display_on); +int cts_tcs_set_display_on(const struct cts_device *cts_dev, u8 display_on); +int cts_tcs_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode); + +int cts_tcs_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +enum TcsCmdIndex { + TP_STD_CMD_INFO_CHIP_FW_ID_RO, + TP_STD_CMD_INFO_FW_VER_RO, + TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + TP_STD_CMD_TP_DATA_READ_START_RO, + TP_STD_CMD_TP_DATA_COORDINATES_RO, + TP_STD_CMD_TP_DATA_RAW_RO, + TP_STD_CMD_TP_DATA_DIFF_RO, + TP_STD_CMD_TP_DATA_BASE_RO, + TP_STD_CMD_TP_DATA_CNEG_RO, + TP_STD_CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO, + TP_STD_CMD_TP_DATA_WR_REG_RAM_BATCH_WO, + TP_STD_CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO, + TP_STD_CMD_GET_DATA_BY_POLLING_RO, + + TP_STD_CMD_SYS_STS_READ_RO, + TP_STD_CMD_SYS_STS_WORK_MODE_RW, + TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW, + TP_STD_CMD_SYS_STS_PWR_STATE_RW, + TP_STD_CMD_SYS_STS_DDI_CODE_VER_RO, + TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, + TP_STD_CMD_SYS_STS_VSTIM_LVL_RW, + TP_STD_CMD_SYS_STS_CNEG_RDY_FLAG_RW, + TP_STD_CMD_SYS_STS_RESET_WO, + TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW, + TP_STD_CMD_SYS_STS_INT_MODE_RW, + TP_STD_CMD_SYS_STS_INT_KEEP_TIME_RW, + TP_STD_CMD_SYS_STS_CURRENT_WORKMODE_RO, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + + TP_STD_CMD_MNT_EN_RW, + TP_STD_CMD_MNT_FORCE_EXIT_MNT_WO, + + TP_STD_CMD_DDI_ESD_EN_RW, + TP_STD_CMD_DDI_ESD_OPTIONS_RW, + + TP_STD_CMD_CNEG_EN_RW, + + TP_STD_CMD_COORD_FLIP_X_EN_RW, + TP_STD_CMD_COORD_FLIP_Y_EN_RW, + TP_STD_CMD_COORD_SWAP_AXES_EN_RW, + + TP_STD_CMD_OPENSHORT_EN_RW, + TP_STD_CMD_OPENSHORT_MODE_SEL_RW, + TP_STD_CMD_OPENSHORT_SHORT_SEL_RW, + TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, +}; + +#ifdef _CTS_TCS_C_ +static TcsCmdValue_t TcsCmdValue[] = { +/*--------------------------------------------------- + *baseFlage, classID, cmdID, isRead, isWrite, isData + *--------------------------------------------------- + */ + { 0, 0, 3, 1, 0, 0 }, /* TP_STD_CMD_INFO_CHIP_FW_ID_RO */ + { 0, 0, 5, 1, 0, 0 }, /* TP_STD_CMD_INFO_FW_VER_RO */ + { 0, 0, 7, 1, 0, 0 }, /* TP_STD_CMD_INFO_TOUCH_XY_INFO_RO */ + + { 0, 1, 1, 1, 1, 0 }, /* TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW */ + { 0, 1, 2, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_READ_START_RO */ + { 0, 1, 3, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_COORDINATES_RO */ + { 0, 1, 4, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_RAW_RO */ + { 0, 1, 5, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_DIFF_RO */ + { 0, 1, 6, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_BASE_RO */ + { 0, 1, 10, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_CNEG_RO */ + { 0, 1, 20, 0, 1, 1 }, /* TP_STD_CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO */ + { 0, 1, 21, 0, 1, 1 }, /* TP_STD_CMD_TP_DATA_WR_REG_RAM_BATCH_WO */ + { 0, 1, 22, 0, 1, 1 }, /* TP_STD_CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO */ + { 0, 1, 35, 1, 0, 0 }, /* TP_STD_CMD_GET_DATA_BY_POLLING_RO */ + + { 0, 2, 0, 1, 0, 1 }, /* TP_STD_CMD_SYS_STS_READ_RO *//*CHECK!!*/ + { 0, 2, 1, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_WORK_MODE_RW */ + { 0, 2, 3, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW */ + { 0, 2, 4, 1, 1, 1 }, /* TP_STD_CMD_SYS_STS_PWR_STATE_RW */ + { 0, 2, 6, 1, 0, 0 }, /* TP_STD_CMD_SYS_STS_DDI_CODE_VER_RO */ + { 0, 2, 7, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW */ + { 0, 2, 8, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_VSTIM_LVL_RW */ + { 0, 2, 17, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_CNEG_RDY_FLAG_RW */ + { 0, 2, 22, 0, 1, 0 }, /* TP_STD_CMD_SYS_STS_RESET_WO */ + { 0, 2, 25, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW */ + { 0, 2, 35, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_INT_MODE_RW */ + { 0, 2, 36, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_INT_KEEP_TIME_RW */ + { 0, 2, 51, 1, 0, 0 }, /* TP_STD_CMD_SYS_STS_CURRENT_WORKMODE_RO */ + { 0, 2, 63, 1, 0, 0 }, /* TP_STD_CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO */ + { 0, 2, 64, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW */ + { 0, 2, 65, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW */ + + { 0, 4, 1, 1, 1, 0 }, /* TP_STD_CMD_MNT_EN_RW */ + { 0, 4, 3, 0, 1, 0 }, /* TP_STD_CMD_MNT_FORCE_EXIT_MNT_WO */ + + { 0, 5, 1, 1, 1, 0 }, /* TP_STD_CMD_DDI_ESD_EN_RW */ + { 0, 5, 2, 1, 1, 0 }, /* TP_STD_CMD_DDI_ESD_OPTIONS_RW */ + + { 0, 6, 1, 1, 1, 0 }, /* TP_STD_CMD_CNEG_EN_RW */ + + { 0, 7, 2, 1, 1, 0 }, /* TP_STD_CMD_COORD_FLIP_X_EN_RW */ + { 0, 7, 3, 1, 1, 0 }, /* TP_STD_CMD_COORD_FLIP_Y_EN_RW */ + { 0, 7, 4, 1, 1, 0 }, /* TP_STD_CMD_COORD_SWAP_AXES_EN_RW */ + + { 0, 11, 1, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_EN_RW */ + { 0, 11, 2, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_MODE_SEL_RW */ + { 0, 11, 3, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_SHORT_SEL_RW */ + { 0, 11, 4, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW */ +}; +#endif /* _CTS_TCS_C_ */ + +extern struct cts_dev_ops tcs_ops; + +#endif /* _CTS_TCS_H_ */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c new file mode 100644 index 000000000000..8f3bf20f41dc --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c @@ -0,0 +1,1350 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Test" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" + +/* ICNL9911/S/C */ +#define CTS_FIRMWARE_WORK_MODE_NORMAL (0x00) +#define CTS_FIRMWARE_WORK_MODE_FACTORY (0x01) +#define CTS_FIRMWARE_WORK_MODE_CONFIG (0x02) +#define CTS_FIRMWARE_WORK_MODE_TEST (0x03) + +/* ICNL9916/9922 */ +#define CTS_FIRMWARE_WORK_MODE_CFG (0x01) +#define CTS_FIRMWARE_WORK_MODE_OPEN_SHORT (0x02) + +#define CTS_TEST_SHORT (0x01) +#define CTS_TEST_OPEN (0x02) + +#define CTS_SHORT_TEST_UNDEFINED (0x00) +#define CTS_SHORT_TEST_BETWEEN_COLS (0x01) +#define CTS_SHORT_TEST_BETWEEN_ROWS (0x02) +#define CTS_SHORT_TEST_BETWEEN_GND (0x03) + +#define TEST_RESULT_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +int disable_fw_esd_protection(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 342, 1); +} + +int disable_fw_monitor_mode(struct cts_device *cts_dev) +{ + int ret; + u8 value; + + ret = cts_fw_reg_readb(cts_dev, 0x8000 + 344, &value); + if (ret) + return ret; + + if (value & BIT(0)) + return cts_fw_reg_writeb(cts_dev, 0x8000 + 344, + value & (~BIT(0))); + + return 0; +} + +int disable_fw_auto_compensate(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 276, 1); +} + +int set_fw_work_mode(const struct cts_device *cts_dev, u8 mode) +{ + int ret, retries; + u8 pwr_mode; + + cts_info("Set firmware work mode to %u", mode); + + ret = cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); + if (ret) { + cts_err("Write firmware work mode register failed %d", ret); + return ret; + } + + ret = cts_fw_reg_readb(cts_dev, 0x05, &pwr_mode); + if (ret) { + cts_err("Read firmware power mode register failed %d", ret); + return ret; + } + + if (pwr_mode == 1) { + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + return ret; + } + + msleep(50); + } + + retries = 0; + do { + u8 sys_busy, curr_mode; + + msleep(10); + + ret = cts_fw_reg_readb(cts_dev, 0x01, &sys_busy); + if (ret) { + cts_err("Read firmware system busy register failed %d", + ret); + return ret; + } + if (sys_busy) + continue; + + ret = cts_fw_reg_readb(cts_dev, 0x3F, &curr_mode); + if (ret) { + cts_err("Read firmware current work mode failed %d", + ret); + return ret; + } + + if (curr_mode == mode || curr_mode == 0xFF) + break; + } while (retries++ < 1000); + + return (retries >= 1000 ? -ETIMEDOUT : 0); +} + +int set_display_state(const struct cts_device *cts_dev, bool active) +{ + int ret; + u8 access_flag; + + cts_info("Set display state to %s", active ? "ACTIVE" : "SLEEP"); + + ret = cts_hw_reg_readb_relaxed(cts_dev, 0x3002C, &access_flag); + if (ret) { + cts_err("Read display access flag failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3002C, access_flag | 0x01); + if (ret) { + cts_err("Write display access flag %02x failed %d", access_flag, + ret); + return ret; + } + + if (active) { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C044, 0x55); + if (ret) { + cts_err("Write DCS-CMD11 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A4, 0x55); + if (ret) { + cts_err("Write DCS-CMD29 fail"); + return ret; + } + + msleep(100); + } else { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A0, 0x55); + if (ret) { + cts_err("Write DCS-CMD28 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C040, 0x55); + if (ret) { + cts_err("Write DCS-CMD10 fail"); + return ret; + } + + msleep(100); + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3002C, access_flag); + if (ret) { + cts_err("Restore display access flag %02x failed %d", + access_flag, ret); + return ret; + } + + return 0; +} + +static int wait_test_complete(struct cts_device *cts_dev, int skip_frames) +{ + int ret, i, j; + + cts_info("Wait test complete skip %d frames", skip_frames); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + return 0; + } + + for (i = 0; i < (skip_frames + 1); i++) { + u8 ready; + + for (j = 0; j < 100; j++) { + mdelay(10); + + ready = 0; + ret = + cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + return ret; + } + + if (ready) + break; + } + + if (ready == 0) + return -ETIMEDOUT; + + if (i < skip_frames) { + ret = cts_dev->ops->clr_data_ready_flag(cts_dev); + if (ret) { + cts_err("Clr data ready flag failed %d", ret); + return ret; + } + } + } + + return 0; +} + +struct cts_fw_short_test_param { + u8 type; + u32 col_pattern[2]; + u32 row_pattern[2]; +}; + +bool set_short_test_type(struct cts_device *cts_dev, u8 type) +{ + static struct cts_fw_short_test_param param = { + .type = CTS_SHORT_TEST_BETWEEN_COLS, + .col_pattern = { 0, 0 }, + .row_pattern = { 0, 0 } + }; + int i, ret; + + cts_info("Set short test type to %u", type); + + param.type = type; + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = + cts_fw_reg_writesb(cts_dev, 0x5000, ¶m, sizeof(param)); + if (ret) { + cts_err("Set short test type to %u failed %d", type, + ret); + continue; + } + ret = cts_fw_reg_readb(cts_dev, 0x5000, &type_readback); + if (ret) { + cts_err("Get short test type failed %d", ret); + continue; + } + if (type == type_readback) + return 0; + else { + cts_err("Set test type %u != readback %u", type, + type_readback); + continue; + } + } + + return ret; +} + +static void dump_test_data(struct cts_device *cts_dev, + const char *desc, const u16 *data) +{ +#define SPLIT_LINE_STR \ + "--------------------------------------------------------"\ + "--------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%-5u " +#define DATA_FORMAT_STR "%-5u " + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[128]; + int count = 0; + + max = min = data[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = data[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + cts_info(SPLIT_LINE_STR); + count += snprintf(line_buf + count, sizeof(line_buf) - count, + " %s test data MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + desc, min_r, min_c, min, max_r, max_c, max, average); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + + count = 0; + count += snprintf(line_buf + count, sizeof(line_buf) - count, " | "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += snprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += snprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, + data[r * cts_dev->hwdata->num_col + c]); + } + cts_info("%s", line_buf); + } + cts_info(SPLIT_LINE_STR); + +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +/* Return number of failed nodes */ +static int validate_test_result(struct cts_device *cts_dev, + const char *desc, u16 *test_result, u16 min, + u16 max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + if (test_result[r * cts_dev->hwdata->num_col + c] < min + || test_result[r * cts_dev->hwdata->num_col + c] > + max) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, + r, c, + test_result[r * + cts_dev->hwdata->num_col + + c]); + } + } + } + + if (failed_cnt) + cts_info(SPLIT_LINE_STR); + + cts_info("%s test %d node total failed", desc, failed_cnt); + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +static int test_short_to_gnd(struct cts_device *cts_dev, + u8 feature_ver, u16 *test_result, u16 threshold) +{ + int ret; + + cts_info("Test short to GND"); + + ret = + cts_dev->ops->set_short_test_type(cts_dev, + CTS_SHORT_TEST_BETWEEN_GND); + if (ret) { + cts_err("Set short test type to SHORT_TO_GND failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + return ret; + } + + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + return ret; + } + + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = + cts_polling_test_data(cts_dev, (u8 *) test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "GND-short", test_result); + + return validate_test_result(cts_dev, "GND-short", test_result, + threshold, USHRT_MAX); +} + +#define SHORT_COLS_TEST_LOOP 3 + +static int test_short_between_cols(struct cts_device *cts_dev, + u16 *test_result, u16 threshold, + bool skip_first_frame) +{ + int ret; + int loop = 0; + + cts_info("Test short between columns"); + + ret = + cts_dev->ops->set_short_test_type(cts_dev, + CTS_SHORT_TEST_BETWEEN_COLS); + if (ret) { + cts_err("Set short test type to BETWEEN_COLS failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + return ret; + } + + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + return ret; + } + + for (loop = 0; loop < SHORT_COLS_TEST_LOOP; loop++) { + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = + cts_polling_test_data(cts_dev, test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "Col-short", test_result); + + ret = validate_test_result(cts_dev, "Col-short", test_result, + threshold, USHRT_MAX); + if (ret != 0) + break; + } + + return ret; +} + +#define SHORT_ROWS_TEST_LOOP 6 +static int test_short_between_rows(struct cts_device *cts_dev, + u16 *test_result, u16 threshold) +{ + int ret; + int loopcnt; + + cts_info("Test short between rows"); + + for (loopcnt = 0; loopcnt < SHORT_ROWS_TEST_LOOP; loopcnt++) { + ret = + cts_dev->ops->set_short_test_type(cts_dev, + CTS_SHORT_TEST_BETWEEN_ROWS); + if (ret) { + cts_err("Set short test type to BETWEEN_ROWS failed %d", + ret); + return ret; + } + + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + return ret; + } + + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err + ("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + return ret; + } + + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = + cts_polling_test_data(cts_dev, test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "Row-short", test_result); + + ret = validate_test_result(cts_dev, "Row-short", test_result, + threshold, USHRT_MAX); + if (ret != 0) + break; + } + + return ret; +} + +static int wait_fw_to_normal_work(struct cts_device *cts_dev) +{ + int i = 0; + int ret; + + cts_info("Wait fw to normal work"); + + do { + u8 work_mode; + + ret = cts_dev->ops->get_workmode(cts_dev, &work_mode); + if (ret) { + cts_err("Get fw curr work mode failed %d", work_mode); + continue; + } else { + if (work_mode == CTS_FIRMWARE_WORK_MODE_NORMAL) + return 0; + } + + mdelay(10); + } while (++i < 100); + + return ret ? ret : -ETIMEDOUT; +} + +static int prepare_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Prepare test"); + + cts_reset_device(cts_dev); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_esd_enable(cts_dev, 0x00); + if (ret) { + cts_err("Disable firmware ESD protection failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_mnt_enable(cts_dev, 0x00); + if (ret) { + cts_err("Disable firmware monitor mode failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_cneg_enable(cts_dev, 0x00); + if (ret) { + cts_err("Disable firmware auto compensate failed %d", ret); + return ret; + } + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_CFG); + else + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_CONFIG); + + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", + ret); + return ret; + } + + cts_dev->rtdata.testing = true; + + return 0; +} + +static void post_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Post test"); + + cts_reset_device(cts_dev); + + ret = + cts_dev->ops->set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_NORMAL); + if (ret) + cts_err("Set firmware work mode to WORK_MODE_NORMAL failed %d", + ret); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) + cts_err("Wait fw to normal work failed %d", ret); + + cts_dev->rtdata.testing = false; +} + +/* Return 0 success + * negative value while error occurs + * positive value means how many nodes fail + */ +int cts_short_test(struct cts_device *cts_dev, u16 threshold) +{ + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + u8 feature_ver = 0; + u8 old_int_data_method = cts_dev->fwdata.int_data_method; + u16 old_int_data_types = cts_dev->fwdata.int_data_types; + + cts_info("Start short test, threshold = %u", threshold); + + test_result = + (u16 *) kmalloc(TEST_RESULT_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate test result buffer failed"); + return -ENOMEM; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_dev->ops->is_display_on(cts_dev, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = cts_dev->ops->set_display_on(cts_dev, 0x00); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + ret = test_short_to_gnd(cts_dev, feature_ver, test_result, threshold); + if (ret) { + cts_err("Short to GND test failed %d", ret); + goto err_recovery_display_state; + } + + ret = test_short_between_cols(cts_dev, test_result, threshold, + need_display_on == 0); + if (ret) { + cts_err("Short between columns test failed %d", ret); + goto err_recovery_display_state; + } + + ret = test_short_between_rows(cts_dev, test_result, threshold); + if (ret) { + cts_err("Short between rows test failed %d", ret); + goto err_recovery_display_state; + } + +err_recovery_display_state: + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + + if (recovery_display_state) { + int r = cts_dev->ops->set_display_on(cts_dev, 0x01); + + if (r) + cts_err("Set display state to ACTIVE failed %d", r); + } +err_free_test_result: + kfree(test_result); + post_test(cts_dev); + + cts_unlock_device(cts_dev); + + cts_info("Short test %s", ret ? "FAILED" : "PASSED"); + + cts_start_device(cts_dev); + + return ret; +} + +/* Return 0 success + * negative value while error occurs + * positive value means how many nodes fail + */ +int cts_open_test(struct cts_device *cts_dev, u16 threshold) +{ + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + u8 old_int_data_method = cts_dev->fwdata.int_data_method; + u16 old_int_data_types = cts_dev->fwdata.int_data_types; + + cts_info("Start open test, threshold = %u", threshold); + + test_result = + (u16 *) kmalloc(TEST_RESULT_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate memory for test result faild"); + return -ENOMEM; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_dev->ops->is_display_on(cts_dev, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = cts_dev->ops->set_display_on(cts_dev, 0x00); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911)) { + ret = cts_send_command(cts_dev, CTS_CMD_RECOVERY_TX_VOL); + if (ret) { + cts_err("Recovery tx voltage failed %d", ret); + goto err_recovery_display_state; + } + } + + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_OPEN); + if (ret) { + cts_err("Set test type to OPEN_TEST failed %d", ret); + goto err_recovery_display_state; + } + + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + goto err_recovery_display_state; + } + + ret = wait_test_complete(cts_dev, 20); + if (ret) { + cts_err("Wait test complete failed %d", ret); + goto err_recovery_display_state; + } + + ret = + cts_polling_test_data(cts_dev, test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto err_recovery_display_state; + } + + dump_test_data(cts_dev, "Open-circuit", test_result); + + ret = validate_test_result(cts_dev, "Open-circuit", test_result, + threshold, USHRT_MAX); + +err_recovery_display_state: + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + + if (recovery_display_state) { + int r = cts_dev->ops->set_display_on(cts_dev, 0x01); + + if (r) + cts_err("Set display state to ACTIVE failed %d", r); + } +err_free_test_result: + kfree(test_result); + post_test(cts_dev); + + cts_unlock_device(cts_dev); + cts_info("Open test %s", ret ? "FAILED" : "PASSED"); + + cts_start_device(cts_dev); + + return ret; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_reset_test(struct cts_device *cts_dev) +{ + int ret; + int val = 0; + + cts_info("Reset Pin"); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + + cts_plat_set_reset(cts_dev->pdata, 0); + mdelay(50); +#ifdef CONFIG_CTS_I2C_HOST + /* Check whether device is in normal mode */ + if (!cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) { +#else + if (!cts_plat_is_normal_mode(cts_dev->pdata)) +#endif /* CONFIG_CTS_I2C_HOST */ + val++; + else + cts_err("Device is alive while reset is low"); + cts_plat_set_reset(cts_dev->pdata, 1); + mdelay(50); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) + cts_err("Wait fw to normal work failed %d", ret); +#ifdef CONFIG_CTS_I2C_HOST + /* Check whether device is in normal mode */ + if (cts_plat_is_i2c_online(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) { +#else + if (cts_plat_is_normal_mode(cts_dev->pdata)) +#endif /* CONFIG_CTS_I2C_HOST */ + val++; + else + cts_err("Device is offline while reset is high"); +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + cts_unlock_device(cts_dev); + + ret = cts_start_device(cts_dev); + if (ret) + cts_err("Start device failed %d", ret); + + cts_info("Reset-Pin test %s", val == 2 ? "PASS" : "FAIL"); + if (val == 2) { + if (!cts_dev->rtdata.program_mode) + cts_set_normal_addr(cts_dev); + return 0; + } + + return -EFAULT; +} +#endif + +int cts_test_int_pin(struct cts_device *cts_dev) +{ + int ret; + + cts_info("INT-pin"); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + + ret = cts_send_command(cts_dev, CTS_CMD_WRTITE_INT_HIGH); + if (ret) { + cts_err("Send command WRTITE_INT_HIGH failed %d", ret); + goto unlock_device; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) == 0) { + cts_err("INT pin state != HIGH"); + ret = -EFAULT; + goto unlock_device; + } + + ret = cts_send_command(cts_dev, CTS_CMD_WRTITE_INT_LOW); + if (ret) { + cts_err("Send command WRTITE_INT_LOW failed %d", ret); + goto unlock_device; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) != 0) { + cts_err("INT pin state != LOW"); + ret = -EFAULT; + goto unlock_device; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RELASE_INT_TEST); + if (ret) { + cts_err("Send command RELASE_INT_TEST failed %d", ret); + ret = 0; + } + mdelay(10); + +unlock_device: + cts_unlock_device(cts_dev); + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + ret = 0; + } + + cts_info("Int-Pin test %s", ret == 0 ? "PASS" : "FAIL"); + return ret; +} + +void dump_compensate_cap(struct cts_device *cts_dev, u8 *cap) +{ +#define SPLIT_LINE_STR \ + "--------------------------------------"\ + "---------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%3u " +#define DATA_FORMAT_STR "%4d" + + int r, c; + char line_buf[128]; + int count = 0; + + cts_info(SPLIT_LINE_STR); + count += snprintf(line_buf + count, sizeof(line_buf) - count, " "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += snprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += snprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, + cap[r * cts_dev->hwdata->num_col + c]); + } + cts_info("%s", line_buf); + } + cts_info(SPLIT_LINE_STR); +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +static int validate_compensate_cap(struct cts_device *cts_dev, + const char *desc, u8 *test_result, u16 min, + u16 max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + if (test_result[r * cts_dev->hwdata->num_col + c] < min + || test_result[r * cts_dev->hwdata->num_col + c] > + max) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, + r, c, + test_result[r * + cts_dev->hwdata->num_col + + c]); + } + } + } + + if (failed_cnt) + cts_info("%s", SPLIT_LINE_STR); + + cts_info("%s test %d node total failed", desc, failed_cnt); + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +int cts_compensate_cap_test(struct cts_device *cts_dev, u8 min_thres, + u8 max_thres) +{ + u8 *cap = NULL; + int ret = 0; + + cts_info("Compensate cap, threshold [%u, %u]", min_thres, max_thres); + + cap = kzalloc(cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, + GFP_KERNEL); + if (cap == NULL) { + cts_err("Allocate mem for cap failed %d", ret); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = cts_dev->ops->top_get_cnegdata(cts_dev, cap, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Get compensate cap failed %d", ret); + goto free_cap; + } + + dump_compensate_cap(cts_dev, cap); + ret = validate_compensate_cap(cts_dev, "Compensate-Cap", cap, + min_thres, max_thres); + +free_cap: + kfree(cap); + + cts_info("Compensate-Cap test %s", ret == 0 ? "PASS" : "FAIL"); + return ret; +} + +int cts_rawdata_test(struct cts_device *cts_dev, u16 min, u16 max) +{ + int ret; + bool data_valid = false; + u16 *rawdata; + int i; + + cts_info("Rawdata, threshold[%u, %u]", min, max); + + rawdata = (u16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + data_valid = false; + goto unlock_device; + } + data_valid = true; + goto unlock_device; + } + + for (i = 0; i < 5; i++) { + int r; + u8 val; + + r = cts_dev->ops->enable_get_rawdata(cts_dev); + if (r) { + cts_err("Enable get ts data failed %d", r); + continue; + } + mdelay(1); + r = cts_dev->ops->is_enabled_get_rawdata(cts_dev, &val); + if (r) { + cts_err("Read enable get ts data failed %d", r); + continue; + } + if (val != 0) + break; + } + + if (i >= 5) { + cts_err("Enable read tsdata failed"); + ret = -EIO; + goto unlock_device; + } + + msleep(50); + for (i = 0; i < 3; i++) { + ret = cts_get_rawdata(cts_dev, rawdata); + if (ret) { + cts_err("Get rawdata failed %d", ret); + continue; + } else { + data_valid = true; + break; + } + mdelay(30); + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; + } + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->disable_get_rawdata(cts_dev); + + if (r) { + cts_err("Disable get rawdata failed %d", r); + continue; + } else + break; + } + +unlock_device: + cts_unlock_device(cts_dev); + + if (data_valid) { + dump_test_data(cts_dev, "Rawdata", rawdata); + ret = + validate_test_result(cts_dev, "Rawdata", rawdata, min, max); + } + kfree(rawdata); + + cts_info("Rawdata test %s", + (data_valid && (ret == 0)) ? "PASS" : "FAIL"); + + return ret; +} + +int cts_noise_test(struct cts_device *cts_dev, u32 frames, u16 max) +{ + u16 *curr_rawdata = NULL; + u16 *max_rawdata = NULL; + u16 *min_rawdata = NULL; + u16 *noise = NULL; + bool first_frame = true; + bool data_valid = false; + u16 i; + int ret; + + cts_info("Noise frames: %u, threshold: %u", frames, max); + + curr_rawdata = + (u16 *) kmalloc(TEST_RESULT_BUFFER_SIZE(cts_dev) * 4, GFP_KERNEL); + if (curr_rawdata == NULL) { + cts_err("Alloc mem for rawdata failed"); + return -ENOMEM; + } + max_rawdata = + curr_rawdata + cts_dev->fwdata.rows * cts_dev->fwdata.cols; + min_rawdata = max_rawdata + cts_dev->fwdata.rows * cts_dev->fwdata.cols; + noise = min_rawdata + cts_dev->fwdata.rows * cts_dev->fwdata.cols; + + cts_lock_device(cts_dev); + + for (i = 0; i < 5; i++) { + int r; + u8 val; + + r = cts_dev->ops->enable_get_rawdata(cts_dev); + if (r) { + cts_err("Enable get ts data failed %d", r); + continue; + } + mdelay(1); + r = cts_dev->ops->is_enabled_get_rawdata(cts_dev, &val); + if (r) { + cts_err("Read enable get ts data failed %d", r); + continue; + } + if (val != 0) + break; + } + + if (i >= 5) { + cts_err("Enable read tsdata failed"); + ret = -EIO; + goto unlock_device; + } + + msleep(50); + + while (frames--) { + for (i = 0; i < 3; i++) { + int r; + + r = cts_get_rawdata(cts_dev, curr_rawdata); + if (r) { + cts_err("Get rawdata failed %d", r); + continue; + } else + break; + + mdelay(30); + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + /* Dump raw data */ + dump_test_data(cts_dev, "Noise-rawdata", curr_rawdata); + + if (unlikely(first_frame)) { + memcpy(max_rawdata, curr_rawdata, + TEST_RESULT_BUFFER_SIZE(cts_dev)); + memcpy(min_rawdata, curr_rawdata, + TEST_RESULT_BUFFER_SIZE(cts_dev)); + first_frame = false; + + continue; + } else { + for (i = 0; + i < cts_dev->fwdata.rows * cts_dev->fwdata.cols; + i++) { + if (curr_rawdata[i] > max_rawdata[i]) + max_rawdata[i] = curr_rawdata[i]; + else if (curr_rawdata[i] < min_rawdata[i]) + min_rawdata[i] = curr_rawdata[i]; + } + } + } + + data_valid = true; + +disable_get_tsdata: + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->disable_get_rawdata(cts_dev); + + if (r) { + cts_err("Disable get rawdata failed %d", r); + continue; + } else + break; + } + +unlock_device: + cts_unlock_device(cts_dev); + + if (data_valid) { + for (i = 0; i < cts_dev->fwdata.rows * cts_dev->fwdata.cols; + i++) { + noise[i] = max_rawdata[i] - min_rawdata[i]; + } + + dump_test_data(cts_dev, "Noise", noise); + + ret = + validate_test_result(cts_dev, "Noise test", noise, 0, max); + } + + kfree(curr_rawdata); + + cts_info("Noise test %s", (data_valid && (ret == 0)) ? "PASS" : "FAIL"); + + return ret; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h new file mode 100644 index 000000000000..d8b8d0608758 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_TEST_H +#define CTS_TEST_H + +struct cts_device; + +extern int cts_short_test(struct cts_device *cts_dev, u16 threshold); +extern int cts_open_test(struct cts_device *cts_dev, u16 threshold); +extern int cts_reset_test(struct cts_device *cts_dev); +extern int cts_compensate_cap_test(struct cts_device *cts_dev, u8 min_thres, + u8 max_thres); +extern int cts_rawdata_test(struct cts_device *cts_dev, u16 min_thres, + u16 max_thres); +extern int cts_noise_test(struct cts_device *cts_dev, u32 frames, u16 max); +extern int cts_test_int_pin(struct cts_device *cts_dev); + +#endif /* CTS_TEST_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c new file mode 100644 index 000000000000..497a26330efb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c @@ -0,0 +1,803 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Tool" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_test.h" + +#ifdef CONFIG_CTS_LEGACY_TOOL + +#pragma pack(1) +/** Tool command structure */ +struct cts_tool_cmd { + u8 cmd; + u8 flag; + u8 circle; + u8 times; + u8 retry; + u32 data_len; + u8 addr_len; + u8 addr[4]; + u8 tcs[2]; + u8 data[PAGE_SIZE]; + +}; +#pragma pack() + +#define CTS_TOOL_CMD_HEADER_LENGTH (16) + +enum cts_tool_cmd_code { + CTS_TOOL_CMD_GET_PANEL_PARAM = 0, + CTS_TOOL_CMD_GET_DOWNLOAD_STATUS = 2, + CTS_TOOL_CMD_GET_RAW_DATA = 4, + CTS_TOOL_CMD_GET_DIFF_DATA = 6, + CTS_TOOL_CMD_READ_HOSTCOMM = 12, + CTS_TOOL_CMD_READ_ADC_STATUS = 14, + CTS_TOOL_CMD_READ_GESTURE_INFO = 16, + CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE = 18, + CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE = 20, + CTS_TOOL_CMD_READ_ICTYPE = 22, + CTS_TOOL_CMD_I2C_DIRECT_READ = 24, + CTS_TOOL_CMD_GET_DRIVER_INFO = 26, + CTS_TOOL_CMD_TCS_READ_HW_CMD = 28, + CTS_TOOL_CMD_TCS_READ_DDI_CMD = 30, + CTS_TOOL_CMD_TCS_READ_FW_CMD = 32, + CTS_TOOL_CMD_GET_BASE_DATA = 34, + + CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM = 1, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME = 3, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE = 5, + CTS_TOOL_CMD_WRITE_HOSTCOMM = 11, + CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE = 15, + CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE = 17, + CTS_TOOL_CMD_I2C_DIRECT_WRITE = 19, + CTS_TOOL_CMD_TCS_WRITE_HW_CMD = 21, + CTS_TOOL_CMD_TCS_WRITE_DDI_CMD = 23, + CTS_TOOL_CMD_TCS_WRITE_FW_CMD = 25, + CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD = 27, + CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD = 29, + +}; + +struct cts_test_rawdata_ioctl_data { + int frames; + int min; + int max; +}; + +struct cts_test_noise_ioctl_data { + int frames; + int max; +}; + +struct cts_test_open_ioctl_data { + int min; + int max; +}; + +struct cts_test_short_ioctl_data { + int min; + int max; + bool display_on; + bool disable_gas; +}; + +struct cts_test_comp_cap_ioctl_data { + int min; + int max; +}; + +#define CTS_TOOL_IOCTL_GET_DRIVER_VERSION _IOR('C', 0x00, unsigned int) +#define CTS_TOOL_IOCTL_GET_DEVICE_TYPE _IOR('C', 0x01, unsigned int) +#define CTS_TOOL_IOCTL_GET_FW_VERSION _IOR('C', 0x02, unsigned short) + +#define CTS_TOOL_IOCTL_TEST_RESET_PIN _IO('C', 0x11) +#define CTS_TOOL_IOCTL_TEST_INT_PIN _IO('C', 0x12) +#define CTS_TOOL_IOCTL_TEST_DEVICE_TYPE _IOW('C', 0x13, unsigned int) +#define CTS_TOOL_IOCTL_TEST_FW_VERSION _IOW('C', 0x14, unsigned short) +#define CTS_TOOL_IOCTL_TEST_RAWDATA \ + _IOW('C', 0x15, struct cts_test_rawdata_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_NOISE \ + _IOW('C', 0x16, struct cts_test_noise_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_OPEN \ + _IOW('C', 0x17, struct cts_test_open_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_SHORT \ + _IOW('C', 0x18, struct cts_test_short_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_COMP_CAP \ + _IOW('C', 0x19, struct cts_test_comp_cap_ioctl_data *) + +#define CTS_DRIVER_VERSION_CODE ((CFG_CTS_DRIVER_MAJOR_VERSION << 16) | \ + (CFG_CTS_DRIVER_MINOR_VERSION << 8) | \ + (CFG_CTS_DRIVER_PATCH_VERSION << 0)) + +static struct cts_tool_cmd cts_tool_cmd; +static char cts_tool_firmware_filepath[PATH_MAX]; +/* If CFG_CTS_MAX_I2C_XFER_SIZE < 58(PC tool length), this is neccessary */ +#ifdef CONFIG_CTS_I2C_HOST +/*static u32 cts_tool_direct_access_addr = 0;*/ +#endif /* CONFIG_CTS_I2C_HOST */ + +static int cts_tool_open(struct inode *inode, struct file *file) +{ + file->private_data = PDE_DATA(inode); + return 0; +} + +static ssize_t cts_tool_read(struct file *file, + char __user *buffer, size_t count, loff_t *ppos) +{ +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + struct chipone_ts_data *cts_data; + struct cts_tool_cmd *cmd; + struct cts_device *cts_dev; + int ret = 0; + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Read with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + cts_info("read: flag:%d, addr:0x%06x, tcm:0x%04x, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), cmd->data_len); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_TCS_READ_HW_CMD: + cts_dev->ops->read_hw_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_READ_DDI_CMD: + cts_dev->ops->read_ddi_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_READ_FW_CMD: + cts_dev->ops->read_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_GET_PANEL_PARAM: + cts_info("Get panel param len: %u", cmd->data_len); + ret = cts_get_panel_param(cts_dev, cmd->data, cmd->data_len); + if (ret) { + cts_err("Get panel param len: %u failed %d", + cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_GET_DOWNLOAD_STATUS: + cmd->data[0] = 100; + cts_info("Get update status = %hhu", cmd->data[0]); + break; + + case CTS_TOOL_CMD_GET_RAW_DATA: + case CTS_TOOL_CMD_GET_DIFF_DATA: + case CTS_TOOL_CMD_GET_BASE_DATA: + cts_info("Get %s data row: %u col: %u len: %u", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", + cmd->addr[1], cmd->addr[0], cmd->data_len); + + if (cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA) { + // ret = cts_get_rawdata(cts_dev, cmd->data); + ret = + cts_dev->ops->top_get_rawdata(cts_dev, + (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE + (cts_dev)); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_DIFF_DATA) { + // ret = cts_get_diffdata(cts_dev, cmd->data); + ret = + cts_dev->ops->top_get_manual_diff(cts_dev, + (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE + (cts_dev)); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_BASE_DATA) { + // ret = cts_get_basedata(cts_dev, cmd->data); + ret = + cts_dev->ops->top_get_basedata(cts_dev, + (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE + (cts_dev)); + } + if (ret) { + cts_err("Get %s data failed %d", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : + cmd->cmd == + CTS_TOOL_CMD_GET_DIFF_DATA ? "diff" : "base", + ret); + } + break; + + case CTS_TOOL_CMD_READ_HOSTCOMM: + ret = cts_fw_reg_readb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data); + if (ret) { + cts_err("Read firmware reg addr 0x%04x failed %d", + get_unaligned_le16(cmd->addr), ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x, val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + } + break; + +#ifdef CFG_CTS_GESTURE + case CTS_TOOL_CMD_READ_GESTURE_INFO: + ret = cts_get_gesture_info(cts_dev, cmd->data, true); + if (ret) + cts_err("Get gesture info failed %d", ret); + break; +#endif /* CFG_CTS_GESTURE */ + + case CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE: + cmd->data_len = min((size_t)cmd->data_len, sizeof(cmd->data)); + ret = cts_fw_reg_readsb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err + ("Read firmware reg addr 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + } + break; + + case CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Read under program mode addr 0x%06x len %u", + (cmd->flag << 16) | get_unaligned_le16(cmd->addr), + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_readsb(&cts_data->cts_dev, + get_unaligned_le16(cmd->addr), cmd->data, + cmd->data_len); + if (ret) { + cts_err("Read under program mode I2C xfer failed %d", + ret); + } + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_READ_ICTYPE: + cts_info("Get IC type"); + if (cts_dev->hwdata) { + switch (cts_dev->hwdata->hwid) { + case CTS_DEV_HWID_ICNL9911: + cmd->data[0] = 0x91; + break; + case CTS_DEV_HWID_ICNL9911S: + cmd->data[0] = 0x91; + break; + case CTS_DEV_HWID_ICNL9911C: + cmd->data[0] = 0x91; + break; + default: + cmd->data[0] = 0x00; + break; + } + } else { + cmd->data[0] = 0x10; + } + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_READ: + { + u32 addr_width; + char *wr_buff = NULL; + u8 addr_buff[4]; + size_t left_size, max_xfer_size; + u8 *data; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + } + + cts_dbg + ("Direct read from i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, + cmd->data_len); + + left_size = cmd->data_len; + max_xfer_size = + cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cmd->data; + while (left_size) { + size_t xfer_size = + min(left_size, max_xfer_size); + ret = + cts_plat_i2c_read(cts_data->pdata, + cmd->addr[0], wr_buff, + addr_width, data, + xfer_size, 1, 0); + if (ret) { + cts_err + ("Direct read i2c_addr 0x%02x addr 0x%06x " + "len %zu failed %d", cmd->addr[0], + cts_tool_direct_access_addr, + xfer_size, ret); + break; + } + + left_size -= xfer_size; + if (left_size) { + data += xfer_size; + cts_tool_direct_access_addr += + xfer_size; + if (addr_width == 2) { + put_unaligned_be16 + (cts_tool_direct_access_addr, + addr_buff); + } else if (addr_width == 3) { + put_unaligned_be24 + (cts_tool_direct_access_addr, + addr_buff); + } + wr_buff = addr_buff; + } + } + } + break; +#endif + case CTS_TOOL_CMD_GET_DRIVER_INFO: + break; + + default: + cts_warn("Read unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + if (ret == 0) { + if (cmd->cmd == CTS_TOOL_CMD_I2C_DIRECT_READ) { + ret = copy_to_user(buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data, cmd->data_len); + } else { + ret = copy_to_user(buffer, cmd->data, cmd->data_len); + } + if (ret) { + cts_err("Copy data to user buffer failed %d", ret); + return 0; + } + + return cmd->data_len; + } + + return 0; +} + +static ssize_t cts_tool_write(struct file *file, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + struct cts_tool_cmd *cmd; + int ret = 0; + + if (count < CTS_TOOL_CMD_HEADER_LENGTH || count > PAGE_SIZE) { + cts_err("Write len %zu invalid", count); + return -EFAULT; + } + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Write with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + ret = copy_from_user(cmd, buffer, CTS_TOOL_CMD_HEADER_LENGTH); + cts_info + ("write: flag:%d, addr:0x%06x, tcmd:0x%04x data:0x%02x, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), cmd->data[0], cmd->data_len); + + if (ret) { + cts_err("Copy command header from user buffer failed %d", ret); + return -EIO; + } else + ret = CTS_TOOL_CMD_HEADER_LENGTH; + + if (cmd->data_len > PAGE_SIZE) { + cts_err("Write with invalid count %d", cmd->data_len); + return -EIO; + } + + if (cmd->cmd & BIT(0)) { + if (cmd->data_len) { + ret = + copy_from_user(cmd->data, + buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data_len); + if (ret) { + cts_err + ("Copy command payload from user buffer len %u failed %d", + cmd->data_len, ret); + return -EIO; + } + } + } else { + /* Prepare for read command */ + cts_dbg("Write read command(%d) header, prepare read size: %d", + cmd->cmd, cmd->data_len); + return CTS_TOOL_CMD_HEADER_LENGTH + cmd->data_len; + } + + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_TCS_WRITE_HW_CMD: + cts_dev->ops->write_hw_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_WRITE_DDI_CMD: + cts_dev->ops->write_ddi_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_WRITE_FW_CMD: + cts_dev->ops->write_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD: + cts_info("Enable touch data"); + ret = cts_dev->ops->enable_get_rawdata(cts_dev); + if (ret) + cts_err("Enable get touch data failed"); + break; + case CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD: + cts_info("Disable touch data"); + ret = cts_dev->ops->disable_get_rawdata(cts_dev); + if (ret) + cts_err("Disable get touch data failed"); + break; + case CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM: + cts_info("Write panel param len %u data\n", cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM, + cmd->data, cmd->data_len); + if (ret) { + cts_err("Write panel param failed %d", ret); + break; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RESET); + + ret = cts_set_work_mode(cts_dev, 1); + + mdelay(100); + + ret = cts_set_work_mode(cts_dev, 0); + + mdelay(100); + + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME: + cts_info("Write firmware path: '%.*s'", + cmd->data_len, cmd->data); + + memcpy(cts_tool_firmware_filepath, cmd->data, cmd->data_len); + cts_tool_firmware_filepath[cmd->data_len] = '\0'; + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE: + cts_info("Start download firmware path: '%s'", + cts_tool_firmware_filepath); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + break; + } +/** + *TODO: Use async mode such as thread, otherwise, + *host can not get update status. + */ + ret = cts_update_firmware_from_file(cts_dev, + cts_tool_firmware_filepath, + true); + if (ret) + cts_err("Updata firmware failed %d", ret); + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM: + cts_dbg("Write firmware reg addr: 0x%04x val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + + ret = cts_fw_reg_writeb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data[0]); + if (ret) { + cts_err + ("Write firmware reg addr: 0x%04x val=0x%02x failed %d", + get_unaligned_le16(cmd->addr), cmd->data[0], ret); + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE: + cts_dbg("Write firmare reg addr: 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err + ("Write firmare reg addr: 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Write to addr 0x%06x size %u under program mode", + (cmd->flag << 16) | (cmd->addr[1] << 8) | cmd->addr[0], + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_writesb(cts_dev, + (cmd->flag << 16) | (cmd->addr[1] << 8) | + cmd->addr[0], cmd->data, cmd->data_len); + if (ret) + cts_err("Write program mode multibyte failed %d", ret); + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_WRITE: + { + u32 addr_width; + size_t left_payload_size; /* Payload exclude address field */ + size_t max_xfer_size; + char *payload; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + cts_tool_direct_access_addr = + get_unaligned_be16(cmd->data); + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + cts_tool_direct_access_addr = + get_unaligned_be24(cmd->data); + } + + if (cmd->data_len < addr_width) { + cts_err + ("Direct write too short %d < address width %d", + cmd->data_len, addr_width); + ret = -EINVAL; + break; + } + + cts_dbg + ("Direct write to i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, + cmd->data_len); + + left_payload_size = cmd->data_len - addr_width; + max_xfer_size = + cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + payload = cmd->data + addr_width; + do { + size_t xfer_payload_size = + min(left_payload_size, + max_xfer_size - addr_width); + size_t xfer_len = + xfer_payload_size + addr_width; + + ret = cts_plat_i2c_write(cts_data->pdata, + cmd->addr[0], + payload - addr_width, + xfer_len, 1, 0); + if (ret) { + cts_err + ("Direct write i2c_addr 0x%02x addr 0x%06x len %zu failed %d", + cmd->addr[0], + cts_tool_direct_access_addr, + xfer_len, ret); + break; + } + + left_payload_size -= xfer_payload_size; + if (left_payload_size) { + payload += xfer_payload_size; + cts_tool_direct_access_addr += + xfer_payload_size; + if (addr_width == 2) { + put_unaligned_be16 + (cts_tool_direct_access_addr, + payload - addr_width); + } else if (addr_width == 3) { + put_unaligned_be24 + (cts_tool_direct_access_addr, + payload - addr_width); + } + } + } while (left_payload_size); + } + break; +#endif + default: + cts_warn("Write unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + return ret ? 0 : cmd->data_len + CTS_TOOL_CMD_HEADER_LENGTH; +} + +static long cts_tool_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + + cts_info("ioctl, cmd=0x%04x, arg=0x%02lx", cmd, arg); + + cts_data = file->private_data; + if (cts_data == NULL) { + cts_err("IOCTL with private data = NULL"); + return -EFAULT; + } + + cts_dev = &cts_data->cts_dev; + + switch (cmd) { + case CTS_TOOL_IOCTL_GET_DRIVER_VERSION: + return put_user(CTS_DRIVER_VERSION_CODE, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_DEVICE_TYPE: + return put_user(cts_dev->hwdata->hwid, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_FW_VERSION: + return put_user(cts_dev->fwdata.version, + (unsigned short __user *)arg); + + case CTS_TOOL_IOCTL_TEST_RESET_PIN: + return cts_reset_test(cts_dev); + case CTS_TOOL_IOCTL_TEST_INT_PIN: + return cts_test_int_pin(cts_dev); + case CTS_TOOL_IOCTL_TEST_DEVICE_TYPE: + if (cts_dev->hwdata->hwid == (u32) arg) + return 0; + return -EINVAL; + case CTS_TOOL_IOCTL_TEST_FW_VERSION: + if (cts_dev->fwdata.version == (u16) arg) + return 0; + return -EINVAL; + case CTS_TOOL_IOCTL_TEST_RAWDATA:{ + struct cts_test_rawdata_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_rawdata_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_rawdata_test(cts_dev, param.min, param.max); + } + case CTS_TOOL_IOCTL_TEST_NOISE:{ + struct cts_test_noise_ioctl_data param; + + if (copy_from_user(¶m, + (struct + cts_test_noise_ioctl_data__user *) + arg, sizeof(param))) + return -EFAULT; + + return cts_noise_test(cts_dev, param.frames, param.max); + } + case CTS_TOOL_IOCTL_TEST_OPEN:{ + struct cts_test_open_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_open_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_open_test(cts_dev, param.max); + } + case CTS_TOOL_IOCTL_TEST_SHORT:{ + struct cts_test_short_ioctl_data param; + + if (copy_from_user + (¶m, + (struct cts_test_short_ioctl_data __user *)arg, + sizeof(param))) + return -EFAULT; + + return cts_short_test(cts_dev, param.max); + } + case CTS_TOOL_IOCTL_TEST_COMP_CAP:{ + struct cts_test_comp_cap_ioctl_data param; + + if (copy_from_user + (¶m, + (struct cts_test_comp_cap_ioctl_data __user *)arg, + sizeof(param))) + return -EFAULT; + + return cts_compensate_cap_test(cts_dev, param.min, + param.max); + } + + default: + break; + } + + return -ENOTSUPP; +} + +static struct file_operations cts_tool_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = cts_tool_open, + .read = cts_tool_read, + .write = cts_tool_write, + .unlocked_ioctl = cts_tool_ioctl, +}; + +int cts_tool_init(struct chipone_ts_data *cts_data) +{ + cts_info("Init"); + + cts_data->procfs_entry = proc_create_data(CFG_CTS_TOOL_PROC_FILENAME, + 0600, NULL, &cts_tool_fops, + cts_data); + if (cts_data->procfs_entry == NULL) { + cts_err("Create proc entry failed"); + return -EFAULT; + } + + return 0; +} + +void cts_tool_deinit(struct chipone_ts_data *data) +{ + cts_info("Deinit"); + + if (data->procfs_entry) + remove_proc_entry(CFG_CTS_TOOL_PROC_FILENAME, NULL); +} +#endif /* CONFIG_CTS_LEGACY_TOOL */