From 44f70fc15bef6b5d4cc7aa4345697906fd5db77c Mon Sep 17 00:00:00 2001 From: Binyuan Lan Date: Sun, 28 Jan 2024 03:13:43 +0000 Subject: [PATCH] input: touchscreen: support tp chipone_9551r Signed-off-by: Binyuan Lan Change-Id: Ia62dfe3670af23c69f857ab0fcc251607187974d --- drivers/input/touchscreen/Kconfig | 2 + drivers/input/touchscreen/Makefile | 1 + .../input/touchscreen/chipone_tddi/Kconfig | 12 + .../input/touchscreen/chipone_tddi/Makefile | 17 + .../chipone_tddi/cts_builtin_firmware.h | 25 + .../chipone_tddi/cts_charger_detect.c | 1024 ++++++ .../chipone_tddi/cts_charger_detect.h | 45 + .../touchscreen/chipone_tddi/cts_config.h | 242 ++ .../input/touchscreen/chipone_tddi/cts_core.c | 2874 ++++++++++++++++ .../input/touchscreen/chipone_tddi/cts_core.h | 988 ++++++ .../touchscreen/chipone_tddi/cts_driver.c | 1231 +++++++ .../chipone_tddi/cts_earjack_detect.c | 649 ++++ .../chipone_tddi/cts_earjack_detect.h | 42 + .../touchscreen/chipone_tddi/cts_firmware.c | 1044 ++++++ .../touchscreen/chipone_tddi/cts_firmware.h | 33 + .../input/touchscreen/chipone_tddi/cts_oem.c | 2127 ++++++++++++ .../input/touchscreen/chipone_tddi/cts_oem.h | 12 + .../touchscreen/chipone_tddi/cts_platform.c | 1919 +++++++++++ .../touchscreen/chipone_tddi/cts_platform.h | 309 ++ .../touchscreen/chipone_tddi/cts_sfctrl.h | 38 + .../touchscreen/chipone_tddi/cts_sfctrlv2.c | 342 ++ .../touchscreen/chipone_tddi/cts_spi_flash.c | 536 +++ .../touchscreen/chipone_tddi/cts_spi_flash.h | 45 + .../touchscreen/chipone_tddi/cts_strerror.c | 175 + .../touchscreen/chipone_tddi/cts_strerror.h | 11 + .../touchscreen/chipone_tddi/cts_sysfs.c | 2904 ++++++++++++++++ .../touchscreen/chipone_tddi/cts_sysfs.h | 27 + .../input/touchscreen/chipone_tddi/cts_tcs.c | 1901 +++++++++++ .../input/touchscreen/chipone_tddi/cts_tcs.h | 127 + .../input/touchscreen/chipone_tddi/cts_test.c | 2927 +++++++++++++++++ .../input/touchscreen/chipone_tddi/cts_test.h | 113 + .../input/touchscreen/chipone_tddi/cts_tool.c | 1207 +++++++ 32 files changed, 22949 insertions(+) create mode 100644 drivers/input/touchscreen/chipone_tddi/Kconfig create mode 100644 drivers/input/touchscreen/chipone_tddi/Makefile create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_builtin_firmware.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_charger_detect.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_charger_detect.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_config.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_core.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_core.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_driver.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_firmware.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_firmware.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_oem.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_oem.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_platform.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_platform.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_sfctrl.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_sfctrlv2.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_spi_flash.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_spi_flash.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_strerror.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_strerror.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_sysfs.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_sysfs.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_tcs.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_tcs.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_test.c create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_test.h create mode 100644 drivers/input/touchscreen/chipone_tddi/cts_tool.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index eb5324b90a4f..6129ce0ac901 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -198,6 +198,8 @@ config TOUCHSCREEN_CHIPONE_ICN8505 To compile this driver as a module, choose M here: the module will be called chipone_icn8505. +source "drivers/input/touchscreen/chipone_tddi/Kconfig" + config TOUCHSCREEN_CY8CTMA140 tristate "cy8ctma140 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index ecb3b041e762..311c707dbfba 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o +obj-$(CONFIG_TOUCHSCREEN_CHIPONE_9551R) += chipone_tddi/ obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o diff --git a/drivers/input/touchscreen/chipone_tddi/Kconfig b/drivers/input/touchscreen/chipone_tddi/Kconfig new file mode 100644 index 000000000000..d5d30af6cd16 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# chipone Touchscreen driver configuration +# + +config TOUCHSCREEN_CHIPONE_9551R + tristate "chipone Touchscreen" + depends on I2C + default n + help + Say Y here if you have chipone touch panel. + If unsure, say N. diff --git a/drivers/input/touchscreen/chipone_tddi/Makefile b/drivers/input/touchscreen/chipone_tddi/Makefile new file mode 100644 index 000000000000..b8815b9558fb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_TOUCHSCREEN_CHIPONE_9551R) += chipone-ts.o +chipone-ts-y += cts_driver.o +chipone-ts-y += cts_core.o +chipone-ts-y += cts_sfctrlv2.o +chipone-ts-y += cts_spi_flash.o +chipone-ts-y += cts_firmware.o +chipone-ts-y += cts_test.o +chipone-ts-y += cts_charger_detect.o +chipone-ts-y += cts_earjack_detect.o +chipone-ts-y += cts_tcs.o +chipone-ts-y += cts_platform.o +chipone-ts-y += cts_tool.o +chipone-ts-y += cts_sysfs.o +chipone-ts-y += cts_strerror.o +chipone-ts-y += cts_oem.o diff --git a/drivers/input/touchscreen/chipone_tddi/cts_builtin_firmware.h b/drivers/input/touchscreen/chipone_tddi/cts_builtin_firmware.h new file mode 100644 index 000000000000..8eb9d940f3a4 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_builtin_firmware.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +static u8 icnl9951_driver_builtin_firmware[] = { +}; + +static u8 icnl9951r_driver_builtin_firmware[] = { +}; + +static struct cts_firmware cts_driver_builtin_firmwares[] = { + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9951, + .fwid = CTS_DEV_FWID_ICNL9951, + .data = icnl9951_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9951_driver_builtin_firmware), + }, + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9951R, + .fwid = CTS_DEV_FWID_ICNL9951R, + .data = icnl9951r_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9951r_driver_builtin_firmware), + }, + +}; diff --git a/drivers/input/touchscreen/chipone_tddi/cts_charger_detect.c b/drivers/input/touchscreen/chipone_tddi/cts_charger_detect.c new file mode 100644 index 000000000000..952f03f163fe --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_charger_detect.c @@ -0,0 +1,1024 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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 + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + /* Polling power supply property */ + struct delayed_work psp_poll_work; +#endif + + struct work_struct set_charger_state_work; + + /* Sysfs */ +#ifdef CONFIG_CTS_SYSFS + bool sysfs_created; +#endif + + 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 + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + INIT_DELAYED_WORK(&cd_data->psp_poll_work, poll_psp_work); +#endif + + 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 + +#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); + + cts_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); + + cts_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); + + cts_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); + + cts_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/cts_charger_detect.h b/drivers/input/touchscreen/chipone_tddi/cts_charger_detect.h new file mode 100644 index 000000000000..8cf8d7d7cbfc --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_charger_detect.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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/cts_config.h b/drivers/input/touchscreen/chipone_tddi/cts_config.h new file mode 100644 index 000000000000..d9581056203c --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_config.h @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_CONFIG_H +#define CTS_CONFIG_H + +/** Driver version */ +#define CFG_CTS_DRIVER_MAJOR_VERSION 3 +#define CFG_CTS_DRIVER_MINOR_VERSION 6 +#define CFG_CTS_DRIVER_PATCH_VERSION 3 +#define CFG_CTS_DRIVER_VERSION "v3.6.3" + +/* Support Mediatek TPD arch */ +/* #define CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + +/* Support Spreadtrum platform */ +#define CFG_CTS_PLATFORM_SPRD_SUPPORTED + +/* Support cascade application */ +//#define CFG_CTS_CASCADE_SUPPORTED + +/* Support RK dsi extcon notifier for suspend and resume */ +#define CONFIG_PM_DSI_EXTCON_NOTIFIER + +/* Support both finger and stylus protocol, 11 */ +// #define CFG_CTS_FINGER_STYLUS_SUPPORTED + +/* #define CFG_CTS_STYLUS_BTN_SUPPORTED */ + +#define CONFIG_CTS_I2C_HOST + +/* For Goole Security */ +#define CFG_CTS_FOR_GKI + +/** Whether reset pin is used */ +#define CFG_CTS_HAS_RESET_PIN + +#ifndef CONFIG_CTS_I2C_HOST +#ifndef CFG_CTS_HAS_RESET_PIN +#define CFG_CTS_HAS_RESET_PIN +#endif +#define CFG_CTS_SPI_SPEED_KHZ 9600 +#endif /* CONFIG_CTS_I2C_HOST */ + +/* #define CFG_CTS_FORCE_UP */ + +/* #define CFG_CTS_HEARTBEAT_MECHANISM */ + +/* #define CFG_CTS_PALM_DETECT */ +#ifdef CFG_CTS_PALM_DETECT +#define CFG_CTS_PALM_EVENT 240 +#define CFG_CTS_PALM_ID 0x80 +#endif + +#define CONFIG_GENERIC_HARDIRQS + +/** 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 +/* #define CFG_CTS_FW_UPDATE_SYS */ +#define CFG_CTS_FIRMWARE_FILENAME "chipone_firmware.bin" +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +#define CFG_CTS_FIRMWARE_SIZE (120 * 1024) +#define CFG_CTS_FACTORY_LIMIT_FILENAME "chipone_limit.bin" + +/* IC type support */ +#define CFG_CTS_CHIP_NAME "CHIPONE-TDDI" + +#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) +#define CFG_CTS_MAX_STYLUS_NUM (4) + +/* 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 GESTURE_TAP 0x7f + +#define CFG_CTS_NUM_GESTURE (14u) +#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,}, \ + { GESTURE_TAP, KEY_F2,}, \ +} +#define CFG_CTS_GESTURE_REPORT_TRACE 0 +#endif /* CFG_CTS_GESTURE */ + +#define CONFIG_CTS_GLOVE +//#define CONFIG_CTS_EARJACK_DETECT +//#define CONFIG_CTS_CHARGER_DETECT + +/* #define CONFIG_CTS_TP_PROXIMITY */ + +/* 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 + +/* #define CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +#ifdef CONFIG_CTS_LEGACY_TOOL +#define CFG_CTS_TOOL_PROC_FILENAME "cts_tool" +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#define CFG_CTS_UPDATE_CRCCHECK + + +#ifdef CONFIG_OF +#define CONFIG_CTS_OF +#endif + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +/**************************************************************************** + * MTK Platform configurations + ****************************************************************************/ +#ifdef CONFIG_MTK_I2C_EXTENSION +#define TPD_SUPPORT_I2C_DMA +#define CFG_CTS_MAX_I2C_XFER_SIZE (250) +#define CFG_CTS_MAX_I2C_FIFO_XFER_SIZE (8) +#else +#define CFG_CTS_MAX_I2C_XFER_SIZE (128) +#endif /* CONFIG_MTK_I2C_EXTENSION */ + +#define CFG_CTS_MAX_I2C_READ_SIZE (8192u) +#define CFG_CTS_MAX_SPI_XFER_SIZE (8192u) +#define CFG_CTS_INT_DATA_MAX_SIZE (8192u) + +#define CTS_FW_LOG_REDIRECT_SIGN 0x60 +#define CTS_FW_LOG_BUF_LEN 128 + +//#define CFG_MTK_LEGEND_PLATFORM + +#define CFG_CTS_DEVICE_NAME TPD_DEVICE +#define CFG_CTS_DRIVER_NAME "chipone-tddi" + +#ifdef CONFIG_CTS_OF +#define CFG_CTS_OF_DEVICE_ID_NAME "mediatek,cap_touch" +#endif /* CONFIG_CTS_OF */ + +#else /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ +/**************************************************************************** + * QCOM Platform configurations + ****************************************************************************/ +#ifndef CFG_CTS_PLATFORM_SPRD_SUPPORTED +#define CONFIG_CTS_PM_FB_NOTIFIER +#endif +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CONFIG_DRM +#define CFG_CTS_DRM_NOTIFIER +#endif /*CONFIG_DRM */ +#else /*CONFIG_CTS_PM_FB_NOTIFIER */ +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_SUSPEND) + /* #define CONFIG_CTS_PM_GENERIC */ +#endif /* CONFIG_PM_SLEEP */ + +#ifndef 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_I2C_READ_SIZE (8192u) +#define CFG_CTS_MAX_SPI_XFER_SIZE (8192u) +#define CFG_CTS_INT_DATA_MAX_SIZE (8192u) + +#define CTS_FW_LOG_REDIRECT_SIGN 0x60 +#define CTS_FW_LOG_BUF_LEN 128 + +#define CFG_CTS_DEVICE_NAME "chipone-tddi" +#define CFG_CTS_DRIVER_NAME "chipone-tddi" + +#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 /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + +#endif /* CTS_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi/cts_core.c b/drivers/input/touchscreen/chipone_tddi/cts_core.c new file mode 100644 index 000000000000..13866d43babd --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_core.c @@ -0,0 +1,2874 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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_earjack_detect.h" +#include "cts_tcs.h" +#include "cts_test.h" + +#ifdef CONFIG_CTS_I2C_HOST +int cts_i2c_writeb(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(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(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); +} + +int cts_i2c_writesb(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; +} + +int cts_i2c_readb(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(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; +} + +int cts_i2c_readl(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(struct cts_device *cts_dev, + u32 addr, u8 *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(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(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(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(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(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(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(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(struct cts_device *cts_dev, + u32 addr, u8 *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(struct cts_device *cts_dev, + u32 addr, u8 *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(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(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(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(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(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(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(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(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(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(struct cts_device *cts_dev, + size_t len, u32 crc, int retry) +{ + int ret = 0, retries; + u32 addr[3]; + + addr[0] = 0x037ff0; + addr[1] = 0x037ff8; + addr[2] = 0x037ffc; + + retries = 0; + do { + ret = cts_dev_writel(cts_dev, addr[0], 0xCC33555A, 10, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[1], crc, 10, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[2], len, 10, 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(struct cts_device *cts_dev, + u32 addr, const u8 *src, size_t len, int retry, int delay) +{ + int i, ret; + const u8 *psrc = src; + u8 buff[5]; + + for (i = 0; i < len; i++) { + put_unaligned_le32(addr, buff); + buff[4] = *(u8 *) psrc; + + addr++; + psrc++; + + 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(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(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(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(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(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(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_sram_writesb_boot_crc_retry(cts_dev, len, fw_crc, 10); + 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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); +} + +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, CTS_DEV_HW_REG_HPPROG, enable ? 1 : 0); + if (ret) { + cts_err("Write CTS_DEV_HW_REG_HPPROG 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 icnl9951_sfctrl = { + .reg_base = 0x74000, + .xchg_sram_base = 160 * 1024, + .xchg_sram_size = 64 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9951r_sfctrl = { + .reg_base = 0x74000, + .xchg_sram_base = 160 * 1024, + .xchg_sram_size = 64 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + + +const static struct cts_device_hwdata cts_device_hwdatas[] = { + { + .name = "ICNL9951", + .hwid = CTS_DEV_HWID_ICNL9951, + .fwid = CTS_DEV_FWID_ICNL9951, + .num_row = 36, + .num_col = 22, + .sram_size = 128 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9951_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + + { + .name = "ICNL9951R", + .hwid = CTS_DEV_HWID_ICNL9951R, + .fwid = CTS_DEV_FWID_ICNL9951R, + .num_row = 50, + .num_col = 32, + .sram_size = 128 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9951r_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(struct cts_device *cts_dev) +{ + cts_dbg("*** Lock ***"); + + mutex_lock(&cts_dev->pdata->dev_lock); +} + +void cts_unlock_device(struct cts_device *cts_dev) +{ + cts_dbg("### Un-Lock ###"); + + mutex_unlock(&cts_dev->pdata->dev_lock); +} + +int cts_send_command(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_dev_boot_mode(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_tcs_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(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; + } + + return 0; +} + +static int cts_init_fwdata(struct cts_device *cts_dev) +{ + + struct cts_device_fwdata *fwdata = &cts_dev->fwdata; + u8 info[16]; + 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_tcs_get_hwid_info(cts_dev, info); + if (ret < 0) { + cts_err("get_hwid_info failed"); + } else { + if (((info[5]<<16)|(info[6]<<8)|info[7]) == 0x55AA55) { + fwdata->ic_application = info[8]; + fwdata->pen_freq_num = info[9]; + fwdata->cascade_num = info[10]; + cts_info("ic_application=%d, pen_freq_num=%d, cascade_num=%d", info[8],info[9],info[10]); + } else { + fwdata->ic_application = 0; + } + } + + ret = cts_tcs_get_fw_ver(cts_dev, &fwdata->version); + if (ret < 0) { + cts_err("get_fw_ver failed"); + return -EINVAL; + } + ret = cts_tcs_get_lib_ver(cts_dev, &fwdata->lib_version); + if (ret < 0) { + cts_err("get_lib_ver failed"); + return -EINVAL; + } + ret = cts_tcs_get_ddi_ver(cts_dev, &fwdata->ddi_version); + if (ret < 0) { + cts_err("get_ddi_ver failed"); + return -EINVAL; + } + ret = cts_tcs_get_res_x(cts_dev, &fwdata->res_x); + if (ret < 0) { + cts_err("get_res_x failed"); + return -EINVAL; + } + ret = cts_tcs_get_res_y(cts_dev, &fwdata->res_y); + if (ret < 0) { + cts_err("get_res_y failed"); + return -EINVAL; + } + ret = cts_tcs_get_rows(cts_dev, &fwdata->rows); + if (ret < 0) { + cts_err("get_rows failed"); + return -EINVAL; + } + ret = cts_tcs_get_cols(cts_dev, &fwdata->cols); + if (ret < 0) { + cts_err("get_cols failed"); + return -EINVAL; + } + + ret = cts_tcs_get_rx_tx_info(cts_dev, info); + if (ret < 0) { + cts_err("get_rx_tx_info failed"); + } else { + fwdata->pc_cols = info[4]; + fwdata->pc_rows = info[5]; + fwdata->pr_cols = info[6]; + fwdata->pr_rows = info[7]; + } + + ret = cts_tcs_get_panel_rx_tx_info(cts_dev, info); + if (ret < 0) { + cts_err("get_panel_rx_tx_info failed"); + } else { + fwdata->pc_cols_used = info[11]; + fwdata->pc_rows_used = info[12]; + fwdata->pr_cols_used = info[13]; + fwdata->pr_rows_used = info[14]; + } + + ret = cts_tcs_get_flip_x(cts_dev, &fwdata->flip_x); + if (ret < 0) { + cts_err("get_flip_x failed"); + return -EINVAL; + } + ret = cts_tcs_get_flip_y(cts_dev, &fwdata->flip_y); + if (ret < 0) { + cts_err("get_flip_y failed"); + return -EINVAL; + } + ret = cts_tcs_get_swap_axes(cts_dev, &fwdata->swap_axes); + if (ret < 0) { + cts_err("get_swap_axes failed"); + return -EINVAL; + } + ret = cts_tcs_get_int_mode(cts_dev, &fwdata->int_mode); + if (ret < 0) { + cts_err("get_int_mode failed"); + return -EINVAL; + } + ret = cts_tcs_get_int_keep_time(cts_dev, &fwdata->int_keep_time); + if (ret < 0) { + cts_err("get_int_keep_time failed"); + return -EINVAL; + } + + ret = cts_tcs_get_rawdata_target(cts_dev, &fwdata->rawdata_target); + if (ret < 0) { + cts_err("get_rawdata_target failed"); + return -EINVAL; + } + + ret = cts_tcs_get_esd_method(cts_dev, &fwdata->esd_method); + if (ret < 0) { + cts_err("get_esd_method failed"); + return -EINVAL; + } + + ret = cts_tcs_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) { + ret = cts_tcs_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_CNT) { + cts_err("Invalid int data method: %d", + fwdata->int_data_method); + return -EINVAL; + } + + ret = cts_tcs_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; + + ret = cts_tcs_calc_int_data_size(cts_dev); + if (ret < 0) { + cts_err("calc_int_data_size 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); + 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); + + cts_err("pc_cols: %ld", fwdata->pc_cols); + cts_err("pc_rows: %ld", fwdata->pc_rows); + cts_err("pr_cols: %ld", fwdata->pr_cols); + cts_err("pr_rows: %ld", fwdata->pr_rows); + cts_err("pc_cols_used: %ld", fwdata->pc_cols_used); + cts_err("pc_rows_used: %ld", fwdata->pc_rows_used); + cts_err("pr_cols_used: %ld", fwdata->pr_cols_used); + cts_err("pr_rows_used: %ld", fwdata->pr_rows_used); + + return 0; +} + +#ifdef CONFIG_CTS_TP_PROXIMITY +static bool cts_is_proximity_enable(struct cts_device *cts_dev) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + return !!status->proximity; +} + +static int cts_enable_proximity_mode(struct cts_device *cts_dev) +{ + return cts_tcs_set_proximity_mode(cts_dev, 1); +} + +static int cts_exit_proximity_mode(struct cts_device *cts_dev) +{ + return cts_tcs_set_proximity_mode(cts_dev, 0); +} + +static void cts_handle_proximity_event(bool status) +{ + cts_info("Handler proximity '%s' event", status ? "Near" : "Far"); +} +#endif + +int cts_irq_handler(struct cts_device *cts_dev) +{ + struct cts_device_touch_info *touch_info; + u8 pwrmode = 3; + int ret; + + cts_dbg("Enter IRQ handler"); + + touch_info = &cts_dev->rtdata.touch_info; + ret = cts_tcs_get_touchinfo(cts_dev, touch_info); + if (ret) { + cts_err("Get touch info failed %d", ret); + return ret; + } + + if (unlikely(cts_dev->rtdata.suspended)) { +#ifdef CFG_CTS_GESTURE + if (cts_dev->rtdata.gesture_wakeup_enabled) { + struct cts_device_gesture_info *gesture_info; + + gesture_info = &cts_dev->rtdata.gesture_info; + memcpy(gesture_info, touch_info, sizeof(struct cts_device_gesture_info)); + + ret = cts_plat_process_gesture_info(cts_dev->pdata, gesture_info); + + if (cts_dev->fwdata.int_data_method != INT_DATA_METHOD_HOST) { + if (ret) + cts_err("Process gesture info failed %d", ret); + + ret = cts_tcs_set_pwr_mode(cts_dev, pwrmode); + if (ret) + cts_warn("Reenter suspend with gesture wakeup failed %d", ret); + + if (cts_tcs_clr_gstr_ready_flag(cts_dev)) + cts_err("Clear gesture ready flag failed"); + } + } else { + cts_warn("IRQ triggered while device suspended " + "without gesture wakeup enable"); + } +#endif /* CFG_CTS_GESTURE */ + } else { + cts_dbg("Touch info: vkey_state %x, finger num_msg %u", + touch_info->vkey_state, touch_info->num_msg); + +#ifdef CFG_CTS_PALM_DETECT + if (touch_info->finger_or_stylus == FINGER_ONLY && + CFG_CTS_PALM_ID == touch_info->vkey_state) { + cts_report_palm_event(cts_dev->pdata); + cts_plat_release_all_touch(cts_dev->pdata); + return 0; + } +#endif + +#ifdef CONFIG_CTS_TP_PROXIMITY + if (touch_info->finger_or_stylus == FINGER_ONLY && + cts_is_proximity_enable(cts_dev)) { + cts_dbg("proximity status:%d", cts_dev->rtdata.proximity_status); + if (!cts_dev->rtdata.proximity_status + && touch_info->vkey_state == CTS_CMD_PROXIMITY_STATUS) { + cts_dev->rtdata.proximity_num++; + if (cts_dev->rtdata.proximity_num == 4) { + cts_dev->rtdata.proximity_num = 0; + cts_dev->rtdata.proximity_status = true; + cts_handle_proximity_event(true); + return 0; + } + } else if (cts_dev->rtdata.proximity_status + && (touch_info->vkey_state == 0)) { + cts_dev->rtdata.proximity_num = 0; + cts_dev->rtdata.proximity_status = false; + cts_handle_proximity_event(false); + cts_plat_release_all_touch(cts_dev->pdata); + return 0; + } + } +#endif + + if (touch_info->finger_or_stylus == FINGER_ONLY) { + ret = cts_plat_process_touch_msg(cts_dev->pdata, + touch_info->msgs, touch_info->num_msg); + } else if (touch_info->finger_or_stylus == STYLUS_ONLY) { + struct cts_device_stylus_info *stylus_info = + (struct cts_device_stylus_info *)touch_info; + ret = cts_plat_process_stylus_msg(cts_dev->pdata, + stylus_info->msgs, stylus_info->event_num); + } else if (touch_info->finger_or_stylus == FINGER_STYLUS) { +#ifdef CFG_CTS_FINGER_STYLUS_SUPPORTED + ret = cts_plat_process_touch_stylus(cts_dev->pdata, touch_info); +#endif + } else { + cts_err("Unsupported finger_or_stylus: %d", + touch_info->finger_or_stylus); + ret = -EINVAL; + } + 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 + } + + return 0; +} + +bool cts_is_device_suspended(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"); + +/* Disable this check for sleep/gesture switch */ +/* + 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; + } + } + + cts_info("Set suspend mode:%s", + cts_dev->rtdata.gesture_wakeup_enabled ? "gesture" : "sleep"); + + buf = cts_dev->rtdata.gesture_wakeup_enabled ? 3 : 2; + ret = cts_tcs_set_pwr_mode(cts_dev, buf); + if (ret) { + cts_err("Suspend device failed %d", ret); + return ret; + } + + cts_info("Device suspended ..."); + cts_dev->rtdata.suspended = true; + + return 0; +} + +int cts_resume_device(struct cts_device *cts_dev) +{ + int retries = 0; + int ret = 0; + + cts_info("Resume device"); + + cts_reset_device(cts_dev); + + if (!cts_dev->rtdata.gesture_wakeup_enabled) { +#ifdef CONFIG_CTS_I2C_HOST + cts_info("Resume from sleep mode with flash, do not update."); +#else + if(cts_plat_is_normal_mode(cts_dev->pdata)) { + cts_info("Resume from sleep mode without flash, and firmware online then to normal state."); + goto normal_exit; + } else { + cts_info("Resume from sleep mode without flash, update now."); + goto update_firmware; + } +#endif + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret < 0) { + cts_err("Wait current normal mode failed"); + goto update_firmware; + } else { + goto normal_exit; + } + +update_firmware: + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + do { + ret = cts_update_firmware(cts_dev); + if (ret) { + cts_err("Update firmware failed times: %d", retries); + cts_reset_device(cts_dev); + } else { + break; + } + } while (++retries < 3); + if (ret) { + cts_err("Update default firmware failed %d", ret); + goto err_set_program_mode; + } + +normal_exit: + +#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 + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) { + cts_err("Set dev earjack attached failed %d", r); + } + } +#endif + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CONFIG_CTS_TP_PROXIMITY + if (cts_is_proximity_enable(cts_dev)) { + cts_enable_proximity_mode(cts_dev); + cts_info("after update firmware, enable proximity mode."); + } +#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(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.gesture_d_tap_enabled = false; + cts_dev->rtdata.esd_count = 0; +#ifdef CONFIG_CTS_TP_PROXIMITY + cts_dev->rtdata.proximity_num = 0; + cts_dev->rtdata.proximity_status = false; +#endif +} + +#ifdef FIX_DEFAULT_ENABLE_DRW +static int cts_disable_drw(struct cts_device *cts_dev) +{ + int ret; + + u8 txbuf[] = { + 0x60, + 0x07, 0x80, 0x34, + 0x00, 0x00, 0x04, + 0xdd, 0xe8, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0xdf, 0xfc, + 0x00, 0x00, 0x00 + }; + u8 rxbuf[] = { + 0x60, + 0x07, 0x80, 0x34, + 0x00, 0x00, 0x04, + 0xdd, 0xe8, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0xdf, 0xfc, + 0x00, 0x00, 0x00 + }; + + ret = cts_spi_send_recv(cts_dev->pdata, sizeof(txbuf), txbuf, rxbuf); + if (ret) + cts_err("disable drw ret=%d", ret); + return ret; +} +#endif + +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; */ + } + + if (cts_plat_is_normal_mode(cts_dev->pdata)) { + cts_tcs_wait_krang_stop(cts_dev); + } + +#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); +#else + cts_set_program_addr(cts_dev); + ret = cts_spi_send_recv(cts_dev->pdata, sizeof(magic_num), (u8 *)magic_num, NULL); + if (ret) { + cts_err("Write magic number to spi_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); + +#ifdef FIX_DEFAULT_ENABLE_DRW + cts_disable_drw(cts_dev); +#endif + + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) { + cts_err("Read BOOT_MODE failed %d", ret); + return ret; + } + /* Note: the following CTS_DEV_BOOT_MODE_TCH_PRG_9916 + is used by both ICNL9916 and ICNL9916C */ +#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--) { +#ifdef CFG_CTS_UPDATE_CRCCHECK + if (first_boot == 1) { +#else + if (first_boot == 1 || auto_boot == 0) { +#endif + 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; + + cts_set_normal_addr(cts_dev); + + 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_tcs_get_fw_id(cts_dev, &fwid); + if (ret) + cts_err("Get firmware id failed %d, retries %d", ret, retries); + else { + cts_info("Get firmware id: 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(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) || \ + defined(CONFIG_CTS_EARJACK_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 CFG_CTS_HEARTBEAT_MECHANISM + cts_enable_heartbeat_mechanism(cts_data); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_enable_esd_protection(cts_data); +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_start_charger_detect(cts_data); +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_start_earjack_detect(cts_data); +#endif + + 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 CFG_CTS_HEARTBEAT_MECHANISM + cts_disable_heartbeat_mechanism(cts_data); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_disable_esd_protection(cts_data); +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_stop_charger_detect(cts_data); +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_stop_earjack_detect(cts_data); +#endif + +#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_data->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 + + 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; +} + +static 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; + } + + 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; +} + +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; + + cts_dev->fwdata.version = 0; + + cts_info("Probe device"); + + cts_init_rtdata_with_normal_mode(cts_dev); + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { + cts_warn("Normal mode spi addr is offline"); + } else { + ret = cts_tcs_get_fw_id(cts_dev, &fwid); + if (ret) { + cts_err("Get firmware id failed %d", ret); + } else { + cts_info("Firmware id: 0x%04x", fwid); + ret = cts_tcs_get_fw_ver(cts_dev, &cts_dev->fwdata.version); + if (ret) { + cts_err("Read firmware version failed %d", ret); + cts_dev->fwdata.version = 0; + } else { + cts_info("Firmware version: 0x%04x", cts_dev->fwdata.version); + } + goto init_hwdata; + } + } + +read_hwid: + 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; + } + +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; + } + + cts_info("Touch info size:%zu", sizeof(struct cts_device_touch_info)); + + 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(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.gesture_wakeup_enabled; +} + +int cts_get_gesture_info(struct cts_device *cts_dev, void *gesture_info) +{ + 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_tcs_get_gestureinfo(cts_dev, gesture_info); + if (ret) { + cts_err("Read gesture info header failed %d", ret); + return ret; + } + + return 0; +} + +bool cts_is_d_tap_supported(struct cts_device *cts_dev) +{ + return true; +} + +int cts_set_d_tap_enabled(struct cts_device *cts_dev, bool enabled) +{ + cts_dev->rtdata.gesture_d_tap_enabled = enabled; + return 0; +} +#else +bool cts_is_d_tap_supported(struct cts_device *cts_dev) +{ + return false; +} + +int cts_set_d_tap_enabled(struct cts_device *cts_dev, bool enabled) +{ + return 0; +} +#endif /* CFG_CTS_GESTURE */ + +int cts_set_int_data_types(struct cts_device *cts_dev, u16 types) +{ + int ret; + u16 realtypes = types & INT_DATA_TYPE_MASK; + + cts_info("Set int data types: %#06x, mask to %#06x", types, realtypes); + + ret = cts_tcs_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; + + cts_tcs_calc_int_data_size(cts_dev); + + 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_CNT) { + cts_err("Invalid int data method"); + return -EINVAL; + } + + ret = cts_tcs_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; + + cts_tcs_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; + struct cts_device *cts_dev; + int ret = 0; + + cts_dbg("ESD protection work"); + cts_data = container_of(work, struct chipone_ts_data, esd_work.work); + cts_dev = &cts_data->cts_dev; + + cts_lock_device(cts_dev); + if (!cts_plat_is_normal_mode(cts_data->pdata)) { + 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_dev); + 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) { + cts_warn("ESD protection check failed, update firmware!!!"); + cts_stop_device_esdrecover(cts_dev); + cts_reset_device(cts_dev); + ret = cts_update_firmware(cts_dev); + if (ret) + cts_err("Update default firmware failed %d", ret); + + cts_start_device_esdrecover(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_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 CFG_CTS_HEARTBEAT_MECHANISM +void cts_show_touch_debug_msg(void *debug) +{ + struct cts_touch_debug_msg *msg = (struct cts_touch_debug_msg *)debug; + struct cts_firmware_status *status = (struct cts_firmware_status *) + &msg->firmware_status; + + cts_info("FW_Ver : 0x%02X", msg->fw_version); + cts_info("Reset_Flag : 0x%08X", msg->reset_flag); + + cts_info("Charger : %d", status->charger); + cts_info("Proxmty : %d", status->proximity); + cts_info("Earjack : %d", status->earjack); + cts_info("Knuckle : %d", status->knuckle); + cts_info("Glove : %d", status->glove); + cts_info("Pocket : %d", status->pocket); + cts_info("Game : %d", status->game); + + cts_info("High_Temp : %d", status->high_temperature); + cts_info("Low_Temp : %d", status->low_temperature); + cts_info("Bend : %d", status->bend_mode); + cts_info("Palm : %d", status->palm_status); + cts_info("Noise : %d", status->noise_mode); + cts_info("Base_Trace : %d", status->base_trace); + cts_info("Water : %d", status->water_mode); + cts_info("Ground : %d", status->ground); + + cts_info("Proxmity_Sts: 0x%02x", msg->proximity); + + cts_info("Work_Mode : %d", msg->work_mode); + cts_info("Power_Mode : %d", msg->power_mode); + cts_info("Freq : %d", msg->curr_freq); + cts_info("Esd_0a : %d", msg->esd_0a_status); + cts_info("Fw_Esd : %d", msg->fw_esd_status); + cts_info("Landscape : %d", msg->landscape_mode); + + cts_info("Freq_Noise : %d", msg->curr_freq_noise); + cts_info("Max_Diff : %d", msg->max_diff); + cts_info("Row : %d", msg->max_diff_row); + cts_info("Col : %d", msg->max_diff_col); + cts_info("Max_Neg : -%d", msg->max_neg_diff); + cts_info("Neg_Row : %d", msg->max_neg_diff_row); + cts_info("Neg_Col : %d", msg->max_neg_diff_col); +} + +void cts_heartbeat_mechanism_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + struct cts_device_touch_info info; + u8 curr_status; + int ret = 0; + + cts_data = container_of(work, struct chipone_ts_data, heart_work.work); + cts_dev = &cts_data->cts_dev; + + cts_dbg("heartbeat mechanism work"); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_touch_status(cts_dev); + if (ret) { + cts_err("get touch status failed"); + + cts_reset_device(cts_dev); + + if (++cts_dev->rtdata.esd_count < 3) + goto end; + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + ret = cts_update_firmware(cts_dev); + if (ret) + cts_err("Update default firmware failed %d", ret); + + cts_tcs_reinit_fw_status(cts_dev); + goto end; + } + + memcpy(&info, cts_dev->int_data, sizeof(info)); + + curr_status = info.debug_msg.firmware_status; + + cts_dev->rtdata.esd_count = 0; + + /* 9922C reset_flag:0xFFFFFF; 9922 9916 9916C reset_flag:0xFFFF */ + if ((info.debug_msg.reset_flag & 0xFFFFFF) != 0xFFFFFF + || (info.debug_msg.reset_flag & 0xFFFF) != 0xFFFF) { + cts_err("heartbeat reset flag:0x%08x error", info.debug_msg.reset_flag); + cts_show_touch_debug_msg(&info.debug_msg); + } + + if (curr_status != cts_dev->rtdata.firmware_status) { + cts_err("firmware status error"); + cts_show_touch_debug_msg(&info.debug_msg); + cts_tcs_reinit_fw_status(cts_dev); + } + +end: + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + cts_unlock_device(cts_dev); +} + +void cts_enable_heartbeat_mechanism(struct chipone_ts_data *cts_data) +{ + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); +} + +void cts_disable_heartbeat_mechanism(struct chipone_ts_data *cts_data) +{ + cancel_delayed_work_sync(&cts_data->heart_work); +} +#endif + +#ifdef CONFIG_CTS_GLOVE +int cts_enter_glove_mode(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Enter glove mode"); + + ret = cts_tcs_set_glove_mode(cts_dev, 1); + if (ret) + cts_err("Enable Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = true; + + return ret; +} + +int cts_exit_glove_mode(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Exit glove mode"); + + ret = cts_tcs_set_glove_mode(cts_dev, 0); + if (ret) + cts_err("Exit Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = false; + + return ret; +} + +int cts_is_glove_enabled(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.glove_mode_enabled; +} + +int cts_set_glove_enabled(struct cts_device *cts_dev, bool enabled) +{ + cts_dev->rtdata.glove_mode_enabled = enabled; + return 0; +} +#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; + u8 buf; + + cts_info("Set dev charger %s", attached ? "ATTACHED" : "DETATCHED"); + buf = attached ? 1 : 0; + + ret = cts_tcs_set_charger_plug(cts_dev, buf); + if (ret) + cts_err("Set failed %d", ret); + + return ret; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_EARJACK_DETECT +bool cts_is_earjack_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_earjack_attached(cts_data, &attached); + if (ret) { + cts_err("Get earjack state failed %d", ret); + } + + return attached; +} + +int cts_set_dev_earjack_attached(struct cts_device *cts_dev, bool attached) +{ + int ret; + u8 buf; + + cts_info("Set dev earjack %s", attached ? "ATTACHED" : "DETATCHED"); + buf = attached ? 1 : 0; + ret = cts_tcs_set_earjack_plug(cts_dev, buf); + if (ret) + cts_err("Set failed %d", ret); + + return ret; +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +#if 0 +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; +} +#endif + +void cts_firmware_upgrade_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + u32 hwid; + int retries; + int ret = 0; + + cts_info("Firmware upgrade work"); + + cts_data = container_of(work, struct chipone_ts_data, fw_upgrade_work.work); + cts_dev = &cts_data->cts_dev; + hwid = cts_dev->hwdata->hwid; + + ret = cts_request_firmware(cts_dev, hwid, CTS_DEV_FWID_ANY, 0); + if (ret) { + cts_err("Get firmware failed!!!, Please add builtin or bin firmware!"); + return; + } + + cts_lock_device(cts_dev); + if (cts_dev->fwdata.version >= FIRMWARE_VERSION(cts_dev->firmware)) { + cts_info("Do not update, (curr) 0x%04x >= 0x%04x (local)", + cts_dev->fwdata.version, FIRMWARE_VERSION(cts_dev->firmware)); + cts_set_program_addr(cts_dev); + cts_enter_normal_mode(cts_dev); + cts_start_device(cts_dev); + cts_unlock_device(cts_dev); + return; + } + + for(retries = 0; retries < 3; retries++) { + cts_reset_device(cts_dev); + ret = cts_update_firmware(cts_dev); + if (ret) { + cts_err("Update firmware failed times: %d", retries); + } else { + cts_info("Update firmware successfully"); + break; + } + } + cts_start_device(cts_dev); + cts_unlock_device(cts_dev); + + return; +} + +int cts_reset_device(struct cts_device *cts_dev) +{ + return cts_plat_reset_device(cts_dev->pdata); +} + +int cts_wait_current_mode(struct cts_device *cts_dev, u8 tar_mode) +{ + int i = 0; + int ret; + u8 work_mode; + + do { + mdelay(100); + ret = cts_tcs_get_krang_current_workmode(cts_dev, &work_mode); + if (ret == 0 && work_mode == tar_mode) { + cts_info("wait work_mode:%d okay", work_mode); + return ret; + } + } while (++i < 10); + + cts_err("wait tar_mode:%d failed, retries:%d",tar_mode, i); + return -ETIMEDOUT; +} + +static struct file *cts_log_filp; +static int cts_log_to_file_level; + +static char *cts_log_buffer; +static int cts_log_buf_size; +static int cts_log_buf_wr_size; + +static bool cts_log_redirect; + +int cts_start_driver_log_redirect(const char *filepath, bool append_to_file, + char *log_buffer, int log_buf_size, int log_level) +{ +#define START_BANNER \ + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + int ret = 0; + + cts_info("Start driver log redirect"); + + cts_log_to_file_level = log_level; + + if (log_buffer && log_buf_size) { + cts_info(" - Start driver log to buffer: %p size: %d level: %d", + log_buffer, log_buf_size, log_level); + cts_log_buffer = log_buffer; + cts_log_buf_size = log_buf_size; + cts_log_buf_wr_size = 0; + } + + if (filepath && filepath[0]) { + cts_info(" - Start driver log to file : '%s' level: %d", + filepath, log_level); +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); +#else + cts_log_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append_to_file ? O_APPEND : O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_log_filp)) { + ret = PTR_ERR(cts_log_filp); + cts_log_filp = NULL; + cts_err("Open file '%s' for driver log failed %d", + filepath, ret); + } else { + cts_write_file(cts_log_filp, START_BANNER, strlen(START_BANNER)); + } +#endif + } + + cts_log_redirect = true; + + return ret; +#undef START_BANNER +} + +void cts_stop_driver_log_redirect(void) +{ +#define END_BANNER \ + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" +#ifndef CFG_CTS_FOR_GKI + int ret; +#endif + + cts_log_redirect = false; + + cts_info("Stop driver log redirect"); + + if (cts_log_filp) { + cts_info(" - Stop driver log to file"); + + cts_write_file(cts_log_filp, END_BANNER, strlen(END_BANNER)); +#ifndef CFG_CTS_FOR_GKI + ret = filp_close(cts_log_filp, NULL); + if (ret) { + cts_err("Close driver log file failed %d", ret); + } +#endif + cts_log_filp = NULL; + } + + if (cts_log_buffer) { + cts_info(" - Stop driver log to buffer"); + + cts_log_buffer = NULL; + cts_log_buf_size = 0; + cts_log_buf_wr_size = 0; + } + +#undef END_BANNER +} + +int cts_get_driver_log_redirect_size(void) +{ + if (cts_log_redirect && cts_log_buffer && cts_log_buf_wr_size) { + return cts_log_buf_wr_size; + } else { + return 0; + } +} + +void cts_log(int level, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (cts_log_redirect) { + if (cts_log_buffer && + cts_log_buf_wr_size < cts_log_buf_size && + level <= cts_log_to_file_level) { + cts_log_buf_wr_size += vscnprintf(cts_log_buffer + cts_log_buf_wr_size, + cts_log_buf_size - cts_log_buf_wr_size, fmt, args); + } + + if (cts_log_filp != NULL && level <= cts_log_to_file_level) { + char buf[512]; + int count = vscnprintf(buf, sizeof(buf), fmt, args); + + cts_write_file(cts_log_filp, buf, count); + } + } + + if (level < CTS_DRIVER_LOG_DEBUG || cts_show_debug_log) { + vprintk(fmt, args); + } + + va_end(args); +} + + +#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 diff --git a/drivers/input/touchscreen/chipone_tddi/cts_core.h b/drivers/input/touchscreen/chipone_tddi/cts_core.h new file mode 100644 index 000000000000..955491378292 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_core.h @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_CORE_H +#define CTS_CORE_H + +#include "cts_config.h" + +enum cts_dev_hw_reg { + CTS_DEV_HW_REG_HARDWARE_ID = 0x70000u, + CTS_DEV_HW_REG_CLOCK_GATING = 0x70004u, + CTS_DEV_HW_REG_RESET_CONFIG = 0x70008u, + CTS_DEV_HW_REG_SW_RST_CTRL = 0x7000Au, + CTS_DEV_HW_REG_BOOT_MODE = 0x70010u, + CTS_DEV_HW_REG_CURRENT_MODE = 0x70011u, + CTS_DEV_HW_REG_DDI_REG_CTRL = 0x7002Cu, + CTS_DEV_HW_REG_CLK_DIV_CFG = 0x70033u, + CTS_DEV_HW_REG_HPPROG = 0x70074u, + CTS_DEV_HW_REG_SET_RESET = 0x700F8u, + CTS_DEV_HW_REG_HW_STATUS = 0x7401Bu, + CTS_DEV_HW_REG_I2C_CFG = 0x77001u, + CTS_DEV_HW_REG_SPI_CFG = 0x78438u, +}; + +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, +}; + +/* ICNL9951/ICNL9951R */ +#define CTS_PWR_MODE_ACTIVE (0x00) +#define CTS_PWR_MODE_MNT (0x01) +#define CTS_PWR_MODE_HIBERNATE (0x02) +#define CTS_PWR_MODE_GSTR_WAKEUP (0x03) +#define CTS_PWR_MODE_MNT_DBG (0x04) +#define CTS_PWR_MODE_GSTR_DBG (0x05) +#define CTS_PWR_MODE_WAIT_TO_HIBERNATE (0x06) +#define CTS_PWR_MODE_WAIT_TO_GSTR (0x07) + +//Krang work mode value +#define CTS_KRANG_NORMAL_MODE (0x00) +#define CTS_KRANG_STY_DBG_MODE (0x01) +#define CTS_KRANG_FIN_DBG_MODE (0x02) +#define CTS_KRANG_MNT_DBG_MODE (0x03) +#define CTS_KRANG_GS_DBG_MODE (0x04) +#define CTS_KRANG_OPEN_SHORT_DET_MODE (0x05) +#define CTS_KRANG_CFG_MODE (0x06) +#define CTS_KRANG_TEST_MODE (0x07) +#define CTS_KRANG_DEF_MODE (0x08) + +/** 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, + 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, + 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_ICNL9951 = 0x990510u, + CTS_DEV_HWID_ICNL9951R = 0x991510u, + + 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_ICNL9951 = 0x9951u, + CTS_DEV_FWID_ICNL9951R = 0x99a3u, + + 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, + +#ifdef CONFIG_CTS_TP_PROXIMITY + CTS_CMD_PROXIMITY_STATUS = 0x80, + /* For vivo code, make a distinction between palm func and proximity func */ + CTS_CMD_PROXIMITY_NEAR_VIVO = 0x30, + CTS_CMD_PROXIMITY_FAR_VIVO = 0x31, +#endif +}; + +#pragma pack(1) +/** Touch message read back from chip */ +struct cts_device_touch_msg { + u8 event:4; + u8 id:4; + + u8 xh:4; + u8 yh:4; + + u8 xl; + u8 yl; + + u8 pressure; + +#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) + +}; + +/** Stylus message read back from chip */ +struct cts_device_stylus_msg { + u8 event:4; + u8 id:4; + + u8 tip_xh:4; + u8 tip_yh:4; + u8 tip_xl; + u8 tip_yl; + + u8 ring_xh:4; + u8 ring_yh:4; + u8 ring_xl; + u8 ring_yl; + + u8 battary; + u8 tilt; + + u8 pressure_l; + u8 pressure_h:5;//use low 5 bits + u8 btn0:1; + u8 btn1:1; + u8 btn2:1; + + u8 tiltx; + u8 tilty; +}; + +struct cts_firmware_status { + uint16_t charger:1; + uint16_t proximity:1; + uint16_t earjack:1; + uint16_t knuckle:1; + uint16_t glove:1; + uint16_t pocket:1; + uint16_t game:1; + uint16_t reserved:1; + uint16_t high_temperature:1; + uint16_t low_temperature:1; + uint16_t bend_mode:1; + uint16_t palm_status:1; + uint16_t noise_mode:1; + uint16_t base_trace:1; + uint16_t water_mode:1; + uint16_t ground:1; +}; +/** Touch debug messages read back from chip [40 bytes] */ +struct cts_touch_debug_msg { + u8 protocol_version; + u8 fw_version; + u32 reset_flag; + u16 frame_index; + u16 firmware_status; + + u8 proximity; + u8 work_mode; + u8 power_mode; + u8 curr_freq; + u8 esd_0a_status; + u8 fw_esd_status; + u8 landscape_mode; + u16 curr_freq_noise; + u16 max_diff; + u8 max_diff_row; + u8 max_diff_col; + u16 max_neg_diff; + u8 max_neg_diff_row; + u8 max_neg_diff_col; + u8 data_method; + u8 data_type; + u8 reserved[11]; +}; + +/** Touch information read back from chip */ +struct cts_device_touch_info { + u8 vkey_state; + + u8 num_msg:4; +#define PROTO_NORMAL 0x00 +#define PROTO_COMPRESSED 0x01 + u8 compressed_proto:2; +#define FINGER_ONLY 0x01 +#define STYLUS_ONLY 0x02 +#define FINGER_STYLUS 0x03 + u8 finger_or_stylus:2; + + u8 reserved0; + + struct cts_device_touch_msg msgs[CFG_CTS_MAX_TOUCH_NUM]; + + u8 proximity; + u8 palm_major; + u8 palm_minor; + u8 palm_flags; + +#ifdef CFG_CTS_FINGER_STYLUS_SUPPORTED + u8 stylus_num:4; + u8 stylus_vernum:4; + + struct cts_device_stylus_msg smsgs[CFG_CTS_MAX_STYLUS_NUM]; + + u8 reserved1; + u8 reserved2; +#endif +}; + +struct cts_device_stylus_info { + u8 vkey_state; + + u8 num_msg:4; + u8 compressed_proto:2; + u8 finger_or_stylus:2; + + u8 reserved0; + + u8 event_num:4; + u8 stylus_vernum:4; + + struct cts_device_stylus_msg msgs[CFG_CTS_MAX_STYLUS_NUM]; + + u8 reserved1; +}; + + +/** Gesture trace point read back from chip */ +struct cts_device_gesture_point { + __le16 x; + __le16 y; +}; + +/** Gesture information read back from chip */ +/* total size 112 bytes */ +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) +#define CTS_GESTURE_TAP (0x7F) + + u8 num_points; + +#define CTS_CHIP_MAX_GESTURE_TRACE_POINT (27u) + struct cts_device_gesture_point points[CTS_CHIP_MAX_GESTURE_TRACE_POINT]; + u8 reserved[2]; + +}; + +struct cts_tcs_cmd { + uint16_t cmd_id : 8; + uint16_t class_id : 5; + uint16_t is_write : 1; + uint16_t is_read : 1; + uint16_t base_flag : 1; +}; +#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 = 0xF03F, +}; + +enum int_data_method { + INT_DATA_METHOD_NONE = 0, + INT_DATA_METHOD_HOST = 1, + INT_DATA_METHOD_POLLING = 2, + INT_DATA_METHOD_DEBUG = 3, + INT_DATA_METHOD_CNT = 4, +}; + +/** 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; + + u8 ic_application; + u8 pen_freq_num; + u8 cascade_num; + + u8 pc_cols; + u8 pc_rows; + u8 pr_cols; + u8 pr_rows; + + u8 pc_cols_used; + u8 pc_rows_used; + u8 pr_cols_used; + u8 pr_rows_used; +}; + +/** 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 gesture_d_tap_enabled; + bool charger_exist; + bool fw_log_redirect_enabled; + bool glove_mode_enabled; + + u8 esd_count; + u16 firmware_status; + + struct cts_device_touch_info touch_info; + struct cts_device_gesture_info gesture_info; + +#ifdef CONFIG_CTS_TP_PROXIMITY + bool proximity_status; + int proximity_num; +#endif + + u16 tcscmd[ALIGN(PAGE_SIZE + 10, 4)]; + int tcscmd_len; + u16 curr_cmd; + int curr_len; +}; + +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; +}; + +struct cts_device { + struct cts_platform_data *pdata; + + const struct cts_device_hwdata *hwdata; + struct cts_device_fwdata fwdata; + struct cts_device_rtdata rtdata; + struct cts_firmware *firmware; + const struct cts_flash *flash; + bool enabled; + u8 int_data[ALIGN(CFG_CTS_INT_DATA_MAX_SIZE + 10, 4)]; +}; + +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 + 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 + +#ifdef CONFIG_CTS_EARJACK_DETECT + void *earjack_detect_data; +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + struct workqueue_struct *esd_workqueue; + struct delayed_work esd_work; + bool esd_enabled; + int esd_check_fail_cnt; +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + struct workqueue_struct *heart_workqueue; + struct delayed_work heart_work; +#endif + +#ifdef CONFIG_CTS_LEGACY_TOOL + struct proc_dir_entry *procfs_entry; +#endif + + void *oem_data; + + bool force_reflash; + // struct kobject *suspend_kobj; +#ifdef CONFIG_PM_DSI_EXTCON_NOTIFIER + struct extcon_dev *edev; + struct notifier_block extcon_nb; +#endif +}; + +/*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(struct cts_device *cts_dev); +extern void cts_unlock_device(struct cts_device *cts_dev); + +extern int cts_dev_readb(struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_dev_writeb(struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); + +extern int cts_sram_writeb_retry(struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +extern int cts_sram_writew_retry(struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay); +extern int cts_sram_writel_retry(struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay); +extern int cts_sram_writesb_retry(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(struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, u32 crc, int retry); + +extern int cts_sram_readb_retry(struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_sram_readw_retry(struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay); +extern int cts_sram_readl_retry(struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay); +extern int cts_sram_readsb_retry(struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay); + +extern int cts_fw_reg_writeb_retry(struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_fw_reg_writew_retry(struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_fw_reg_writel_retry(struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_fw_reg_writesb_retry(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(struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_fw_reg_readw_retry(struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_fw_reg_readl_retry(struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_fw_reg_readsb_retry(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(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(struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_hw_reg_writew_retry(struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_hw_reg_writel_retry(struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_hw_reg_writesb_retry(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(struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_hw_reg_readw_retry(struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_hw_reg_readl_retry(struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_hw_reg_readsb_retry(struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, int delay); + +static inline int cts_hw_reg_readb(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(struct cts_device *cts_dev, u32 addr, + const void *src, size_t len) +{ + return cts_sram_writesb_retry(cts_dev, addr, src, len, 10, 0); +} + +static inline int cts_sram_readb(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(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(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(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; +} + +int cts_i2c_writeb(struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +int cts_i2c_writeb(struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +int cts_i2c_writesb(struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay); +int cts_i2c_readb(struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +int cts_i2c_readl(struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay); + +#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(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(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_send_command(struct cts_device *cts_dev, u8 cmd); + +extern int cts_read_sram_normal_mode(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(struct cts_device *cts_dev); +extern int cts_get_gesture_info(struct cts_device *cts_dev, void *gesture_info); +#endif /* CFG_CTS_GESTURE */ +extern bool cts_is_d_tap_supported(struct cts_device *cts_dev); +extern int cts_set_d_tap_enabled(struct cts_device *cts_dev, bool enabled); + +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 CFG_CTS_HEARTBEAT_MECHANISM +void cts_show_touch_debug_msg(void *debug); +void cts_heartbeat_mechanism_work(struct work_struct *work); +void cts_enable_heartbeat_mechanism(struct chipone_ts_data *cts_data); +void cts_disable_heartbeat_mechanism(struct chipone_ts_data *cts_data); +#endif + +#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(struct cts_device *cts_dev); +int cts_set_glove_enabled(struct cts_device *cts_dev, bool enabled); +#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(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_set_glove_enabled(struct cts_device *cts_dev, bool enabled) +{ + 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_EARJACK_DETECT +extern bool cts_is_earjack_exist(struct cts_device *cts_dev); +extern int cts_set_dev_earjack_attached(struct cts_device *cts_dev, bool attached); +#else /* CONFIG_CTS_EARJACK_DETECT */ +static inline bool cts_is_earjack_exist(struct cts_device *cts_dev) +{ + return false; +} +static inline int cts_set_dev_earjack_attached(struct cts_device *cts_dev, bool attached) +{ + return 0; +} +#endif /* CONFIG_CTS_EARJACK_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(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_UPDATE_CRCCHECK +extern int cts_sram_writesb_boot_crc_retry(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 int cts_reset_device(struct cts_device *cts_dev); +extern int cts_wait_current_mode(struct cts_device *cts_dev, u8 tar_mode); + +extern int kstrtobool(const char *s, bool *res); + +extern int cts_suspend(struct chipone_ts_data *cts_data); +extern int cts_resume(struct chipone_ts_data *cts_data); + +extern struct chipone_ts_data *g_cts_data; + +#endif /* CTS_CORE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi/cts_driver.c b/drivers/input/touchscreen/chipone_tddi/cts_driver.c new file mode 100644 index 000000000000..3ebd444b2eb0 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_driver.c @@ -0,0 +1,1231 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef CONFIG_CTS_I2C_HOST +#define LOG_TAG "I2CDrv" +#else +#define LOG_TAG "SPIDrv" +#endif +#include +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_charger_detect.h" +#include "cts_earjack_detect.h" +#include "cts_oem.h" +#include "cts_strerror.h" +#include "cts_firmware.h" + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#include "tpd.h" +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#else /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ +#ifdef CFG_CTS_DRM_NOTIFIER +#include +static struct drm_panel *active_panel; +static int check_dt(struct device_node *np); +#endif /* CFG_CTS_DRM_NOTIFIER */ +#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + +static void cts_resume_work_func(struct work_struct *work); + +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"); + +struct chipone_ts_data *g_cts_data = NULL; + +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; +} + +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); +} + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#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 || !evdata->data || !cts_data) + return 0; + + blank = *(int *)evdata->data; + cts_info("action=%lu, blank=%d", action, blank); + + if (action == DRM_PANEL_EARLY_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_POWERDOWN) + cts_suspend(cts_data); + } else { + 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) + cts_err("%s: %s not actived", __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", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + cts_err("%s:fail to read %s %d", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + cts_err("no match compatible, %s, %s", compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} +#endif +#endif + +#ifdef CTS_MTK_GET_PANEL +char panel_name[50] = { 0 }; + +static int cts_get_panel(void) +{ + int ret = -1; + + cts_info("Enter cts_get_panel"); + 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_PM_DSI_EXTCON_NOTIFIER +static int ts_extcon_notifier(struct notifier_block *self, + unsigned long event, void *data) +{ + struct chipone_ts_data *cts_data = + container_of(self, struct chipone_ts_data, extcon_nb); + if (extcon_get_state(cts_data->edev, EXTCON_JACK_VIDEO_OUT)){ + pr_info("%s %d\n", __func__, __LINE__); + //cts_plat_set_reset(cts_data->pdata, 1); + //mdelay(1); + //cts_plat_set_reset(cts_data->pdata, 0); + //mdelay(5);/* 1ms */ + //cts_plat_set_reset(cts_data->pdata, 1); + cts_resume(cts_data); + } else { + cts_suspend(cts_data); + pr_info("%s %d\n", __func__, __LINE__); + //cts_plat_set_reset(cts_data->pdata, 0); + } + + 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; + struct cts_device *cts_dev = NULL; + struct cts_firmware *firmware = 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 + if (client == NULL) { + cts_err("Probe i2c client = NULL"); + return -EINVAL; + } + 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; + } +#else + if (client == NULL) { + cts_info("Probe spi client = NULL"); + return -EINVAL; + } + cts_info("Probe spi device '%s': " + "mode='%u' speed=%u bits_per_word=%u " + "chip_select=%u, cs_gpio=%d irq=%d", + client->modalias, client->mode, client->max_speed_hz, client->bits_per_word, + client->chip_select, client->cs_gpio, client->irq); +#endif + + cts_data = kzalloc(sizeof(struct chipone_ts_data), GFP_KERNEL); + if (cts_data == NULL) { + cts_err("Allocate chipone_ts_data failed"); + return -ENOMEM; + } + + cts_data->pdata = 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 + + ret = cts_init_platform_data(cts_data->pdata, client); + if (ret) { + cts_err("Init platform data failed %d", ret); + goto err_free_pdata; + } + + cts_data->cts_dev.pdata = cts_data->pdata; + cts_data->pdata->cts_dev = &cts_data->cts_dev; + cts_dev = cts_data->pdata->cts_dev; + g_cts_data = cts_data; + + firmware = kzalloc(sizeof(struct cts_firmware), GFP_KERNEL); + if (firmware == NULL) { + cts_err("Create firmware buffer failed"); + goto err_free_firmware_buffer; + } + + firmware->data = kzalloc(CFG_CTS_FIRMWARE_SIZE, GFP_KERNEL); + if (firmware->data == NULL) { + cts_err("Create firmware data buffer failed"); + goto err_free_firmware_data_buffer; + } + + cts_dev->firmware = firmware; + + 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 + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + cts_data->heart_workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-heart_workqueue"); + if (cts_data->heart_workqueue == NULL) { + cts_err("Create heart workqueue failed"); + ret = -ENOMEM; + goto err_destroy_esd_workqueue; + } +#endif + + ret = cts_plat_request_resource(cts_data->pdata); + if (ret < 0) { + cts_err("Request resource failed %d", ret); + goto err_destroy_heart_workqueue; + } + + ret = cts_reset_device(cts_dev); + if (ret < 0) { + cts_err("Reset device failed %d", ret); + goto err_free_resource; + } + + ret = cts_probe_device(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); + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#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 +#endif + + 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_CHARGER_DETECT + ret = cts_charger_detect_init(cts_data); + if (ret) + cts_err("Init charger detect failed %d", ret); + /* Ignore this error */ +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + ret = cts_earjack_detect_init(cts_data); + if (ret) { + cts_err("Init earjack detect failed %d", ret); + // Ignore this error + } +#endif + + ret = cts_oem_init(cts_data); + if (ret < 0) { + cts_warn("Init oem specific faild %d", ret); + goto err_deinit_oem; + } + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + INIT_DELAYED_WORK(&cts_data->heart_work, cts_heartbeat_mechanism_work); +#endif + INIT_WORK(&cts_data->ts_resume_work, cts_resume_work_func); + + /* 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(5 * 1000)); + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#ifdef CONFIG_MTK_PLATFORM + tpd_load_status = 1; +#endif /* CONFIG_MTK_PLATFORM */ +#endif + +#ifdef CONFIG_PM_DSI_EXTCON_NOTIFIER + cts_data->edev = extcon_get_edev_by_phandle(&client->dev, 0); + if (IS_ERR(cts_data->edev)) { + if (PTR_ERR(cts_data->edev) != -EPROBE_DEFER) + dev_err(&client->dev, "Invalid or missing extcon\n"); + pr_info("register notifier...but extcon_get_edev error\n"); + return 0; + } + + cts_data->extcon_nb.notifier_call = ts_extcon_notifier; + ret = extcon_register_notifier(cts_data->edev, EXTCON_JACK_VIDEO_OUT, + &cts_data->extcon_nb); + pr_info("register notifier...\n"); + if (ret < 0) { + pr_info("fail to register notifier...\n"); + dev_err(&client->dev, "failed to register notifier for TP\n"); + return ret; + } +#endif + + return 0; + +err_deinit_oem: + cts_oem_deinit(cts_data); + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_charger_detect_deinit(cts_data); +#endif +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_earjack_detect_deinit(cts_data); +#endif + + cts_plat_free_irq(cts_data->pdata); + +err_register_fb: +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +err_deinit_sysfs: +#endif +#endif + + cts_sysfs_remove_device(&client->dev); +#ifdef CONFIG_CTS_LEGACY_TOOL + cts_tool_deinit(cts_data); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_deinit_esd_protection(cts_data); +#endif + +#ifdef CFG_CTS_GESTURE + cts_plat_deinit_gesture(cts_data->pdata); +#endif + +err_deinit_vkey_device: +#ifdef CONFIG_CTS_VIRTUALKEY + cts_plat_deinit_vkey_device(cts_data->pdata); +#endif + +err_deinit_touch_device: + cts_plat_deinit_touch_device(cts_data->pdata); + +err_free_resource: + cts_plat_free_resource(cts_data->pdata); + +err_destroy_heart_workqueue: +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + destroy_workqueue(cts_data->heart_workqueue); +err_destroy_esd_workqueue: +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + destroy_workqueue(cts_data->esd_workqueue); +err_destroy_workqueue: +#endif + destroy_workqueue(cts_data->workqueue); + +err_deinit_platform_data: + if (firmware->data) { + kfree(firmware->data); + firmware->data = NULL; + } + +err_free_firmware_data_buffer: + if (firmware) { + kfree(firmware); + firmware = NULL; + } + +err_free_firmware_buffer: +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + cts_deinit_platform_data(cts_data->pdata); +#endif +err_free_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 void cts_driver_remove(struct i2c_client *client) +#else +static void 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_CHARGER_DETECT + cts_charger_detect_deinit(cts_data); +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_earjack_detect_deinit(cts_data); +#endif + + cts_plat_free_irq(cts_data->pdata); + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +#endif +#endif + + 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); + + cts_oem_deinit(cts_data); + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (cts_data->heart_workqueue) + destroy_workqueue(cts_data->heart_workqueue); +#endif + +#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); + + if (cts_data->cts_dev.firmware->data) { + kfree(cts_data->cts_dev.firmware->data); + cts_data->cts_dev.firmware->data = NULL; + } + + if (cts_data->cts_dev.firmware) { + kfree(cts_data->cts_dev.firmware); + cts_data->cts_dev.firmware = NULL; + } + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + cts_deinit_platform_data(cts_data->pdata); +#endif + if (cts_data->pdata) + kfree(cts_data->pdata); + + kfree(cts_data); + } else { + cts_warn("Chipone i2c driver remove while NULL chipone_ts_data"); + } + +} + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#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 */ +#endif + +#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 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_driver_config_attrs[] = { + &driver_attr_reset_pin.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_driver_config_group = { + .name = "config", + .attrs = cts_driver_config_attrs, +}; + +static const struct attribute_group *cts_driver_config_groups[] = { + &cts_driver_config_group, + NULL, +}; +#endif /* CONFIG_CTS_SYSFS */ + +#ifdef CONFIG_CTS_OF +static const struct of_device_id cts_driver_of_match_table[] = { + {.compatible = CFG_CTS_OF_DEVICE_ID_NAME, }, + { }, +}; + +MODULE_DEVICE_TABLE(of, cts_driver_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_driver_of_match_table), +#endif /* CONFIG_CTS_OF */ +#ifdef CONFIG_CTS_SYSFS + .groups = cts_driver_config_groups, +#endif /* CONFIG_CTS_SYSFS */ +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#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 */ +#endif + }, + .id_table = cts_device_id_table, +}; + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +static int cts_driver_init(void) +#else +static int __init cts_driver_init(void) +#endif +{ + int ret = 0; + + cts_info("Chipone touch driver init, version: "CFG_CTS_DRIVER_VERSION); + +#ifdef CONFIG_CTS_I2C_HOST + cts_info(" - Register i2c driver"); + ret = i2c_add_driver(&cts_i2c_driver); + if (ret) { + cts_info("Register i2c driver failed %d(%s)", ret, cts_strerror(ret)); + return ret; + } +#else + cts_info(" - Register spi driver"); + ret = spi_register_driver(&cts_spi_driver); + if (ret) { + cts_info("Register spi driver failed %d(%s)", ret, cts_strerror(ret)); + return ret; + } +#endif + + cts_info(" - Register touch driver successfully"); + + return 0; +} + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +static void cts_driver_exit(void) +#else +static void __exit cts_driver_exit(void) +#endif +{ + cts_info("Exit"); + +#ifdef CONFIG_CTS_I2C_HOST + cts_info(" - Delete i2c driver"); + i2c_del_driver(&cts_i2c_driver); +#else + cts_info(" - Delete spi driver"); + spi_unregister_driver(&cts_spi_driver); +#endif +} + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +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"); + +#else /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + +static int chipone_tpd_local_init(void) +{ +#if (defined(TPD_WARP_START) && defined(TPD_WARP_END)) + static int tpd_wb_start_local[TPD_WARP_CNT] = TPD_WARP_START; + static int tpd_wb_end_local[TPD_WARP_CNT] = TPD_WARP_END; +#endif + +#if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION)) + static int tpd_def_calmat_local[8] = TPD_CALIBRATION_MATRIX; +#endif + + int ret; + + if ((ret = cts_driver_init()) != 0) { + cts_err("Init driver failed %d", ret); + return ret; + } + + if (tpd_load_status == 0) { + cts_err("Driver not load successfully, exit."); + cts_driver_exit(); + return -1; + } + +#if (defined(TPD_WARP_START) && defined(TPD_WARP_END)) + TPD_DO_WARP = 1; + memcpy(tpd_wb_start, tpd_wb_start_local, TPD_WARP_CNT * 4); + memcpy(tpd_wb_end, tpd_wb_start_local, TPD_WARP_CNT * 4); +#endif + +#if (defined(CONFIG_TPD_HAVE_CALIBRATION) && !defined(CONFIG_TPD_CUSTOM_CALIBRATION)) + memcpy(tpd_calmat, tpd_def_calmat_local, 8 * 4); + memcpy(tpd_def_calmat, tpd_def_calmat_local, 8 * 4); +#endif + + tpd_type_cap = 1; + + return 0; +} + +/* TPD pass dev = NULL */ +static void chipone_tpd_suspend(struct device *dev) +{ + cts_suspend(g_cts_data); +} + +/* TPD pass dev = NULL */ +static void chipone_tpd_resume(struct device *dev) +{ + cts_resume(g_cts_data); +} + +static struct tpd_driver_t chipone_tpd_driver = { + .tpd_device_name = CFG_CTS_DRIVER_NAME, + .tpd_local_init = chipone_tpd_local_init, + .suspend = chipone_tpd_suspend, + .resume = chipone_tpd_resume, + .tpd_have_button = 0, + .attrs = { + .attr = NULL, + .num = 0, + } +}; + +static int __init chipone_tpd_driver_init(void) +{ + int ret; + + cts_info("Chipone TDDI TPD driver %s", CFG_CTS_DRIVER_VERSION); + + tpd_get_dts_info(); + if (tpd_dts_data.touch_max_num < 2) { + tpd_dts_data.touch_max_num = 2; + } else if (tpd_dts_data.touch_max_num > CFG_CTS_MAX_TOUCH_NUM) { + tpd_dts_data.touch_max_num = CFG_CTS_MAX_TOUCH_NUM; + } + + if((ret = tpd_driver_add(&chipone_tpd_driver)) < 0) { + cts_err("Add TPD driver failed %d", ret); + return ret; + } + + return 0; +} + +static void __exit chipone_tpd_driver_exit(void) +{ + cts_info("Chipone TPD driver exit"); + + tpd_driver_remove(&chipone_tpd_driver); +} + +module_init(chipone_tpd_driver_init); +module_exit(chipone_tpd_driver_exit); + +#ifdef CFG_CTS_KERNEL_BUILTIN_FIRMWARE +MODULE_FIRMWARE(CFG_CTS_FIRMWARE_FILENAME_9916); +#endif /* CFG_CTS_KERNEL_BUILTIN_FIRMWARE */ + +MODULE_DESCRIPTION("Chipone TDDI TPD Driver for MTK platform"); +MODULE_VERSION(CFG_CTS_DRIVER_VERSION); +MODULE_AUTHOR("Miao Defang "); +MODULE_LICENSE("GPL"); + +#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ diff --git a/drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.c b/drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.c new file mode 100644 index 000000000000..393ed5a165e2 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define LOG_TAG "Earjack" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_strerror.h" + +#ifdef CONFIG_CTS_EARJACK_DETECT +struct cts_earjack_detect_data { + bool enable; + bool running; + bool state; + + const char *earjack_state_filepath; + struct delayed_work poll_work; + u32 poll_interval; + +#ifdef CONFIG_CTS_SYSFS + bool sysfs_attr_group_created; +#endif /* CONFIG_CTS_SYSFS */ + + struct chipone_ts_data *cts_data; +}; + +/* Over-ride setting for DTS */ +//#define CFG_CTS_DEF_EARJACK_DET_ENABLE + +#define CFG_CTS_DEF_EARJACK_POLL_INTERVAL 2000u +#if defined(CONFIG_MTK_PLATFORM) +#define CFG_CTS_DEF_EARJACK_STATE_FILEPATH \ + "/sys/bus/platform/drivers/Accdet_Driver/state" +#elif defined(CONFIG_ARCH_SPREADRUM) +#define CFG_CTS_DEF_EARJACK_STATE_FILEPATH \ + "/sys/kernel/headset/state" +#else +/* Current QCOM NOT support earjack detect, please disable it or define the file path */ +#define CFG_CTS_DEF_EARJACK_STATE_FILEPATH "" +#endif + +static const char *earjack_state_str(bool state) +{ + return state ? "ATTACHED" : "DETACHED"; +} + +static int parse_earjack_detect_dt(struct cts_earjack_detect_data *ed_data, + struct device_node *np) +{ + const char *filepath; + int ret; + + cts_info("Parse dt"); + +#ifdef CFG_CTS_DEF_EARJACK_DET_ENABLE + ed_data->enable = true; +#else /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + ed_data->enable = + of_property_read_bool(np, "chipone,touch-earjack-detect-enable"); +#endif /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + + ret = of_property_read_string(np, + "chipone,touch-earjack-state-filepath", &filepath); + if (ret) { + cts_warn("Parse state filepath failed %d(%s)", ret, cts_strerror(ret)); + filepath = CFG_CTS_DEF_EARJACK_STATE_FILEPATH; + } + ed_data->earjack_state_filepath = kstrdup(filepath, GFP_KERNEL); + if (ed_data->earjack_state_filepath == NULL) { + cts_err("Dup earjack state filepath failed"); + return -ENOMEM; + } + + ed_data->poll_interval = CFG_CTS_DEF_EARJACK_POLL_INTERVAL; + ret = of_property_read_u32(np, + "chipone,touch-earjack-poll-interval", &ed_data->poll_interval); + if (ret) { + cts_warn("Parse poll interval failed %d(%s)", ret, cts_strerror(ret)); + } + + return 0; +} + +static int start_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + if (!ed_data->enable) { + cts_warn("Start detect while NOT enabled"); + return -EINVAL; + } + + if (ed_data->running) { + cts_warn("Start detect while already RUNNING"); + return 0; + } + + if (ed_data->earjack_state_filepath == NULL || + ed_data->earjack_state_filepath[0] == '\n') { + cts_warn("Start detect with filepath = NULL/NUL"); + return -EINVAL; + } + + cts_info("Start detect check file: '%s'", ed_data->earjack_state_filepath); + + if (!queue_delayed_work(ed_data->cts_data->workqueue, + &ed_data->poll_work, + msecs_to_jiffies(ed_data->poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } + + ed_data->running = true; + + return 0; +} + +static int stop_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + if (!ed_data->running) { + cts_warn("Stop detect while NOT running"); + return 0; + } + + cts_info("Stop detect"); + + if (!cancel_delayed_work_sync(&ed_data->poll_work)) { + cts_warn("Cancel poll work while NOT pending"); + } + ed_data->running = false; + + return 0; +} + +static int get_earjack_state(struct cts_earjack_detect_data *ed_data) +{ + int ret; + char buff[10]; + u32 state; +#ifndef CFG_CTS_FOR_GKI + struct file *file = NULL; + loff_t pos = 0; + int read_size; +#endif + + cts_dbg("Get state from file '%s'", ed_data->earjack_state_filepath); + +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); +#else + file = filp_open(ed_data->earjack_state_filepath, O_RDONLY, 0); + if (IS_ERR(file)) { + ret = (int)PTR_ERR(file); + cts_err("Open file '%s' failed %d(%s)", + ed_data->earjack_state_filepath, ret, cts_strerror(ret)); + return PTR_ERR(file); + } + + memset(buff, 0, sizeof(buff)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + read_size = kernel_read(file, buff, sizeof(buff), &pos); +#else + read_size = kernel_read(file, pos, buff, sizeof(buff)); +#endif + if (read_size < 0) { + cts_err("Read state file '%s' failed %d(%s)", + ed_data->earjack_state_filepath, + read_size, cts_strerror(read_size)); + filp_close(file, NULL); + return read_size; + } + + cts_dbg("Read state file content: '%s'", buff); + + ret = filp_close(file, NULL); + if (ret) { + cts_warn("Close file '%s' failed %d(%s)", + ed_data->earjack_state_filepath, ret, cts_strerror(ret)); + } +#endif + + ret = kstrtou32(buff, 0, &state); + if (ret) { + cts_err("Invalid string from state file: '%s'", buff); + return ret; + } + + /* Assume "0" means detached */ + ed_data->state = !!state; + + cts_dbg("State: %s", earjack_state_str(ed_data->state)); + + return 0; +} + +/* Sysfs */ +#ifdef CONFIG_CTS_SYSFS + +#define EARJACK_DET_SYSFS_GROUP_NAME "earjack-det" + +static int enable_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + cts_info("Enable detect"); + + ed_data->enable = true; + + return 0; +} + +static int disable_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + int ret; + + cts_info("Disable detect"); + + ret = stop_earjack_detect(ed_data); + if (ret) { + cts_err("Stop detect failed %d(%s)", ret, cts_strerror(ret)); + } + + ed_data->enable = false; + + return 0; +} + +static ssize_t earjack_detect_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Earjack detect: %s\n", + ed_data->enable ? "ENABLED" : "DISABLED"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > enable */ +static ssize_t earjack_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_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + bool enable; + int ret; + + cts_info("Write sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + cts_parse_arg(buf, count); + + if (cts_argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(cts_argv[0], &enable); + if (ret) { + cts_err("Invalid param of enable"); + return ret; + } + + if (enable) { + ret = enable_earjack_detect(ed_data); + } else { + ret = disable_earjack_detect(ed_data); + } + if (ret) { + cts_err("%s earjack detect failed %d(%s)", + enable ? "Enable" : "Disable", ret, cts_strerror(ret)); + return ret; + } + + return count; +} +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + earjack_detect_enable_show, earjack_detect_enable_store); + +static ssize_t earjack_detect_running_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Earjack detect: %sRunning\n", + ed_data->running ? "" : "Not-"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > runing */ +static ssize_t earjack_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_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + bool running; + int ret; + + cts_info("Write sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + cts_parse_arg(buf, count); + + if (cts_argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(cts_argv[0], &running); + if (ret) { + cts_err("Invalid param of running"); + return ret; + } + + if (running) { + ret = start_earjack_detect(ed_data); + } else { + ret = stop_earjack_detect(ed_data); + } + if (ret) { + cts_err("%s earjack detect failed %d(%s)", + running ? "Start" : "Stop", ret, cts_strerror(ret)); + return ret; + } + + return count; +} +static DEVICE_ATTR(running, S_IWUSR | S_IRUGO, + earjack_detect_running_show, earjack_detect_running_store); + +static ssize_t earjack_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + int ret; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + ret = get_earjack_state(ed_data); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Get earjack state failed %d(%s)\n", + ret, cts_strerror(ret)); + } + + return scnprintf(buf, PAGE_SIZE, + "Earjack state: %s\n", earjack_state_str(ed_data->state)); +} +static DEVICE_ATTR(state, S_IRUGO, earjack_state_show, NULL); + +static ssize_t earjack_detect_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Filepath: '%s'\n" + "Poll int: %dms\n", + ed_data->earjack_state_filepath, ed_data->poll_interval); +} + +/* echo filepath [interval] > param */ +static ssize_t earjack_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_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + u32 interval; + int ret; + + cts_info("Write sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + cts_parse_arg(buf, count); + + if (cts_argc < 1 || cts_argc > 2) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + interval = ed_data->poll_interval; + if (cts_argc > 1) { + ret = kstrtou32(cts_argv[1], 0, &interval); + if (ret) { + cts_err("Arg interval is invalid"); + return -EINVAL; + } + } + + if (ed_data->earjack_state_filepath) { + kfree(ed_data->earjack_state_filepath); + } + ed_data->earjack_state_filepath = kstrdup(cts_argv[0], GFP_KERNEL); + if (ed_data->earjack_state_filepath == NULL) { + cts_err("Dup earjack state filepath failed"); + return -ENOMEM; + } + ed_data->poll_interval = interval; + + return count; +} +static DEVICE_ATTR(param, S_IWUSR | S_IRUGO, + earjack_detect_param_show, earjack_detect_param_store); + +static struct attribute *earjack_detect_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_running.attr, + &dev_attr_state.attr, + &dev_attr_param.attr, + NULL +}; + +static const struct attribute_group earjack_detect_attr_group = { + .name = EARJACK_DET_SYSFS_GROUP_NAME, + .attrs = earjack_detect_attrs, +}; +#endif /* CONFIG_CTS_SYSFS */ + +static void poll_earjack_state_work(struct work_struct *work) +{ + struct cts_earjack_detect_data *ed_data; + bool prev_state = false; + int ret; + + cts_dbg("Poll earjack state work"); + + ed_data = container_of(to_delayed_work(work), + struct cts_earjack_detect_data, poll_work); + + prev_state = ed_data->state; + + ret = get_earjack_state(ed_data); + if (ret) { + cts_err("Get state failed %d(%s)", ret, cts_strerror(ret)); + } else { + if (ed_data->state != prev_state) { + cts_info("State changed: %s -> %s", + earjack_state_str(prev_state), + earjack_state_str(ed_data->state)); + + cts_lock_device(&ed_data->cts_data->cts_dev); + ret = cts_set_dev_earjack_attached( + &ed_data->cts_data->cts_dev, ed_data->state); + cts_unlock_device(&ed_data->cts_data->cts_dev); + if (ret) { + cts_err("Set dev earjack attached to %s failed %d(%s)", + earjack_state_str(ed_data->state), + ret, cts_strerror(ret)); + /* Set to previous state, try set again in next loop */ + ed_data->state = prev_state; + } + } + } + + if (!queue_delayed_work(ed_data->cts_data->workqueue, + &ed_data->poll_work, + msecs_to_jiffies(ed_data->poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } +} + +int cts_earjack_detect_init(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + int ret = 0; + + if (cts_data == NULL) { + cts_err("Init detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = kzalloc(sizeof(*ed_data), GFP_KERNEL); + if (ed_data == NULL) { + cts_err("Alloc earjack detect data failed"); + return -ENOMEM; + } + + cts_info("Init detect"); + +#ifdef CONFIG_CTS_OF + ret = parse_earjack_detect_dt(ed_data, cts_data->device->of_node); +#else /* CONFIG_CTS_OF */ +#ifdef CFG_CTS_DEF_EARJACK_DET_ENABLE + ed_data->enable = true; +#else /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + ed_data->enable = false; +#endif /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + ed_data->poll_interval = CFG_CTS_DEF_EARJACK_POLL_INTERVAL; + ed_data->earjack_state_filepath = kstrdup( + CFG_CTS_DEF_EARJACK_STATE_FILEPATH, GFP_KERNEL); + if (ed_data->earjack_state_filepath == NULL) { + cts_err("Dup earjack state filepath failed"); + ret = -ENOMEM; + } +#endif /* CONFIG_CTS_OF */ + if (ret) { + cts_err("Get detect param failed %d(%s)", + ret, cts_strerror(ret)); + goto free_ed_data; + } + + cts_info("Detect: %sABLED", ed_data->enable ? "EN" : "DIS"); + cts_info(" Filepath: '%s'", ed_data->earjack_state_filepath); + cts_info(" Poll Int: %dms", ed_data->poll_interval); + + INIT_DELAYED_WORK(&ed_data->poll_work, poll_earjack_state_work); + +#ifdef CONFIG_CTS_SYSFS + cts_info("Create sysfs attr group '%s'", EARJACK_DET_SYSFS_GROUP_NAME); + ret = sysfs_create_group(&cts_data->device->kobj, + &earjack_detect_attr_group); + if (ret) { + cts_warn("Create sysfs attr group '%s' failed %d(%s)", + EARJACK_DET_SYSFS_GROUP_NAME, ret, cts_strerror(ret)); + } else { + ed_data->sysfs_attr_group_created = true; + } +#endif /* CONFIG_CTS_SYSFS */ + + cts_data->earjack_detect_data = ed_data; + ed_data->cts_data = cts_data; + + return 0; + +free_ed_data: + kfree(ed_data); + + return ret; +} + +int cts_earjack_detect_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + int ret; + + if (cts_data == NULL) { + cts_err("Deinit detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_warn("Deinit detect with earjack_detect_data = NULL"); + return 0; + } + + cts_info("Deinit detect"); + + if (ed_data->running) { + ret = stop_earjack_detect(ed_data); + if (ret) { + cts_err("Stop detect failed %d(%s)", + ret, cts_strerror(ret)); + } + } + +#ifdef CONFIG_CTS_SYSFS + if (ed_data->sysfs_attr_group_created) { + cts_info("Remove sysfs attr group '%s'", EARJACK_DET_SYSFS_GROUP_NAME); + sysfs_remove_group(&cts_data->device->kobj, &earjack_detect_attr_group); + ed_data->sysfs_attr_group_created = false; + } +#endif /* CONFIG_CTS_SYSFS */ + + if(ed_data->earjack_state_filepath) { + kfree(ed_data->earjack_state_filepath); + ed_data->earjack_state_filepath = NULL; + } + + kfree(ed_data); + cts_data->earjack_detect_data = NULL; + + return 0; +} + +int cts_is_earjack_attached(struct chipone_ts_data *cts_data, bool *attached) +{ + struct cts_earjack_detect_data *ed_data; + int ret; + + if (cts_data == NULL) { + cts_err("Get state with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_err("Get state with earjack_detect_data = NULL"); + return -ENODEV; + } + + ret = get_earjack_state(ed_data); + if (ret) { + cts_err("Get state failed %d(%s)", ret, cts_strerror(ret)); + return ret; + } + + cts_info("Get curr state: %s", earjack_state_str(ed_data->state)); + + *attached = ed_data->state; + + return 0; +} + +int cts_start_earjack_detect(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + + if (cts_data == NULL) { + cts_err("Start detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_err("Start detect with earjack_detect_data = NULL"); + return -ENODEV; + } + + return start_earjack_detect(ed_data); +} + +int cts_stop_earjack_detect(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + + if (cts_data == NULL) { + cts_err("Stop detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_err("Stop detect with earjack_detect_data = NULL"); + return -ENODEV; + } + + return stop_earjack_detect(ed_data); +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.h b/drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.h new file mode 100644 index 000000000000..051a38b8acf9 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_earjack_detect.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_EARJACK_DETECT_H +#define CTS_EARJACK_DETECT_H + +#include "cts_config.h" + +//struct chipone_ts_data; + +#ifdef CONFIG_CTS_EARJACK_DETECT +extern int cts_earjack_detect_init(struct chipone_ts_data *cts_data); +extern int cts_earjack_detect_deinit(struct chipone_ts_data *cts_data); +extern int cts_start_earjack_detect(struct chipone_ts_data *cts_data); +extern int cts_stop_earjack_detect(struct chipone_ts_data *cts_data); +extern int cts_is_earjack_attached(struct chipone_ts_data *cts_data, + bool *attached); +#else /* CONFIG_CTS_EARJACK_DETECT */ +static inline int cts_earjack_detect_init(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} +static inline int cts_earjack_detect_deinit(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} +static inline int cts_start_earjack_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} +static inline int cts_stop_earjack_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} +static inline int cts_is_earjack_attached(struct chipone_ts_data *cts_data, + bool *attached) +{ + return -ENODEV; +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +#endif /* CTS_EARJACK_DETECT_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_firmware.c b/drivers/input/touchscreen/chipone_tddi/cts_firmware.c new file mode 100644 index 000000000000..5d78aad5ff38 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_firmware.c @@ -0,0 +1,1044 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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 "cts_strerror.h" +#include "cts_tcs.h" +#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 = 0x30000, + CTS_DDIPARAM_SECTION_OFFSET = 0x00031000, + CTS_DDIPARAM_CRC_SECTION_OFFSET = 0x33000, +}; + +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 (20) + +#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 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[20] = {0xff}; + + 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 */ + put_unaligned_le32(0x7473756E, crc_sect + 16); + 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 +static int cts_request_newer_driver_builtin_firmware(struct cts_device *cts_dev, + 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)) + + 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; + } + + if (FIRMWARE_VERSION(firmware) > device_fw_ver) { + 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); + memcpy(cts_dev->firmware->data, firmware->data, firmware->size); + cts_dev->firmware->size = firmware->size; + return 0; + } + } + } + + cts_info("No newer driver builtin found"); + + return -EINVAL; + +#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) +// { +// #ifdef CFG_CTS_FOR_GKI +// cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); +// return false; +// #else +// 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; +// #endif +// } + +#ifndef CFG_CTS_FOR_GKI +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); + cts_info("Do not update"); + ret = -EINVAL; + goto err_close_file; + } + + cts_info("File '%s' size: %zu version: %04x", + filepath, firmware->size, version); + + 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; + firmware = NULL; + goto err_close_file; + } + + filp_close(file, NULL); + + return 0; + +err_close_file: + filp_close(file, NULL); + return ret; +} +#endif + +#ifdef CFG_CTS_FIRMWARE_IN_FS +static int cts_wrap_request_firmware(struct cts_firmware *firmware, + const char *name, struct device *device, int curr_version) +{ + struct cts_firmware *local = NULL; + int ret; + + local = kzalloc(sizeof(struct cts_firmware), GFP_KERNEL); + if (local == NULL) { + cts_err("Alloc local buffer failed"); + return -ENOMEM; + } + + ret = request_firmware(&local->fw, name, device); + if (ret) { + cts_err("Could not load firmware from %s: %d", name, ret); + return ret; + } + + local->data = (u8 *) local->fw->data; + local->size = local->fw->size; + + if (!is_firmware_valid(local)) { + cts_err("Request firmware file INVALID, size %zu", local->size); + ret = -EINVAL; + goto err_release_firmware; + } + + if (curr_version > 0 && curr_version >= FIRMWARE_VERSION(local)) { + cts_info("Request firmware file version 0x%04x <= 0x%04x curr", + FIRMWARE_VERSION(local), curr_version); + ret = -EINVAL; + goto err_release_firmware; + } + + if (firmware == NULL) { + cts_err("firmware was null"); + ret = -EINVAL; + } else { + memcpy(firmware->data, (u8 *) local->fw->data, local->size); + firmware->size = local->size; + cts_info("Update firmware buffer successfully"); + } + +err_release_firmware: + release_firmware(local->fw); + local->data = NULL; + kfree(local); + return ret; +} + +int cts_request_newer_firmware_from_fs(struct cts_device *cts_dev, + const char *filepath, u16 curr_version) +{ + 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 -EINVAL; + } + + if (strchr(filepath, '/') != NULL) { + cts_info("Filepath is fullpath, direct read it out"); +#ifdef CFG_CTS_FOR_GKI + cts_err("Google GKI unsupport kernel_read, please use request_firmware"); + ret = -EPERM; +#else + /* Sometimes vendor/firmware path was not have permission, then use sdcard */ + ret = cts_request_firmware_full_filepath(cts_dev->firmware, filepath, + curr_version); +#endif + } else { + ret = cts_wrap_request_firmware(cts_dev->firmware, filepath, + &cts_dev->pdata->ts_input_dev->dev, curr_version); + } + + return ret; +} + +int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath) +{ + int ret; + + cts_info("Update from file '%s'", filepath); + + cts_reset_device(cts_dev); + + ret = cts_request_newer_firmware_from_fs(cts_dev, filepath, 0); + if (ret) { + cts_err("Request from file '%s' failed", filepath); + return -EFAULT; + } + + ret = cts_update_firmware(cts_dev); + if (ret) { + cts_err("Update from file failed %d", ret); + return ret; + } + + cts_info("Update from file success"); + + return ret; +} +#endif /*CFG_CTS_FIRMWARE_IN_FS */ + +int cts_request_firmware(struct cts_device *cts_dev, + u32 hwid, u16 fwid, u16 curr_firmware_ver) +{ + int builtin = -1, bin = -1; + + 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 + builtin = cts_request_newer_driver_builtin_firmware(cts_dev, + hwid, fwid, curr_firmware_ver); + if (builtin == 0) { + curr_firmware_ver = FIRMWARE_VERSION(cts_dev->firmware); + } +#endif + +#ifdef CFG_CTS_FIRMWARE_IN_FS + bin = cts_request_newer_firmware_from_fs(cts_dev, + CFG_CTS_FIRMWARE_FILENAME, curr_firmware_ver); + if (bin) { + cts_err("Request firmware from file failed"); + } +#endif + + if (bin == 0 || builtin == 0) + return 0; + else + return -1; +} + +#ifdef CONFIG_CTS_I2C_HOST +static int calc_crc_in_flash(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 int validate_flash_data(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_from_sram_to_flash(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); + + 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(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; +} + +static int cts_update_firmware_to_flash(struct cts_device *cts_dev, + struct cts_firmware_sect_info firmware_info) +{ + int retries; + int ret; + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_warn("Prepare flash operation failed %d", ret); + /* Go through and try */ + } + + if (!cts_dev->rtdata.has_flash) { + cts_err("Update firmware to flash is UNKNOWN/NON-EXIST"); + ret = -ENODEV; + goto post_flash_operation; + } + + 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_from_sram_to_flash(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); + } + +post_flash_operation: + cts_post_flash_operation(cts_dev); + return ret; +} +#else /* CONFIG_CTS_I2C_HOST */ +#ifdef CFG_CTS_CASCADE_SUPPORTED +static int cts_switch_to_ic(struct cts_device *cts_dev, uint8_t type) +{ + uint8_t dat = 0x83; + int count = 0; + int retries = 10; + int rc; + + if (type == 1) { + dat = 0x81; + } else if (type == 2) { + dat = 0x82; + } else if (type == 3) { + dat = 0x83; + } + + while (retries--) { + rc = cts_spi_send_recv(cts_dev->pdata, sizeof(dat), &dat, NULL); + if (rc) + cts_err("Send %d to ic failed: rc=%d", dat, rc); + else { + count++; + if (count == 2) + return 0; + } + } + + return -EIO; +} + +int cts_switch_to_master(struct cts_device *cts_dev) +{ + return cts_switch_to_ic(cts_dev, 1); +} + +int cts_switch_to_slave(struct cts_device *cts_dev) +{ + return cts_switch_to_ic(cts_dev, 2); +} + +int cts_switch_to_all(struct cts_device *cts_dev) +{ + return cts_switch_to_ic(cts_dev, 3); +} +#endif /* CFG_CTS_CASCADE_SUPPORTED */ +#endif /* CONFIG_CTS_I2C_HOST */ + +#if defined(CFG_CTS_CASCADE_SUPPORTED) && !defined(CONFIG_CTS_I2C_HOST) +static int cts_slave_enter_prog_mode(struct cts_device *cts_dev) +{ + u8 magic_num[] = { 0xCC, 0x33, 0x55, 0x5A }; + int ret; + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_plat_i2c_write(cts_dev->pdata, + CTS_DEV_PROGRAM_MODE_I2CADDR, (const u8 *)magic_num, 4, 5, 10); + if (ret) { + cts_err("Write slave magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_I2CADDR, ret); + } +#else + ret = cts_spi_send_recv(cts_dev->pdata, sizeof(magic_num), magic_num, NULL); + if (ret) { + cts_err("Write slave magic number to spi_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_SPIADDR, ret); + } +#endif + + return ret; +} +#endif + +static int cts_prepare_update_firmware(struct cts_device *cts_dev, + struct cts_firmware_sect_info firmware_info) +{ + int ret; + +#if defined(CFG_CTS_CASCADE_SUPPORTED) && !defined(CONFIG_CTS_I2C_HOST) + ret = cts_switch_to_master(cts_dev); + if (ret) { + cts_err("Switch to master faild %d", ret); + return ret; + } + + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Device enter program mode failed %d", ret); + return ret; + } + + ret = cts_switch_to_slave(cts_dev); + if (ret) { + cts_err("Switch to slave field %d", ret); + return ret; + } + + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_SPI_CFG, 0x12, 5, 0); + if (ret) { + cts_err("Disable Slave Spi Tx forward failed %d", ret); + return ret; + } + + ret = cts_slave_enter_prog_mode(cts_dev); + if (ret) { + cts_err("Slave device enter program mode failed %d", ret); + return ret; + } + + ret = cts_switch_to_all(cts_dev); + if (ret) { + cts_err("Switch to all failed %d", ret); + return ret; + } +#else + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Device enter program mode failed %d", ret); + return ret; + } +#endif + return ret; +} + +int cts_update_firmware(struct cts_device *cts_dev) +{ + struct cts_firmware_sect_info firmware_info; + struct cts_firmware *firmware = cts_dev->firmware; + int ret; + + cts_info("Update firmware ver: %04x size: %zu", + 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_prepare_update_firmware(cts_dev, firmware_info); + if (ret) { + cts_err("Prepare update firmware failed, ret=%d", ret); + goto out; + } + + 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, 10); + if (ret) { + cts_err("Write firmware section to sram failed %d", ret); + } + +#ifdef CFG_CTS_UPDATE_CRCCHECK + cts_sram_writesb_boot_crc_retry(cts_dev, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, 3); +#endif + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_update_firmware_to_flash(cts_dev, firmware_info); + if (ret) { + cts_err("Update firmware to flash failed"); + } +#endif + +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 + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) + cts_err("Set dev earjack attached failed %d", r); + } +#endif + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + + return ret; +} + +bool cts_is_firmware_updating(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.updating; +} diff --git a/drivers/input/touchscreen/chipone_tddi/cts_firmware.h b/drivers/input/touchscreen/chipone_tddi/cts_firmware.h new file mode 100644 index 000000000000..55033becacd8 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_firmware.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_FIRMWARE_H +#define CTS_FIRMWARE_H + +#include "cts_config.h" +#include +#include + + + +#define FIRMWARE_VERSION_OFFSET 0x100 +#define FIRMWARE_VERSION(firmware) \ + get_unaligned_le16((firmware)->data + FIRMWARE_VERSION_OFFSET) + +struct cts_device; +extern int cts_request_newer_firmware_from_fs(struct cts_device *cts_dev, + const char *filepath, u16 curr_version); +extern bool cts_is_firmware_updating(struct cts_device *cts_dev); +extern int cts_request_firmware(struct cts_device *cts_dev, + u32 hwid, u16 fwid, u16 device_fw_ver); +extern int cts_update_firmware(struct cts_device *cts_dev); + +#ifdef CFG_CTS_FIRMWARE_IN_FS +extern int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath); + +#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/cts_oem.c b/drivers/input/touchscreen/chipone_tddi/cts_oem.c new file mode 100644 index 000000000000..0b9aacda61b9 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_oem.c @@ -0,0 +1,2127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define LOG_TAG "Oem" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_oem.h" +#include "cts_test.h" +#include "cts_tcs.h" +#include "cts_firmware.h" + +#include +#include +#include + +/* Following options override device tree settings */ +#define OEM_OF_DEF_PROPVAL_TEST_RESET_PIN true +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#define OEM_OF_DEF_PROPVAL_TEST_INT_PIN false +#else +#define OEM_OF_DEF_PROPVAL_TEST_INT_PIN true +#endif +#define OEM_OF_DEF_PROPVAL_TEST_RAWDATA true +#define OEM_OF_DEF_PROPVAL_TEST_NOISE true +#define OEM_OF_DEF_PROPVAL_TEST_OPEN true +#define OEM_OF_DEF_PROPVAL_TEST_SHORT true +#define OEM_OF_DEF_PROPVAL_TEST_COMP_CAP true + +/* Default settings if device tree NOT exist */ +#define OEM_OF_DEF_PROPVAL_RAWDATA_FRAMES 1 +#define OEM_OF_DEF_PROPVAL_RAWDATA_MIN 1000 +#define OEM_OF_DEF_PROPVAL_RAWDATA_MAX 2000 +#define OEM_OF_DEF_PROPVAL_NOISE_FRAMES 50 +#define OEM_OF_DEF_PROPVAL_NOISE_MAX 80 +#define OEM_OF_DEF_PROPVAL_OPEN_MIN 1000 +#define OEM_OF_DEF_PROPVAL_SHORT_MIN 500 +#define OEM_OF_DEF_PROPVAL_COMP_CAP_MIN 1 +#define OEM_OF_DEF_PROPVAL_COMP_CAP_MAX 126 + +#define OEM_OF_PROPNAME_PREFIX "chipone," //modify + +#define OEM_OF_PROPNAME_TEST_RESET_PIN OEM_OF_PROPNAME_PREFIX"test-reset-pin" +#define OEM_OF_PROPNAME_TEST_INT_PIN OEM_OF_PROPNAME_PREFIX"test-int-pin" +#define OEM_OF_PROPNAME_TEST_RAWDATA OEM_OF_PROPNAME_PREFIX"test-rawdata" +#define OEM_OF_PROPNAME_RAWDATA_FRAMES OEM_OF_PROPNAME_PREFIX"test-rawdata-frames" +#define OEM_OF_PROPNAME_RAWDATA_MIN OEM_OF_PROPNAME_PREFIX"rawdata-min" +#define OEM_OF_PROPNAME_RAWDATA_MAX OEM_OF_PROPNAME_PREFIX"rawdata-max" +#define OEM_OF_PROPNAME_TEST_NOISE OEM_OF_PROPNAME_PREFIX"test-noise" +#define OEM_OF_PROPNAME_NOISE_FRAMES OEM_OF_PROPNAME_PREFIX"test-noise-frames" +#define OEM_OF_PROPNAME_NOISE_MAX OEM_OF_PROPNAME_PREFIX"noise-max" +#define OEM_OF_PROPNAME_TEST_OPEN OEM_OF_PROPNAME_PREFIX"test-open" +#define OEM_OF_PROPNAME_OPEN_MIN OEM_OF_PROPNAME_PREFIX"open-min" +#define OEM_OF_PROPNAME_TEST_SHORT OEM_OF_PROPNAME_PREFIX"test-short" +#define OEM_OF_PROPNAME_SHORT_MIN OEM_OF_PROPNAME_PREFIX"short-min" +#define OEM_OF_PROPNAME_TEST_COMP_CAP OEM_OF_PROPNAME_PREFIX"test-compensate-cap" +#define OEM_OF_PROPNAME_COMP_CAP_MIN OEM_OF_PROPNAME_PREFIX"compensate-cap-min" +#define OEM_OF_PROPNAME_COMP_CAP_MAX OEM_OF_PROPNAME_PREFIX"compensate-cap-max" + +#define OEM_SELFTEST_PROC_FILENAME "cts_selftest" +#define OEM_LIMIT_PROC_FILENAME "cts_limit" +#define OEM_FACTORY_TEST_PROC_FILENAME "cts_factory_test" +#define OEM_RAWDATA_PROC_FILENAME "cts_rawdata" +#define OEM_MANUAL_PROC_FILENAME "cts_manual" +#define OEM_DIFFDATA_PROC_FILENAME "cts_diffdata" +#define OEM_CNEGDATA_PROC_FILENAME "cts_compensate_cap" + +#define OEM_TEST_DATA_DIR "/sdcard" +#define OEM_RAWDATA_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/FWMutualTest.csv" +#define OEM_NOISE_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/NoiseTest.csv" +#define OEM_OPEN_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/OpenTest.csv" +#define OEM_SHORT_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/ShortTest.csv" +#define OEM_COMP_CAP_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/FWCCTest.csv" + +#define TOUCH_DATA_DIRECTORY_PREFIX "/sdcard" +#define RAWDATA_TEST_DATA_FILENAME "rawdata-test-data.txt" +#define NOISE_TEST_DATA_FILENAME "noise-test-data.txt" +#define OPEN_TEST_DATA_FILENAME "open-test-data.txt" +#define SHORT_TEST_DATA_FILENAME "short-test-data.txt" +#define COMP_CAP_TEST_DATA_FILENAME "comp-cap-test-data.txt" +#define STYLUS_RAWDATA_TEST_DATA_FILENAME "stylus-rawdata-test-data.txt" +#define STYLUS_NOISE_TEST_DATA_FILENAME "stylus-noise-test-data.txt" +#define STYLUS_MNT_NOISE_TEST_DATA_FILENAME "stylus-mnt-noise-test-data.txt" + +#pragma pack(1) +struct cts_limit { + u8 header[7]; + u8 version; + u16 int_item : 1; + u16 reset_item : 1; + u16 normal_rawdata_item : 1; + u16 normal_noise_item : 1; + u16 open_item : 1; + u16 short_item : 1; + u16 comp_cap_item : 1; + u16 gstr_rawdata_item : 1; + u16 gstrlp_rawdata_item : 1; + u16 gstr_noise_item : 1; + u16 gstrlp_noise_item : 1; + u16 stylus_rawdata_item : 1; + u16 stylus_noise_item : 1; + u16 stylus_mnt_rawdata_item : 1; + u16 type : 2; + u16 normal_rawdata_frames; + u16 normal_rawdata_min; + u16 normal_rawdata_max; + u16 normal_noise_frames; + u16 normal_noise_max; + u16 normal_open_min; + u16 normal_short_min; + u16 normal_compcap_min; + u16 normal_compcap_max; + u16 gstr_rawdata_frames; + u16 gstr_rawdata_min; + u16 gstr_rawdata_max; + u16 gstrlp_rawdata_frames; + u16 gstrlp_rawdata_min; + u16 gstrlp_rawdata_max; + u16 gstr_noise_frames; + u16 gstr_noise_max; + u16 gstrlp_noise_frames; + u16 gstrlp_noise_max; + u16 stylus_rawdata_frames; + u16 stylus_rawdata_min; + u16 stylus_rawdata_max; + u16 stylus_noise_frames; + u16 stylus_noise_max; + u16 stylus_mnt_rawdata_frames; + u16 stylus_mnt_rawdata_min; + u16 stylus_mnt_rawdata_max; +}; +#pragma pack() + +struct cts_oem_data { + struct proc_dir_entry *selftest_proc_entry; + struct proc_dir_entry *limit_entry; + struct proc_dir_entry *factory_test_entry; + struct proc_dir_entry *manual_proc_entry; + struct proc_dir_entry *diffdata_proc_entry; + struct proc_dir_entry *rawdata_proc_entry; + struct proc_dir_entry *cnegdata_proc_entry; + + bool test_config_from_dt_has_parsed; + + /* Test configuration from device tree */ + bool test_reset_pin; + int reset_pin_test_result; + + bool test_int_pin; + int int_pin_test_result; + + bool test_rawdata; + u32 rawdata_test_frames; + int rawdata_test_result; + u16 *rawdata_test_data; + int rawdata_test_data_buff_size; + int rawdata_test_data_wr_size; + int rawdata_min; + int rawdata_max; + + bool test_noise; + u32 noise_test_frames; + int noise_test_result; + u16 *noise_test_data; + int noise_test_data_buff_size; + int noise_test_data_wr_size; + int noise_max; + + bool test_open; + int open_test_result; + u16 *open_test_data; + int open_test_data_buff_size; + int open_test_data_wr_size; + int open_min; + + bool test_short; + int short_test_result; + u16 *short_test_data; + int short_test_data_buff_size; + int short_test_data_wr_size; + int short_min; + + bool test_comp_cap; + int comp_cap_test_result; + u8 *comp_cap_test_data; + int comp_cap_test_data_buff_size; + int comp_cap_test_data_wr_size; + int comp_cap_min; + int comp_cap_max; + + bool test_stylus_rawdata; + u32 stylus_rawdata_test_frames; + int stylus_rawdata_test_result; + u8 *stylus_rawdata_test_data; + int stylus_rawdata_test_data_buff_size; + int stylus_rawdata_test_data_wr_size; + int stylus_rawdata_min; + int stylus_rawdata_max; + + bool test_stylus_noise; + u32 stylus_noise_test_frames; + int stylus_noise_test_result; + u8 *stylus_noise_test_data; + int stylus_noise_test_data_buff_size; + int stylus_noise_test_data_wr_size; + int stylus_noise_max; + + bool test_stylus_mnt_rawdata; + u32 stylus_mnt_rawdata_test_frames; + int stylus_mnt_rawdata_test_result; + u8 *stylus_mnt_rawdata_test_data; + int stylus_mnt_rawdata_test_data_buff_size; + int stylus_mnt_rawdata_test_data_wr_size; + int stylus_mnt_rawdata_min; + int stylus_mnt_rawdata_max; + + struct chipone_ts_data *cts_data; +}; + +/* struct proc_dir_entry *cts_tp_work_proc; */ + +#define ALLOC_TEST_DATA_MEM(type, size) \ + do { \ + if (oem_data->test_##type) { \ + if (oem_data->type##_test_data == NULL) { \ + cts_info(" - Alloc " #type " test data mem size %d", size); \ + oem_data->type##_test_data = kmalloc(size, GFP_KERNEL); \ + if (oem_data->type##_test_data == NULL) { \ + cts_err("Alloc " #type " test data mem failed"); \ + return -ENOMEM; \ + } \ + oem_data->type##_test_data_buff_size = size; \ + } \ + memset(oem_data->type##_test_data, 0, size); \ + } \ + } while (0) + +/* NOTE: Any test data mem alloc failed will NOT clean other mem */ +static int alloc_selftest_data_mem(struct cts_oem_data *oem_data, int nodes) +{ + cts_info("Alloc selftest data"); + + ALLOC_TEST_DATA_MEM(rawdata, + nodes * 2 * oem_data->rawdata_test_frames); + ALLOC_TEST_DATA_MEM(noise, + nodes * 2 * (oem_data->noise_test_frames + 3)); + ALLOC_TEST_DATA_MEM(open, nodes * 2); + ALLOC_TEST_DATA_MEM(short, nodes * 2 * 5); + ALLOC_TEST_DATA_MEM(comp_cap, nodes); + return 0; +} + +#define FREE_TEST_DATA_MEM(type) \ + do { \ + if (oem_data->type##_test_data) { \ + cts_info("- Free " #type " test data mem"); \ + kfree(oem_data->type##_test_data); \ + oem_data->type##_test_data = NULL; \ + oem_data->type##_test_data_buff_size = 0; \ + } \ + } while(0) +static void free_selftest_data_mem(struct cts_oem_data *oem_data) +{ + cts_info("Free selftest data"); + + FREE_TEST_DATA_MEM(rawdata); + FREE_TEST_DATA_MEM(noise); + FREE_TEST_DATA_MEM(open); + FREE_TEST_DATA_MEM(short); + FREE_TEST_DATA_MEM(comp_cap); +} + +/* NOTE: Any test data mem alloc failed will NOT clean other mem +static int alloc_stylus_test_data_mem(struct cts_oem_data *oem_data, int nodes) +{ + cts_info("Alloc stylus selftest data"); + + ALLOC_TEST_DATA_MEM(stylus_rawdata, + nodes * 2 * oem_data->stylus_rawdata_test_frames); + ALLOC_TEST_DATA_MEM(stylus_noise, + nodes * 2 * (oem_data->stylus_noise_test_frames + 3)); + ALLOC_TEST_DATA_MEM(stylus_mnt_rawdata, + nodes * 2 * oem_data->stylus_mnt_rawdata_test_frames); + return 0; +} + +static void free_stylus_test_data_mem(struct cts_oem_data *oem_data) +{ + cts_info("Free stylus selftest data"); + + FREE_TEST_DATA_MEM(stylus_rawdata); + FREE_TEST_DATA_MEM(stylus_noise); + FREE_TEST_DATA_MEM(stylus_mnt_rawdata); +} +*/ +#undef ALLOC_TEST_DATA_MEM +#undef FREE_TEST_DATA_MEM + +static int parse_selftest_dt(struct cts_oem_data *oem_data, + struct device_node *np) +{ + int ret; + + cts_info("Parse selftest dt"); + + /** reset pin **/ + oem_data->test_reset_pin = OEM_OF_DEF_PROPVAL_TEST_RESET_PIN || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_RESET_PIN); + + /** int pin **/ + oem_data->test_int_pin = OEM_OF_DEF_PROPVAL_TEST_INT_PIN || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_INT_PIN); + + /** rawdata **/ + oem_data->test_rawdata = OEM_OF_DEF_PROPVAL_TEST_RAWDATA || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_RAWDATA); + if (oem_data->test_rawdata) { + oem_data->rawdata_test_frames = OEM_OF_DEF_PROPVAL_RAWDATA_FRAMES; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_RAWDATA_FRAMES, + &oem_data->rawdata_test_frames); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_RAWDATA_FRAMES"' failed %d", ret); + } + oem_data->rawdata_min = OEM_OF_DEF_PROPVAL_RAWDATA_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_RAWDATA_MIN, + (u32 *)&oem_data->rawdata_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_RAWDATA_MIN"' failed %d", ret); + } + oem_data->rawdata_max = OEM_OF_DEF_PROPVAL_RAWDATA_MAX; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_RAWDATA_MAX, + (u32 *)&oem_data->rawdata_max); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_RAWDATA_MAX"' failed %d", ret); + } + } + + /** nosie **/ + oem_data->test_noise = OEM_OF_DEF_PROPVAL_TEST_NOISE || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_NOISE); + if (oem_data->test_noise) { + oem_data->noise_test_frames = OEM_OF_DEF_PROPVAL_NOISE_FRAMES; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_NOISE_FRAMES, + &oem_data->noise_test_frames); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_NOISE_FRAMES"' failed %d", ret); + } + oem_data->noise_max = OEM_OF_DEF_PROPVAL_NOISE_MAX; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_NOISE_MAX, + (u32 *)&oem_data->noise_max); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_NOISE_MAX"' failed %d", ret); + } + } + + /** open **/ + oem_data->test_open = OEM_OF_DEF_PROPVAL_TEST_OPEN || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_OPEN); + if (oem_data->test_open) { + oem_data->open_min = OEM_OF_DEF_PROPVAL_OPEN_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_OPEN_MIN, + (u32 *)&oem_data->open_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_OPEN_MIN"' failed %d", ret); + } + } + + /** short **/ + oem_data->test_short = OEM_OF_DEF_PROPVAL_TEST_SHORT || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_SHORT); + if (oem_data->test_short) { + oem_data->short_min = OEM_OF_DEF_PROPVAL_SHORT_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_SHORT_MIN, + (u32 *)&oem_data->short_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_SHORT_MIN"' failed %d", ret); + } + } + + /** comp cap **/ + oem_data->test_comp_cap = OEM_OF_DEF_PROPVAL_TEST_COMP_CAP || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_COMP_CAP); + if (oem_data->test_comp_cap) { + oem_data->comp_cap_min = OEM_OF_DEF_PROPVAL_COMP_CAP_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_COMP_CAP_MIN, + (u32 *)&oem_data->comp_cap_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_COMP_CAP_MIN"' failed %d", ret); + } + oem_data->comp_cap_max = OEM_OF_DEF_PROPVAL_COMP_CAP_MAX; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_COMP_CAP_MAX, + (u32 *)&oem_data->comp_cap_max); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_COMP_CAP_MAX"' failed %d", ret); + } + } + + oem_data->test_config_from_dt_has_parsed = true; + + return 0; +} + +static void print_selftest_config(const struct cts_oem_data *oem_data) +{ + cts_info("Seltest configuration:"); + + /** reset pin **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_RESET_PIN, + oem_data->test_reset_pin ? 'Y' : 'N'); + + /** int pin **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_INT_PIN, + oem_data->test_int_pin ? 'Y' : 'N'); + + /** rawdata **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_RAWDATA, + oem_data->test_rawdata ? 'Y' : 'N'); + if (oem_data->test_rawdata) { + cts_info(" - %-32s = %u", OEM_OF_PROPNAME_RAWDATA_FRAMES, + oem_data->rawdata_test_frames); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_RAWDATA_MIN, + oem_data->rawdata_min); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_RAWDATA_MAX, + oem_data->rawdata_max); + } + + /** noise **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_NOISE, + oem_data->test_noise ? 'Y' : 'N'); + if (oem_data->test_noise) { + cts_info(" - %-32s = %u", OEM_OF_PROPNAME_NOISE_FRAMES , + oem_data->noise_test_frames); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_NOISE_MAX, + oem_data->noise_max); + } + + /** open **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_OPEN, + oem_data->test_open ? 'Y' : 'N'); + if (oem_data->test_open) { + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_OPEN_MIN, + oem_data->open_min); + } + + /** short **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_SHORT, + oem_data->test_short ? 'Y' : 'N'); + if (oem_data->test_short) { + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_SHORT_MIN, + oem_data->short_min); + } + + /** comp cap **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_COMP_CAP, + oem_data->test_comp_cap ? 'Y' : 'N'); + if (oem_data->test_comp_cap) { + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_COMP_CAP_MIN, + oem_data->comp_cap_min); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_COMP_CAP_MAX, + oem_data->comp_cap_max); + } +} + +static void do_selftest(struct cts_oem_data *oem_data) +{ + struct cts_test_param test_param; + int retry = 3; + + cts_info("Do selftest"); + + /** reset pin test **/ + if (oem_data->test_reset_pin) { + memset(&test_param, 0, sizeof(test_param)); + test_param.test_item = CTS_TEST_RESET_PIN; + oem_data->reset_pin_test_result = + cts_test_reset_pin(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->reset_pin_test_result) { + cts_err("Test reset pin failed %d", oem_data->reset_pin_test_result); + } + } + + /** int pin test **/ + if (oem_data->test_int_pin) { + memset(&test_param, 0, sizeof(test_param)); + test_param.test_item = CTS_TEST_INT_PIN; + oem_data->int_pin_test_result = + cts_test_int_pin(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->int_pin_test_result) { + cts_err("Test int pin failed %d", oem_data->int_pin_test_result); + } + } + + /** rawdata test **/ + if (oem_data->test_rawdata) { + struct cts_rawdata_test_priv_param priv_param = {0}; + memset(&test_param, 0, sizeof(test_param)); + oem_data->rawdata_test_data_wr_size = 0; + + test_param.test_item = CTS_TEST_RAWDATA; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->rawdata_min; + test_param.max = &oem_data->rawdata_max; + test_param.test_data_buf = oem_data->rawdata_test_data; + test_param.test_data_buf_size = oem_data->rawdata_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->rawdata_test_data_wr_size; + + priv_param.frames = oem_data->rawdata_test_frames; + test_param.priv_param = &priv_param; + test_param.priv_param_size = sizeof(priv_param); + + retry = 3; + do { + oem_data->rawdata_test_result = + cts_test_rawdata(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->rawdata_test_result < 0 && retry--); + if (oem_data->rawdata_test_result) { + cts_err("Test rawdata failed %d", oem_data->rawdata_test_result); + } + } + + /** noise test **/ + if (oem_data->test_noise) { + struct cts_noise_test_priv_param priv_param = {0}; + memset(&test_param, 0, sizeof(test_param)); + oem_data->noise_test_data_wr_size = 0; + + test_param.test_item = CTS_TEST_NOISE; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.max = &oem_data->noise_max; + test_param.test_data_buf = oem_data->noise_test_data; + test_param.test_data_buf_size = oem_data->noise_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->noise_test_data_wr_size; + + priv_param.frames = oem_data->noise_test_frames; + test_param.priv_param = &priv_param; + test_param.priv_param_size = sizeof(priv_param); + + retry = 3; + do { + oem_data->noise_test_result = + cts_test_noise(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->noise_test_result < 0 && retry--); + if (oem_data->noise_test_result) { + cts_err("Test noise failed %d", oem_data->noise_test_result); + } + } + + /** open test **/ + if (oem_data->test_open) { + memset(&test_param, 0, sizeof(test_param)); + oem_data->open_test_data_wr_size = 0; + test_param.test_item = CTS_TEST_OPEN; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->open_min; + test_param.test_data_buf = oem_data->open_test_data; + test_param.test_data_buf_size = oem_data->open_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->open_test_data_wr_size; + + retry = 3; + do { + oem_data->open_test_result = + cts_test_open(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->open_test_result < 0 && retry--); + if (oem_data->open_test_result) { + cts_err("Test open failed %d", oem_data->open_test_result); + } + } + + /** short test **/ + if (oem_data->test_short) { + memset(&test_param, 0, sizeof(test_param)); + oem_data->short_test_data_wr_size = 0; + test_param.test_item = CTS_TEST_SHORT; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->short_min; + test_param.test_data_buf = oem_data->short_test_data; + test_param.test_data_buf_size = oem_data->short_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->short_test_data_wr_size; + + retry = 3; + do { + oem_data->short_test_result = + cts_test_short(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->short_test_result < 0 && retry--); + if (oem_data->short_test_result) { + cts_err("Test short failed %d", oem_data->short_test_result); + } + } + + /** comp cap test **/ + if (oem_data->test_comp_cap) { + memset(&test_param, 0, sizeof(test_param)); + oem_data->comp_cap_test_data_wr_size = 0; + test_param.test_item = CTS_TEST_COMPENSATE_CAP; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->comp_cap_min; + test_param.max = &oem_data->comp_cap_max; + test_param.test_result = &oem_data->comp_cap_test_result; + test_param.test_data_buf = oem_data->comp_cap_test_data; + test_param.test_data_buf_size = oem_data->comp_cap_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->comp_cap_test_data_wr_size; + + retry = 3; + do { + oem_data->comp_cap_test_result = + cts_test_compensate_cap(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->comp_cap_test_result < 0 && retry--); + if (oem_data->comp_cap_test_result) { + cts_err("Test compensate cap failed %d", oem_data->comp_cap_test_result); + } + } + +/* + if (oem_data->test_stylus_rawdata) { + struct cts_rawdata_test_priv_param priv_param = {0}; + memset(&test_param, 0, sizeof(test_param)); + oem_data->stylus_rawdata_test_data_wr_size = 0; + + test_param.test_item = CTS_TEST_STYLUS_RAWDATA; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->stylus_rawdata_min; + test_param.max = &oem_data->stylus_rawdata_max; + test_param.test_data_buf = oem_data->stylus_rawdata_test_data; + test_param.test_data_buf_size = oem_data->stylus_rawdata_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->stylus_rawdata_test_data_wr_size; + + priv_param.frames = oem_data->stylus_rawdata_test_frames; + test_param.priv_param = &priv_param; + test_param.priv_param_size = sizeof(priv_param); + + retry = 3; + do { + oem_data->stylus_rawdata_test_result = + cts_test_stylus_rawdata(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->stylus_rawdata_test_result < 0 && retry--); + if (oem_data->stylus_rawdata_test_result) { + cts_err("stylus test rawdata failed %d", oem_data->stylus_rawdata_test_result); + } + } + + if (oem_data->stylus_test_noise) { + struct cts_noise_test_priv_param priv_param = {0}; + memset(&test_param, 0, sizeof(test_param)); + oem_data->stylus_noise_test_data_wr_size = 0; + + test_param.test_item = CTS_TEST_STYLUS_NOISE; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.max = &oem_data->stylus_noise_max; + test_param.test_data_buf = oem_data->stylus_noise_test_data; + test_param.test_data_buf_size = oem_data->stylus_noise_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->stylus_noise_test_data_wr_size; + + priv_param.frames = oem_data->stylus_noise_test_frames; + test_param.priv_param = &priv_param; + test_param.priv_param_size = sizeof(priv_param); + + retry = 3; + do { + oem_data->stylus_noise_test_result = + cts_test_stylus_noise(&oem_data->cts_data->cts_dev, &test_param); + } while (oem_data->stylus_noise_test_result < 0 && retry--); + if (oem_data->stylus_noise_test_result) { + cts_err("stylus test noise failed %d", oem_data->stylus_noise_test_result); + } + } +*/ +} + +static int dump_tsdata_row_to_buffer(char *buf, size_t size, const u16 *data, + int cols, const char *prefix, const char *suffix, char seperator) +{ + int c, count = 0; + + if (prefix) { + count += scnprintf(buf, size, "%s", prefix); + } + + for (c = 0; c < cols; c++) { + count += scnprintf(buf + count, size - count, + "%4u%c ", data[c], seperator); + } + + if (suffix) { + count += scnprintf(buf + count, size - count, "%s", suffix); + } + + return count; +} + +static int dump_tsdata_to_csv_file(const char *filepath, + int flags, const u16 *data, int frames, int rows, int cols) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return -EPERM; +#else + loff_t pos = 0; + int i, r, ret; + struct file *file; + + cts_info("Dump tsdata to csv file: '%s' flags: 0x%x data: %p frames: %d row: %d col: %d", + filepath, flags, data, frames, cols, rows); + + file = filp_open(filepath, flags, 0666); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + for (i = 0; i < frames; i++) { + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_tsdata_row_to_buffer(linebuf, sizeof(linebuf), + data, cols, NULL, "\n", ','); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(file, linebuf, len, &pos); +#else + ret = kernel_write(file, linebuf, len, pos); + pos += len; +#endif + if (ret != len) { + cts_err("Write to file '%s' failed %d", filepath, ret); + goto close_file; + } + data += cols; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(file, "\n", 1, &pos); +#else + ret = kernel_write(file, "\n", 1, pos); + pos ++; +#endif + if (ret != 1) { + cts_err("Write newline to file '%s' failed %d", filepath, ret); + goto close_file; + } + } + +close_file: { + int r = filp_close(file, NULL); + if (r) { + cts_err("Close file '%s' failed %d", filepath, r); + } + } + + return ret; +#endif +} + +static void dump_tsdata_to_seq_file(struct seq_file *m, + const u16 *data, int rows, int cols) +{ + int r; + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_tsdata_row_to_buffer(linebuf, sizeof(linebuf), + data, cols, NULL, "\n", ','); + seq_puts(m, linebuf); + + data += cols; + } +} + +static int dump_comp_cap_row_to_buffer(char *buf, size_t size, const u8 *cap, + int cols, const char *prefix, const char *suffix, char seperator) +{ + int c, count = 0; + + if (prefix) { + count += scnprintf(buf, size, "%s", prefix); + } + + for (c = 0; c < cols; c++) { + count += scnprintf(buf + count, size - count, + "%3u%c ", cap[c], seperator); + } + + if (suffix) { + count += scnprintf(buf + count, size - count, "%s", suffix); + } + + return count; +} + +static int dump_comp_cap_to_csv_file(const char *filepath, + int flags, const u8 *cap, int rows, int cols) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return -EPERM; +#else + struct file *file; + int r, ret = 0; + loff_t pos = 0; + + cts_info("Dump compensate cap to csv file: '%s' flags: 0x%x row: %d col: %d", + filepath, flags, rows, cols); + + file = filp_open(filepath, flags, 0666); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_comp_cap_row_to_buffer(linebuf, sizeof(linebuf), + cap, cols, NULL, "\n", ','); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(file, linebuf, len, &pos); +#else + ret = kernel_write(file, linebuf, len, pos); + pos += len; +#endif + if (ret != len) { + cts_err("Write to file '%s' failed %d", filepath, ret); + goto close_file; + } + + cap += cols; + } + +close_file: { + int r = filp_close(file, NULL); + if (r) { + cts_err("Close file '%s' failed %d", filepath, ret); + } + } + + return ret; +#endif +} + +static void dump_comp_cap_to_seq_file(struct seq_file *m, + const u8 *data, int rows, int cols) +{ + int r; + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_comp_cap_row_to_buffer(linebuf, sizeof(linebuf), + data, cols, NULL, "\n", ','); + seq_puts(m, linebuf); + + data += cols; + } +} + +static int save_selftest_data_to_file(struct cts_oem_data *oem_data) +{ + int rows, cols; + int ret; + + cts_info("Save selftest data to file"); + + rows = oem_data->cts_data->cts_dev.fwdata.rows; + cols = oem_data->cts_data->cts_dev.fwdata.cols; + + if (oem_data->test_rawdata) { + ret = dump_tsdata_to_csv_file(OEM_RAWDATA_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->rawdata_test_data, + oem_data->rawdata_test_frames, cols, rows); + if (ret < 0) { + cts_err("Dump rawdata test data to file failed"); + return ret; + } + } + + if (oem_data->test_noise) { + ret = dump_tsdata_to_csv_file(OEM_NOISE_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->noise_test_data, + oem_data->noise_test_frames + 3, cols, rows); + if (ret < 0) { + cts_err("Dump noise test data to file failed"); + return ret; + } + } + + if (oem_data->test_open) { + ret = dump_tsdata_to_csv_file(OEM_OPEN_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->open_test_data, + 1, cols, rows); + if (ret < 0) { + cts_err("Dump open test data to file failed"); + return ret; + } + } + + if (oem_data->test_short) { + ret = dump_tsdata_to_csv_file(OEM_SHORT_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->short_test_data, + 5, cols, rows); + if (ret < 0) { + cts_err("Dump short test data to file failed"); + return ret; + } + } + + if (oem_data->test_comp_cap) { + ret = dump_comp_cap_to_csv_file(OEM_COMP_CAP_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, + oem_data->comp_cap_test_data, cols, rows); + if (ret < 0) { + cts_err("Dump compensate cap test data to file failed"); + return ret; + } + } + + return 0; +} + +static void *selftest_seq_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *selftest_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void selftest_seq_stop(struct seq_file *m, void *v) +{ + return; +} + +static int selftest_seq_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = (struct chipone_ts_data *)m->private; + struct cts_oem_data *oem_data = NULL; + int i, rows, cols; + + cts_info("Show seq selftest"); + + if (cts_data == NULL) { + cts_err("Selftest seq file private data = NULL"); + return -EFAULT; + } + + oem_data = cts_data->oem_data; + rows = cts_data->cts_dev.fwdata.rows; + cols = cts_data->cts_dev.fwdata.cols; + + seq_printf(m, "FW Version %04x!\n\n", cts_data->cts_dev.fwdata.version); + + if (oem_data->test_reset_pin) { + seq_printf(m, "Reset-Pin Test %s!\n\n", + oem_data->reset_pin_test_result == 0 ? "PASS" : "FAIL"); + } + if (oem_data->test_int_pin) { + seq_printf(m, "Int-Pin Test %s!\n\n", + oem_data->int_pin_test_result == 0 ? "PASS" : "FAIL"); + } + if (oem_data->test_rawdata) { + seq_printf(m, "FW Rawdata Test"); + if (oem_data->rawdata_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->rawdata_test_result > 0) { + seq_printf(m, " FAIL!\n"); + for(i = 0; i < oem_data->rawdata_test_frames; i++) { + dump_tsdata_to_seq_file(m, oem_data->rawdata_test_data + + i * rows * cols, cols, rows); + seq_putc(m, '\n'); + } + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->rawdata_test_result); + } + } + if (oem_data->test_noise) { + seq_printf(m, "Noise Test"); + if (oem_data->noise_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->noise_test_result > 0) { + seq_printf(m, " FAIL!\n"); + for(i = 0; i < oem_data->noise_test_frames; i++) { + dump_tsdata_to_seq_file(m, oem_data->noise_test_data + + i * rows * cols, rows, cols); + seq_putc(m, '\n'); + } + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->noise_test_result); + } + } + if (oem_data->test_open) { + seq_printf(m, "Open Test"); + if (oem_data->open_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->open_test_result > 0) { + seq_printf(m, " FAIL!\n"); + dump_tsdata_to_seq_file(m, oem_data->open_test_data, + rows, cols); + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->open_test_result); + } + } + if (oem_data->test_short) { + seq_printf(m, "Short Test"); + if (oem_data->short_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->short_test_result > 0) { + seq_printf(m, " FAIL!\n"); + for (i = 0; i < 10; i++) { + dump_tsdata_to_seq_file(m, oem_data->short_test_data + + i * rows * cols, cols, rows); + seq_putc(m, '\n'); + } + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->short_test_result); + } + } + if (oem_data->test_comp_cap) { + seq_printf(m, "Compensate-Cap Test"); + if (oem_data->comp_cap_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->comp_cap_test_result > 0) { + seq_printf(m, " FAIL!\n"); + dump_comp_cap_to_seq_file(m, oem_data->comp_cap_test_data, + rows, cols); + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->comp_cap_test_result); + } + } + + return 0; +} + +static const struct seq_operations selftest_seq_ops = { + .start = selftest_seq_start, + .next = selftest_seq_next, + .stop = selftest_seq_stop, + .show = selftest_seq_show, +}; + +static int32_t selftest_proc_open(struct inode *inode, struct file *file) +{ + struct chipone_ts_data *cts_data = pde_data(inode); + struct cts_oem_data *oem_data = NULL; + int ret; + + if (cts_data == NULL) { + cts_err("Open selftest proc with cts_data = NULL"); + return -EFAULT; + } + + oem_data = cts_data->oem_data; + if (oem_data == NULL) { + cts_err("Open selftest proc with oem_data = NULL"); + return -EFAULT; + } + + cts_info("Open '/proc/" OEM_SELFTEST_PROC_FILENAME "'"); + + if (!oem_data->test_config_from_dt_has_parsed) { +#ifndef CONFIG_CTS_I2C_HOST + ret = parse_selftest_dt(oem_data, cts_data->pdata->spi_client->dev.of_node); +#else + ret = parse_selftest_dt(oem_data, cts_data->device->of_node); +#endif + if (ret) { + cts_err("Parse selftest dt failed %d", ret); + return ret; + } + } + + print_selftest_config(oem_data); + + ret = alloc_selftest_data_mem(oem_data, + cts_data->cts_dev.fwdata.rows * cts_data->cts_dev.fwdata.cols); + if (ret) { + cts_err("Alloc test data mem failed"); + return ret; + } + + do_selftest(oem_data); + + ret = save_selftest_data_to_file(oem_data); + if (ret) { + cts_err("Save selftest data to file failed %d", ret); + } + + ret = seq_open(file, &selftest_seq_ops); + if (ret) { + cts_err("Open selftest seq file failed %d", ret); + return ret; + } + + ((struct seq_file *)file->private_data)->private = cts_data; + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations selftest_proc_fops = { + .owner = THIS_MODULE, + .open = selftest_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#else +static const struct proc_ops selftest_proc_fops = { + .proc_open = selftest_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; +#endif + +static int cts_rawdata_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + s16 *data; + u8 hwrows, hwcols, fwrows, fwcols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + hwrows = cts_dev->hwdata->num_row; + hwcols = cts_dev->hwdata->num_col; + fwrows = cts_dev->fwdata.rows; + fwcols = cts_dev->fwdata.cols; + + rawdata = kzalloc(hwrows * hwcols * 2, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_rawdata(cts_dev, rawdata, hwrows * hwcols * 2, 0); + cts_unlock_device(cts_dev); + + data = (s16 *)rawdata; + for (i = 0; i < fwcols; i++) { + for (j = 0; j < fwrows; j++) { + seq_printf(m, "%5d", *data++); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} +static int cts_rawdata_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_rawdata_show, pde_data(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_rawdata_ops = { + .owner = THIS_MODULE, + .open = cts_rawdata_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_rawdata_ops = { + .proc_open = cts_rawdata_open, + .proc_read = seq_read, +}; +#endif + +static int cts_diffdata_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + s16 *data; + u8 hwrows, hwcols, fwrows, fwcols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + hwrows = cts_dev->hwdata->num_row; + hwcols = cts_dev->hwdata->num_col; + fwrows = cts_dev->fwdata.rows; + fwcols = cts_dev->fwdata.cols; + + rawdata = kzalloc(hwrows * hwcols * 2, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_real_diff(cts_dev, rawdata, hwrows * hwcols * 2, 0); + cts_unlock_device(cts_dev); + + data = (s16 *)rawdata; + for (i = 0; i < fwcols; i++) { + for (j = 0; j < fwrows; j++) { + seq_printf(m, "%5d", *data++); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} +static int cts_diffdata_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_diffdata_show, pde_data(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_diffdata_ops = { + .owner = THIS_MODULE, + .open = cts_diffdata_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_diffdata_ops = { + .proc_open = cts_diffdata_open, + .proc_read = seq_read, +}; +#endif + +static int cts_manual_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + s16 *data; + u8 hwrows, hwcols, fwrows, fwcols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + hwrows = cts_dev->hwdata->num_row; + hwcols = cts_dev->hwdata->num_col; + fwrows = cts_dev->fwdata.rows; + fwcols = cts_dev->fwdata.cols; + + rawdata = kzalloc(hwrows * hwcols * 2, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_manual_diff(cts_dev, rawdata, hwrows * hwcols * 2, 0); + cts_unlock_device(cts_dev); + + data = (s16 *)rawdata; + for (i = 0; i < fwcols; i++) { + for (j = 0; j < fwrows; j++) { + seq_printf(m, "%5d", *data++); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} +static int cts_manual_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_manual_show, pde_data(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_manual_ops = { + .owner = THIS_MODULE, + .open = cts_manual_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_manual_ops = { + .proc_open = cts_manual_open, + .proc_read = seq_read, +}; +#endif + +static int cts_cnegdata_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + u8 *data; + u8 hwrows, hwcols, fwrows, fwcols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + hwrows = cts_dev->hwdata->num_row; + hwcols = cts_dev->hwdata->num_col; + fwrows = cts_dev->fwdata.rows; + fwcols = cts_dev->fwdata.cols; + + rawdata = kzalloc(hwrows * hwcols, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_cnegdata(cts_dev, rawdata, hwrows * hwcols, 0); + cts_unlock_device(cts_dev); + + data = (u8 *)rawdata; + for (i = 0; i < fwcols; i++) { + for (j = 0; j < fwrows; j++) { + seq_printf(m, "%5d", *data++); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} + +static int cts_cnegdata_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_cnegdata_show, pde_data(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_cnegdata_ops = { + .owner = THIS_MODULE, + .open = cts_cnegdata_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_cnegdata_ops = { + .proc_open = cts_cnegdata_open, + .proc_read = seq_read, +}; +#endif + +/* Use limit bin */ +static int cts_limit_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct firmware *limit_fw = NULL; + struct cts_limit limit; + char header[7] = {'C', 'h', 'i', 'p', 'o', 'n', 'e'}; + int ret; + + ret = request_firmware(&limit_fw, CFG_CTS_FACTORY_LIMIT_FILENAME, + &cts_dev->pdata->ts_input_dev->dev); + if (ret) { + seq_printf(m, "Request %s failed!\n", CFG_CTS_FACTORY_LIMIT_FILENAME); + return 0; + } + + ret = memcmp(header, limit_fw->data, sizeof(header)); + if (ret) { + seq_printf(m, "LIMIT file was not matched\n"); + release_firmware(limit_fw); + return 0; + } else { + cts_info("Matched limit file!"); + } + + memcpy(&limit, limit_fw->data, limit_fw->size); + release_firmware(limit_fw); + + seq_printf(m, "Limit version: %x, type:%d\n", limit.version, limit.type); + seq_printf(m, "\n"); + seq_printf(m, "Switch status:\n"); + seq_printf(m, " int_item : %d\n", limit.int_item); + seq_printf(m, " reset_item : %d\n", limit.reset_item); + seq_printf(m, " normal_rawdata_item : %d\n", limit.normal_rawdata_item); + seq_printf(m, " normal_noise_item : %d\n", limit.normal_noise_item); + seq_printf(m, " open_item : %d\n", limit.open_item); + seq_printf(m, " short_item : %d\n", limit.short_item); + seq_printf(m, " comp_cap_item : %d\n", limit.comp_cap_item); + seq_printf(m, " gstr_rawdata_item : %d\n", limit.gstr_rawdata_item); + seq_printf(m, " gstrlp_rawdata_item : %d\n", limit.gstrlp_rawdata_item); + seq_printf(m, " gstr_noise_item : %d\n", limit.gstr_noise_item); + seq_printf(m, " gstrlp_noise_item : %d\n", limit.gstrlp_noise_item); + seq_printf(m, " stylus_rawdata_item : %d\n", limit.stylus_rawdata_item); + seq_printf(m, " stylus_noise_item : %d\n", limit.stylus_noise_item); + seq_printf(m, " stylus_mnt_rawdata_item : %d\n", limit.stylus_mnt_rawdata_item); + seq_printf(m, "\n"); + seq_printf(m, "Threshold:\n"); + seq_printf(m, " normal_rawdata_frames : %d\n", limit.normal_rawdata_frames); + seq_printf(m, " normal_rawdata_min : %d\n", limit.normal_rawdata_min); + seq_printf(m, " normal_rawdata_max : %d\n", limit.normal_rawdata_max); + seq_printf(m, " normal_noise_frames : %d\n", limit.normal_noise_frames); + seq_printf(m, " normal_noise_max : %d\n", limit.normal_noise_max); + seq_printf(m, " normal_open_min : %d\n", limit.normal_open_min); + seq_printf(m, " normal_short_min : %d\n", limit.normal_short_min); + seq_printf(m, " normal_compcap_min : %d\n", limit.normal_compcap_min); + seq_printf(m, " normal_compcap_max : %d\n", limit.normal_compcap_max); + seq_printf(m, " gstr_rawdata_frames : %d\n", limit.gstr_rawdata_frames); + seq_printf(m, " gstr_rawdata_min : %d\n", limit.gstr_rawdata_min); + seq_printf(m, " gstr_rawdata_max : %d\n", limit.gstr_rawdata_max); + seq_printf(m, " gstrlp_rawdata_frames : %d\n", limit.gstrlp_rawdata_frames); + seq_printf(m, " gstrlp_rawdata_min : %d\n", limit.gstrlp_rawdata_min); + seq_printf(m, " gstrlp_rawdata_max : %d\n", limit.gstrlp_rawdata_max); + seq_printf(m, " gstr_noise_frames : %d\n", limit.gstr_noise_frames); + seq_printf(m, " gstr_noise_max : %d\n", limit.gstr_noise_max); + seq_printf(m, " gstrlp_noise_frames : %d\n", limit.gstrlp_noise_frames); + seq_printf(m, " gstrlp_noise_max : %d\n", limit.gstrlp_noise_max); + + seq_printf(m, " stylus_rawdata_frames : %d\n", limit.stylus_rawdata_frames); + seq_printf(m, " stylus_rawdata_min : %d\n", limit.stylus_rawdata_min); + seq_printf(m, " stylus_rawdata_max : %d\n", limit.stylus_rawdata_max); + seq_printf(m, " stylus_noise_frames : %d\n", limit.stylus_noise_frames); + seq_printf(m, " stylus_noise_max : %d\n", limit.stylus_noise_max); + seq_printf(m, " stylus_mnt_rawdata_frames : %d\n", limit.stylus_mnt_rawdata_frames); + seq_printf(m, " stylus_mnt_rawdata_min : %d\n", limit.stylus_mnt_rawdata_min); + seq_printf(m, " stylus_mnt_rawdata_max : %d\n", limit.stylus_mnt_rawdata_max); + + return 0; +} +static int cts_limit_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_limit_show, pde_data(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_limit_ops = { + .owner = THIS_MODULE, + .open = cts_limit_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_limit_ops = { + .proc_open = cts_limit_open, + .proc_read = seq_read, +}; +#endif + +static void cts_print_limit_value(struct cts_limit limit) +{ + cts_info("limit version : %x", limit.version); + cts_info("int_item : %d", limit.int_item); + cts_info("reset_item : %d", limit.reset_item); + cts_info("normal_rawdata_item : %d", limit.normal_rawdata_item); + cts_info("normal_noise_item : %d", limit.normal_noise_item); + cts_info("open_item : %d", limit.open_item); + cts_info("short_item : %d", limit.short_item); + cts_info("comp_cap_item : %d", limit.comp_cap_item); + cts_info("gstr_rawdata_item : %d", limit.gstr_rawdata_item); + cts_info("gstrlp_rawdata_item : %d", limit.gstrlp_rawdata_item); + cts_info("gstr_noise_item : %d", limit.gstr_noise_item); + cts_info("gstrlp_noise_item : %d", limit.gstrlp_noise_item); + cts_info("stylus_rawdata_item : %d", limit.stylus_rawdata_item); + cts_info("stylus_noise_item : %d", limit.stylus_noise_item); + cts_info("stylus_mnt_raw_item : %d", limit.stylus_mnt_rawdata_item); + cts_info("type : %d", limit.type); + cts_info("----------------------------"); + cts_info("normal_rawdata_frames : %d", limit.normal_rawdata_frames); + cts_info("normal_rawdata_min : %d", limit.normal_rawdata_min); + cts_info("normal_rawdata_max : %d", limit.normal_rawdata_max); + cts_info("normal_noise_frames : %d", limit.normal_noise_frames); + cts_info("normal_noise_max : %d", limit.normal_noise_max); + cts_info("normal_open_min : %d", limit.normal_open_min); + cts_info("normal_short_min : %d", limit.normal_short_min); + cts_info("normal_compcap_min : %d", limit.normal_compcap_min); + cts_info("normal_compcap_max : %d", limit.normal_compcap_max); + cts_info("gstr_rawdata_frames : %d", limit.gstr_rawdata_frames); + cts_info("gstr_rawdata_min : %d", limit.gstr_rawdata_min); + cts_info("gstr_rawdata_max : %d", limit.gstr_rawdata_max); + cts_info("gstrlp_rawdata_frames : %d", limit.gstrlp_rawdata_frames); + cts_info("gstrlp_rawdata_min : %d", limit.gstrlp_rawdata_min); + cts_info("gstrlp_rawdata_max : %d", limit.gstrlp_rawdata_max); + cts_info("gstr_noise_frames : %d", limit.gstr_noise_frames); + cts_info("gstr_noise_max : %d", limit.gstr_noise_max); + cts_info("gstrlp_noise_frames : %d", limit.gstrlp_noise_frames); + cts_info("gstrlp_noise_max : %d", limit.gstrlp_noise_max); + cts_info("stylus_rawdata_frames : %d", limit.stylus_rawdata_frames); + cts_info("stylus_rawdata_min : %d", limit.stylus_rawdata_min); + cts_info("stylus_rawdata_max : %d", limit.stylus_rawdata_max); + cts_info("stylus_noise_frames : %d", limit.stylus_noise_frames); + cts_info("stylus_noise_max : %d", limit.stylus_noise_max); + cts_info("stylus_mnt_raw_frames : %d", limit.stylus_mnt_rawdata_frames); + cts_info("stylus_mnt_raw_min : %d", limit.stylus_mnt_rawdata_min); + cts_info("stylus_mnt_raw_max : %d", limit.stylus_mnt_rawdata_max); +} + + +static int cts_touch_test(struct seq_file *m, void *v, struct cts_limit limit) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev = &cts_data->cts_dev; + + s64 reset_pin_test_elapsed_time = 0; + struct cts_test_param reset_pin_test_param = { + .test_item = CTS_TEST_RESET_PIN, + .flags = 0, + .elapsed_time_ms = &reset_pin_test_elapsed_time, + }; + s64 int_pin_test_elapsed_time = 0; + struct cts_test_param int_pin_test_param = { + .test_item = CTS_TEST_INT_PIN, + .flags = 0, + .elapsed_time_ms = &int_pin_test_elapsed_time, + }; + struct cts_rawdata_test_priv_param rawdata_test_priv_param = { + .frames = 16, + //.work_mode = 0, + }; + s64 rawdata_test_elapsed_time = 0; + struct cts_test_param rawdata_test_param = { + .test_item = CTS_TEST_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &rawdata_test_elapsed_time, + .priv_param = &rawdata_test_priv_param, + .priv_param_size = sizeof(rawdata_test_priv_param), + }; + struct cts_noise_test_priv_param noise_test_priv_param = { + .frames = 50, + //.work_mode = 0, + }; + s64 noise_test_elapsed_time = 0; + struct cts_test_param noise_test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &noise_test_elapsed_time, + .priv_param = &noise_test_priv_param, + .priv_param_size = sizeof(noise_test_priv_param), + }; + s64 open_test_elapsed_time = 0; + struct cts_test_param open_test_param = { + .test_item = CTS_TEST_OPEN, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &open_test_elapsed_time, + }; + s64 short_test_elapsed_time = 0; + struct cts_test_param short_test_param = { + .test_item = CTS_TEST_SHORT, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &short_test_elapsed_time, + }; + s64 comp_cap_test_elapsed_time = 0; + struct cts_test_param comp_cap_test_param = { + .test_item = CTS_TEST_COMPENSATE_CAP, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &comp_cap_test_elapsed_time, + }; + + int normal_rawdata_frames; + int normal_rawdata_min; + int normal_rawdata_max; + int normal_noise_frames; + int normal_noise_max; + int normal_open_min; + int normal_short_min; + int normal_compcap_min; + int normal_compcap_max; + + int rawdata_test_result = 0; + int noise_test_result = 0; + int open_test_result = 0; + int short_test_result = 0; + int comp_cap_test_result = 0; + + char touch_data_filepath[256]; + int retry; + int result_all = 0; + + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"RAWDATA_TEST_DATA_FILENAME); + rawdata_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"NOISE_TEST_DATA_FILENAME); + noise_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"OPEN_TEST_DATA_FILENAME); + open_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"SHORT_TEST_DATA_FILENAME); + short_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"COMP_CAP_TEST_DATA_FILENAME); + comp_cap_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + + normal_rawdata_frames = limit.normal_rawdata_frames; + normal_rawdata_min = limit.normal_rawdata_min; + normal_rawdata_max = limit.normal_rawdata_max; + normal_noise_frames = limit.normal_noise_frames; + normal_noise_max = limit.normal_noise_max; + normal_open_min = limit.normal_open_min; + normal_short_min = limit.normal_short_min; + normal_compcap_min = limit.normal_compcap_min; + normal_compcap_max = limit.normal_compcap_max; + + rawdata_test_priv_param.frames = normal_rawdata_frames; + rawdata_test_param.min = &normal_rawdata_min; + rawdata_test_param.max = &normal_rawdata_max; + noise_test_priv_param.frames = normal_noise_frames; + noise_test_param.max = &normal_noise_max; + open_test_param.min = &normal_open_min; + short_test_param.min = &normal_short_min; + comp_cap_test_param.min = &normal_compcap_min; + comp_cap_test_param.max = &normal_compcap_max; + + if (limit.int_item && cts_test_int_pin(cts_dev, &int_pin_test_param)) { + result_all++; + } + if (limit.reset_item && cts_test_reset_pin(cts_dev, &reset_pin_test_param)) { + result_all++; + } + if (limit.normal_rawdata_item) { + retry = 3; + do { + rawdata_test_result = cts_test_rawdata(cts_dev, &rawdata_test_param); + } while (rawdata_test_result < 0 && retry--); + if (rawdata_test_result) { + result_all++; + } + } + if (limit.normal_noise_item) { + retry = 3; + do { + noise_test_result = cts_test_noise(cts_dev, &noise_test_param); + } while (noise_test_result < 0 && retry--); + if (noise_test_result) { + result_all++; + } + } + if (limit.open_item) { + retry = 3; + do { + open_test_result = cts_test_open(cts_dev, &open_test_param); + } while (open_test_result < 0 && retry--); + if (open_test_result) { + result_all++; + } + } + if (limit.short_item) { + retry = 3; + do { + short_test_result = cts_test_short(cts_dev, &short_test_param); + } while (short_test_result < 0 && retry--); + if (short_test_result) { + result_all++; + } + } + if (limit.comp_cap_item) { + retry = 3; + do { + comp_cap_test_result = + cts_test_compensate_cap(cts_dev, &comp_cap_test_param); + } while (comp_cap_test_result < 0 && retry--); + if (comp_cap_test_result) { + result_all++; + } + } + + if (rawdata_test_param.test_data_filepath) { + kfree(rawdata_test_param.test_data_filepath); + } + if (noise_test_param.test_data_filepath) { + kfree(noise_test_param.test_data_filepath); + } + if (open_test_param.test_data_filepath) { + kfree(open_test_param.test_data_filepath); + } + if (short_test_param.test_data_filepath) { + kfree(short_test_param.test_data_filepath); + } + if (comp_cap_test_param.test_data_filepath) { + kfree(comp_cap_test_param.test_data_filepath); + } + + return result_all; +} + +static int cts_stylus_test(struct seq_file *m, void *v, struct cts_limit limit) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev = &cts_data->cts_dev; + + struct cts_rawdata_test_priv_param stylus_rawdata_test_priv_param = { + .frames = 16, + //.work_mode = 0, + }; + s64 stylus_rawdata_test_elapsed_time = 0; + struct cts_test_param stylus_rawdata_test_param = { + .test_item = CTS_TEST_STYLUS_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &stylus_rawdata_test_elapsed_time, + .priv_param = &stylus_rawdata_test_priv_param, + .priv_param_size = sizeof(stylus_rawdata_test_priv_param), + }; + + struct cts_noise_test_priv_param stylus_noise_test_priv_param = { + .frames = 50, + //.work_mode = 0, + }; + s64 stylus_noise_test_elapsed_time = 0; + struct cts_test_param stylus_noise_test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &stylus_noise_test_elapsed_time, + .priv_param = &stylus_noise_test_priv_param, + .priv_param_size = sizeof(stylus_noise_test_priv_param), + }; + + struct cts_rawdata_test_priv_param stylus_mnt_rawdata_test_priv_param = { + .frames = 16, + //.work_mode = 0, + }; + s64 stylus_mnt_rawdata_test_elapsed_time = 0; + struct cts_test_param stylus_mnt_rawdata_test_param = { + .test_item = CTS_TEST_STYLUS_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &stylus_mnt_rawdata_test_elapsed_time, + .priv_param = &stylus_mnt_rawdata_test_priv_param, + .priv_param_size = sizeof(stylus_mnt_rawdata_test_priv_param), + }; + + int stylus_rawdata_frames; + int stylus_rawdata_min; + int stylus_rawdata_max; + int stylus_noise_frames; + int stylus_noise_max; + int stylus_mnt_rawdata_frames; + int stylus_mnt_rawdata_min; + int stylus_mnt_rawdata_max; + + int stylus_rawdata_test_result = 0; + int stylus_noise_test_result = 0; + int stylus_mnt_rawdata_test_result = 0; + + char touch_data_filepath[256]; + int retry; + int result_all = 0; + + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"STYLUS_RAWDATA_TEST_DATA_FILENAME); + stylus_rawdata_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"STYLUS_NOISE_TEST_DATA_FILENAME); + stylus_noise_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"STYLUS_MNT_NOISE_TEST_DATA_FILENAME); + stylus_mnt_rawdata_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + + stylus_rawdata_frames = limit.stylus_rawdata_frames; + stylus_rawdata_min = limit.stylus_rawdata_min; + stylus_rawdata_max = limit.stylus_rawdata_max; + stylus_noise_frames = limit.stylus_noise_frames; + stylus_noise_max = limit.stylus_noise_max; + stylus_mnt_rawdata_frames = limit.stylus_mnt_rawdata_frames; + stylus_mnt_rawdata_min = limit.stylus_mnt_rawdata_min; + stylus_mnt_rawdata_max = limit.stylus_mnt_rawdata_max; + + stylus_rawdata_test_priv_param.frames = stylus_rawdata_frames; + stylus_rawdata_test_param.min = &stylus_rawdata_min; + stylus_rawdata_test_param.max = &stylus_rawdata_max; + stylus_noise_test_priv_param.frames = stylus_noise_frames; + stylus_noise_test_param.max = &stylus_noise_max; + stylus_mnt_rawdata_test_priv_param.frames = stylus_mnt_rawdata_frames; + stylus_mnt_rawdata_test_param.min = &stylus_mnt_rawdata_min; + stylus_mnt_rawdata_test_param.max = &stylus_mnt_rawdata_max; + + if (limit.stylus_rawdata_item) { + retry = 3; + do { + stylus_rawdata_test_result = + cts_test_stylus_rawdata(cts_dev, &stylus_rawdata_test_param); + } while (stylus_rawdata_test_result < 0 && retry--); + if (stylus_rawdata_test_result) { + result_all++; + } + } + if (limit.stylus_noise_item) { + retry = 3; + do { + stylus_noise_test_result = + cts_test_stylus_noise(cts_dev, &stylus_noise_test_param); + } while (stylus_noise_test_result < 0 && retry--); + if (stylus_noise_test_result) { + result_all++; + } + } + if (limit.stylus_mnt_rawdata_item) { + retry = 3; + do { + stylus_mnt_rawdata_test_result = + cts_test_stylus_mnt_rawdata(cts_dev, &stylus_mnt_rawdata_test_param); + } while (stylus_mnt_rawdata_test_result < 0 && retry--); + if (stylus_mnt_rawdata_test_result) { + result_all++; + } + } + + if (stylus_rawdata_test_param.test_data_filepath) { + kfree(stylus_rawdata_test_param.test_data_filepath); + } + if (stylus_noise_test_param.test_data_filepath) { + kfree(stylus_noise_test_param.test_data_filepath); + } + if (stylus_mnt_rawdata_test_param.test_data_filepath) { + kfree(stylus_mnt_rawdata_test_param.test_data_filepath); + } + + return result_all; +} + + +static int cts_factory_test_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct firmware *limit_fw = NULL; + struct cts_limit limit; + char header[7] = {'C', 'h', 'i', 'p', 'o', 'n', 'e'}; + + ktime_t start_time, end_time, delta_time; + u16 fw_version; + int result_all = 0; + int ret; + + ret = request_firmware(&limit_fw, CFG_CTS_FACTORY_LIMIT_FILENAME, + &cts_dev->pdata->ts_input_dev->dev); + if (ret) { + cts_err("Request %s failed!", CFG_CTS_FACTORY_LIMIT_FILENAME); + return 0; + } + + ret = memcmp(header, limit_fw->data, sizeof(header)); + if (ret) { + cts_err("LIMIT file was not matched"); + release_firmware(limit_fw); + return 0; + } else { + cts_info("Matched limit file!"); + } + + memcpy(&limit, limit_fw->data, limit_fw->size); + release_firmware(limit_fw); + + cts_print_limit_value(limit); + + start_time = ktime_get(); + + ret = cts_tcs_get_fw_ver(cts_dev, &fw_version); + if (ret) { + cts_err("Factory test get firmware version failed"); + fw_version = 0; + } + cts_info("Firmware Version: 0x%04X", fw_version); + + result_all += cts_touch_test(m, v, limit); + result_all += cts_stylus_test(m, v, limit); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + cts_info("Factory test, total ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + seq_printf(m, "%s\n", result_all ? "FAIL" : "PASS"); + return 0; +} + +static int cts_factory_test_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_factory_test_show, pde_data(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_factory_test_ops = { + .owner = THIS_MODULE, + .open = cts_factory_test_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_factory_test_ops = { + .proc_open = cts_factory_test_open, + .proc_read = seq_read, +}; +#endif + + +int cts_oem_init(struct chipone_ts_data *cts_data) +{ + struct cts_oem_data *oem_data = NULL; + int ret; + + if (cts_data == NULL) { + cts_err("Init with cts_data = NULL"); + return -EINVAL; + } + + cts_info("Init"); + + cts_data->oem_data = NULL; + + oem_data = kzalloc(sizeof(*oem_data), GFP_KERNEL); + if (oem_data == NULL) { + cts_err("Alloc oem data failed"); + return -ENOMEM; + } + + cts_info(" - Create '/proc/"OEM_SELFTEST_PROC_FILENAME"'"); + oem_data->selftest_proc_entry = + proc_create_data(OEM_SELFTEST_PROC_FILENAME, + S_IRUGO, NULL, &selftest_proc_fops, cts_data); + if (oem_data->selftest_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_SELFTEST_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->limit_entry = + proc_create_data(OEM_LIMIT_PROC_FILENAME, + S_IRUGO, NULL, &cts_limit_ops, cts_data); + if (oem_data->limit_entry == NULL) { + cts_err("Create '/proc/"OEM_LIMIT_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->factory_test_entry = + proc_create_data(OEM_FACTORY_TEST_PROC_FILENAME, + S_IRUGO, NULL, &cts_factory_test_ops, cts_data); + if (oem_data->factory_test_entry == NULL) { + cts_err("Create '/proc/"OEM_FACTORY_TEST_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->rawdata_proc_entry = proc_create_data(OEM_RAWDATA_PROC_FILENAME, + S_IRUGO, NULL, &cts_rawdata_ops, cts_data); + if (oem_data->rawdata_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_RAWDATA_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->manual_proc_entry = proc_create_data(OEM_MANUAL_PROC_FILENAME, + S_IRUGO, NULL, &cts_manual_ops, cts_data); + if (oem_data->manual_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_MANUAL_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->diffdata_proc_entry = proc_create_data(OEM_DIFFDATA_PROC_FILENAME, + S_IRUGO, NULL, &cts_diffdata_ops, cts_data); + if (oem_data->diffdata_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_DIFFDATA_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->cnegdata_proc_entry = proc_create_data(OEM_CNEGDATA_PROC_FILENAME, + S_IRUGO, NULL, &cts_cnegdata_ops, cts_data); + if (oem_data->cnegdata_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_CNEGDATA_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + cts_data->oem_data = oem_data; + oem_data->cts_data = cts_data; + return 0; + +free_oem_data: + kfree(oem_data); + return ret; +} + +int cts_oem_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_oem_data *oem_data = NULL; + + if (cts_data == NULL) { + cts_err("Deinit with cts_data = NULL"); + return -EINVAL; + } + + if (cts_data->oem_data == NULL) { + cts_warn("Deinit with oem_data = NULL"); + return 0; + } + + cts_info("Deinit"); + + oem_data = cts_data->oem_data; + + if (oem_data->cnegdata_proc_entry) { + cts_info(" Remove '/proc/"OEM_CNEGDATA_PROC_FILENAME"'"); + remove_proc_entry(OEM_CNEGDATA_PROC_FILENAME, NULL); + } + + if (oem_data->selftest_proc_entry) { + cts_info(" Remove '/proc/"OEM_SELFTEST_PROC_FILENAME"'"); + remove_proc_entry(OEM_SELFTEST_PROC_FILENAME, NULL); + } + + if (oem_data->limit_entry) { + cts_info(" Remove '/proc/"OEM_LIMIT_PROC_FILENAME"'"); + remove_proc_entry(OEM_LIMIT_PROC_FILENAME, NULL); + } + if (oem_data->factory_test_entry) { + cts_info(" Remove '/proc/"OEM_FACTORY_TEST_PROC_FILENAME"'"); + remove_proc_entry(OEM_FACTORY_TEST_PROC_FILENAME, NULL); + } + + if (oem_data->rawdata_proc_entry) { + cts_info(" Remove '/proc/"OEM_RAWDATA_PROC_FILENAME"'"); + remove_proc_entry(OEM_RAWDATA_PROC_FILENAME, NULL); + } + + if (oem_data->manual_proc_entry) { + cts_info(" Remove '/proc/"OEM_MANUAL_PROC_FILENAME"'"); + remove_proc_entry(OEM_MANUAL_PROC_FILENAME, NULL); + } + + if (oem_data->diffdata_proc_entry) { + cts_info(" Remove '/proc/"OEM_DIFFDATA_PROC_FILENAME"'"); + remove_proc_entry(OEM_DIFFDATA_PROC_FILENAME, NULL); + } + + free_selftest_data_mem(oem_data); + + kfree(cts_data->oem_data); + cts_data->oem_data = NULL; + + return 0; +} + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_oem.h b/drivers/input/touchscreen/chipone_tddi/cts_oem.h new file mode 100644 index 000000000000..ced0349f96c0 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_oem.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_OEM_H +#define CTS_OEM_H + +struct chipone_ts_data; + +extern int cts_oem_init(struct chipone_ts_data *cts_data); +extern int cts_oem_deinit(struct chipone_ts_data *cts_data); + +#endif /* CTS_VENDOR_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_platform.c b/drivers/input/touchscreen/chipone_tddi/cts_platform.c new file mode 100644 index 000000000000..4b4dd1bc535c --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_platform.c @@ -0,0 +1,1919 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define LOG_TAG "Plat" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_sysfs.h" +#include "cts_tcs.h" + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +int tpd_rst_gpio_index = 0; +int tpd_int_gpio_index = 1; +static int tpd_history_x, tpd_history_y; +#endif + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_plat_set_reset(struct cts_platform_data *pdata, int val) +{ + cts_info("Set Reset to %s", val ? "HIGH" : "LOW"); +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + if (val) + tpd_gpio_output(tpd_rst_gpio_index, 1); + else + tpd_gpio_output(tpd_rst_gpio_index, 0); +#else + if (val) + gpio_set_value(pdata->rst_gpio, 1); + else + gpio_set_value(pdata->rst_gpio, 0); + +#endif + return 0; +} + +int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + /* !!!can not be modified */ + /* !!!can not be modified */ + /* !!!can not be modified */ + cts_plat_set_reset(pdata, 1); + mdelay(1); + cts_plat_set_reset(pdata, 0); + mdelay(5);/* 1ms */ + cts_plat_set_reset(pdata, 1); + /* under normal mode, delay over 50ms */ +#ifdef CONFIG_CTS_I2C_HOST + mdelay(120); +#else + mdelay(70); +#endif + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS +int cts_plat_set_cs(struct cts_platform_data *pdata, int val) +{ +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + if (val) + pinctrl_select_state(pdata->pinctrl1, pdata->spi_cs_high); + else + pinctrl_select_state(pdata->pinctrl1, pdata->spi_cs_low); +#else + if (val) + gpio_set_value(pdata->cs_gpio, 1); + else + gpio_set_value(pdata->cs_gpio, 0); +#endif + return 0; +} +#endif /* CFG_CTS_MANUAL_CS */ + + +#ifdef CONFIG_CTS_I2C_HOST +size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata) +{ +#ifdef TPD_SUPPORT_I2C_DMA + if (pdata->i2c_dma_available) { + return CFG_CTS_MAX_I2C_XFER_SIZE; + } else { + return CFG_CTS_MAX_I2C_FIFO_XFER_SIZE; + } +#else + return CFG_CTS_MAX_I2C_XFER_SIZE; +#endif +} + +u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ +#ifdef TPD_SUPPORT_I2C_DMA + if (pdata->i2c_dma_available && xfer_size > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) { + return pdata->i2c_dma_buff_va; + } else +#endif /* TPD_SUPPORT_I2C_DMA */ + 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; + +#ifdef TPD_SUPPORT_I2C_DMA + struct i2c_msg msg = { + .addr = i2c_addr, + .flags = !I2C_M_RD, + .len = len, + .timing = 300, + }; + + if (pdata->i2c_dma_available && len > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) { + msg.ext_flag = (pdata->i2c_client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msg.buf = (u8 *)pdata->i2c_dma_buff_pa; + memcpy(pdata->i2c_dma_buff_va, src, len); + } else { + msg.buf = (u8 *)src; + } + msg.len = len; +#else /* TPD_SUPPORT_I2C_DMA */ + struct i2c_msg msg = { + .flags = !I2C_M_RD, + .addr = i2c_addr, + .buf = (u8 *) src, + .len = len, + }; +#endif /* TPD_SUPPORT_I2C_DMA */ + + 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; + +#ifdef TPD_SUPPORT_I2C_DMA + struct i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = !I2C_M_RD, + .len = wlen, + .buf = (u8 *)wbuf, + .timing = 300, + }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .len = rlen, + .timing = 300, + }, + }; + + if (pdata->i2c_dma_available && rlen > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) { + msgs[1].ext_flag = (pdata->i2c_client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msgs[1].buf = (u8 *)pdata->i2c_dma_buff_pa; + } else { + msgs[1].buf = (u8 *)rbuf; + } +#else /* TPD_SUPPORT_I2C_DMA */ + struct i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = !I2C_M_RD, + .buf = (u8 *) wbuf, + .len = wlen }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = (u8 *) rbuf, + .len = rlen } + }; +#endif /* TPD_SUPPORT_I2C_DMA */ + + 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 { +#ifdef TPD_SUPPORT_I2C_DMA + if (pdata->i2c_dma_available && rlen > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) { + memcpy(rbuf, pdata->i2c_dma_buff_va, rlen); + } +#endif /* TPD_SUPPORT_I2C_DMA */ + + return 0; + } + } while (++retries < retry); + + return ret; +} + +#else /*CONFIG_CTS_I2C_HOST*/ + +#ifdef CFG_MTK_LEGEND_PLATFORM +struct mt_chip_conf cts_spi_conf_mt65xx = { + .setuptime = 15, + .holdtime = 15, + .high_time = 21, //for mt6582, 104000khz/(4+4) = 130000khz + .low_time = 21, + .cs_idletime = 20, + .ulthgh_thrsh = 0, + + .cpol = 0, + .cpha = 0, + + .rx_mlsb = 1, + .tx_mlsb = 1, + + .tx_endian = 0, + .rx_endian = 0, + + .com_mod = FIFO_TRANSFER, + .pause = 1, + .finish_intr = 1, + .deassert = 0, + .ulthigh = 0, + .tckdly = 0, +}; + +typedef enum { + SPEED_500KHZ = 500, + SPEED_1MHZ = 1000, + SPEED_2MHZ = 2000, + SPEED_3MHZ = 3000, + SPEED_4MHZ = 4000, + SPEED_6MHZ = 6000, + SPEED_8MHZ = 8000, + SPEED_KEEP, + SPEED_UNSUPPORTED +} SPI_SPEED; + +static int cts_plat_spi_set_mode(struct spi_device *spi, SPI_SPEED speed, int flag) +{ + struct mt_chip_conf *mcc = &cts_spi_conf_mt65xx; + int ret; + + if (flag == 0) { + mcc->com_mod = FIFO_TRANSFER; + } else { + mcc->com_mod = DMA_TRANSFER; + } + + switch (speed) { + case SPEED_500KHZ: + mcc->high_time = 120; + mcc->low_time = 120; + break; + case SPEED_1MHZ: + mcc->high_time = 60; + mcc->low_time = 60; + break; + case SPEED_2MHZ: + mcc->high_time = 30; + mcc->low_time = 30; + break; + case SPEED_3MHZ: + mcc->high_time = 20; + mcc->low_time = 20; + break; + case SPEED_4MHZ: + mcc->high_time = 15; + mcc->low_time = 15; + break; + case SPEED_6MHZ: + mcc->high_time = 10; + mcc->low_time = 10; + break; + case SPEED_8MHZ: + mcc->high_time = 8; + mcc->low_time = 8; + break; + case SPEED_KEEP: + case SPEED_UNSUPPORTED: + break; + } + + ret = spi_setup(spi); + if (ret) { + cts_err("Spi setup failed %d(%s)", ret, cts_strerror(ret)); + } + return ret; +} +#endif /* CFG_MTK_LEGEND_PLATFORM */ + +int cts_plat_spi_setup(struct cts_platform_data *pdata) +{ + int ret; + + pdata->spi_client->chip_select = 0; + pdata->spi_client->mode = SPI_MODE_0; + pdata->spi_client->bits_per_word = 8; + + cts_info(" - chip_select :%d", pdata->spi_client->chip_select); + cts_info(" - spi_mode :%d", pdata->spi_client->mode); + cts_info(" - bits_per_word:%d", pdata->spi_client->bits_per_word); + +#ifdef CFG_MTK_LEGEND_PLATFORM + pdata->spi_client->controller_data = (void *)&cts_spi_conf_mt65xx; + ret = spi_setup(pdata->spi_client); + cts_plat_spi_set_mode(pdata->spi_client, pdata->spi_speed, 0); +#else /* CFG_MTK_LEGEND_PLATFORM */ + ret = spi_setup(pdata->spi_client); +#endif /* CFG_MTK_LEGEND_PLATFORM */ + if (ret) { + cts_err("spi_setup err!"); + } + return ret; +} + +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 = { + .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, */ + }; + 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 + 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); + +#ifdef CFG_CTS_MANUAL_CS + udelay(100); + 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 crc16_calc; + size_t data_len; + + if (len > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write too much data:wlen=%zu", 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]); + crc16_calc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[5]); + memcpy(&pdata->spi_tx_buf[7], (char *)src + 2, data_len); + crc16_calc = (u16) cts_crc32((char *)src + 2, data_len); + put_unaligned_le16(crc16_calc, &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 crc16_calc, crc16_recv; + + 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]); + crc16_calc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc16_calc, &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); + crc16_calc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + crc16_recv = get_unaligned_le16(&pdata->spi_rx_buf[rlen]); + if (crc16_recv != crc16_calc) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + crc16_recv, crc16_calc); + 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; +} +#endif /*CONFIG_CTS_I2C_HOST*/ + +int cts_plat_is_normal_mode(struct cts_platform_data *pdata) +{ + struct chipone_ts_data *cts_data; + u16 fwid; + int ret; + + cts_set_normal_addr(pdata->cts_dev); + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + ret = cts_tcs_get_fw_id(pdata->cts_dev, &fwid); + if (ret || !cts_is_fwid_valid(fwid)) + return false; + + return true; +} + +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 + + 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 + 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 CFG_CTS_FORCE_UP +static void cts_plat_touch_timeout_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = container_of(work, + struct cts_platform_data, touch_timeout.work); + + cts_warn("Touch event timeout work"); + + cts_plat_release_all_touch(pdata); +} +#endif + + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#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."); + } else + cts_info("chipone,build_id=0x%04X", 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."); + } else + cts_info("chipone,config_id=0x%04X", 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", ret); + } else + cts_info("panel supplier=%s", (char *)pdata->panel_supplier); +#endif + + return 0; +} +#endif /* CONFIG_CTS_OF */ + +#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; + struct input_dev *pen_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 */ + + mutex_init(&pdata->dev_lock); + 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_PALM_DETECT + set_bit(CFG_CTS_PALM_EVENT, input_dev->keybit); +#endif + +#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; + + + pen_dev = input_allocate_device(); + if (pen_dev == NULL) { + cts_err("Failed to allocate pen device."); + return -ENOMEM; + } + +#ifdef CONFIG_CTS_I2C_HOST + pen_dev->id.bustype = BUS_I2C; + pen_dev->dev.parent = &pdata->i2c_client->dev; +#else + pen_dev->id.bustype = BUS_SPI; + pen_dev->dev.parent = &pdata->spi_client->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + pen_dev->name = "chipone-tddi,pen"; + pen_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(ABS_X, pen_dev->absbit); + __set_bit(ABS_Y, pen_dev->absbit); + __set_bit(BTN_STYLUS, pen_dev->keybit); + __set_bit(BTN_STYLUS2, pen_dev->keybit); + __set_bit(BTN_TOUCH, pen_dev->keybit); + __set_bit(BTN_TOOL_PEN, pen_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit); + input_set_abs_params(pen_dev, ABS_X, 0, pdata->res_x, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, 0, pdata->res_y, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_X, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_Y, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_Z, 0, 36000, 0, 0); + + ret = input_register_device(pen_dev); + if (ret) { + cts_err("Input pen device registration failed"); + input_free_device(pen_dev); + pen_dev = NULL; + return ret; + } + pdata->pen_input_dev = pen_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_timeout, cts_plat_touch_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 +} + +#else /*CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED*/ + +#ifndef CONFIG_CTS_I2C_HOST +static int cts_plat_init_dts(struct cts_platform_data *pdata, struct device *device) +{ +#ifdef CFG_CTS_MANUAL_CS + struct device_node *node; + + pdata->pinctrl1 = devm_pinctrl_get(device); + node = device->of_node; + if (node) { + pdata->spi_cs_low = pinctrl_lookup_state(pdata->pinctrl1, "spi_cs_low"); + if (IS_ERR(pdata->spi_cs_low)) { + cts_err("Cannot find pinctrl spi cs high!\n"); + return -ENOENT; + } + pdata->spi_cs_high = pinctrl_lookup_state(pdata->pinctrl1, "spi_cs_high"); + if (IS_ERR(pdata->spi_cs_high)) { + return -ENOENT; + } + return 0; + } + return -ENOENT; +#else + return 0; +#endif +} +#endif /* CONFIG_CTS_I2C_HOST */ + +#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 device_node *node = NULL; + u32 ints[2] = { 0, 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 */ + } +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST + pdata->i2c_client = i2c_client; +#else + pdata->spi_client = spi; +#endif /* CONFIG_CTS_I2C_HOST */ + + pdata->ts_input_dev = tpd->dev; + + spin_lock_init(&pdata->irq_lock); + mutex_init(&pdata->dev_lock); + +#if !defined(CONFIG_GENERIC_HARDIRQS) + INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ + + if ((node = of_find_matching_node(node, touch_of_match)) == NULL) { + cts_err("Find touch eint node failed"); + return -ENODATA; + } + if (of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints)) == 0) { + gpio_set_debounce(ints[0], ints[1]); + } else { + cts_info("Debounce time not found"); + } + pdata->irq = irq_of_parse_and_map(node, 0); + if (pdata->irq == 0) { + cts_err("Parse irq in dts failed"); + return -ENODEV; + } + +#ifdef CONFIG_CTS_VIRTUALKEY + pdata->vkey_num = tpd_dts_data.tpd_keycnt; +#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 TPD_SUPPORT_I2C_DMA + tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + pdata->i2c_dma_buff_va = (u8 *)dma_alloc_coherent(&tpd->dev->dev, + CFG_CTS_MAX_I2C_XFER_SIZE, &pdata->i2c_dma_buff_pa, GFP_KERNEL); + if (pdata->i2c_dma_buff_va == NULL) { + cts_err("Allocate I2C DMA Buffer failed!"); + //return -ENOMEM; + } else { + pdata->i2c_dma_available = true; + } +#endif /* TPD_SUPPORT_I2C_DMA */ + +#ifdef CFG_CTS_FORCE_UP + INIT_DELAYED_WORK(&pdata->touch_timeout, cts_plat_touch_timeout_work); +#endif + +#ifndef CONFIG_CTS_I2C_HOST + cts_plat_init_dts(pdata, &spi->dev); + pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ; + cts_plat_spi_setup(pdata); +#endif + return 0; +} + +int cts_plat_request_resource(struct cts_platform_data *pdata) +{ + cts_info("Request resource"); + + tpd_gpio_as_int(tpd_int_gpio_index); + tpd_gpio_output(tpd_rst_gpio_index, 1); + + return 0; +} + +void cts_plat_free_resource(struct cts_platform_data *pdata) +{ + cts_info("Free resource"); + + /** + * Note: + * If resource request without managed, should free all resource + * requested in cts_plat_request_resource(). + */ +#ifdef TPD_SUPPORT_I2C_DMA + if (pdata->i2c_dma_buff_va) { + dma_free_coherent(&tpd->dev->dev, CFG_CTS_MAX_I2C_XFER_SIZE, + pdata->i2c_dma_buff_va, pdata->i2c_dma_buff_pa); + pdata->i2c_dma_buff_va = NULL; + pdata->i2c_dma_buff_pa = 0; + } +#endif /* TPD_SUPPORT_I2C_DMA */ +} +#endif /*CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED*/ + + +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; +} + +int cts_plat_get_int_pin(struct cts_platform_data *pdata) +{ +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + return gpio_get_value(pdata->int_gpio); +#else + cts_err("MTK platform can not get INT pin value"); + return -ENOTSUPP; +#endif +} + +int cts_plat_init_touch_device(struct cts_platform_data *pdata) +{ + cts_info("Init touch device"); + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + return input_mt_init_slots(pdata->ts_input_dev, + tpd_dts_data.touch_max_num, INPUT_MT_DIRECT); +#endif + 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 */ +} + +#ifdef CFG_CTS_PALM_DETECT +void cts_report_palm_event(struct cts_platform_data *pdata) +{ + input_report_key(pdata->ts_input_dev, CFG_CTS_PALM_EVENT, 1); + input_sync(pdata->ts_input_dev); + msleep(100); + input_report_key(pdata->ts_input_dev, CFG_CTS_PALM_EVENT, 0); + input_sync(pdata->ts_input_dev); +} +#endif + +int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num) +{ + struct chipone_ts_data *cts_data; + 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); + + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (num == 0 || num > CFG_CTS_MAX_TOUCH_NUM) + return 0; + + for (i = 0; i < num; i++) { + u16 x, y; + + x = (msgs[i].xl) | (msgs[i].xh << 8); + y = (msgs[i].yl) | (msgs[i].yh << 8); + + 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: +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + TPD_DEBUG_SET_TIME; + TPD_EM_PRINT(x, y, x, y, msgs[i].id, 1); + tpd_history_x = x; + tpd_history_y = y; +#ifdef CONFIG_MTK_BOOT + if (tpd_dts_data.use_tpd_button) { + if (FACTORY_BOOT == get_boot_mode() || + RECOVERY_BOOT == get_boot_mode()) + tpd_button(x, y, 1); + } +#endif /* CONFIG_MTK_BOOT */ +#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + 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: +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + TPD_DEBUG_SET_TIME; + TPD_EM_PRINT(tpd_history_x, tpd_history_y, tpd_history_x, tpd_history_y, msgs[i].id, 0); + tpd_history_x = 0; + tpd_history_y = 0; +#ifdef CONFIG_MTK_BOOT + if (tpd_dts_data.use_tpd_button) { + if (FACTORY_BOOT == get_boot_mode() || + RECOVERY_BOOT == get_boot_mode()) + tpd_button(0, 0, 0); + } +#endif /* CONFIG_MTK_BOOT */ + //input_report_key(input_dev, BTN_TOUCH, 0); + //input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); +#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + 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 /* CONFIG_CTS_SLOTPROTOCOL */ + if (contact == 0) { + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + input_sync(input_dev); + +#ifdef CFG_CTS_FORCE_UP + if (contact) { + if (delayed_work_pending(&pdata->touch_timeout)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_timeout, msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_timeout, msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_timeout); + } +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (contact) { + if (delayed_work_pending(&cts_data->heart_work)) { + mod_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } else { + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } + } +#endif + + return 0; +} + +int cts_plat_process_stylus_msg(struct cts_platform_data *pdata, + struct cts_device_stylus_msg *msgs, int num) +{ + struct chipone_ts_data *cts_data; + struct input_dev *pen_dev = pdata->pen_input_dev; + int i; + int contact = 0; + + cts_dbg("Process stylus %d msgs", num); + + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (num == 0 || num > CFG_CTS_MAX_STYLUS_NUM) + return 0; + + for (i = 0; i < num; i++) { + u16 x, y, p; + + x = (msgs[i].tip_xl) | (msgs[i].tip_xh << 8); + y = (msgs[i].tip_yl) | (msgs[i].tip_yh << 8); + p = msgs[i].pressure_l | (msgs[i].pressure_h << 8); + + cts_dbg(" Process stylus msg[%d]: id[%u] ev=%u x=%u y=%u p=%u" + " tx=%d ty=%d btn0=%d btn1=%d btn2=%d", + i, msgs[i].id, msgs[i].event, x, y, p, msgs[i].tiltx, msgs[i].tilty, + msgs[i].btn0, msgs[i].btn1, msgs[i].btn2); + + /* Report stylus button */ + input_report_key(pen_dev, BTN_STYLUS, msgs[i].btn0); + input_report_key(pen_dev, BTN_STYLUS2, msgs[i].btn1); + + 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(pen_dev, ABS_X, x); + input_report_abs(pen_dev, ABS_Y, y); + input_report_abs(pen_dev, ABS_PRESSURE, p); + input_report_abs(pen_dev, ABS_TILT_X, msgs[i].tiltx); + input_report_abs(pen_dev, ABS_TILT_Y, msgs[i].tilty); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + break; + default: + cts_warn("Process stylus msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } + } + + input_report_key(pen_dev, BTN_TOUCH, contact > 0); + input_report_key(pen_dev, BTN_TOOL_PEN, contact > 0); + input_sync(pen_dev); + +#ifdef CFG_CTS_FORCE_UP + if (contact) { + if (delayed_work_pending(&pdata->touch_timeout)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_timeout, msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_timeout, msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_timeout); + } +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (contact) { + if (delayed_work_pending(&cts_data->heart_work)) { + mod_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } else { + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } + } +#endif + + return 0; +} + +#ifdef CFG_CTS_FINGER_STYLUS_SUPPORTED +int cts_plat_process_touch_stylus(struct cts_platform_data *pdata, + struct cts_device_touch_info *touch_info) +{ + struct chipone_ts_data *cts_data; + struct input_dev *input_dev = pdata->ts_input_dev; + struct input_dev *pen_dev = pdata->pen_input_dev; + struct cts_device_touch_msg *msgs = touch_info->msgs; + int touch_num = touch_info->num_msg; + struct cts_device_stylus_msg *smsgs = touch_info->smsgs; + int stylus_num = touch_info->stylus_num; + 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, stylus %d msgs", touch_num, stylus_num); + + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (touch_num == 0 || touch_num > CFG_CTS_MAX_TOUCH_NUM) { + goto process_stylus; + } + + for (i = 0; i < touch_num; i++) { + u16 x, y; + + x = (msgs[i].xl) | (msgs[i].xh << 8); + y = (msgs[i].yl) | (msgs[i].yh << 8); + + 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: +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + TPD_DEBUG_SET_TIME; + TPD_EM_PRINT(x, y, x, y, msgs[i].id, 1); + tpd_history_x = x; + tpd_history_y = y; +#ifdef CONFIG_MTK_BOOT + if (tpd_dts_data.use_tpd_button) { + if (FACTORY_BOOT == get_boot_mode() || + RECOVERY_BOOT == get_boot_mode()) + tpd_button(x, y, 1); + } +#endif /* CONFIG_MTK_BOOT */ +#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + 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: +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + TPD_DEBUG_SET_TIME; + TPD_EM_PRINT(tpd_history_x, tpd_history_y, tpd_history_x, tpd_history_y, msgs[i].id, 0); + tpd_history_x = 0; + tpd_history_y = 0; +#ifdef CONFIG_MTK_BOOT + if (tpd_dts_data.use_tpd_button) { + if (FACTORY_BOOT == get_boot_mode() || + RECOVERY_BOOT == get_boot_mode()) + tpd_button(0, 0, 0); + } +#endif /* CONFIG_MTK_BOOT */ +#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */ + 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); + +process_stylus: + if (stylus_num == 0 || stylus_num > CFG_CTS_MAX_STYLUS_NUM) { + return 0; + } + + for (i = 0; i < stylus_num; i++) { + u16 x, y, p; + + x = (smsgs[i].tip_xl) | (smsgs[i].tip_xh << 8); + y = (smsgs[i].tip_yl) | (smsgs[i].tip_yh << 8); + p = smsgs[i].pressure_l | (smsgs[i].pressure_h << 8); + + cts_dbg(" Process stylus msg[%d]: id[%u] ev=%u x=%u y=%u p=%u" + " tx=%d ty=%d btn0=%d btn1=%d btn2=%d", + i, smsgs[i].id, smsgs[i].event, x, y, p, smsgs[i].tiltx, smsgs[i].tilty, + smsgs[i].btn0, smsgs[i].btn1, smsgs[i].btn2); + + /* Report stylus button */ + input_report_key(pen_dev, BTN_STYLUS, smsgs[i].btn0); + input_report_key(pen_dev, BTN_STYLUS2, smsgs[i].btn1); + + switch (smsgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_report_abs(pen_dev, ABS_X, x); + input_report_abs(pen_dev, ABS_Y, y); + input_report_abs(pen_dev, ABS_PRESSURE, p); + input_report_abs(pen_dev, ABS_TILT_X, smsgs[i].tiltx); + input_report_abs(pen_dev, ABS_TILT_Y, smsgs[i].tilty); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + break; + default: + cts_warn("Process stylus msg with unknwon event %u id %u", + smsgs[i].event, smsgs[i].id); + break; + } + } + + input_report_key(pen_dev, BTN_TOUCH, contact > 0); + input_report_key(pen_dev, BTN_TOOL_PEN, contact > 0); + input_sync(pen_dev); + + +#ifdef CFG_CTS_FORCE_UP + if (contact) { + if (delayed_work_pending(&pdata->touch_timeout)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_timeout, msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_timeout, msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_timeout); + } +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (contact) { + if (delayed_work_pending(&cts_data->heart_work)) { + mod_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } else { + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } + } +#endif + + return 0; +} +#endif + +int cts_plat_release_all_touch(struct cts_platform_data *pdata) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + struct input_dev *pen_dev = pdata->pen_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); + + input_report_key(pen_dev, BTN_TOUCH, 0); + input_report_key(pen_dev, BTN_TOOL_PEN, 0); + input_sync(pen_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; + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + for (i = 0; i < pdata->vkey_num; i++) { + input_set_capability(pdata->ts_input_dev, + EV_KEY, pdata->vkey_keycodes[i]); + } +#else + if (tpd_dts_data.use_tpd_button) { + cts_info("Init vkey"); + + pdata->vkey_state = 0; + tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, + tpd_dts_data.tpd_key_dim_local); + } +#endif + 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); + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + 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); + } +#else + for (i = 0; i < pdata->vkey_num; i++) { + if (event & BIT(i)) { + tpd_button(x, y, vkey_state & BIT(i)); + + /* MTK fobidon more than one key pressed in the same time */ + break; + } + } +#endif + 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"); + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + 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); + } + } +#else + for (i = 0; i < pdata->vkey_num; i++) { + if (pdata->vkey_state & BIT(i)) { + tpd_button(x, y, 0); + } + } +#endif + 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) +{ + int ret; + + cts_info("Enable IRQ wake"); + + if (pdata->irq > 0) { + ret = enable_irq_wake(pdata->irq); + if (ret < 0) { + cts_err("Enable irq wake failed"); + return -EINVAL; + } + pdata->irq_wake_enabled = true; + return 0; + } + + cts_warn("Enable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_disable_irq_wake(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Disable IRQ wake"); + + if (pdata->irq > 0) { + ret = disable_irq_wake(pdata->irq); + if (ret < 0) { + cts_warn("Disable irq wake while already disabled"); + return -EINVAL; + } + pdata->irq_wake_enabled = false; + return 0; + } + + 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]) { + if (gesture_info->gesture_id == GESTURE_D_TAP) { + if (!pdata->cts_dev->rtdata.gesture_d_tap_enabled) { + cts_info("Ingore double tap"); + return 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/cts_platform.h b/drivers/input/touchscreen/chipone_tddi/cts_platform.h new file mode 100644 index 000000000000..777acf3b75be --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_platform.h @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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 */ +#include +#include + + +#ifdef CONFIG_OF +#include +#include +#include +#endif /* CONFIG_OF */ + +#include +#include + +#include +#include + +#include "cts_config.h" +#include "cts_core.h" + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#ifdef TPD_SUPPORT_I2C_DMA +#include +#endif /* TPD_SUPPORT_I2C_DMA */ + +#ifdef CONFIG_MTK_BOOT +#include "mtk_boot_common.h" +#endif /* CONFIG_MTK_BOOT */ + +#include "tpd.h" +#include "tpd_debug.h" +#include "upmu_common.h" +#endif + +extern bool cts_show_debug_log; + +#ifndef LOG_TAG +#define LOG_TAG "" +#endif /* LOG_TAG */ + +enum cts_driver_log_level { + CTS_DRIVER_LOG_ERROR, + CTS_DRIVER_LOG_WARN, + CTS_DRIVER_LOG_INFO, + CTS_DRIVER_LOG_DEBUG, +}; + +extern int cts_start_driver_log_redirect(const char *filepath, bool append_to_file, + char *log_buffer, int log_buf_size, int log_level); +extern void cts_stop_driver_log_redirect(void); +extern int cts_get_driver_log_redirect_size(void); +extern void cts_log(int level, const char *fmt, ...); + +#define cts_err(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_ERROR, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_warn(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_WARN, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_info(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_INFO, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_dbg(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_DEBUG, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) + + +struct cts_device; +struct cts_device_touch_msg; +struct cts_device_gesture_info; + +struct cts_platform_data { + int irq; + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + int int_gpio; +#ifdef CFG_CTS_HAS_RESET_PIN + int rst_gpio; +#endif + +#ifdef CFG_CTS_MANUAL_CS + int cs_gpio; +#endif +#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 + +#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; + struct input_dev *pen_input_dev; + +#ifndef CONFIG_GENERIC_HARDIRQS + struct work_struct ts_irq_work; +#endif + + struct mutex dev_lock; + 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 + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#ifdef TPD_SUPPORT_I2C_DMA + u8 *i2c_dma_buff_va; + dma_addr_t i2c_dma_buff_pa; +#endif /* TPD_SUPPORT_I2C_DMA */ +#else +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + struct notifier_block fb_notifier; +#endif +#endif +#ifdef CFG_CTS_FORCE_UP + struct delayed_work touch_timeout; +#endif + +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; + u8 i2c_fifo_buf[CFG_CTS_MAX_I2C_XFER_SIZE]; + u8 i2c_rbuf[ALIGN(CFG_CTS_MAX_I2C_READ_SIZE, 4)]; +#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; + +#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +#ifdef CFG_CTS_MANUAL_CS + struct pinctrl *pinctrl1; + struct pinctrl_state *spi_cs_low, *spi_cs_high; +#endif +#endif/* CONFIG_CTS_I2C_HOST */ + +#ifdef TPD_SUPPORT_I2C_DMA + bool i2c_dma_available; +#endif /* TPD_SUPPORT_I2C_DMA */ +#endif +}; + +#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); +#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_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi); +#endif + +extern int cts_plat_is_normal_mode(struct cts_platform_data *pdata); +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +extern int cts_deinit_platform_data(struct cts_platform_data *pdata); +#endif +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); +#ifdef CFG_CTS_PALM_DETECT +void cts_report_palm_event(struct cts_platform_data *pdata); +#endif +extern int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num); +extern int cts_plat_process_stylus_msg(struct cts_platform_data *pdata, + struct cts_device_stylus_msg *msgs, int num); +#ifdef CFG_CTS_FINGER_STYLUS_SUPPORTED +extern int cts_plat_process_touch_stylus(struct cts_platform_data *pdata, + struct cts_device_touch_info *touch_info); +#endif +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 */ + +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/cts_sfctrl.h b/drivers/input/touchscreen/chipone_tddi/cts_sfctrl.h new file mode 100644 index 000000000000..769522e60934 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_sfctrl.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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)(struct cts_device *cts_dev, u32 *id); + int (*se)(struct cts_device *cts_dev, u32 sector_addr); + int (*be)(struct cts_device *cts_dev, u32 sector_addr); + int (*read)(struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t len); + int (*read_to_sram)(struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*program)(struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t len); + int (*program_from_sram)(struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*calc_sram_crc)(struct cts_device *cts_dev, + u32 sram_addr, size_t len, u32 *crc); + int (*calc_flash_crc)(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/cts_sfctrlv2.c b/drivers/input/touchscreen/chipone_tddi/cts_sfctrlv2.c new file mode 100644 index 000000000000..3125b7b568dd --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_sfctrlv2.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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(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(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(1); + } while (status && retries++ < 1000); + + return -ETIMEDOUT; +} + +static int sfctrlv2_rdsr(struct cts_device *cts_dev, u8 *status) +{ +#define RDSR_XCHG_SRAM_ADDR (cts_dev->hwdata->sfctrl->xchg_sram_base + cts_dev->hwdata->sfctrl->xchg_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(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(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(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(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(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(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(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(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(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); +} + +static int sfctrlv2_calc_sram_crc(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; +} + +static int sfctrlv2_calc_flash_crc(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/cts_spi_flash.c b/drivers/input/touchscreen/chipone_tddi/cts_spi_flash.c new file mode 100644 index 000000000000..8c279367238d --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_spi_flash.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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" +#include "cts_strerror.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 const struct cts_flash cts_flashes[] = { + /* Winbond */ + { "Winbond,W25Q10EW", + 0xEF6011, 256, 0x1000, 0x8000, 0x20000}, + { "Winbond,W25Q20EW", + 0xEF6012, 256, 0x1000, 0x8000, 0x40000}, + { "Winbond,W25Q40EW", + 0xEF6013, 256, 0x1000, 0x8000, 0x80000}, + { "Winbond,W25Q20BW", + 0xEF5012, 256, 0x1000, 0x8000, 0x40000}, + { "Winbond,W25Q40BW", + 0xEF5013, 256, 0x1000, 0x8000, 0x80000}, + + /* Giga device */ + { "Giga,GD25LQ10B", + 0xC86011, 256, 0x1000, 0x8000, 0x20000}, + { "Giga,GD25LD20CEIGR", + 0xC86012, 256, 0x1000, 0x8000, 0x40000}, + { "Giga,GD25LD40CEIGR", + 0xC86013, 256, 0x1000, 0x8000, 0x80000}, + + /* Macronix */ + { "Macronix,MX25U1001E", + 0xC22531, 32, 0x1000, 0x10000, 0x20000}, + { "Macronix,MX25R2035F", + 0xC22812, 256, 0x1000, 0x8000, 0x40000}, + { "Macronix,MX25R4035F", + 0xC22813, 256, 0x1000, 0x8000, 0x80000}, + + /* Puya-Semi */ + { "Puya-Semi,P25Q20LUVHIR", + 0x856012, 256, 0x1000, 0x8000, 0x40000}, + { "Puya-Semi,P25Q40LUXHIR", + 0x856013, 256, 0x1000, 0x8000, 0x80000}, + { "Puya-Semi,P25Q11L", + 0x854011, 256, 0x1000, 0x8000, 0x20000}, + { "Puya-Semi,P25Q21L", + 0x854012, 256, 0x1000, 0x8000, 0x40000}, + { "Puya-Semi,P25T22L", + 0x854412, 256, 0x1000, 0x8000, 0x40000}, + + + /* Boya */ + { "Boya,BY25D10", + 0x684011, 256, 0x1000, 0x8000, 0x20000}, + + /* EON */ + { "EoN,EN25S20A", + 0x1C3812, 256, 0x1000, 0x8000, 0x40000}, + + /* XTXTECH */ + { "XTX,XT25Q01", + 0x0B6011, 256, 0x1000, 0, 0x20000}, + { "XTX,XT25Q02", + 0x0B6012, 256, 0x1000, 0, 0x40000}, + + /* Xinxin-Semi */ + { "Xinxin-Semi,XM25QU20B", + 0x205012, 256, 0x1000, 0x8000, 0x40000}, + { "Xinxin-Semi,XM25QU40B", + 0x205013, 256, 0x1000, 0x8000, 0x80000}, + + { "KangYong,XK25Q20T", + 0xEB6012, 256, 0x1000, 0x8000, 0x40000}, + + /*ZBIT*/ + { "ZBIT,ZB25LD40B", + 0x5E1013, 256, 0x1000, 0x8000, 0x80000}, +}; + +static const struct cts_flash *find_flash_by_jedec_id(u32 jedec_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_flashes); i++) { + if (cts_flashes[i].jedec_id == jedec_id) { + return &cts_flashes[i]; + } + } + + return NULL; +} + +static int probe_flash(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Probe flash"); + + if (cts_dev->hwdata->sfctrl->ops->rdid != NULL) { + u32 id; + + cts_info("Read JEDEC ID"); + + ret = cts_dev->hwdata->sfctrl->ops->rdid(cts_dev, &id); + if (ret) { + cts_err("Read JEDEC ID failed %d(%s)", + ret, cts_strerror(ret)); + return ret; + } + + cts_dev->flash = find_flash_by_jedec_id(id); + if (cts_dev->flash == NULL) { + cts_err("Unknown JEDEC ID: %06x", id); + return -ENODEV; + } + + cts_info("Flash type: '%s'", cts_dev->flash->name); + return 0; + } else { + cts_err("Probe flash with sfctrl->ops->rdid == NULL"); + return -ENOTSUPP; + } +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static int erase_sector_retry(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(%s) retries %d", + sector_addr, ret, cts_strerror(ret), retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static inline int erase_sector(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(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(%s) retries %d", + block_addr, ret, cts_strerror(ret), retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure block addr is block aligned && < flash total size */ +static inline int erase_block(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); + u32 hwid; + bool enabled = cts_is_device_enabled(cts_dev); + + cts_info("Prepare for flash operation"); + + if (!program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d(%s)", + ret, cts_strerror(ret)); + goto err_start_device; + } + } + + ret = cts_hw_reg_readb_retry(cts_dev, CTS_DEV_HW_REG_CLK_DIV_CFG, &diva, 5, 0); + + if (ret) { + cts_warn("Read DIVA failed %d(%s)", ret, cts_strerror(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 */ + hwid = cts_dev->hwdata->hwid; + if (hwid == CTS_DEV_HWID_ICNL9951) { + + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_CLK_DIV_CFG, 0x0C, 5, 0); + if (ret) { + cts_err("Write DIVA failed %d(%s)", + ret, cts_strerror(ret)); + goto err_enter_normal_mode; + } + } + + /* Reset SFCTL */ + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_RESET_CONFIG, 0xFB, 5, 0); + if (ret) { + cts_err("Reset sfctl failed %d(%s)", + ret, cts_strerror(ret)); + goto err_enter_normal_mode; + } + + retries = 0; + do { + u8 state; + + ret = cts_hw_reg_readb_relaxed(cts_dev, CTS_DEV_HW_REG_HW_STATUS, &state); + if (ret == 0 && (state & 0x40) != 0) + goto init_flash; + + mdelay(2); + } while (++retries < 1000); + + if (ret) { + cts_warn("Wait SFCTRL ready failed %d(%s)", ret, cts_strerror(ret)); + } + // Go through and try + } + +init_flash: + if (cts_dev->flash == NULL) { + cts_info("Flash is not initialized, try to probe..."); + if ((ret = probe_flash(cts_dev)) != 0) { + cts_dev->rtdata.has_flash = false; + cts_warn("Probe flash failed %d(%s)", + ret, cts_strerror(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(%s)", + r, cts_strerror(r)); + } + } +err_start_device: + if (enabled) { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d(%s)", + r, cts_strerror(r)); + } + } + + return ret; +} + +int cts_post_flash_operation(struct cts_device *cts_dev) +{ + u32 hwid; + cts_info("Post flash operation"); + + hwid = cts_dev->hwdata->hwid; + if (hwid == CTS_DEV_HWID_ICNL9951) { + return cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_CLK_DIV_CFG, 4, 5, 0); + } + return 0; +} + +int cts_read_flash_retry(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(%s)", + flash_addr, size, ret, cts_strerror(ret)); + return ret; + } + + dst += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_program_flash(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(%s)", + flash_addr, l, ret, cts_strerror(ret)); + return ret; + } + + src += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_program_flash_from_sram(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(%s)", + flash_addr, sram_addr, l, ret, cts_strerror(ret)); + return ret; + } + + size -= l; + flash_addr += l; + sram_addr += l; + } + + return 0; +} + +int cts_erase_flash(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(%s)", + addr, flash->sector_size, ret, cts_strerror(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(%s)", + addr, flash->block_size, ret, cts_strerror(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(%s)", + addr, flash->sector_size, ret, cts_strerror(ret)); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + return 0; +} + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_spi_flash.h b/drivers/input/touchscreen/chipone_tddi/cts_spi_flash.h new file mode 100644 index 000000000000..18db4386b6b7 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_spi_flash.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#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(struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, int retry); +static inline int cts_read_flash(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_erase_flash(struct cts_device *cts_dev, u32 addr, + size_t size); + +extern int cts_program_flash(struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size); +extern int cts_program_flash_from_sram(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/cts_strerror.c b/drivers/input/touchscreen/chipone_tddi/cts_strerror.c new file mode 100644 index 000000000000..fe5eac2f40d9 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_strerror.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "cts_strerror.h" + +#define ERROR_TEXT_ENTRY(errno, err_desc_str) \ + [(errno)] = #errno ", " err_desc_str + +static char *_error_text[] = { + [0] = "Success", + ERROR_TEXT_ENTRY(EPERM, "Operation not permitted"), + ERROR_TEXT_ENTRY(ENOENT, "No such file or directory"), + ERROR_TEXT_ENTRY(ESRCH, "No such process"), + ERROR_TEXT_ENTRY(EINTR, "Interrupted system call"), + ERROR_TEXT_ENTRY(EIO, "Input/output error"), + ERROR_TEXT_ENTRY(ENXIO, "No such device or address"), + ERROR_TEXT_ENTRY(E2BIG, "Argument list too long"), + ERROR_TEXT_ENTRY(ENOEXEC, "Exec format error"), + ERROR_TEXT_ENTRY(EBADF, "Bad file descriptor"), + ERROR_TEXT_ENTRY(ECHILD, "No child processes"), + ERROR_TEXT_ENTRY(EAGAIN, "Resource temporarily unavailable"), + ERROR_TEXT_ENTRY(ENOMEM, "Cannot allocate memory"), + ERROR_TEXT_ENTRY(EACCES, "Permission denied"), + ERROR_TEXT_ENTRY(EFAULT, "Bad address"), + ERROR_TEXT_ENTRY(ENOTBLK, "Block device required"), + ERROR_TEXT_ENTRY(EBUSY, "Device or resource busy"), + ERROR_TEXT_ENTRY(EEXIST, "File exists"), + ERROR_TEXT_ENTRY(EXDEV, "Invalid cross-device link"), + ERROR_TEXT_ENTRY(ENODEV, "No such device"), + ERROR_TEXT_ENTRY(ENOTDIR, "Not a directory"), + ERROR_TEXT_ENTRY(EISDIR, "Is a directory"), + ERROR_TEXT_ENTRY(EINVAL, "Invalid argument"), + ERROR_TEXT_ENTRY(EMFILE, "Too many open files"), + ERROR_TEXT_ENTRY(ENFILE, "Too many open files in system"), + ERROR_TEXT_ENTRY(ENOTTY, "Inappropriate ioctl for device"), + ERROR_TEXT_ENTRY(ETXTBSY, "Text file busy"), + ERROR_TEXT_ENTRY(EFBIG, "File too large"), + ERROR_TEXT_ENTRY(ENOSPC, "No space left on device"), + ERROR_TEXT_ENTRY(ESPIPE, "Illegal seek"), + ERROR_TEXT_ENTRY(EROFS, "Read-only file system"), + ERROR_TEXT_ENTRY(EMLINK, "Too many links"), + ERROR_TEXT_ENTRY(EPIPE, "Broken pipe"), + ERROR_TEXT_ENTRY(EDOM, "Numerical argument out of domain"), + ERROR_TEXT_ENTRY(ERANGE, "Numerical result out of range"), + ERROR_TEXT_ENTRY(EDEADLK, "Resource deadlock avoided"), + ERROR_TEXT_ENTRY(ENAMETOOLONG, "File name too long"), + ERROR_TEXT_ENTRY(ENOLCK, "No locks available"), + ERROR_TEXT_ENTRY(ENOSYS, "Function not implemented"), + ERROR_TEXT_ENTRY(ENOTEMPTY, "Directory not empty"), + ERROR_TEXT_ENTRY(ELOOP, "Too many levels of symbolic links"), + ERROR_TEXT_ENTRY(EWOULDBLOCK, "Operation would block"), + ERROR_TEXT_ENTRY(ENOMSG, "No message of desired type"), + ERROR_TEXT_ENTRY(EIDRM, "Identifier removed"), + ERROR_TEXT_ENTRY(ECHRNG, "Channel number out of range"), + ERROR_TEXT_ENTRY(EL2NSYNC, "Level 2 not synchronized"), + ERROR_TEXT_ENTRY(EL3HLT, "Level 3 halted"), + ERROR_TEXT_ENTRY(EL3RST, "Level 3 reset"), + ERROR_TEXT_ENTRY(ELNRNG, "Link number out of range"), + ERROR_TEXT_ENTRY(EUNATCH, "Protocol driver not attached"), + ERROR_TEXT_ENTRY(ENOCSI, "No CSI structure available"), + ERROR_TEXT_ENTRY(EL2HLT, "Level 2 halted"), + ERROR_TEXT_ENTRY(EBADE, "Invalid exchange"), + ERROR_TEXT_ENTRY(EBADR, "Invalid request descriptor"), + ERROR_TEXT_ENTRY(EXFULL, "Exchange full"), + ERROR_TEXT_ENTRY(ENOANO, "No anode"), + ERROR_TEXT_ENTRY(EBADRQC, "Invalid request code"), + ERROR_TEXT_ENTRY(EBADSLT, "Invalid slot"), + ERROR_TEXT_ENTRY(EDEADLOCK, "File locking deadlock error"), + ERROR_TEXT_ENTRY(EBFONT, "Bad font file format"), + ERROR_TEXT_ENTRY(ENOSTR, "Device not a stream"), + ERROR_TEXT_ENTRY(ENODATA, "No data available"), + ERROR_TEXT_ENTRY(ETIME, "Timer expired"), + ERROR_TEXT_ENTRY(ENOSR, "Out of streams resources"), + ERROR_TEXT_ENTRY(ENONET, "Machine is not on the network"), + ERROR_TEXT_ENTRY(ENOPKG, "Package not installed"), + ERROR_TEXT_ENTRY(EREMOTE, "Object is remote"), + ERROR_TEXT_ENTRY(ENOLINK, "Link has been severed"), + ERROR_TEXT_ENTRY(EADV, "Advertise error"), + ERROR_TEXT_ENTRY(ESRMNT, "Srmount error"), + ERROR_TEXT_ENTRY(ECOMM, "Communication error on send"), + ERROR_TEXT_ENTRY(EPROTO, "Protocol error"), + ERROR_TEXT_ENTRY(EMULTIHOP, "Multihop attempted"), + ERROR_TEXT_ENTRY(EDOTDOT, "RFS specific error"), + ERROR_TEXT_ENTRY(EBADMSG, "Bad message"), + ERROR_TEXT_ENTRY(EOVERFLOW, "Value too large for defined data type"), + ERROR_TEXT_ENTRY(ENOTUNIQ, "Name not unique on network"), + ERROR_TEXT_ENTRY(EBADFD, "File descriptor in bad state"), + ERROR_TEXT_ENTRY(EREMCHG, "Remote address changed"), + ERROR_TEXT_ENTRY(ELIBACC, "Can not access a needed shared library"), + ERROR_TEXT_ENTRY(ELIBBAD, "Accessing a corrupted shared library"), + ERROR_TEXT_ENTRY(ELIBSCN, ".lib section in a.out corrupted"), + ERROR_TEXT_ENTRY(ELIBMAX, "Attempting to link in too many shared libraries"), + ERROR_TEXT_ENTRY(ELIBEXEC, "Cannot exec a shared library directly"), + ERROR_TEXT_ENTRY(EILSEQ, "Invalid or incomplete multibyte or wide character"), + ERROR_TEXT_ENTRY(ERESTART, "Interrupted system call should be restarted"), + ERROR_TEXT_ENTRY(ESTRPIPE, "Streams pipe error"), + ERROR_TEXT_ENTRY(EUSERS, "Too many users"), + ERROR_TEXT_ENTRY(ENOTSOCK, "Socket operation on non-socket"), + ERROR_TEXT_ENTRY(EDESTADDRREQ, "Destination address required"), + ERROR_TEXT_ENTRY(EMSGSIZE, "Message too long"), + ERROR_TEXT_ENTRY(EPROTOTYPE, "Protocol wrong type for socket"), + ERROR_TEXT_ENTRY(ENOPROTOOPT, "Protocol not available"), + ERROR_TEXT_ENTRY(EPROTONOSUPPORT, "Protocol not supported"), + ERROR_TEXT_ENTRY(ESOCKTNOSUPPORT, "Socket type not supported"), + ERROR_TEXT_ENTRY(EOPNOTSUPP, "Operation not supported"), + ERROR_TEXT_ENTRY(EPFNOSUPPORT, "Protocol family not supported"), + ERROR_TEXT_ENTRY(EAFNOSUPPORT, "Address family not supported by protocol"), + ERROR_TEXT_ENTRY(EADDRINUSE, "Address already in use"), + ERROR_TEXT_ENTRY(EADDRNOTAVAIL, "Cannot assign requested address"), + ERROR_TEXT_ENTRY(ENETDOWN, "Network is down"), + ERROR_TEXT_ENTRY(ENETUNREACH, "Network is unreachable"), + ERROR_TEXT_ENTRY(ENETRESET, "Network dropped connection on reset"), + ERROR_TEXT_ENTRY(ECONNABORTED, "Software caused connection abort"), + ERROR_TEXT_ENTRY(ECONNRESET, "Connection reset by peer"), + ERROR_TEXT_ENTRY(ENOBUFS, "No buffer space available"), + ERROR_TEXT_ENTRY(EISCONN, "Transport endpoint is already connected"), + ERROR_TEXT_ENTRY(ENOTCONN, "Transport endpoint is not connected"), + ERROR_TEXT_ENTRY(ESHUTDOWN, "Cannot send after transport endpoint shutdown"), + ERROR_TEXT_ENTRY(ETOOMANYREFS, "Too many references: cannot splice"), + ERROR_TEXT_ENTRY(ETIMEDOUT, "Connection timed out"), + ERROR_TEXT_ENTRY(ECONNREFUSED, "Connection refused"), + ERROR_TEXT_ENTRY(EHOSTDOWN, "Host is down"), + ERROR_TEXT_ENTRY(EHOSTUNREACH, "No route to host"), + ERROR_TEXT_ENTRY(EALREADY, "Operation already in progress"), + ERROR_TEXT_ENTRY(EINPROGRESS, "Operation now in progress"), + ERROR_TEXT_ENTRY(ESTALE, "Stale file handle"), + ERROR_TEXT_ENTRY(EUCLEAN, "Structure needs cleaning"), + ERROR_TEXT_ENTRY(ENOTNAM, "Not a XENIX named type file"), + ERROR_TEXT_ENTRY(ENAVAIL, "No XENIX semaphores available"), + ERROR_TEXT_ENTRY(EISNAM, "Is a named type file"), + ERROR_TEXT_ENTRY(EREMOTEIO, "Remote I/O error"), + ERROR_TEXT_ENTRY(EDQUOT, "Disk quota exceeded"), + ERROR_TEXT_ENTRY(ENOMEDIUM, "No medium found"), + ERROR_TEXT_ENTRY(EMEDIUMTYPE, "Wrong medium type"), + ERROR_TEXT_ENTRY(ECANCELED, "Operation canceled"), + ERROR_TEXT_ENTRY(ENOKEY, "Required key not available"), + ERROR_TEXT_ENTRY(EKEYEXPIRED, "Key has expired"), + ERROR_TEXT_ENTRY(EKEYREVOKED, "Key has been revoked"), + ERROR_TEXT_ENTRY(EKEYREJECTED, "Key was rejected by service"), + ERROR_TEXT_ENTRY(EOWNERDEAD, "Owner died"), + ERROR_TEXT_ENTRY(ENOTRECOVERABLE, "State not recoverable"), + ERROR_TEXT_ENTRY(ERFKILL, "Operation not possible due to RF-kill"), + ERROR_TEXT_ENTRY(EHWPOISON, "Memory page has hardware error"), + ERROR_TEXT_ENTRY(ERESTARTSYS, "Restart system"), + ERROR_TEXT_ENTRY(ERESTARTNOINTR, "Restart monitor"), + ERROR_TEXT_ENTRY(ERESTARTNOHAND, "Restart if no handler"), + ERROR_TEXT_ENTRY(ENOIOCTLCMD, "No ioctl command"), + ERROR_TEXT_ENTRY(ERESTART_RESTARTBLOCK, "Restart by calling sys_restart_syscall"), + ERROR_TEXT_ENTRY(EPROBE_DEFER, "Driver requests probe retry"), + ERROR_TEXT_ENTRY(EOPENSTALE, "Open found a stale dentry"), + ERROR_TEXT_ENTRY(EBADHANDLE, "Illegal NFS file handle"), + ERROR_TEXT_ENTRY(ENOTSYNC, "Update synchronization mismatch"), + ERROR_TEXT_ENTRY(EBADCOOKIE, "Cookie is stale"), + ERROR_TEXT_ENTRY(ENOTSUPP, "Operation is not supported"), + ERROR_TEXT_ENTRY(ETOOSMALL, "Buffer or request is too small"), + ERROR_TEXT_ENTRY(ESERVERFAULT, "An untranslatable error occurred"), + ERROR_TEXT_ENTRY(EBADTYPE, "Type not supported by server"), + ERROR_TEXT_ENTRY(EJUKEBOX, "Request initiated, but will not complete before timeout"), + ERROR_TEXT_ENTRY(EIOCBQUEUED, "IOCB queued, will get completion event"), +}; + +#define ERROR_TEXT_ARRAY_SIZE (sizeof(_error_text) / sizeof(_error_text[0])) +char *cts_strerror(int errno) +{ + if (errno > 0) { + return ""; + } else if (-errno < (int) ERROR_TEXT_ARRAY_SIZE) { + char *s = _error_text[-errno]; + if (s) { + return s; + } + } + return ""; +} diff --git a/drivers/input/touchscreen/chipone_tddi/cts_strerror.h b/drivers/input/touchscreen/chipone_tddi/cts_strerror.h new file mode 100644 index 000000000000..dfdcf640c182 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_strerror.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_STRERROR_H +#define CTS_STRERROR_H + +#define CTS_ERR_FMT_STR "%d(%s)" +#define CTS_ERR_ARG(errno) cts_strerror(errno) +char *cts_strerror(int errno); + +#endif + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_sysfs.c b/drivers/input/touchscreen/chipone_tddi/cts_sysfs.c new file mode 100644 index 000000000000..c25fc5f1edf2 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_sysfs.c @@ -0,0 +1,2904 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define LOG_TAG "Sysfs" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_tcs.h" +#include "cts_test.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include "cts_strerror.h" +#include "cts_sysfs.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 RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 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 cts_argc; +char *cts_argv[MAX_ARG_NUM]; + +int cts_parse_arg(const char *buf, size_t count) +{ + char *p; + size_t size; + + size = min((size_t)MAX_ARG_LENGTH, count); + memcpy(cmdline_param, buf, size); + cmdline_param[size] = '\0'; + + cts_argc = 0; + p = strim(cmdline_param); + if (p == NULL || p[0] == '\0') + return 0; + + while (p && p[0] != '\0' && cts_argc < MAX_ARG_NUM) + cts_argv[cts_argc++] = strsep(&p, " ,"); + + return cts_argc; +} + + +#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); + 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); + +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 ssize_t write_tcs_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; + + cts_parse_arg(buf, count); + + cts_info("Write firmware register '%.*s'", (int)count, buf); + + if (cts_argc < 2) { + cts_err("Too few args %d", cts_argc); + return -EFAULT; + } + + ret = kstrtou16(cts_argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", cts_argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(cts_argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < cts_argc; i++) { + ret = kstrtou8(cts_argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", cts_argv[i]); + goto free_data; + } + } + + ret = cts_tcs_write(cts_dev, addr, data, cts_argc - 1); + if (ret) { + cts_err("Write tcs register addr: 0x%04x size: %d failed", + addr, cts_argc - 1); + goto free_data; + } + +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} +static DEVICE_ATTR(write_tcs_reg, S_IWUSR, NULL, write_tcs_register_store); + +static ssize_t read_tcs_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 tcs register "); + + if (cts_argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo (0x)addr size > read_reg\n" + " 2. cat read_reg\n", cts_argc); + } + + ret = kstrtou16(cts_argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", cts_argv[0]); + } + ret = kstrtou16(cts_argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", cts_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 tcs register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + ret = cts_tcs_read(cts_dev, addr, data, (size_t)size); + cts_unlock_device(cts_dev); + if (ret) { + count = snprintf(buf, PAGE_SIZE, + "Read tcs 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 +} + +static ssize_t read_tcs_register_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_parse_arg(buf, count); + + return (cts_argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_tcs_reg, S_IWUSR | S_IRUSR, + read_tcs_register_show, read_tcs_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 (cts_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", cts_argc); + } + + ret = kstrtou32(cts_argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", cts_argv[0]); + } + ret = kstrtou32(cts_argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", cts_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->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_tcs_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) +{ + cts_parse_arg(buf, count); + + return (cts_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; + + cts_parse_arg(buf, count); + + cts_info("Write hw register"); + + if (cts_argc < 2) { + cts_err("Too few args %d", cts_argc); + return -EFAULT; + } + + ret = kstrtou32(cts_argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", cts_argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(cts_argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < cts_argc; i++) { + ret = kstrtou8(cts_argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", cts_argv[i]); + goto free_data; + } + } + + cts_lock_device(cts_dev); + + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < cts_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_tcs_write_hw_reg(cts_dev, addr, data, cts_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_tcs_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_tcs_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_tcs_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_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; + bool to_flash = true; + int ret; + + cts_parse_arg(buf, count); + + if (cts_argc > 2) { + cts_err("Invalid num args %d\n" + " echo filepath [flash/sram] > update_from_file\n", + cts_argc); + return -EFAULT; + } else if (cts_argc > 1) { + if (strncasecmp(cts_argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(cts_argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err("Invalid location '%s', must be 'flash' or 'sram'", cts_argv[1]); + return -EINVAL; + } + } + + cts_info("Update firmware from file '%s'", cts_argv[0]); + + ret = cts_request_newer_firmware_from_fs(cts_dev, cts_argv[0], 0); + if (ret) { + cts_err("Request firmware from file '%s' failed", cts_argv[0]); + return -ENOENT; + } + + cts_reset_device(cts_dev); + + 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); + 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; + } + + ret = count; + +err_release_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_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; +#ifndef CFG_CTS_FOR_GKI + loff_t pos = 0; +#endif + + if (cts_argc != 2 && cts_argc != 3) + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", cts_argc); + + ret = kstrtou32(cts_argv[0], 0, &flash_addr); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid flash addr: %s\n", cts_argv[0]); + ret = kstrtou32(cts_argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", cts_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, + cts_argc == 3 ? " to file " : "", cts_argc == 3 ? cts_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 (cts_argc == 3) { +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); +#else + struct file *file; + + cts_info("Write flash data to file '%s'", cts_argv[2]); + + file = filp_open(cts_argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + count += snprintf(buf, PAGE_SIZE, "Open file '%s' failed %ld", + cts_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", cts_argv[2], ret); + + ret = filp_close(file, NULL); + if (ret) + count += snprintf(buf, PAGE_SIZE, + "Close file '%s' failed %d", cts_argv[2], ret); +#endif + } 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) +{ + cts_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; + + cts_parse_arg(buf, count); + + if (cts_argc != 2) { + cts_err("Invalid num args %d", cts_argc); + return -EFAULT; + } + + ret = kstrtou32(cts_argv[0], 0, &flash_addr); + if (ret) { + cts_err("Invalid flash addr: %s", cts_argv[0]); + return -EINVAL; + } + ret = kstrtou32(cts_argv[1], 0, &size); + if (ret) { + cts_err("Invalid size: %s", cts_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; + struct cts_test_param test_param = { + .test_item = CTS_TEST_OPEN, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/open-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc != 1) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", cts_argc); + } + + ret = kstrtoint(cts_argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); + } + + cts_info("Open test, threshold = %u", min); + + test_param.min = &min; + + start_time = ktime_get(); + + ret = cts_test_open(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Open test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", + ret, min, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Open test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Open test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } + +} + +/* echo threshod > open_test */ +static ssize_t open_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_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; + struct cts_test_param test_param = { + .test_item = CTS_TEST_SHORT, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/short-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc != 1) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", cts_argc); + } + + ret = kstrtoint(cts_argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", cts_argv[0]); + } + + cts_info("Short test, threshold = %u", min); + + test_param.min = &min; + + start_time = ktime_get(); + + ret = cts_test_short(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Short test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", + ret, min, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Short test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Short test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t short_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_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_pin_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef CFG_CTS_HAS_RESET_PIN + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RESET_PIN, + .flags = 0, + }; + int ret; + ktime_t start_time, end_time, delta_time; + + start_time = ktime_get(); + + ret = cts_test_reset_pin(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +#else /* CFG_CTS_HAS_RESET_PIN */ + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test NOT supported(CFG_CTS_HAS_RESET_PIN not defined)\n"); +#endif +} +static DEVICE_ATTR(reset_pin_test, S_IRUGO, reset_pin_test_show, NULL); +#endif + +static ssize_t int_pin_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; + struct cts_test_param test_param = { + .test_item = CTS_TEST_INT_PIN, + .flags = 0, + }; + int ret; + ktime_t start_time, end_time, delta_time; + + start_time = ktime_get(); + + ret = cts_test_int_pin(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Int-Pin test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} +static DEVICE_ATTR(int_pin_test, S_IRUGO, int_pin_test_show, NULL); + +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; + struct cts_test_param test_param = { + .test_item = CTS_TEST_COMPENSATE_CAP, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/comp-cap-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0, max = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc != 2) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > compensate_cap_test\n" + " 2. cat compensate_cap_test\n"); + } + + ret = kstrtoint(cts_argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", cts_argv[0]); + } + + ret = kstrtoint(cts_argv[1], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", cts_argv[1]); + } + + cts_info("Compensate cap test, min: %u, max: %u", min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_compensate_cap(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test has %d nodes FAIL, " + "threshold[%u, %u], ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t compensate_cap_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_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; + struct cts_rawdata_test_priv_param priv_param = { + .frames = 16, + //.work_mode = 0, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/rawdata-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int min, max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc < 2 || cts_argc > 3) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max [frames] > rawdata_test\n" + " 2. cat rawdata_test\n"); + } + + ret = kstrtoint(cts_argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", cts_argv[0]); + } + + ret = kstrtoint(cts_argv[1], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", cts_argv[1]); + } + + if (cts_argc > 2) { + ret = kstrtou32(cts_argv[2], 0, &priv_param.frames); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", cts_argv[2]); + } + } + cts_info("Rawdata test, frames: %u min: %d, max: %d", + priv_param.frames, min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_rawdata(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Rawdata test has %d nodes FAIL, threshold[%u, %u], " + "ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Rawdata test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t rawdata_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(rawdata_test, S_IWUSR | S_IRUGO, + rawdata_test_show, rawdata_test_store); + +static ssize_t noise_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; + struct cts_noise_test_priv_param priv_param = { + .frames = 50, + //.work_mode = 0, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/noise-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc < 1 || cts_argc > 2) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo threshold [frames] > noise_test\n" + " 2. cat noise_test\n"); + } + + ret = kstrtoint(cts_argv[0], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", cts_argv[0]); + } + + if (cts_argc > 1) { + ret = kstrtou32(cts_argv[1], 0, &priv_param.frames); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", cts_argv[1]); + } + } + cts_info("Noise test, frames: %u threshold: %d", + priv_param.frames, max); + + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_noise(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Noise test has %d nodes FAIL, max: %u, ELAPSED TIME: %lldms\n", + ret, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Noise test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Noise test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +static ssize_t noise_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_parse_arg(buf, count); + + return count; +} +static DEVICE_ATTR(noise_test, S_IWUSR | S_IRUGO, + noise_test_show, noise_test_store); + + +static ssize_t stylus_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; + struct cts_rawdata_test_priv_param priv_param = { + .frames = 16, + //.work_mode = 0, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/stylus-rawdata-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int min, max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc < 2 || cts_argc > 3) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max [frames] > stylus_rawdata_test\n" + " 2. cat stylus_rawdata_test\n"); + } + + ret = kstrtoint(cts_argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", cts_argv[0]); + } + + ret = kstrtoint(cts_argv[1], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", cts_argv[1]); + } + + if (cts_argc > 2) { + ret = kstrtou32(cts_argv[2], 0, &priv_param.frames); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", cts_argv[2]); + } + } + cts_info("stylus rawdata test, frames: %u min: %d, max: %d", + priv_param.frames, min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_stylus_rawdata(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "stylus rawdata test has %d nodes FAIL, threshold[%u, %u], " + "ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "stylus rawdata test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "stylus rawdata test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t stylus_rawdata_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(stylus_rawdata_test, S_IWUSR | S_IRUGO, + stylus_rawdata_test_show, stylus_rawdata_test_store); + + +static ssize_t stylus_noise_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; + struct cts_noise_test_priv_param priv_param = { + .frames = 50, + //.work_mode = 0, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/stylus-noise-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (cts_argc < 1 || cts_argc > 2) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo threshold [frames] > stylus_noise_test\n" + " 2. cat stylus_noise_test\n"); + } + + ret = kstrtoint(cts_argv[0], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", cts_argv[0]); + } + + if (cts_argc > 1) { + ret = kstrtou32(cts_argv[1], 0, &priv_param.frames); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", cts_argv[1]); + } + } + cts_info("stylus noise test, frames: %u threshold: %d", + priv_param.frames, max); + + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_stylus_noise(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "stylus noise test has %d nodes FAIL, max: %u, ELAPSED TIME: %lldms\n", + ret, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "stylus noise test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "stylus noise test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +static ssize_t stylus_noise_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cts_parse_arg(buf, count); + + return count; +} +static DEVICE_ATTR(stylus_noise_test, S_IWUSR | S_IRUGO, + stylus_noise_test_show, stylus_noise_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_pin_test.attr, +#endif + &dev_attr_int_pin_test.attr, + &dev_attr_compensate_cap_test.attr, + &dev_attr_rawdata_test.attr, + &dev_attr_noise_test.attr, + &dev_attr_stylus_rawdata_test.attr, + &dev_attr_stylus_noise_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; + + cts_parse_arg(buf, count); + + if (cts_argc != 1) { + cts_err("Invalid num args %d", cts_argc); + return -EFAULT; + } + + if (*cts_argv[0] == '1' || tolower(*cts_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 (*cts_argv[0] == '0' || tolower(*cts_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); + +#ifdef CFG_CTS_HAS_RESET_PIN +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +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)); +} +#endif + +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; +} + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +static DEVICE_ATTR(reset_pin, S_IRUSR | S_IWUSR, reset_pin_show, + reset_pin_store); +#else +static DEVICE_ATTR(reset_pin, S_IWUSR, NULL, reset_pin_store); +#endif +#endif + +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED +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); +#endif + +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 scnprintf(buf, PAGE_SIZE, + "IRQ num: %d, depth: %u, " + "count: %u, unhandled: %u, last unhandled eslape: %lu, irq flags: 0x%x, int_mode: %s\n", + cts_data->pdata->irq, desc->depth, + desc->irq_count, desc->irqs_unhandled, + desc->last_unhandled, desc->action->flags, + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_RISING ? "IRQF_TRIGGER_RISING" : + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_FALLING ? "IRQF_TRIGGER_FALLING" : + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_HIGH ? "IRQF_TRIGGER_HIGH" : + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_LOW ? "IRQF_TRIGGER_LOW " : "IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING"); +} + +static DEVICE_ATTR(irq_info, S_IRUGO, irq_info_show, NULL); + +#ifndef CONFIG_CTS_I2C_HOST +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; + + cts_parse_arg(buf, count); + + if (cts_argc != 1) { + cts_err("Invalid num args %d", cts_argc); + return -EFAULT; + } + + ret = kstrtou16(cts_argv[0], 0, &s); + if (ret) { + cts_err("Invalid spi speed: %s", cts_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); +#endif + +#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; + + cts_parse_arg(buf, count); + + if (cts_argc != 1) { + cts_err("Invalid num args %d", cts_argc); + return -EFAULT; + } + + ret = kstrtou16(cts_argv[0], 0, &type); + if (ret) { + cts_err("Invalid int data types: %s", cts_argv[0]); + return -EINVAL; + } + + cts_lock_device(&cts_data->cts_dev); + ret = cts_set_int_data_types(&cts_data->cts_dev, type); + cts_unlock_device(&cts_data->cts_dev); + 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; + + cts_parse_arg(buf, count); + + if (cts_argc != 1) { + cts_err("Invalid num args %d", cts_argc); + return -EFAULT; + } + + ret = kstrtou8(cts_argv[0], 0, &method); + if (ret) { + cts_err("Invalid int data method: %s", cts_argv[0]); + return -EINVAL; + } else if (method >= INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method: %s", cts_argv[0]); + return -EINVAL; + } + + cts_lock_device(&cts_data->cts_dev); + ret = cts_set_int_data_method(&cts_data->cts_dev, method); + cts_unlock_device(&cts_data->cts_dev); + 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 ssize_t cts_charger_state_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 val; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_charger_plug(cts_dev, &val); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Get charger state failed %d(%s)", ret, cts_strerror(ret)); + return -1; + } + switch (val) { + case 0: return scnprintf(buf, PAGE_SIZE, "Detached\n"); + case 1: return scnprintf(buf, PAGE_SIZE, "Attached\n"); + default: return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } +} + +static ssize_t cts_charger_state_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 state; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + switch (buf[0]) { + case '0': state = 0; break; + case '1': state = 1; break; + default: + cts_err("Invalid arg for state"); + return -EINVAL; + } + + cts_info("state = %d", state); + + cts_lock_device(cts_dev); + ret = cts_tcs_set_charger_plug(cts_dev, state); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Set charger state failed %d(%s)", ret, cts_strerror(ret)); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(charger_state, S_IRUGO | S_IWUSR, + cts_charger_state_show, cts_charger_state_store); + +static ssize_t cts_earjack_state_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 val; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_earjack_plug(cts_dev, &val); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Get earjack state failed %d(%s)", + ret, cts_strerror(ret)); + return -1; + } + switch (val) { + case 0: return scnprintf(buf, PAGE_SIZE, "Detached\n"); + case 1: return scnprintf(buf, PAGE_SIZE, "Attached\n"); + default: return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } +} + +static ssize_t cts_earjack_state_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 state; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + switch (buf[0]) { + case '0': state = 0; break; + case '1': state = 1; break; + default: + cts_err("Invalid arg for state"); + return -EINVAL; + } + + cts_info("state = %d", state); + + cts_lock_device(cts_dev); + ret = cts_tcs_set_earjack_plug(cts_dev, state); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Set earjack state failed %d(%s)", ret, cts_strerror(ret)); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(earjack_state, S_IRUGO | S_IWUSR, + cts_earjack_state_show, cts_earjack_state_store); + +static ssize_t cts_edge_restain_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 direction; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_panel_direction(cts_dev, &direction); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("get panel direction failed!"); + return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } + + return scnprintf(buf, PAGE_SIZE, "direction: 0x%02x\n", direction); +} + +static ssize_t cts_edge_restain_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 direction; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + switch (buf[0]) { + case '0': direction = 0; break;//normal + case '1': direction = 1; break;//notch left + case '2': direction = 2; break;//notch right + default: + cts_err("Invalid arg for mode"); + return -EINVAL; + } + cts_info("direction = %d", direction); + + cts_lock_device(cts_dev); + ret = cts_tcs_set_panel_direction(cts_dev, direction); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Set edge restain failed"); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(edge_restain, S_IWUSR | S_IRUGO, + cts_edge_restain_show, cts_edge_restain_store); + +static ssize_t cts_game_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; + u8 enabled; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_game_mode(cts_dev, &enabled); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("get game mode failed!"); + return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } + + return scnprintf(buf, PAGE_SIZE, "game mode: 0x%02x\n", enabled); +} + +static ssize_t cts_game_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 enable = -1; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + if (count >= 1) { + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } else if (buf[0] == 'N' || buf[0] == 'n' || buf[0] == '0') { + enable = 0; + } + } + if (enable == -1) { + cts_err("Invalid arg for game mode enable"); + return -EINVAL; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_set_game_mode(cts_dev, enable); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Set game mode failed"); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(game_mode, S_IWUSR | S_IRUGO, + cts_game_mode_show, cts_game_mode_store); + +#ifdef CONFIG_CTS_TP_PROXIMITY +static ssize_t cts_proximity_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; + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, "proximity mode: %d\n", + status->proximity); +} + +static ssize_t cts_proximity_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 enable = -1; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + if (count >= 1) { + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } else if (buf[0] == 'N' || buf[0] == 'n' || buf[0] == '0') { + enable = 0; + } + } + if (enable == -1) { + cts_err("Invalid arg for proximity mode enable"); + return -EINVAL; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_set_proximity_mode(cts_dev, enable); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Set proximity mode failed"); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(proximity_mode, S_IWUSR | S_IRUGO, + cts_proximity_mode_show, cts_proximity_mode_store); +#endif + + +static ssize_t cts_tcs_cmd_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, "chipone_cmds.bin size:%zu\n", + cts_dev->rtdata.tcscmd_len * sizeof(u16)); +} + +static ssize_t cts_tcs_cmd_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 firmware *tcscmds; + char *update = "update"; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + ret = memcmp(update, buf, count - 1); + if (ret) { + cts_err("Not update tcs cmd buf, %s", buf); + return count; + } + + ret = request_firmware(&tcscmds, "chipone_cmds.bin", dev); + if (ret) { + cts_err("Could not load firmware from chipone_cmds.bin: %d", ret); + return ret; + } + + if (tcscmds->size > PAGE_SIZE) { + cts_err("tcscmds length is over one PAGE_SIZE"); + return count; + } + + memcpy((u8 *)cts_dev->rtdata.tcscmd, tcscmds->data, tcscmds->size); + cts_dev->rtdata.tcscmd_len = tcscmds->size / sizeof(u16); + + release_firmware(tcscmds); + cts_info("update tcscmds successfully"); + + return count; +} +static DEVICE_ATTR(tcs_cmd, S_IWUSR | S_IRUSR, + cts_tcs_cmd_show, cts_tcs_cmd_store); + +static u16 cts_find_tcs_cmd(struct cts_device *cts_dev, u8 class_id, u8 cmd_id) +{ + struct cts_tcs_cmd *tcmd; + u8 cmd_buf[2]; + int i; + + for (i = 0; i < cts_dev->rtdata.tcscmd_len; i++) { + put_unaligned_le16(cts_dev->rtdata.tcscmd[i], cmd_buf); + tcmd = (struct cts_tcs_cmd *)cmd_buf; + if (class_id == tcmd->class_id && cmd_id == tcmd->cmd_id) { + cts_info("Found tcs cmd:0x%04x: { %d, %d, %d, %d, %d}", + cts_dev->rtdata.tcscmd[i], tcmd->base_flag, + tcmd->class_id, tcmd->cmd_id, + tcmd->is_read, tcmd->is_write); + return cts_dev->rtdata.tcscmd[i]; + } + } + + return 0; +} +static ssize_t cts_read_tcs_cmd_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 count = 0; + u8 *read_buf; + int i; + int ret; + + read_buf = kzalloc(cts_dev->rtdata.curr_len, GFP_KERNEL); + if (read_buf == NULL) { + return snprintf(buf, PAGE_SIZE, "kzalloc read_buf failed\n"); + } + + if (!(cts_dev->rtdata.curr_cmd & BIT(14))) { + kfree(read_buf); + return snprintf(buf, PAGE_SIZE, "This cmd %04x is not readable!\n", + cts_dev->rtdata.curr_cmd); + } + + cts_lock_device(cts_dev); + ret = cts_tcs_read(cts_dev, cts_dev->rtdata.curr_cmd, read_buf, + cts_dev->rtdata.curr_len); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("read tcscmd: 0x%04x, failed", cts_dev->rtdata.curr_cmd); + kfree(read_buf); + return ret; + } + + count += snprintf(buf + count, PAGE_SIZE - count, "read_tcs(0x%02x): ", + cts_dev->rtdata.curr_cmd); + for (i = 0; i < cts_dev->rtdata.curr_len; i++) { + count += snprintf(buf + count, PAGE_SIZE - count, "%02x ", read_buf[i]); + } + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + + kfree(read_buf); + return count; +} +static ssize_t cts_read_tcs_cmd_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 cmd; + u8 class_id, cmd_id, read_len; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + cts_parse_arg(buf, count); + + if (cts_argc != 3) { + cts_err("usage: echo class_id cmd_id read_len > read_tcs"); + return count; + } + + ret = kstrtou8(cts_argv[0], 0, &class_id); + if (ret) { + cts_err("kstrtou8 class_id %d failed", class_id); + return ret; + } + + ret = kstrtou8(cts_argv[1], 0, &cmd_id); + if (ret) { + cts_err("kstrtou8 cmd_id %d failed", cmd_id); + return ret; + } + + ret = kstrtou8(cts_argv[2], 0, &read_len); + if (ret) { + cts_err("kstrtou8 read_len %d failed", read_len); + return ret; + } + + cmd = cts_find_tcs_cmd(cts_dev, class_id, cmd_id); + if (cmd == 0) { + cts_err("Not found classid %d, cmdid %d", class_id, cmd_id); + return count; + } + + cts_dev->rtdata.curr_cmd = cmd; + cts_dev->rtdata.curr_len = read_len; + cts_info("tcs cmd:%02x", cmd); + + return count; +} +static DEVICE_ATTR(read_tcs_cmd, S_IWUSR | S_IRUSR, + cts_read_tcs_cmd_show, cts_read_tcs_cmd_store); + + +static ssize_t cts_write_tcs_cmd_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 class_id, cmd_id; + u16 cmd; + u8 wbuf[32]; + int wlen; + int i; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + cts_parse_arg(buf, count); + + if (cts_argc < 3) { + cts_err("usage: echo class_id cmd_id x1 x2 .. > write_tcs"); + return count; + } + + ret = kstrtou8(cts_argv[0], 0, &class_id); + if (ret) { + cts_err("kstrtou8 class_id %d failed", ret); + return ret; + } + + ret = kstrtou8(cts_argv[1], 0, &cmd_id); + if (ret) { + cts_err("kstrtou8 cmd_id %d failed", ret); + return ret; + } + + wlen = cts_argc - 2; + if (wlen > sizeof(wbuf)) { + cts_warn("write size is limit %zu bytes.", sizeof(wbuf)); + wlen = sizeof(wbuf); + } + + for (i = 0; i < wlen; i++) { + ret = kstrtou8(cts_argv[i + 2], 0, wbuf + i); + if (ret) { + cts_err("kstrtou8 wbuf %d failed", i); + return ret; + } + } + + cmd = cts_find_tcs_cmd(cts_dev, class_id, cmd_id); + if (cmd == 0) { + cts_err("Not found classid %d, cmdid %d", class_id, cmd_id); + return count; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_write(cts_dev, cmd, wbuf, wlen); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("write tcscmd: 0x%04x, failed", cmd); + return ret; + } + + return count; +} +static DEVICE_ATTR(write_tcs_cmd, S_IWUSR, NULL, cts_write_tcs_cmd_store); + +static struct attribute *cts_dev_misc_atts[] = { + &dev_attr_ic_type.attr, + &dev_attr_program_mode.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin.attr, +#endif +#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED + &dev_attr_irq_pin.attr, +#endif + &dev_attr_irq_info.attr, + &dev_attr_read_tcs_reg.attr, + &dev_attr_write_tcs_reg.attr, + &dev_attr_read_hw_reg.attr, + &dev_attr_write_hw_reg.attr, +#ifndef CONFIG_CTS_I2C_HOST + &dev_attr_debug_spi.attr, +#endif +#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, + &dev_attr_charger_state.attr, + &dev_attr_earjack_state.attr, + &dev_attr_edge_restain.attr, + &dev_attr_game_mode.attr, +#ifdef CONFIG_CTS_TP_PROXIMITY + &dev_attr_proximity_mode.attr, +#endif + &dev_attr_tcs_cmd.attr, + &dev_attr_read_tcs_cmd.attr, + &dev_attr_write_tcs_cmd.attr, + NULL +}; + +static const struct attribute_group cts_dev_misc_attr_group = { + .name = "misc", + .attrs = cts_dev_misc_atts, +}; + +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 +}; + +#ifdef CFG_CTS_PLATFORM_SPRD_SUPPORTED +static ssize_t cts_suspend_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cts_device *cts_dev = &g_cts_data->cts_dev; + return sprintf(buf, "%s\n", cts_dev->rtdata.suspended ? "true" : "false"); +} +static ssize_t cts_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + if (count >= 1) { + if (buf[0] == '1') { + cts_info("ts_suspend_store suspend."); + cts_suspend(g_cts_data); + } else if (buf[0] == '0') { + cts_info("ts_suspend_store cts_resume."); + cts_resume(g_cts_data); + } else { + cts_err("usage: echo 0/1 > ts_suspend"); + } + } + + return count; +} +static DEVICE_ATTR(ts_suspend, S_IWUSR | S_IRUGO, + cts_suspend_show, cts_suspend_store); + +static struct attribute *cts_dev_ts_suspend_atts[] = { + &dev_attr_ts_suspend.attr, + NULL +}; + +static struct attribute_group ts_suspend_attr_group = { + .attrs = cts_dev_ts_suspend_atts, +}; + +static int cts_suspend_sysfs_init(struct device *dev) +{ + int ret = 0; + cts_info("sprocomm ts_suspend_store suspend. ret=%d",ret); + + ret = sysfs_create_group(&dev->kobj, &ts_suspend_attr_group); + if (ret) { + cts_info("Create sysfs ts_suspend group failed!"); + return -ENOMEM; + } else { + cts_info("Create sysfs ts_suspend group succesfully!"); + } + + ret = sysfs_create_link(NULL, &dev->kobj, "touchscreen"); + if (ret < 0) { + dev_err(dev, "Failed to create touchscreen link!"); + return -ENOMEM; + } + return ret; +} + +static void cts_suspend_sysfs_deinit(struct device *dev) +{ + sysfs_remove_link(NULL, "touchscreen"); + sysfs_remove_group(&dev->kobj, &ts_suspend_attr_group); +} +#endif + +#include +#include + +#ifndef CFG_CTS_FOR_GKI +/* 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; +} +#endif + +/* 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[] = { +#ifndef CFG_CTS_FOR_GKI + __ATTR_RO(path), +#endif + __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); + int ret = 0, 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; + } + + 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", ret); + return ret; + } + +#ifdef CFG_CTS_PLATFORM_SPRD_SUPPORTED + ret = cts_suspend_sysfs_init(dev); + if (ret) { + cts_info("create suspend sysfs node failed"); + return ret; + } +#endif + + 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"); + + 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); + +#ifdef CFG_CTS_PLATFORM_SPRD_SUPPORTED + cts_suspend_sysfs_deinit(dev); +#endif + +} + +#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/cts_sysfs.h b/drivers/input/touchscreen/chipone_tddi/cts_sysfs.h new file mode 100644 index 000000000000..b0224e317908 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_sysfs.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_SYSFS_H +#define CTS_SYSFS_H + +#include "cts_config.h" + +struct device; + +#ifdef CONFIG_CTS_SYSFS +extern int cts_argc; +extern char *cts_argv[]; +extern int cts_sysfs_add_device(struct device *dev); +extern void cts_sysfs_remove_device(struct device *dev); +extern int cts_parse_arg(const char *buf, size_t count); +#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/cts_tcs.c b/drivers/input/touchscreen/chipone_tddi/cts_tcs.c new file mode 100644 index 000000000000..73cdad598a58 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_tcs.c @@ -0,0 +1,1901 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#define LOG_TAG "TCS" + +#include "cts_config.h" +#include "cts_firmware.h" +#include "cts_platform.h" +#include "cts_tcs.h" + +#define TCS_WR_ADDR (0xF0) +#define TCS_RD_ADDR (0xF1) + +#define TCS_READ_BIT (14) +#define TCS_WRITE_BIT (13) +#define TCS_HEADER_SIZE sizeof(STRUCT_TCS_TX_HEAD) +#define TCS_TAIL_SIZE sizeof(STRUCT_TCS_RX_TAIL) + +/* raw touch info without data */ +#define TOUCH_INFO_SIZE sizeof(struct cts_device_touch_info) +#define INT_DATA_VALID_INDEX (174) +#define INT_DATA_HEADER_SIZE (180) + +#define CTS_I2C_MAX_TRANS_SIZE (48) + +#pragma pack(1) +#ifdef CONFIG_CTS_I2C_HOST +typedef struct { + u16 cmd; + u16 datlen; + u8 check_l; + u8 check_h; +} STRUCT_TCS_TX_HEAD; + +typedef struct { + u8 ecode; + u16 cmd; + u8 check_l; + u8 check_h; +} STRUCT_TCS_RX_TAIL; +#else /* CONFIG_CTS_I2C_HOST */ +typedef struct { + u8 addr; + u16 cmd; + u16 datlen; + u16 crc16; +} STRUCT_TCS_TX_HEAD; +typedef struct { + u8 ecode; + u16 cmd; + u16 crc16; +} STRUCT_TCS_RX_TAIL; +#endif /* CONFIG_CTS_I2C_HOST */ +#pragma pack() + +#define CMD_INFO_CHIP_HW_ID_RO (0x4002) +#define CMD_INFO_CHIP_FW_ID_RO (0x4003) +#define CMD_INFO_FW_VER_RO (0x4005) +#define CMD_INFO_TOUCH_XY_INFO_RO (0x4007) +#define CMD_INFO_IC_INFO_RO (0x4009) +#define CMD_INFO_PAD_PANEL_INFO_RO (0x400a) +#define CMD_INFO_MODULE_ID_RO (0x4011) +#define CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW (0x6101) +#define CMD_TP_DATA_READ_START_RO (0x4102) +#define CMD_TP_DATA_COORDINATES_RO (0x4103) +#define CMD_TP_DATA_RAW_RO (0x4104) +#define CMD_TP_DATA_DIFF_RO (0x4105) +#define CMD_TP_DATA_BASE_RO (0x4106) +#define CMD_TP_DATA_CNEG_RO (0x410a) + +#define CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO (0x2114) +#define CMD_TP_DATA_WR_REG_RAM_BATCH_WO (0x2115) +#define CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO (0x2116) +#define CMD_TP_STD_CMD_SET_KRANG_STOP (0x224a) +#define CMD_GET_DATA_BY_POLLING_RO (0x4123) +#define CMD_TP_DATA_STATUS_RO (0x4125) +#define CMD_SYS_STS_READ_RO (0x4200) +#define CMD_SYS_STS_WORK_MODE_RW (0x6201) +#define CMD_SYS_STS_DAT_RDY_FLAG_RW (0x6203) +#define CMD_SYS_STS_PWR_STATE_RW (0x6204) +#define CMD_SYS_STS_CHARGER_PLUGIN_RW (0x6205) +#define CMD_SYS_STS_DDI_CODE_VER_RO (0x4206) +#define CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW (0x6207) +#define CMD_SYS_STS_VSTIM_LVL_RW (0x6208) +#define CMD_SYS_STS_CNEG_RDY_FLAG_RW (0x6211) +#define CMD_SYS_STS_EP_PLUGIN_RW (0x6213) +#define CMD_SYS_STS_RESET_WO (0x2216) +#define CMD_SYS_STS_INT_TEST_EN_RW (0x6217) +#define CMD_SYS_STS_SET_INT_PIN_RW (0x6218) +#define CMD_SYS_STS_CNEG_RD_EN_RW (0x6219) +#define CMD_SYS_STS_HI_SENSE_EN_RW (0x621a) +#define CMD_SYS_STS_INT_MODE_RW (0x6223) +#define CMD_SYS_STS_INT_KEEP_TIME_RW (0x6224) +#define CMD_SYS_STS_CURRENT_WORKMODE_RO (0x4233) +#define CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO (0x423f) +#define CMD_SYS_STS_DATA_CAPTURE_EN_RW (0x6240) +#define CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW (0x6241) +#define CMD_SYS_STS_PANEL_DIRECTION_RW (0x6242) +#define CMD_SYS_STS_KRANG_WORK_MODE_RW (0x6243) +#define CMD_SYS_STS_KRANG_CURRENT_WORKMODE_RO (0x4244) +#define CMD_SYS_STS_GAME_MODE_RW (0x624e) +#define CMD_SYS_STS_POCKET_MODE_EN_RW (0x6250) +#define CMD_SYS_STS_PRODUCTION_TEST_EN_RW (0x6252) +#define CMD_SYS_STS_KRANG_WORK_STS_RO (0x4259) + +#define CMD_GSTR_WAKEUP_EN_RW (0x6301) +#define CMD_GSTR_DAT_RDY_FLAG_GSTR_RW (0x631e) +#define CMD_GSTR_ENTER_MAP_RW (0x6328) +#define CMD_MNT_EN_RW (0x6401) +#define CMD_MNT_FORCE_EXIT_MNT_WO (0x2403) +#define CMD_DDI_ESD_EN_RW (0x6501) +#define CMD_DDI_ESD_OPTIONS_RW (0x6502) +#define CMD_CNEG_EN_RW (0x6601) +#define CMD_CNEG_OPTIONS_RW (0x6602) +#define CMD_COORD_FLIP_X_EN_RW (0x6702) +#define CMD_COORD_FLIP_Y_EN_RW (0x6703) +#define CMD_COORD_SWAP_AXES_EN_RW (0x6704) +#define CMD_PARA_PROXI_EN_RW (0x692a) +#define CMD_PARA_KUNCKLE_RW (0x694b) +#define CMD_OPENSHORT_EN_RW (0x6b01) +#define CMD_OPENSHORT_MODE_SEL_RW (0x6b02) +#define CMD_OPENSHORT_SHORT_SEL_RW (0x6b03) +#define CMD_OPENSHORT_SHORT_DISP_ON_EN_RW (0x6b04) + +#if 0 +static u8 str[1024 * 4]; + +void dump_spi(const char *prefix, u8 *data, size_t datalen) +{ + + 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); +} +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_tcs_tail_check(u8 *buf, u16 cmd, int len) +{ + STRUCT_TCS_RX_TAIL *tail; + u8 check_l = 0; + u8 check_h = 0x01; + int check_len = len - TCS_TAIL_SIZE; + int i; + + tail = (STRUCT_TCS_RX_TAIL *)(buf + check_len); + if (tail->ecode != 0) { + cts_err("error code:0x%02x", tail->ecode); + return -EIO; + } + + if (tail->cmd != cmd) { + cts_err("cmd error: recv %04x != %04x send", tail->cmd, cmd); + return -EIO; + } + + for (i = 0; i < check_len + 3; i++) { + check_l += buf[i]; + } + check_l = ~check_l; + if (tail->check_h != check_h || tail->check_l != check_l) { + cts_err("crc error: recv %02x%02x != %02x%02x calc", + tail->check_h, tail->check_l, check_h, check_l); + return -EIO; + } + + return 0; +} + +static int cts_tcs_i2c_trans(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int i, retries = 0; + int ret; + + 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, + } + }; + + for (i = 0; i < 2; i++) { + for (retries = 0; retries < retry; retries++) { + ret = i2c_transfer(pdata->i2c_client->adapter, &msgs[i], 1); + if (ret == 1) { + ret = 0; + + if (delay > 0) + mdelay(delay); + else + udelay(300); + + break; + } else { + mdelay(10); + } + } + + if (retries >= retry) { + return -EIO; + } + } + + ret = cts_tcs_tail_check(rbuf, get_unaligned_le16(wbuf), rlen); + + return ret; +} + +static int cts_tcs_i2c_trans_1_time(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int retries = 0; + int ret; + + 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, + } + }; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, msgs, 2); + + if (ret != 2) { + if (ret >= 0) { + ret = -EIO; + } + + udelay(delay); + + continue; + } else { + return 0; + } + } while (++retries < retry); + + ret = cts_tcs_tail_check(rbuf, get_unaligned_le16(wbuf), rlen); + + return ret; +} + +static int cts_tcs_i2c_read_pack(u8 *tx, u16 cmd, u16 rdatalen) +{ + STRUCT_TCS_TX_HEAD *txhdr = (STRUCT_TCS_TX_HEAD *) tx; + u16 is_read; + int packlen = 0; + + is_read = cmd & BIT(TCS_READ_BIT); + if (0 == is_read) { + return packlen; + } + txhdr->cmd = (cmd & ~BIT(TCS_WRITE_BIT)); + txhdr->datlen = rdatalen; + txhdr->check_l = ~((txhdr->cmd & 0xff) + + ((txhdr->cmd >> 8) & 0xff) + + (rdatalen & 0xff) + + ((rdatalen >> 8) & 0xff)); + txhdr->check_h = 1; + packlen = TCS_HEADER_SIZE; + + return packlen; +} + +static int cts_tcs_i2c_write_pack(u8 *tx, u16 cmd, u8 *wdata, u16 wlen) +{ + STRUCT_TCS_TX_HEAD *txhdr = (STRUCT_TCS_TX_HEAD *) tx; + int packlen = 0; + u8 check_l = 0; + u16 is_write; + int i; + + is_write = cmd & BIT(TCS_WRITE_BIT); + if (0 == is_write) { + return packlen; + } + txhdr->cmd = (cmd & ~BIT(TCS_READ_BIT)); + txhdr->datlen = wlen; + txhdr->check_l = ~((txhdr->cmd & 0xff) + + ((txhdr->cmd >> 8) & 0xff) + + (wlen & 0xff) + + ((wlen >> 8) & 0xff)); + txhdr->check_h = 1; + packlen += TCS_HEADER_SIZE; + + if (wlen > 0) { + memcpy(tx + TCS_HEADER_SIZE, wdata, wlen); + for (i = 0; i < wlen; i++) { + check_l += wdata[i]; + } + *(tx + TCS_HEADER_SIZE + wlen) = ~check_l; + *(tx + TCS_HEADER_SIZE + wlen + 1) = 1; + packlen += wlen + 2; + } + + return packlen; +} + +static int cts_tcs_i2c_read(struct cts_device *cts_dev, u16 cmd, + u8 *buf, size_t len) +{ + int txlen; + int size; + int ret; + + size = len + TCS_TAIL_SIZE; + + txlen = cts_tcs_i2c_read_pack(cts_dev->pdata->i2c_fifo_buf, cmd, len); + + ret = cts_tcs_i2c_trans(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, size, + 3, 0); + + if (ret == 0) { + memcpy(buf, cts_dev->pdata->i2c_rbuf, len); + } + + return ret; +} + +static int cts_tcs_i2c_write(struct cts_device *cts_dev, + u16 cmd, u8 *wbuf, size_t wlen) +{ + int txlen = cts_tcs_i2c_write_pack(cts_dev->pdata->i2c_fifo_buf, cmd, wbuf, wlen); + return cts_tcs_i2c_trans(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, TCS_TAIL_SIZE, + 3, 0); +} + +static int cts_tcs_i2c_set_krang_stop(struct cts_device *cts_dev, + u16 cmd, u8 *wbuf, size_t wlen) +{ + int txlen = cts_tcs_i2c_write_pack(cts_dev->pdata->i2c_fifo_buf, cmd, wbuf, wlen); + return cts_tcs_i2c_trans(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, TCS_TAIL_SIZE, + 3, 50); +} + +static int cts_tcs_i2c_read_touch(struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len) +{ + int txlen; + int rxlen_without_tail = len - TCS_TAIL_SIZE; + int ret; + + txlen = cts_tcs_i2c_read_pack(cts_dev->pdata->i2c_fifo_buf, + cmd, len - TCS_TAIL_SIZE); + ret = cts_tcs_i2c_trans_1_time(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, len, 3, 10); + + if (ret == 0) { + memcpy(buf, cts_dev->pdata->i2c_rbuf, rxlen_without_tail); + } + + return ret; +} +#else +static int cts_tcs_spi_xtrans(struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen, int delay) +{ + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, cts_dev); + struct spi_transfer xfer[2]; + struct spi_message msg; + u16 crc16_recv, crc16_calc; + u16 cmd_recv, cmd_send; + int ret; + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + 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; + + 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; + } + + if (delay > 0) + mdelay(delay); + else + udelay(500); + + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + 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; + + 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; + } + + cmd_recv = get_unaligned_le16(rx +rxlen - 4); + cmd_send = get_unaligned_le16(tx + 1); + if (cmd_recv != cmd_send) { + cts_dbg("cmd check error, send %04x != %04x recv", cmd_send, cmd_recv); + // return -EIO; + } + + crc16_recv = get_unaligned_le16(rx + rxlen - 2); + crc16_calc = cts_crc16(rx, rxlen - 2); + if (crc16_recv != crc16_calc) { + cts_err("crc error: recv %04x != %04x calc", crc16_recv, crc16_calc); + return -EIO; + } + udelay(100); + + return 0; +} + +static int cts_tcs_spi_xtrans_1_cs(struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, cts_dev); + struct spi_transfer xfer[1]; + struct spi_message msg; + u16 crc16_recv, crc16_calc; + u16 cmd_recv, cmd_send; + int ret; + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + 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; + + 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; + } + + cmd_recv = get_unaligned_le16(rx + rxlen - 4); + cmd_send = get_unaligned_le16(tx + 1); + if (cmd_recv != cmd_send) { + cts_dbg("cmd check error, send %04x != %04x recv", cmd_send, cmd_recv); + // return -EIO; + } + + crc16_recv = get_unaligned_le16(rx + rxlen - 2); + crc16_calc = cts_crc16(rx, rxlen - 2); + if (crc16_recv != crc16_calc) { + cts_err("1cs crc error: recv %04x != %04x calc", crc16_recv, crc16_calc); + return -EIO; + } + + return 0; +} + +static int cts_tcs_spi_read_pack(u8 *tx, u16 cmd, u16 rdatalen) +{ + STRUCT_TCS_TX_HEAD *txhdr = (STRUCT_TCS_TX_HEAD *) tx; + u16 is_read; + int packlen = 0; + u16 crc16; + + is_read = cmd & BIT(TCS_READ_BIT); + if (0 == is_read) { + return packlen; + } + txhdr->addr = TCS_RD_ADDR; + txhdr->cmd = (cmd & ~BIT(TCS_WRITE_BIT)); + txhdr->datlen = rdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(STRUCT_TCS_TX_HEAD, crc16)); + txhdr->crc16 = crc16; + packlen += TCS_HEADER_SIZE; + + return packlen; +} + +static int cts_tcs_spi_write_pack(u8 *tx, u16 cmd, u8 *wdata, u16 wdatalen) +{ + STRUCT_TCS_TX_HEAD *txhdr = (STRUCT_TCS_TX_HEAD *) tx; + u16 is_write; + int packlen = 0; + u16 crc16; + + is_write = cmd & BIT(TCS_WRITE_BIT); + if (0 == is_write) { + return packlen; + } + txhdr->addr = TCS_WR_ADDR; + txhdr->cmd = (cmd & ~BIT(TCS_READ_BIT)); + txhdr->datlen = wdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(STRUCT_TCS_TX_HEAD, crc16)); + txhdr->crc16 = crc16; + packlen += TCS_HEADER_SIZE; + + if (wdatalen > 0) { + memcpy(tx + TCS_HEADER_SIZE, wdata, wdatalen); + crc16 = cts_crc16(wdata, wdatalen); + *(tx + TCS_HEADER_SIZE + wdatalen) = ((crc16 >> 0) & 0xFF); + *(tx + TCS_HEADER_SIZE + wdatalen + 1) = ((crc16 >> 8) & 0xFF); + packlen += wdatalen + sizeof(crc16); + } + + return packlen; +} + +static int cts_tcs_spi_read(struct cts_device *cts_dev, + u16 cmd, u8 *rdata, size_t rdatalen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_read_pack(cts_dev->pdata->spi_tx_buf, cmd, rdatalen); + if (0 == txlen) { + cts_err("spi read pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, rdatalen + TCS_TAIL_SIZE, 0); + if (ret) { + return ret; + } + + memcpy(rdata, cts_dev->pdata->spi_rx_buf, rdatalen); + + return ret; +} + +static int cts_tcs_spi_read_1_cs(struct cts_device *cts_dev, + u16 cmd, u8 *rdata, size_t rdatalen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_read_pack(cts_dev->pdata->spi_tx_buf, cmd, rdatalen); + if (0 == txlen) { + cts_err("spi read pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans_1_cs(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, rdatalen); + if (ret) { + return ret; + } + + memcpy(rdata, cts_dev->pdata->spi_rx_buf, rdatalen); + + return ret; +} + +static int cts_tcs_spi_write(struct cts_device *cts_dev, + u16 cmd, u8 *data, size_t wlen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_write_pack(cts_dev->pdata->spi_tx_buf, cmd, data, wlen); + if (0 == txlen) { + cts_err("spi write pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, TCS_TAIL_SIZE, 0); + return ret; +} + +static int cts_tcs_spi_set_krang_stop(struct cts_device *cts_dev, + u16 cmd, u8 *data, size_t wlen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_write_pack(cts_dev->pdata->spi_tx_buf, cmd, data, wlen); + if (0 == txlen) { + cts_err("spi write pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, TCS_TAIL_SIZE, 50); + return ret; +} +#endif + +int cts_tcs_tool_xtrans(struct cts_device *cts_dev, u8 *tx, size_t txlen, + u8 *rx, size_t rxlen) +{ +#ifdef CONFIG_CTS_I2C_HOST + int ret; + memcpy(cts_dev->pdata->i2c_fifo_buf, tx, txlen); + + ret = cts_tcs_i2c_trans(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, rxlen, + 3, 1); + if (ret == 0) + memcpy(rx, cts_dev->pdata->i2c_rbuf, rxlen); + + return ret; +#else + return cts_tcs_spi_xtrans(cts_dev, tx, txlen, rx, rxlen, 1); +#endif +} + +int cts_tcs_read(struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_tcs_i2c_read(cts_dev, cmd, buf, len); +#else + return cts_tcs_spi_read(cts_dev, cmd, buf, len); +#endif +} +int cts_tcs_write(struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_tcs_i2c_write(cts_dev, cmd, buf, len); +#else + return cts_tcs_spi_write(cts_dev, cmd, buf, len); +#endif +} + +static int cts_tcs_set_krang_stop(struct cts_device *cts_dev) +{ + uint8_t stop = 1; + int ret; + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_set_krang_stop(cts_dev, CMD_TP_STD_CMD_SET_KRANG_STOP, + &stop, sizeof(stop)); +#else + ret = cts_tcs_spi_set_krang_stop(cts_dev, CMD_TP_STD_CMD_SET_KRANG_STOP, + &stop, sizeof(stop)); +#endif + if (ret < 0) { + cts_err("Set krang stop failed!"); + } + + return ret; +} + +int cts_tcs_get_hwid_info(struct cts_device *cts_dev, u8 *info) +{ + u8 buf[12] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_CHIP_HW_ID_RO, buf, sizeof(buf)); + if (ret == 0) { + memcpy(info, buf, sizeof(buf)); + } + return ret; +} + +int cts_tcs_get_fw_ver(struct cts_device *cts_dev, u16 *fwver) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_FW_VER_RO, buf, sizeof(buf)); + if (ret == 0) { + *fwver = buf[0] | (buf[1] << 8); + } + return ret; +} + +int cts_tcs_get_lib_ver(struct cts_device *cts_dev, u16 *libver) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_FW_VER_RO, buf, sizeof(buf)); + if (ret == 0) { + *libver = buf[2] | (buf[3] << 8); + } + return ret; +} + +int cts_tcs_get_fw_id(struct cts_device *cts_dev, u16 *fwid) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_CHIP_FW_ID_RO, buf, sizeof(buf)); + if (ret == 0) { + *fwid = buf[0] | (buf[1] << 8); + } + + return ret; +} + +int cts_tcs_get_ddi_ver(struct cts_device *cts_dev, u8 *ddiver) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DDI_CODE_VER_RO, buf, sizeof(buf)); + if (ret == 0) { + *ddiver = buf[0]; + } + return ret; +} + +int cts_tcs_get_res_x(struct cts_device *cts_dev, u16 *res_x) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *res_x = buf[0] | (buf[1] << 8); + } + return ret; +} + +int cts_tcs_get_res_y(struct cts_device *cts_dev, u16 *res_y) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *res_y = buf[2] | (buf[3] << 8); + } + return ret; +} + +int cts_tcs_get_rows(struct cts_device *cts_dev, u8 *rows) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *rows = buf[5]; + } + return ret; +} + +int cts_tcs_get_cols(struct cts_device *cts_dev, u8 *cols) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *cols = buf[4]; + } + return ret; +} + +int cts_tcs_get_rx_tx_info(struct cts_device *cts_dev, u8 *info) +{ + u8 buf[8] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_IC_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + memcpy(info, buf, sizeof(buf)); + } + return ret; +} + +int cts_tcs_get_panel_rx_tx_info(struct cts_device *cts_dev, u8 *info) +{ + u8 buf[15] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_PAD_PANEL_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + memcpy(info, buf, sizeof(buf)); + } + return ret; +} + +int cts_tcs_get_flip_x(struct cts_device *cts_dev, bool *flip_x) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_COORD_FLIP_X_EN_RW, buf, sizeof(buf)); + if (ret == 0) { + *flip_x = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_flip_y(struct cts_device *cts_dev, bool *flip_y) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_COORD_FLIP_Y_EN_RW, buf, sizeof(buf)); + if (ret == 0) { + *flip_y = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_swap_axes(struct cts_device *cts_dev, bool *swap_axes) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_COORD_SWAP_AXES_EN_RW, buf, sizeof(buf)); + if (ret == 0) { + *swap_axes = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_int_mode(struct cts_device *cts_dev, u8 *int_mode) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_INT_MODE_RW, buf, sizeof(buf)); + if (ret == 0) { + *int_mode = buf[0]; + } + return ret; +} + +int cts_tcs_get_int_keep_time(struct cts_device *cts_dev, + u16 *int_keep_time) +{ + u8 buf[2] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_INT_KEEP_TIME_RW, buf, sizeof(buf)); + if (ret == 0) { + *int_keep_time = (buf[0] | (buf[1] << 8)); + } + return ret; + +} + +int cts_tcs_get_rawdata_target(struct cts_device *cts_dev, + u16 *rawdata_target) +{ + u8 buf[2] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_CNEG_OPTIONS_RW, buf, sizeof(buf)); + if (ret == 0) { + *rawdata_target = (buf[0] | (buf[1] << 8)); + } + return ret; + +} + +int cts_tcs_get_esd_method(struct cts_device *cts_dev, u8 *esd_method) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_DDI_ESD_OPTIONS_RW, buf, sizeof(buf)); + if (ret == 0) { + *esd_method = buf[0]; + } + return ret; +} + +int cts_tcs_get_esd_protection(struct cts_device *cts_dev, + u8 *esd_protection) +{ + u8 buf[4] = { 0 }; + int ret; + + buf[0] = 0x01; + buf[1] = 0x56; + buf[2] = 0x81; + buf[3] = 0x00; + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_read(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + esd_protection, sizeof(u8)); + + return ret; +} + +static int cts_tcs_get_data_ready_flag(struct cts_device *cts_dev, u8 *ready) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DAT_RDY_FLAG_RW, buf, sizeof(buf)); + if (ret == 0) { + *ready = buf[0]; + } + return ret; +} + +int cts_tcs_clr_gstr_ready_flag(struct cts_device *cts_dev) +{ + u8 ready = 0; + + return cts_tcs_write(cts_dev, CMD_GSTR_DAT_RDY_FLAG_GSTR_RW, + &ready, sizeof(ready)); +} + +static int cts_tcs_clr_data_ready_flag(struct cts_device *cts_dev) +{ + u8 ready = 0; + + return cts_tcs_write(cts_dev, CMD_SYS_STS_DAT_RDY_FLAG_RW, + &ready, sizeof(ready)); +} + +int cts_tcs_read_hw_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 buf[4] = { 0 }; + int ret; + + buf[0] = 1; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_read(cts_dev, CMD_TP_DATA_READ_START_RO, regbuf, size); + + return ret; +} + +int cts_tcs_write_hw_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 *buf; + int ret; + + 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_write(cts_dev, 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(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 buf[2] = { 0 }; + int ret; + + buf[0] = 2; + buf[1] = addr; + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_read(cts_dev, CMD_TP_DATA_READ_START_RO, regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_ddi_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 *buf; + int ret; + + 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_write(cts_dev, CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; + +} + +int cts_tcs_read_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_tcs_read(cts_dev, CMD_SYS_STS_READ_RO, regbuf, size); +} + +int cts_tcs_write_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 buf[4] = { 0 }; + int ret; + + buf[0] = 0x01; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_write(cts_dev, 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)) + + u16 data_types = cts_dev->fwdata.int_data_types; + bool is_stylus = !!(cts_dev->fwdata.int_data_types & BIT(14)); + u8 data_method = cts_dev->fwdata.int_data_method; + + u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; + u8 cascade_num = cts_dev->fwdata.cascade_num; + u8 pc_cols = cts_dev->fwdata.pc_cols; + u8 pc_rows = cts_dev->fwdata.pc_rows; + u8 pr_cols = cts_dev->fwdata.pr_cols; + u8 pr_rows = cts_dev->fwdata.pr_rows; + + cts_dev->fwdata.int_data_size = (TOUCH_INFO_SIZE + TCS_TAIL_SIZE); + + if (data_method == INT_DATA_METHOD_NONE) { + return 0; + } else if (data_method == INT_DATA_METHOD_DEBUG) { + cts_dev->fwdata.int_data_size += INT_DATA_TYPE_U16_SIZ; + return 0; + } + + if (data_types != INT_DATA_TYPE_NONE) { + cts_dev->fwdata.int_data_size = INT_DATA_HEADER_SIZE; + if (is_stylus) { + cts_dev->fwdata.int_data_size += (pr_rows * pr_cols + pc_rows * pc_cols) + * pen_freq_num * sizeof(u16) * cascade_num; + } else { + if ((data_types & INT_DATA_TYPE_CNEGDATA)) { + cts_dev->fwdata.int_data_size += INT_DATA_TYPE_U8_SIZ; + } else { + cts_dev->fwdata.int_data_size += INT_DATA_TYPE_U16_SIZ; + } + } + + cts_dev->fwdata.int_data_size += TCS_TAIL_SIZE; + } + + cts_info("data_method:%d, data_type:%04x", data_method, data_types); + cts_info("data_size:%d", cts_dev->fwdata.int_data_size); + return 0; +} + +int cts_tcs_polling_data(struct cts_device *cts_dev, + u8 *buf, size_t size) +{ + int retries = 100; + u8 ready = 0; + int ret; + + 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 { +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_read_touch(cts_dev, CMD_GET_DATA_BY_POLLING_RO, + cts_dev->int_data, data_size); +#else + ret = cts_tcs_spi_read_1_cs(cts_dev, CMD_GET_DATA_BY_POLLING_RO, + cts_dev->int_data, data_size); +#endif + mdelay(1); + } while (ret && --retries); + + if (cts_tcs_clr_data_ready_flag(cts_dev)) + cts_err("Clear data ready flag failed"); + + return ret; +} + +static void cts_rotate_data(struct cts_device *cts_dev, + u8 *dst, u8 *src, enum int_data_type type) +{ + int rows = cts_dev->fwdata.rows; + int cols = cts_dev->fwdata.cols; + u16 *u16dst = (u16 *)dst; + u16 *u16src = (u16 *)src; + int i, j; + + if ((type & 0x3f) ==INT_DATA_TYPE_CNEGDATA) { + for (i = 0; i < cols; i++) { + for (j = 0; j < rows; j++) { + *dst++ = src[j * cols + i]; + } + } + } else { + for (i = 0; i < cols; i++) { + for (j = 0; j < rows; j++) { + *u16dst++ = u16src[j * cols + i]; + } + } + } +} + +static int tool_polling_data(struct cts_device *cts_dev, u8 *buf, + enum int_data_type type) +{ + u8 old_int_data_method; + u16 old_int_data_types; + int retries = 5; + int ret; + + 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_tcs_polling_data(cts_dev, buf, 0); + if (!ret) { + memcpy(buf, cts_dev->int_data, cts_dev->fwdata.int_data_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; +} + +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 offset = INT_DATA_HEADER_SIZE; + int retries = 5; + int ret; + + 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_tcs_polling_data(cts_dev, buf, size); + if (!ret) { + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9951R) { + cts_rotate_data(cts_dev, buf, cts_dev->int_data + offset, type); + } else { + memcpy(buf, cts_dev->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_polling_test_data(struct cts_device *cts_dev, + u8 *buf, size_t size, enum int_data_type type) +{ + int offset = INT_DATA_HEADER_SIZE; + int retries = 5; + int ret; + + while (retries--) { + ret = cts_tcs_polling_data(cts_dev, buf, size); + if (!ret) { + if (cts_dev->int_data[INT_DATA_VALID_INDEX]) { + memcpy(buf, cts_dev->int_data + offset, size); + break; + } + } + } + + return ret; +} + +int cts_tcs_tool_get_rawdata(struct cts_device *cts_dev, u8 *buf, u16 data_source) +{ + return tool_polling_data(cts_dev, buf, data_source | INT_DATA_TYPE_RAWDATA); +} + +int cts_tcs_tool_get_manual_diff(struct cts_device *cts_dev, u8 *buf, u16 data_source) +{ + return tool_polling_data(cts_dev, buf, data_source | INT_DATA_TYPE_MANUAL_DIFF); +} + +int cts_tcs_tool_get_real_diff(struct cts_device *cts_dev, u8 *buf, u16 data_source) +{ + return tool_polling_data(cts_dev, buf, data_source | INT_DATA_TYPE_REAL_DIFF); +} + +int cts_tcs_tool_get_basedata(struct cts_device *cts_dev, u8 *buf, u16 data_source) +{ + return tool_polling_data(cts_dev, buf, data_source | INT_DATA_TYPE_BASEDATA); +} + + +int cts_tcs_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source) +{ + return polling_data(cts_dev, buf, size, data_source | INT_DATA_TYPE_RAWDATA); +} + +int cts_tcs_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source) +{ + return polling_data(cts_dev, buf, size, data_source | INT_DATA_TYPE_MANUAL_DIFF); +} + +int cts_tcs_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source) +{ + return polling_data(cts_dev, buf, size, data_source | INT_DATA_TYPE_REAL_DIFF); +} + +int cts_tcs_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source) +{ + return polling_data(cts_dev, buf, size, data_source | INT_DATA_TYPE_NOISE_DIFF); +} + +int cts_tcs_top_get_basedata(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source) +{ + return polling_data(cts_dev, buf, size, data_source | INT_DATA_TYPE_BASEDATA); +} + +int cts_tcs_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source) +{ + return polling_data(cts_dev, buf, size, data_source | INT_DATA_TYPE_CNEGDATA); +} + +int cts_tcs_reset_device(struct cts_device *cts_dev) +{ +#ifdef CONFIG_CTS_ICTYPE_ICNL9922 + u8 buf[2] = { 0x01, 0xfe }; + int ret; + + cts_info("ICNL9922 use software reset"); + /* normal */ + cts_info("tp reset in normal mode"); + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_RESET_WO, + buf, sizeof(buf)); + 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); +#endif +} + +int cts_tcs_set_int_test(struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_INT_TEST_EN_RW, &enable, + sizeof(enable)); +} + +int cts_tcs_set_int_pin(struct cts_device *cts_dev, u8 high) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_SET_INT_PIN_RW, &high, + sizeof(high)); +} + +int cts_tcs_get_module_id(struct cts_device *cts_dev, u32 *modId) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_MODULE_ID_RO, buf, sizeof(buf)); + if (ret == 0) { + *modId = *(u32 *)buf; + } + return ret; +} + + +int cts_tcs_get_gestureinfo(struct cts_device *cts_dev, + struct cts_device_gesture_info *gesture_info) +{ + size_t size = sizeof(*gesture_info) + TCS_TAIL_SIZE; + int ret; + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_read_touch(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); +#else + ret = cts_tcs_spi_read_1_cs(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); +#endif + if (cts_tcs_clr_gstr_ready_flag(cts_dev)) { + cts_err("Clear gesture ready flag failed"); + } + if (ret < 0) { + cts_err("Get gesture info failed: ret=%d", ret); + return ret; + } + + memcpy(gesture_info, cts_dev->int_data, sizeof(*gesture_info)); + + return ret; +} + +int cts_tcs_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + size_t size = cts_dev->fwdata.int_data_size; + int ret; + + memset(touch_info, 0, sizeof(*touch_info)); + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_read_touch(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); + if (unlikely(ret != 0)) { + cts_err("cts_tcs_i2c_read_touch failed"); + return ret; + } +#else + ret = cts_tcs_spi_read_1_cs(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); + if (unlikely(ret != 0)) { + cts_err("tcs_spi_read_1_cs failed"); + return ret; + } +#endif + memcpy(touch_info, cts_dev->int_data, sizeof(*touch_info)); + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (unlikely((touch_info->debug_msg.reset_flag & 0xFFFFFF) != 0xFFFFFF + || (touch_info->debug_msg.reset_flag & 0xFFFF) != 0xFFFF)) { + cts_err("reset flag:0x%08x error", touch_info->debug_msg.reset_flag); + cts_show_touch_debug_msg(&touch_info->debug_msg); + } +#endif + + return ret; +} + +int cts_tcs_get_touch_status(struct cts_device *cts_dev) +{ + return cts_tcs_read(cts_dev, CMD_TP_DATA_STATUS_RO, + cts_dev->int_data, TOUCH_INFO_SIZE); +} + +int cts_tcs_get_workmode(struct cts_device *cts_dev, u8 *workmode) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_CURRENT_WORKMODE_RO, + &buf, sizeof(buf)); + if (ret == 0) { + *workmode = buf; + } + + return ret; +} + +int cts_tcs_set_workmode(struct cts_device *cts_dev, u8 workmode) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_WORK_MODE_RW, + &workmode, sizeof(workmode)); +} + +int cts_tcs_set_openshort_mode(struct cts_device *cts_dev, u8 mode) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_MODE_SEL_RW, &mode, + sizeof(mode)); +} + +int cts_tcs_get_curr_mode(struct cts_device *cts_dev, u8 *currmode) +{ + u8 buf = 0; + int ret; + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_CURRENT_WORKMODE_RO, + &buf, sizeof(buf)); + if (ret == 0) { + *currmode = buf; + } + + return ret; +} + +int cts_tcs_get_krang_current_workmode(struct cts_device *cts_dev, u8 *workmode) +{ + u8 buf = 0; + int ret = 0; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_KRANG_CURRENT_WORKMODE_RO, + &buf, sizeof(buf)); + if (ret == 0) { + *workmode = buf; + } + + return ret; +} + +int cts_tcs_set_krang_workmode(struct cts_device *cts_dev, u8 workmode) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_KRANG_WORK_MODE_RW, &workmode, + sizeof(workmode)); +} + +/*Tab A9 code for AX6739A-953 by suyurui at 20230609 end*/ +int cts_tcs_set_tx_vol(struct cts_device *cts_dev, u8 txvol) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_VSTIM_LVL_RW, &txvol, + sizeof(txvol)); +} + +int cts_tcs_set_short_test_type(struct cts_device *cts_dev, + u8 short_type) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_SHORT_SEL_RW, + &short_type, sizeof(short_type)); +} + +int cts_tcs_is_openshort_enabled(struct cts_device *cts_dev, u8 *enabled) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_write(cts_dev, CMD_OPENSHORT_EN_RW, &buf, sizeof(buf)); + if (ret == 0) { + *enabled = buf; + } + + return ret; +} + +int cts_tcs_set_openshort_enable(struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_set_esd_enable(struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_DDI_ESD_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_is_cneg_enabled(struct cts_device *cts_dev, u8 *enabled) +{ + return cts_tcs_read(cts_dev, CMD_CNEG_EN_RW, enabled, sizeof(*enabled)); +} + +int cts_tcs_is_mnt_enabled(struct cts_device *cts_dev, u8 *enabled) +{ + return cts_tcs_read(cts_dev, CMD_MNT_EN_RW, enabled, sizeof(*enabled)); +} + +int cts_tcs_set_cneg_enable(struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_CNEG_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_set_mnt_enable(struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_MNT_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_is_display_on(struct cts_device *cts_dev, u8 *display_on) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + &buf, sizeof(buf)); + if (ret == 0) { + *display_on = buf; + } + + return ret; +} + +int cts_tcs_set_pwr_mode(struct cts_device *cts_dev, u8 pwr_mode) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_PWR_STATE_RW, + &pwr_mode, sizeof(pwr_mode)); +} + +int cts_tcs_set_display_on(struct cts_device *cts_dev, u8 display_on) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + &display_on, sizeof(display_on)); +} + + +int cts_tcs_set_charger_plug(struct cts_device *cts_dev, u8 set) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set charger enable:%d", set); + status->charger = set; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_CHARGER_PLUGIN_RW, &set, 1); + if (ret < 0) { + cts_info("Set charger failed!"); + } + + return ret; +} + +int cts_tcs_get_charger_plug(struct cts_device *cts_dev, u8 *isset) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_CHARGER_PLUGIN_RW,&buf, sizeof(buf)); + if (ret == 0) { + *isset = buf; + } + + return ret; +} + +int cts_tcs_set_earjack_plug(struct cts_device *cts_dev, u8 set) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set earjack enable:%d", set); + + status->earjack = set; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_EP_PLUGIN_RW, &set, 1); + if (ret) { + cts_info("Set earjack failed!"); + } + + return ret; +} + +int cts_tcs_get_earjack_plug(struct cts_device *cts_dev, u8 *isset) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_EP_PLUGIN_RW, &buf, sizeof(buf)); + if (ret == 0) { + *isset = buf; + } + + return ret; +} + +int cts_tcs_set_panel_direction(struct cts_device *cts_dev, u8 direction) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_PANEL_DIRECTION_RW, + &direction, sizeof(direction)); +} + +int cts_tcs_get_panel_direction(struct cts_device *cts_dev, u8 *direction) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_PANEL_DIRECTION_RW, + &buf, sizeof(buf)); + if (ret == 0) { + *direction = buf; + } + + return ret; +} + +int cts_tcs_set_game_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set game enable:%d", enable); + + status->game = enable; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_GAME_MODE_RW, + &enable, sizeof(enable)); + if (ret) { + cts_err("Set game failed!"); + } + + return ret; +} + +int cts_tcs_get_game_mode(struct cts_device *cts_dev, u8 *enabled) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_GAME_MODE_RW, buf, sizeof(buf)); + if (ret == 0) { + *enabled = buf[0]; + } + + return ret; +} + +int cts_tcs_get_has_int_data(struct cts_device *cts_dev, bool *has) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO, + buf, sizeof(buf)); + if (ret == 0) { + *has = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_int_data_types(struct cts_device *cts_dev, u16 *type) +{ + u8 buf[2] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + buf, sizeof(buf)); + if (ret == 0) { + *type = buf[0] | (buf[1] << 8); + } + return ret; +} + +int cts_tcs_set_int_data_types(struct cts_device *cts_dev, u16 type) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + (u8 *) &type, sizeof(type)); +} + +int cts_tcs_get_int_data_method(struct cts_device *cts_dev, u8 *method) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DATA_CAPTURE_EN_RW, + buf, sizeof(buf)); + if (ret == 0) { + *method = buf[0]; + } + return ret; +} + +int cts_tcs_set_int_data_method(struct cts_device *cts_dev, u8 method) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_DATA_CAPTURE_EN_RW, + &method, sizeof(method)); +} + +int cts_tcs_set_proximity_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set proximity enable:%d", enable); + status->proximity = enable; + + ret = cts_tcs_write(cts_dev, CMD_PARA_PROXI_EN_RW, &enable, 1); + if (ret != 0) { + cts_err("Set proximity failed!"); + } + + return ret; +} + +static int cts_tcs_set_knuckle_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set knuckle enable:%d", enable); + status->knuckle = enable; + + ret = cts_tcs_write(cts_dev, CMD_PARA_KUNCKLE_RW, &enable, 1); + if (ret != 0) { + cts_err("Set knuckle failed!"); + } + + return ret; +} + +int cts_tcs_set_glove_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set glove enable:%d", enable); + status->glove = enable; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_HI_SENSE_EN_RW, &enable, 1); + if (ret != 0) { + cts_err("Set glove failed!"); + } + + return ret; +} + +static int cts_tcs_set_pocket_enable(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set pocket enable:%d", enable); + status->pocket = enable; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_POCKET_MODE_EN_RW, &enable, 1); + if (ret != 0) { + cts_err("Set pocket failed!"); + } + + return ret; +} + +void cts_tcs_reinit_fw_status(struct cts_device *cts_dev) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + cts_tcs_set_charger_plug(cts_dev, status->charger); + cts_tcs_set_proximity_mode(cts_dev, status->proximity); + cts_tcs_set_earjack_plug(cts_dev, status->earjack); + cts_tcs_set_knuckle_mode(cts_dev, status->knuckle); + cts_tcs_set_glove_mode(cts_dev, status->glove); + cts_tcs_set_pocket_enable(cts_dev, status->pocket); + cts_tcs_set_game_mode(cts_dev, status->game); +} + + +int cts_tcs_set_product_en(struct cts_device *cts_dev, u8 enable) +{ + int ret; + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_PRODUCTION_TEST_EN_RW, + &enable, sizeof(enable)); + if (ret) { + cts_err("Set product_en failed!"); + } + + return ret; +} + +int cts_tcs_wait_krang_stop(struct cts_device *cts_dev) +{ + uint8_t status = 1; + int i; + int ret; + + for (i = 0; i < 3; i++) { + ret = cts_tcs_set_krang_stop(cts_dev); + if (ret < 0) { + cts_err("Set krang stop failed!"); + mdelay(1); + continue; + } + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_KRANG_WORK_STS_RO, &status, sizeof(status)); + if (ret < 0) { + cts_err("Read krang stop status failed!"); + mdelay(1); + continue; + } + + if (0 == status) { + cts_info("krang flag was already stopped!"); + return 0; + } else { + cts_err("Read krang stop status:%d != 0", status); + mdelay(1); + continue; + } + } + + return -EBUSY; +} diff --git a/drivers/input/touchscreen/chipone_tddi/cts_tcs.h b/drivers/input/touchscreen/chipone_tddi/cts_tcs.h new file mode 100644 index 000000000000..c540a97101f7 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_tcs.h @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef _CTS_TCS_H_ +#define _CTS_TCS_H_ + +int cts_tcs_read(struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len); +int cts_tcs_write(struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len); + +int cts_tcs_get_hwid_info(struct cts_device *cts_dev, u8 *info); +int cts_tcs_get_fw_ver(struct cts_device *cts_dev, u16 *fwver); +int cts_tcs_get_lib_ver(struct cts_device *cts_dev, u16 *libver); +int cts_tcs_get_ddi_ver(struct cts_device *cts_dev, u8 *ddiver); +int cts_tcs_get_res_x(struct cts_device *cts_dev, u16 *res_x); +int cts_tcs_get_res_y(struct cts_device *cts_dev, u16 *res_y); +int cts_tcs_get_rows(struct cts_device *cts_dev, u8 *rows); +int cts_tcs_get_cols(struct cts_device *cts_dev, u8 *cols); +int cts_tcs_get_rx_tx_info(struct cts_device *cts_dev, u8 *info); +int cts_tcs_get_panel_rx_tx_info(struct cts_device *cts_dev, u8 *info); +int cts_tcs_get_flip_x(struct cts_device *cts_dev, bool *flip_x); +int cts_tcs_get_flip_y(struct cts_device *cts_dev, bool *flip_y); +int cts_tcs_get_swap_axes(struct cts_device *cts_dev, bool *swap_axes); +int cts_tcs_clr_gstr_ready_flag(struct cts_device *cts_dev); + +int cts_tcs_get_int_mode(struct cts_device *cts_dev, u8 *int_mode); +int cts_tcs_get_int_keep_time(struct cts_device *cts_dev, + u16 *int_keep_time); +int cts_tcs_get_rawdata_target(struct cts_device *cts_dev, + u16 *rawdata_target); +int cts_tcs_get_esd_method(struct cts_device *cts_dev, u8 *esd_method); + +int cts_tcs_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info); +int cts_tcs_get_gestureinfo(struct cts_device *cts_dev, + struct cts_device_gesture_info *gesture_info); +int cts_tcs_get_touch_status(struct cts_device *cts_dev); +int cts_tcs_get_esd_protection(struct cts_device *cts_dev, + u8 *esd_protection); + +int cts_tcs_read_hw_reg(struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_hw_reg(struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_read_ddi_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_write_ddi_reg(struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_read_reg(struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_reg(struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + +int cts_tcs_get_fw_id(struct cts_device *cts_dev, u16 *fwid); +int cts_tcs_get_workmode(struct cts_device *cts_dev, u8 *workmode); +int cts_tcs_set_workmode(struct cts_device *cts_dev, u8 workmode); +int cts_tcs_set_openshort_mode(struct cts_device *cts_dev, u8 mode); +int cts_tcs_get_curr_mode(struct cts_device *cts_dev, u8 *currmode); +int cts_tcs_get_krang_current_workmode(struct cts_device *cts_dev, u8 *workmode); +int cts_tcs_set_krang_workmode(struct cts_device *cts_dev, u8 workmode); + +int cts_tcs_set_tx_vol(struct cts_device *cts_dev, u8 txvol); + +int cts_tcs_set_short_test_type(struct cts_device *cts_dev, u8 short_type); +int cts_tcs_set_openshort_enable(struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_openshort_enabled(struct cts_device *cts_dev, u8 *enabled); + +int cts_tcs_set_esd_enable(struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_cneg_enable(struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_mnt_enable(struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_display_on(struct cts_device *cts_dev, u8 *display_on); +int cts_tcs_set_display_on(struct cts_device *cts_dev, u8 display_on); +int cts_tcs_is_cneg_enabled(struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_is_mnt_enabled(struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_set_pwr_mode(struct cts_device *cts_dev, u8 pwr_mode); +int cts_tcs_get_has_int_data(struct cts_device *cts_dev, bool *has); +int cts_tcs_get_int_data_types(struct cts_device *cts_dev, u16 *type); +int cts_tcs_set_int_data_types(struct cts_device *cts_dev, u16 type); +int cts_tcs_get_int_data_method(struct cts_device *cts_dev, u8 *method); +int cts_tcs_set_int_data_method(struct cts_device *cts_dev, u8 method); +int cts_tcs_calc_int_data_size(struct cts_device *cts_dev); + +int cts_tcs_polling_data(struct cts_device *cts_dev,u8 *buf, size_t size); +int cts_polling_test_data(struct cts_device *cts_dev, + u8 *buf, size_t size, enum int_data_type type); + +int cts_tcs_tool_get_rawdata(struct cts_device *cts_dev, u8 *buf, u16 data_source); +int cts_tcs_tool_get_manual_diff(struct cts_device *cts_dev, u8 *buf, u16 data_source); +int cts_tcs_tool_get_real_diff(struct cts_device *cts_dev, u8 *buf, u16 data_source); +int cts_tcs_tool_get_basedata(struct cts_device *cts_dev, u8 *buf, u16 data_source); + +int cts_tcs_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source); +int cts_tcs_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, + size_t size, u16 data_source); +int cts_tcs_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source); +int cts_tcs_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf,size_t size, + u16 data_source); +int cts_tcs_top_get_basedata(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source); +int cts_tcs_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, size_t size, + u16 data_source); +int cts_tcs_reset_device(struct cts_device *cts_dev); +int cts_tcs_set_int_test(struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_int_pin(struct cts_device *cts_dev, u8 high); +int cts_tcs_get_module_id(struct cts_device *cts_dev, u32 *modId); + +int cts_tcs_tool_xtrans(struct cts_device *cts_dev, u8 *tx, size_t txlen, + u8 *rx, size_t rxlen); + +int cts_tcs_set_charger_plug(struct cts_device *cts_dev, u8 set); +int cts_tcs_get_charger_plug(struct cts_device *cts_dev, u8 *isset); +int cts_tcs_set_earjack_plug(struct cts_device *cts_dev, u8 set); +int cts_tcs_get_earjack_plug(struct cts_device *cts_dev, u8 *isset); +int cts_tcs_set_panel_direction(struct cts_device *cts_dev, u8 direction); +int cts_tcs_get_panel_direction(struct cts_device *cts_dev, u8 *direction); +int cts_tcs_set_game_mode(struct cts_device *cts_dev, u8 enable); +int cts_tcs_get_game_mode(struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_set_proximity_mode(struct cts_device *cts_dev, u8 enable); +void cts_tcs_reinit_fw_status(struct cts_device *cts_dev); + +int cts_tcs_set_product_en(struct cts_device *cts_dev, u8 enable); +int cts_tcs_wait_krang_stop(struct cts_device *cts_dev); + +extern int cts_tcs_set_glove_mode(struct cts_device *cts_dev, u8 enable); +#endif /* _CTS_TCS_H_ */ diff --git a/drivers/input/touchscreen/chipone_tddi/cts_test.c b/drivers/input/touchscreen/chipone_tddi/cts_test.c new file mode 100644 index 000000000000..8f6f3e9935dc --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_test.c @@ -0,0 +1,2927 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define LOG_TAG "Test" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_strerror.h" +#include "cts_test.h" +#include "cts_tcs.h" + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST +/* for ksys_mkdir/sys_mkdir */ +#include +#include +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +const char *cts_test_item_str(int test_item) +{ +#define case_test_item(item) \ + case CTS_TEST_ ## item: return #item "-TEST" + + switch (test_item) { + case_test_item(RESET_PIN); + case_test_item(INT_PIN); + case_test_item(RAWDATA); + case_test_item(NOISE); + case_test_item(OPEN); + case_test_item(SHORT); + case_test_item(COMPENSATE_CAP); + + default: + return "INVALID"; + } +#undef case_test_item +} + +#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 SHORT_COLS_TEST_LOOP (1) +#define SHORT_ROWS_TEST_LOOP (3) + + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +struct cts_fw_short_test_param { + u8 type; + u32 col_pattern[2]; + u32 row_pattern[2]; +}; + +int cts_write_file(struct file *filp, const void *data, size_t size) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): kernel_write is forbiddon with GKI Version!", __func__); + return -EPERM; +#else + loff_t pos; + ssize_t ret; + + pos = filp->f_pos; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(filp, data, size, &pos); +#else + ret = kernel_write(filp, data, size, pos); +#endif + + if (ret >= 0) { + filp->f_pos += ret; + } + + return ret; +#endif +} + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST +/* copied from fs/namei.c */ +long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) +{ + struct dentry *dentry; + struct path path; + int error; + unsigned int lookup_flags = LOOKUP_DIRECTORY; + +retry: + dentry = user_path_create(dfd, pathname, &path, lookup_flags); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + if (!IS_POSIXACL(path.dentry->d_inode)) + mode &= ~current_umask(); + error = security_path_mkdir(&path, dentry, mode); + if (!error) + error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + done_path_create(&path, dentry); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } + return error; +} + +/* Make directory for filepath + * If filepath = "/A/B/C/D.file", it will make dir /A/B/C recursive + * like userspace mkdir -p + */ +int cts_mkdir_for_file(const char *filepath, umode_t mode) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return -EPERM; +#else + char *dirname = NULL; + int dirname_len; + char *s; + int ret; + mm_segment_t fs; + + if (filepath == NULL) { + cts_err("Create dir for file with filepath = NULL"); + return -EINVAL; + } + + if (filepath[0] == '\0' || filepath[0] != '/') { + cts_err("Create dir for file with invalid filepath[0]: %c", filepath[0]); + return -EINVAL; + } + + dirname_len = strrchr(filepath, '/') - filepath; + if (dirname_len == 0) { + cts_warn("Create dir for file '%s' in root dir", filepath); + return 0; + } + + dirname = kstrndup(filepath, dirname_len, GFP_KERNEL); + if (dirname == NULL) { + cts_err("Create dir alloc mem for dirname failed"); + return -ENOMEM; + } + + cts_info("Create dir '%s' for file '%s'", dirname, filepath); + + fs = get_fs(); + set_fs(KERNEL_DS); + + s = dirname + 1; /* Skip leading '/' */ + while (1) { + char c = '\0'; + + /* Bypass leading non-'/'s and then subsequent '/'s */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save current char */ + *s = '\0'; /* and replace it with nul */ + break; + } + ++s; + } + + cts_dbg(" - Create dir '%s'", dirname); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ret = ksys_mkdir(dirname, 0777); +#else + ret = sys_mkdir(dirname, 0777); +#endif + if (ret < 0 && ret != -EEXIST) { + cts_info("Create dir '%s' failed %d(%s)", dirname, ret, + cts_strerror(ret)); + /* Remove any inserted nul from the path */ + *s = c; + break; + } + /* Reset ret to 0 if return -EEXIST */ + ret = 0; + + if (c) { + /* Remove any inserted nul from the path */ + *s = c; + } else { + break; + } + } + + set_fs(fs); + + if (dirname) { + kfree(dirname); + } + + return ret; +#endif +} +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +static struct file *cts_test_data_filp; +int cts_start_dump_test_data_to_file(const char *filepath, bool append_to_file) +{ + int ret; + +#define START_BANNER \ + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + cts_info("Start dump test data to file '%s'", filepath); + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST + ret = cts_mkdir_for_file(filepath, 0777); + if (ret) { + cts_err("Create dir for test data file failed %d", ret); + return ret; + } +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); + ret = -EPERM; + return ret; +#else + cts_test_data_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append_to_file ? O_APPEND : O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_test_data_filp)) { + ret = PTR_ERR(cts_test_data_filp); + cts_test_data_filp = NULL; + cts_err("Open file '%p' for test data failed %d", cts_test_data_filp, ret); + return ret; + } + + cts_write_file(cts_test_data_filp, START_BANNER, strlen(START_BANNER)); + + return 0; +#endif +#undef START_BANNER +} + +void cts_stop_dump_test_data_to_file(void) +{ +#define END_BANNER \ + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" +#ifndef CFG_CTS_FOR_GKI + int r; +#endif + + cts_info("Stop dump test data to file"); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, END_BANNER, strlen(END_BANNER)); +#ifndef CFG_CTS_FOR_GKI + r = filp_close(cts_test_data_filp, NULL); + if (r) { + cts_err("Close test data file failed %d", r); + } +#endif + cts_test_data_filp = NULL; + } else { + cts_warn("Stop dump tsdata to file with filp = NULL"); + } +#undef END_BANNER +} + +static void cts_dump_tsdata(struct cts_device *cts_dev, + const char *desc, const u16 *data, bool to_console) +{ +#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[512]; + 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.cols; r++) { + for (c = 0; c < cts_dev->fwdata.rows; c++) { + u16 val = data[r * cts_dev->fwdata.rows + c]; + + sum += val; + if (val > max) { + max = val; + max_r = c; + max_c = r; + } else if (val < min) { + min = val; + min_r = c; + min_c = r; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count = 0; + count += scnprintf(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); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, " | "); + for (c = 0; c < cts_dev->fwdata.rows; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < cts_dev->fwdata.cols; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.rows; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, data[r * cts_dev->fwdata.rows + c]); + } + if (to_console) { + cts_info("%s", line_buf); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + if (to_console) { + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + + +static void cts_dump_stylus_data(struct cts_device *cts_dev, + const char *desc, const u16 *data, u8 px_rows, u8 px_cols, bool to_console) +{ +#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[512]; + int count = 0; + + max = min = data[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < px_rows; r++) { + for (c = 0; c < px_cols; c++) { + u16 val = data[r * px_cols + c]; + + sum += val; + if (val > max) { + max = val; + max_r = c; + max_c = r; + } else if (val < min) { + min = val; + min_r = c; + min_c = r; + } + } + } + average = sum / (px_rows * px_cols); + + count = 0; + count += scnprintf(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); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, " | "); + for (c = 0; c < px_cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < px_rows; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < px_cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, data[r * px_cols + c]); + } + if (to_console) { + cts_info("%s", line_buf); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + if (to_console) { + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + + +static bool is_invalid_node(u32 *invalid_nodes, u32 num_invalid_nodes, + u16 row, u16 col) +{ + int i; + + for (i = 0; i < num_invalid_nodes; i++) { + if (MAKE_INVALID_NODE(row, col) == invalid_nodes[i]) { + return true; + } + } + + return false; +} + +/* Return number of failed nodes */ +static int validate_tsdata(struct cts_device *cts_dev, const char *desc, u16 *data, + u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int offset = r * cts_dev->fwdata.cols + c; + + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { + continue; + } + + if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || + (max != NULL && data[offset] > max[per_node ? offset : 0])) { + 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, data[offset]); + } + } + } + + 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 +} + +/* Return number of failed nodes */ +static int validate_stylus_pc_data(struct cts_device *cts_dev, const char *desc, u16 *data, + u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; + u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; + u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; + + int r, c; + int failed_cnt = 0; + + cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < pc_rows_used; r++) { + for (c = 0; c < pc_cols_used * pen_freq_num; c++) { + int offset = r * pc_cols_used * pen_freq_num + c; + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { + continue; + } + + if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || + (max != NULL && data[offset] > max[per_node ? offset : 0])) { + 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, data[offset]); + } + } + } + + 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 +} + + +/* Return number of failed nodes */ +static int validate_stylus_pr_data(struct cts_device *cts_dev, const char *desc, u16 *data, + u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; + u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; + u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; + + int r, c; + int failed_cnt = 0; + + cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < pr_rows_used * pen_freq_num; r++) { + for (c = 0; c < pr_cols_used; c++) { + int offset = r * pr_cols_used + c; + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { + continue; + } + + if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || + (max != NULL && data[offset] > max[per_node ? offset : 0])) { + 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, data[offset]); + } + } + } + + 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 +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_test_reset_pin(struct cts_device *cts_dev, struct cts_test_param *param) +{ + ktime_t start_time, end_time, delta_time; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err("Reset-pin test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + cts_info("Reset-Pin test, flags: 0x%08x, drive log file: '%s' buf size: %d", + param->flags, param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto show_test_result; + } + + cts_lock_device(cts_dev); + + cts_plat_set_reset(cts_dev->pdata, 0); + + mdelay(50); + + if (cts_plat_is_normal_mode(cts_dev->pdata)) { + ret = -EIO; + cts_err("Device is alive while reset is low"); + } + cts_plat_set_reset(cts_dev->pdata, 1); + mdelay(120); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { + ret = -EIO; + cts_err("Device is offline while reset is high"); + } + + cts_unlock_device(cts_dev); + cts_start_device(cts_dev); + + if (!cts_dev->rtdata.program_mode) { + cts_set_normal_addr(cts_dev); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret) { + cts_info("Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Reset-Pin test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} +#endif + +int cts_test_int_pin(struct cts_device *cts_dev, struct cts_test_param *param) +{ + ktime_t start_time, end_time, delta_time; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err("Int-pin test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + cts_info("Int-Pin test, flags: 0x%08x, drive log file: '%s' buf size: %d", + param->flags, param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto show_test_result; + } + + cts_lock_device(cts_dev); + + ret = cts_tcs_set_int_test(cts_dev, 1); + if (ret) { + cts_err("Enable Int Test failed"); + goto unlock_device; + } + + ret = cts_tcs_set_int_pin(cts_dev, 1); + if (ret) { + cts_err("Enable Int Test High failed"); + goto exit_int_test; + } + + mdelay(10); + + if (cts_plat_get_int_pin(cts_dev->pdata) == 0) { + cts_err("INT pin state != HIGH"); + ret = -EFAULT; + goto exit_int_test; + } + + ret = cts_tcs_set_int_pin(cts_dev, 0); + if (ret) { + cts_err("Enable Int Test LOW failed"); + goto exit_int_test; + } + + mdelay(10); + + if (cts_plat_get_int_pin(cts_dev->pdata) != 0) { + cts_err("INT pin state != LOW"); + ret = -EFAULT; + goto exit_int_test; + } + +exit_int_test: + { + int r = cts_tcs_set_int_test(cts_dev, 0); + if (r) { + cts_err("Disable Int Test failed %d", r); + } + } + mdelay(10); + +unlock_device: + cts_unlock_device(cts_dev); + cts_start_device(cts_dev); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret) { + cts_info("Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Int-Pin test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +int cts_test_rawdata(struct cts_device *cts_dev, struct cts_test_param *param) +{ + struct cts_rawdata_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool stop_test_if_validate_fail = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + int fail_frame = 0; + u16 *rawdata = NULL; + ktime_t start_time, end_time, delta_time; + int i; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err("Rawdata test with dev or null param"); + return -EINVAL; + } + + if (param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Rawdata test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames <= 0) { + cts_info("Rawdata test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_test_if_validate_fail = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Rawdata test, flags: 0x%08x, frames: %d, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, priv_param->frames, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + rawdata = (u16 *) param->test_data_buf; + } else { + rawdata = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_KRANG_CFG_MODE failed %d", ret); + goto unlock; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_FIN_DBG_MODE); + if (ret) { + cts_err("Set CTS_KRANG_FIN_DBG_MODE failed %d", ret); + goto unlock; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_FIN_DBG_MODE); + if (ret) { + cts_err("Wait CTS_KRANG_FIN_DBG_MODE failed %d", ret); + goto unlock; + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); + goto unlock; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); + goto unlock; + } + + for (frame = 0; frame < priv_param->frames; frame++) { + bool data_valid = false; + + for (i = 0; i < 3; i++) { + ret = cts_polling_test_data(cts_dev, (u8 *)rawdata, + RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + mdelay(30); + } else { + data_valid = true; + break; + } + } + + if (!data_valid) { + ret = -EIO; + break; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Rawdata", rawdata, dump_test_data_to_console); + } + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Rawdata", rawdata, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Rawdata test failed %d", ret); + fail_frame++; + cts_err("Rawdata test has %d nodes failed", ret); + if (stop_test_if_validate_fail) { + break; + } + } + } + + if (dump_test_data_to_user) { + rawdata += num_nodes; + } + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + +unlock: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && rawdata != NULL) { + kfree(rawdata); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) { + cts_info("Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else if (fail_frame > 0) { + cts_info("Rawdata test has %d frame(s) FAIL, ELAPSED TIME: %lldms", + fail_frame, ktime_to_ms(delta_time)); + } else if (fail_frame == 0) { + cts_info("Rawdata test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return (ret ? ret : (fail_frame ? fail_frame : 0)); +} + +int cts_test_noise(struct cts_device *cts_dev, struct cts_test_param *param) +{ + struct cts_noise_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + u16 *buffer = NULL; + int buf_size = 0; + u16 *curr_rawdata = NULL; + u16 *max_rawdata = NULL; + u16 *min_rawdata = NULL; + u16 *noise = NULL; + bool first_frame = true; + ktime_t start_time, end_time, delta_time; + int i; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err("Noise test with dev or null param"); + return -EINVAL; + } + + if (param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Noise test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames < 2) { + cts_err("Noise test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Noise test, flags: 0x%08x, frames: %d, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, priv_param->frames, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + buf_size = (driver_validate_data ? 4 : 1) * tsdata_frame_size; + buffer = (u16 *) kmalloc(buf_size, GFP_KERNEL); + if (buffer == NULL) { + cts_err("Alloc mem for touch data failed"); + ret = -ENOMEM; + goto show_test_result; + } + + curr_rawdata = buffer; + if (driver_validate_data) { + noise = curr_rawdata + 1 * num_nodes; + min_rawdata = curr_rawdata + 2 * num_nodes; + max_rawdata = curr_rawdata + 3 * num_nodes; + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_KRANG_CFG_MODE failed %d", ret); + goto unlock; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_FIN_DBG_MODE); + if (ret) { + cts_err("Set CTS_KRANG_FIN_DBG_MODE failed %d", ret); + goto unlock; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_FIN_DBG_MODE); + if (ret) { + cts_err("Wait CTS_KRANG_FIN_DBG_MODE failed %d", ret); + goto unlock; + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); + goto unlock; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); + goto unlock; + } + + for (frame = 0; frame < priv_param->frames; frame++) { + for (i = 0; i < 3; i++) { + ret = cts_polling_test_data(cts_dev, (u8 *)curr_rawdata, + RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + mdelay(30); + } else { + break; + } + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Noise-rawdata", curr_rawdata, + dump_test_data_to_console); + } + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + frame * 2 * num_nodes, + curr_rawdata, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + } + + if (driver_validate_data) { + if (unlikely(first_frame)) { + memcpy(min_rawdata, curr_rawdata, 2 * num_nodes); + memcpy(max_rawdata, curr_rawdata, 2 * num_nodes); + first_frame = false; + } else { + for (i = 0; i < num_nodes; 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]; + } + } + } + } + } + + if (driver_validate_data) { + for (i = 0; i < num_nodes; i++) { + noise[i] = max_rawdata[i] - min_rawdata[i]; + } + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + (priv_param->frames + 0) * 2 * num_nodes, + noise, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + memcpy(param->test_data_buf + (priv_param->frames + 1) * 2 * num_nodes, + min_rawdata, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + memcpy(param->test_data_buf + (priv_param->frames + 2) * 2 * num_nodes, + max_rawdata, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Noise", noise, + dump_test_data_to_console); + cts_dump_tsdata(cts_dev, "Rawdata MIN", min_rawdata, + dump_test_data_to_console); + cts_dump_tsdata(cts_dev, "Rawdata MAX", max_rawdata, + dump_test_data_to_console); + } + + ret = validate_tsdata(cts_dev, "Noise test", noise, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +disable_get_tsdata: + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + +unlock: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (buffer != NULL) { + kfree(buffer); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Noise test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Noise test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Noise test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +/* Return 0 success + negative value while error occurs + positive value means how many nodes fail */ +int cts_test_open(struct cts_device *cts_dev, struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int ret; + u16 *test_result = NULL; + ktime_t start_time, end_time, delta_time; + u8 old_int_data_method; + u16 old_int_data_types; + + if (cts_dev == NULL || param == NULL) { + cts_err("Open test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + old_int_data_method = cts_dev->fwdata.int_data_method; + old_int_data_types = cts_dev->fwdata.int_data_types; + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Open test, flags: 0x%08x, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + test_result = (u16 *) param->test_data_buf; + } else { + test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate memory for test result faild"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_CFG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_openshort_mode(cts_dev, CTS_TEST_OPEN); + if (ret) { + cts_err("Set test type to OPEN_TEST failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); + if (ret) { + cts_err("wait_to_curr_mode failed %d", ret); + goto unlock_device; + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); + goto unlock_device; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); + goto unlock_device; + } + + ret = cts_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Read test result failed %d", ret); + goto unlock_device; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Open-circuit", test_result, + dump_test_data_to_console); + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Open-circuit", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +unlock_device: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && test_result) { + kfree(test_result); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Open test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Open test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Open test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +/* Return 0 success + negative value while error occurs + positive value means how many nodes fail */ +int cts_test_short(struct cts_device *cts_dev, struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool stop_if_failed = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int loopcnt; + int ret; + u16 *test_result = NULL; + ktime_t start_time, end_time, delta_time; + u8 old_int_data_method; + u16 old_int_data_types; + + if (cts_dev == NULL || param == NULL) { + cts_err("Short test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + old_int_data_method = cts_dev->fwdata.int_data_method; + old_int_data_types = cts_dev->fwdata.int_data_types; + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_if_failed = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Short test, flags: 0x%08x, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + test_result = (u16 *) param->test_data_buf; + } else { + test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate test result buffer failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_free_test_result; + } + + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_CFG_MODE failed %d", ret); + goto unlock_device; + } + + cts_info("Test short to GND"); + ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_UNDEFINED); + if (ret) { + cts_err("Set short test type to SHORT_TO_GND failed %d", ret); + goto unlock_device; + } + ret = cts_tcs_set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + goto unlock_device; + } + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); + if (ret) { + cts_err("wait_to_curr_mode failed %d", ret); + goto unlock_device; + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); + goto unlock_device; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_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); + goto unlock_device; + } + + ret = cts_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Read test result failed %d", ret); + goto unlock_device; + } + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "GND-short", test_result, + dump_test_data_to_console); + } + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "GND-short", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Short to GND test failed %d", ret); + if (stop_if_failed) { + goto stop_dump_test_data_to_file; + } + } + } + if (dump_test_data_to_user) { + test_result += num_nodes; + } + + /* Short between colums */ + cts_info("Test short between columns"); + ret = cts_tcs_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); + goto stop_dump_test_data_to_file; + } + + for (loopcnt = 0; loopcnt < SHORT_COLS_TEST_LOOP; loopcnt++) { + ret = cts_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Read test result failed %d", ret); + goto stop_dump_test_data_to_file; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Col-short", test_result, + dump_test_data_to_console); + } + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Col-short", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Short between columns test failed %d", ret); + if (stop_if_failed) { + goto stop_dump_test_data_to_file; + } + } + } + if (dump_test_data_to_user) { + test_result += num_nodes; + } + } + + /* Short between rows */ + cts_info("Test short between rows"); + ret = cts_tcs_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); + goto stop_dump_test_data_to_file; + } + for (loopcnt = 0; loopcnt < SHORT_ROWS_TEST_LOOP; loopcnt++) { + ret = cts_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); + if (ret) { + cts_err("Read test result failed %d", ret); + goto stop_dump_test_data_to_file; + } + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Row-short", test_result, + dump_test_data_to_console); + } + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Row-short", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Short between rows test failed %d", ret); + if (stop_if_failed) { + goto stop_dump_test_data_to_file; + } + } + } + if (dump_test_data_to_user) { + test_result += num_nodes; + } + } + +stop_dump_test_data_to_file: + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + +unlock_device: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +err_free_test_result: + if (!dump_test_data_to_user && test_result) { + kfree(test_result); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Short test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Short test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Short test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +static int validate_comp_cap(struct cts_device *cts_dev, const char *desc, u8 *cap, + u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + cts_info("Validate %s data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int offset = r * cts_dev->fwdata.cols + c; + + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { + continue; + } + + if ((min != NULL && cap[offset] < min[per_node ? offset : 0]) || + (max != NULL && cap[offset] > max[per_node ? offset : 0])) { + 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, cap[offset]); + } + } + } + + 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 void cts_dump_comp_cap(struct cts_device *cts_dev, u8 *cap, bool to_console) +{ +#define SPLIT_LINE_STR \ + "-----------------------------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%3u " +#define DATA_FORMAT_STR "%4d" + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[512]; + int count; + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.cols; r++) { + for (c = 0; c < cts_dev->fwdata.rows; c++) { + u16 val = cap[r * cts_dev->fwdata.rows + 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 = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + " Compensate Cap MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + min_r, min_c, min, max_r, max_c, max, average); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, " "); + for (c = 0; c < cts_dev->fwdata.rows; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < cts_dev->fwdata.cols; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.rows; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, cap[r * cts_dev->fwdata.rows + c]); + } + if (to_console) { + cts_info("%s", line_buf); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + + if (to_console) { + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +int cts_test_compensate_cap(struct cts_device *cts_dev, + struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + u8 *cap = NULL; + int ret = 0; + ktime_t start_time, end_time, delta_time; + + if (cts_dev == NULL || param == NULL) { + cts_err("Compensate cap test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + tsdata_frame_size = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + if (driver_validate_data) { + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + } + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Compensate cap test, flags: 0x%08x num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + cap = (u8 *) param->test_data_buf; + } else { + cap = (u8 *) kzalloc(tsdata_frame_size, GFP_KERNEL); + if (cap == NULL) { + cts_err("Alloc mem for compensate cap failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_KRANG_CFG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_FIN_DBG_MODE); + if (ret) { + cts_err("Set CTS_KRANG_FIN_DBG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_FIN_DBG_MODE); + if (ret) { + cts_err("Wait CTS_KRANG_FIN_DBG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_top_get_cnegdata(cts_dev, cap, num_nodes, 0); + if (ret) { + cts_err("Get compensate cap failed %d", ret); + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size = num_nodes; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_comp_cap(cts_dev, cap, dump_test_data_to_console); + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + + if (driver_validate_data) { + ret = validate_comp_cap(cts_dev, "Compensate-Cap", cap, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +unlock_device: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && cap) { + kfree(cap); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Compensate-Cap test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Compensate-Cap test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Compensate-Cap test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +int cts_test_stylus_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param) +{ + struct cts_rawdata_test_priv_param *priv_param; + bool stop_test_if_validate_fail = false; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + u16 *stylus_rawdata = NULL; + u16 data_type; + u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; + u8 cascade_num = cts_dev->fwdata.cascade_num; + u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; + u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; + u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; + u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; + u8 pr_rows = cts_dev->fwdata.pr_rows; + u8 pr_cols = cts_dev->fwdata.pr_cols; + u8 pc_rows = cts_dev->fwdata.pc_rows; + u8 pc_cols = cts_dev->fwdata.pc_cols; + int pr_data_index = pc_cols_used * pc_rows_used * pen_freq_num; + int fail_frame = 0; + int i, ret = 0; + ktime_t start_time, end_time, delta_time; + + if (cts_dev == NULL || param == NULL) { + cts_err("stylus rawdata test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + if (param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Stylus rawdata test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames <= 0) { + cts_info("Stylus rawdata test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + num_nodes = (pr_rows_used * pr_cols_used + pc_cols_used * pc_rows_used) + * pen_freq_num; + tsdata_frame_size = (pr_rows * pr_cols + pc_rows * pc_cols) + * pen_freq_num * sizeof(u16) * cascade_num; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + if (driver_validate_data) { + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + } + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_test_if_validate_fail = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Stylus rawdata test, flags: 0x%08x num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + stylus_rawdata = (u16 *) param->test_data_buf; + } else { + stylus_rawdata = (u16 *) kzalloc(tsdata_frame_size, GFP_KERNEL); + if (stylus_rawdata == NULL) { + cts_err("Alloc mem for stylus_rawdata failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_CFG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_STY_DBG_MODE); + if (ret) { + cts_err("Set krang CTS_KRANG_STY_DBG_MODE failed"); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_STY_DBG_MODE); + if (ret) { + cts_err("Wait krang CTS_KRANG_STY_DBG_MODE failed"); + goto unlock_device; + } + + ret = cts_tcs_set_pwr_mode(cts_dev, CTS_PWR_MODE_ACTIVE); + if (ret) { + cts_err("Set PWR_MODE_ACTIVE failed"); + goto unlock_device; + } + + data_type = INT_DATA_TYPE_RAWDATA | BIT(14); + ret = cts_set_int_data_types(cts_dev, data_type); + if (ret) { + cts_err("Set data_type '0x%04x' failed", data_type); + goto unlock_device; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set data_method polling failed"); + goto unlock_device; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + for (frame = 0; frame < priv_param->frames; frame++) { + for (i = 0; i < 3; i++) { + ret = cts_polling_test_data(cts_dev, (u8 *)stylus_rawdata, + tsdata_frame_size, data_type); + if (ret < 0) { + cts_err("Get stylus rawdata failed: %d", ret); + mdelay(30); + } else { + break; + } + } + + if (i >= 3) { + cts_err("Read stylus rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pc", stylus_rawdata, + pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); + cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pr", stylus_rawdata + pr_data_index, + pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); + } + + if (driver_validate_data) { + ret = validate_stylus_pc_data(cts_dev, "Stylus-Rawdata-pc", + stylus_rawdata, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + ret += validate_stylus_pr_data(cts_dev, "Stylus-Rawdata-pr", + stylus_rawdata + pr_data_index, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Stylus rawdata test failed %d", ret); + fail_frame++; + cts_err("Stylus rawdata test has %d nodes failed", ret); + if (stop_test_if_validate_fail) { + break; + } + } + } + } + +disable_get_tsdata: + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + +unlock_device: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && stylus_rawdata) { + kfree(stylus_rawdata); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Stylus-rawdata test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Stylus-rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Stylus-Noise test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + + +int cts_test_stylus_noise(struct cts_device *cts_dev, + struct cts_test_param *param) +{ + struct cts_noise_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + bool first_frame = true; + u16 *stylus_rawdata = NULL; + u16 *max_stylus = NULL; + u16 *min_stylus = NULL; + u16 *noise_stylus = NULL; + u16 data_type; + u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; + u8 cascade_num = cts_dev->fwdata.cascade_num; + u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; + u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; + u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; + u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; + u8 pr_rows = cts_dev->fwdata.pr_rows; + u8 pr_cols = cts_dev->fwdata.pr_cols; + u8 pc_rows = cts_dev->fwdata.pc_rows; + u8 pc_cols = cts_dev->fwdata.pc_cols; + int pr_data_index = pc_cols_used * pc_rows_used * pen_freq_num; + int i, ret = 0; + ktime_t start_time, end_time, delta_time; + + if (cts_dev == NULL || param == NULL) { + cts_err("stylus noise test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + if (param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Stylus noise test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames <= 0) { + cts_info("Stylus noise test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + cts_info("pc_rows=%d", pc_rows); + cts_info("pc_cols=%d", pc_cols); + cts_info("pr_rows=%d", pr_rows); + cts_info("pr_cols=%d", pr_cols); + + cts_info("pc_rows_used=%d", pc_rows_used); + cts_info("pc_cols_used=%d", pc_cols_used); + cts_info("pr_rows_used=%d", pr_rows_used); + cts_info("pr_cols_used=%d", pr_cols_used); + + cts_info("pen_freq_num=%d", pen_freq_num); + cts_info("cascade_num=%d", cascade_num); + + num_nodes = (pr_rows_used * pr_cols_used + pc_cols_used * pc_rows_used) * pen_freq_num; + tsdata_frame_size = (pr_rows * pr_cols + pc_rows * pc_cols) * pen_freq_num * sizeof(u16) * cascade_num; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + if (driver_validate_data) { + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + } + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + + cts_info("Stylus rawdata test, flags: 0x%08x num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + stylus_rawdata = (u16 *) param->test_data_buf; + } else { + stylus_rawdata = (u16 *) kzalloc(tsdata_frame_size * 4, GFP_KERNEL); + if (stylus_rawdata == NULL) { + cts_err("Alloc mem for stylus_rawdata failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + max_stylus = stylus_rawdata + 1 * tsdata_frame_size/sizeof(u16); + min_stylus = stylus_rawdata + 2 * tsdata_frame_size/sizeof(u16); + noise_stylus = stylus_rawdata + 3 * tsdata_frame_size/sizeof(u16); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_CFG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_STY_DBG_MODE); + if (ret) { + cts_err("Set krang CTS_KRANG_STY_DBG_MODE failed"); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_STY_DBG_MODE); + if (ret) { + cts_err("Wait krang CTS_KRANG_STY_DBG_MODE failed"); + goto unlock_device; + } + + ret = cts_tcs_set_pwr_mode(cts_dev, CTS_PWR_MODE_ACTIVE); + if (ret) { + cts_err("Set PWR_MODE_ACTIVE failed"); + goto unlock_device; + } + + data_type = INT_DATA_TYPE_RAWDATA | BIT(14); + ret = cts_set_int_data_types(cts_dev, data_type); + if (ret) { + cts_err("Set data_type '0x%04x' failed", data_type); + goto unlock_device; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set data_method polling failed"); + goto unlock_device; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + for (frame = 0; frame < priv_param->frames; frame++) { + for (i = 0; i < 3; i++) { + ret = cts_polling_test_data(cts_dev, (u8 *)stylus_rawdata, + tsdata_frame_size, data_type); + if (ret < 0) { + cts_err("Get stylus rawdata failed: %d", ret); + mdelay(30); + } else { + break; + } + } + + if (i >= 3) { + cts_err("Read stylus rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pc", stylus_rawdata, + pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); + cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pr", stylus_rawdata + pr_data_index, + pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); + } + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + frame * 2 * num_nodes, + stylus_rawdata, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + } + + if (driver_validate_data) { + if (unlikely(first_frame)) { + memcpy(min_stylus, stylus_rawdata, 2 * num_nodes); + memcpy(max_stylus, stylus_rawdata, 2 * num_nodes); + first_frame = false; + } else { + for (i = 0; i < num_nodes; i++) { + if (stylus_rawdata[i] > max_stylus[i]) { + max_stylus[i] = stylus_rawdata[i]; + } + if (stylus_rawdata[i] < min_stylus[i]) { + min_stylus[i] = stylus_rawdata[i]; + } + } + } + } + } + + if (driver_validate_data) { + for (i = 0; i < num_nodes; i++) { + noise_stylus[i] = max_stylus[i] - min_stylus[i]; + } + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + (priv_param->frames + 0) * 2 * num_nodes, + noise_stylus, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + memcpy(param->test_data_buf + (priv_param->frames + 1) * 2 * num_nodes, + min_stylus, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + memcpy(param->test_data_buf + (priv_param->frames + 2) * 2 * num_nodes, + max_stylus, 2 * num_nodes); + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_stylus_data(cts_dev, "Stylus-Noise-pc", noise_stylus, + pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); + cts_dump_stylus_data(cts_dev, "Stylus-Noise-pr", noise_stylus + pr_data_index, + pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); + + cts_dump_stylus_data(cts_dev, "Stylus-MIN-pc", min_stylus, + pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); + cts_dump_stylus_data(cts_dev, "Stylus-MIN-pr", min_stylus + pr_data_index, + pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); + + cts_dump_stylus_data(cts_dev, "Stylus-MAX-pc", max_stylus, + pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); + cts_dump_stylus_data(cts_dev, "Stylus-MAX-pr", max_stylus + pr_data_index, + pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); + } + + ret = validate_stylus_pc_data(cts_dev, "Stylus-Noise-pc", + noise_stylus, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + ret += validate_stylus_pr_data(cts_dev, "Stylus-Noise-pr", + noise_stylus + pr_data_index, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +disable_get_tsdata: + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + +unlock_device: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && stylus_rawdata) { + kfree(stylus_rawdata); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Stylus-Noise test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Stylus-Noise test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Stylus-Noise test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +int cts_test_stylus_mnt_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param) +{ + struct cts_rawdata_test_priv_param *priv_param; + bool stop_test_if_validate_fail = false; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + u16 *stylus_rawdata = NULL; + u16 data_type; + u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; + u8 cascade_num = cts_dev->fwdata.cascade_num; + u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; + u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; + u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; + u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; + u8 pr_rows = cts_dev->fwdata.pr_rows; + u8 pr_cols = cts_dev->fwdata.pr_cols; + u8 pc_rows = cts_dev->fwdata.pc_rows; + u8 pc_cols = cts_dev->fwdata.pc_cols; + int fail_frame = 0; + int i, ret = 0; + ktime_t start_time, end_time, delta_time; + + if (cts_dev == NULL || param == NULL) { + cts_err("stylus mnt rawdata test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + if (param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Stylus mnt rawdata test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames <= 0) { + cts_info("Stylus mnt rawdata test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + num_nodes = (pr_rows_used * pr_cols_used + pc_cols_used * pc_rows_used) * cascade_num; + tsdata_frame_size = (pr_rows * pr_cols + pc_rows * pc_cols) * pen_freq_num * sizeof(u16) * cascade_num; + + driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + if (driver_validate_data) { + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + } + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_test_if_validate_fail = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Stylus mnt rawdata test, flags: 0x%08x num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + stylus_rawdata = (u16 *) param->test_data_buf; + } else { + stylus_rawdata = (u16 *) kzalloc(tsdata_frame_size, GFP_KERNEL); + if (stylus_rawdata == NULL) { + cts_err("Alloc mem for stylus_rawdata failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + cts_lock_device(cts_dev); + + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); + if (ret) { + cts_err("Wait CTS_CFG_MODE failed %d", ret); + goto unlock_device; + } + + ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_MNT_DBG_MODE); + if (ret) { + cts_err("Set krang CTS_KRANG_MNT_DBG_MODE failed"); + goto unlock_device; + } + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_MNT_DBG_MODE); + if (ret) { + cts_err("Wait krang CTS_KRANG_MNT_DBG_MODE failed"); + goto unlock_device; + } + + ret = cts_tcs_set_pwr_mode(cts_dev, CTS_PWR_MODE_MNT_DBG); + if (ret) { + cts_err("Set CTS_PWR_MODE_MNT_DBG failed"); + goto unlock_device; + } + + data_type = INT_DATA_TYPE_RAWDATA | BIT(14); + ret = cts_set_int_data_types(cts_dev, data_type); + if (ret) { + cts_err("Set data_type '0x%04x' failed", data_type); + goto unlock_device; + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + if (ret) { + cts_err("Set data_method polling failed"); + goto unlock_device; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + for (frame = 0; frame < priv_param->frames; frame++) { + for (i = 0; i < 3; i++) { + ret = cts_polling_test_data(cts_dev, (u8 *)stylus_rawdata, + tsdata_frame_size, data_type); + if (ret < 0) { + cts_err("Get stylus mnt rawdata failed: %d", ret); + mdelay(30); + } else { + break; + } + } + + if (i >= 3) { + cts_err("Read stylus mnt rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += 2 * num_nodes; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_stylus_data(cts_dev, "Stylus-mnt-Rawdata-pc", stylus_rawdata, + pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); + } + + if (driver_validate_data) { + /* Only validate pc data */ + ret = validate_stylus_pc_data(cts_dev, "Stylus-mnt-Rawdata-pc", + stylus_rawdata, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Stylus mnt rawdata test failed %d", ret); + fail_frame++; + cts_err("Stylus mnt rawdata test has %d nodes failed", ret); + if (stop_test_if_validate_fail) { + break; + } + } + } + } + +disable_get_tsdata: + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + +unlock_device: + cts_reset_device(cts_dev); + + ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } + + ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); + if (ret) { + cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); + } + + ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + if (ret) { + cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); + } + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && stylus_rawdata) { + kfree(stylus_rawdata); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Stylus-mnt-rawdata test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Stylus-mnt-rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Stylus-mnt-rawdata test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + diff --git a/drivers/input/touchscreen/chipone_tddi/cts_test.h b/drivers/input/touchscreen/chipone_tddi/cts_test.h new file mode 100644 index 000000000000..b4b6ce4ffb5c --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_test.h @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CTS_TEST_H +#define CTS_TEST_H + +struct cts_device; + +#define CTS_TEST_FLAG_RESET_BEFORE_TEST (1u << 0) +#define CTS_TEST_FLAG_RESET_AFTER_TEST (1u << 1) +#define CTS_TEST_FLAG_DISPLAY_ON (1u << 2) +#define CTS_TEST_FLAG_DISABLE_GAS (1u << 3) +#define CTS_TEST_FLAG_DISABLE_LINESHIFT (1u << 4) + +#define CTS_TEST_FLAG_VALIDATE_DATA (1u << 8) +#define CTS_TEST_FLAG_VALIDATE_PER_NODE (1u << 9) +#define CTS_TEST_FLAG_VALIDATE_MIN (1u << 10) +#define CTS_TEST_FLAG_VALIDATE_MAX (1u << 11) +#define CTS_TEST_FLAG_VALIDATE_SKIP_INVALID_NODE (1u << 12) +#define CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED (1u << 13) + +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE (1u << 16) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE (1u << 17) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE (1u << 18) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND (1u << 19) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_CSV (1u << 20) + +#define CTS_TEST_FLAG_DRIVER_LOG_TO_USERSPACE (1u << 24) +#define CTS_TEST_FLAG_DRIVER_LOG_TO_FILE (1u << 25) +#define CTS_TEST_FLAG_DRIVER_LOG_TO_FILE_APPEND (1u << 26) + +#define MAKE_INVALID_NODE(r, c) (((c) << 16) | (r)) +#define INVALID_NODE_ROW(node) ((u16)(node)) +#define INVALID_NODE_COL(node) ((u16)((node) >> 16)) + +enum cts_test_item { + CTS_TEST_RESET_PIN = 1, + CTS_TEST_INT_PIN, + CTS_TEST_RAWDATA, + CTS_TEST_NOISE, + CTS_TEST_OPEN, + CTS_TEST_SHORT, + CTS_TEST_COMPENSATE_CAP, + CTS_TEST_STYLUS_RAWDATA, + CTS_TEST_STYLUS_NOISE, +}; + +struct cts_test_param { + int test_item; + + __u32 flags; + + __u32 num_invalid_node; + __u32 *invalid_nodes; + int *min; + int *max; + + int *test_result; + s64 *elapsed_time_ms; + + void *test_data_buf; + int test_data_buf_size; + int *test_data_wr_size; + const char *test_data_filepath; + + int driver_log_level; + char *driver_log_buf; + int driver_log_buf_size; + int *driver_log_wr_size; + const char *driver_log_filepath; + + void *priv_param; + int priv_param_size; +}; + +struct cts_rawdata_test_priv_param { + __u32 frames; + //__u8 work_mode; +}; + +struct cts_noise_test_priv_param { + __u32 frames; + //__u8 work_mode; +}; + +extern const char *cts_test_item_str(int test_item); +extern int cts_write_file(struct file *filp, const void *data, size_t size); +extern int cts_mkdir_for_file(const char *filepath, umode_t mode); + +extern int cts_start_dump_test_data_to_file(const char *filepath, + bool append_to_file); +extern void cts_stop_dump_test_data_to_file(void); + +extern int cts_test_reset_pin(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_int_pin(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_noise(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_open(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_short(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_compensate_cap(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_stylus_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_stylus_noise(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_stylus_mnt_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param); +#endif /* CTS_TEST_H */ diff --git a/drivers/input/touchscreen/chipone_tddi/cts_tool.c b/drivers/input/touchscreen/chipone_tddi/cts_tool.c new file mode 100644 index 000000000000..355c410f81cb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi/cts_tool.c @@ -0,0 +1,1207 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#define LOG_TAG "Tool" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_test.h" +#include "cts_tcs.h" + +#ifdef CONFIG_CTS_LEGACY_TOOL + +#define CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ 1024 +#define CTS_TOOL_IOCTL_TCS_RW_OPFLAG_READ 1 +#define CTS_TOOL_IOCTL_TCS_RW_OPFLAG_WRITE 2 + +#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]; +}; +struct cts_ioctl_tcs_rw_data { + u32 opflags; + u32 txlen; + u32 rxlen; + u8 __user *txbuf; + u8 __user *rxbuf; +}; +#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_GET_INT_DATAS = 36, + + 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, + CTS_TOOL_CMD_SET_INT_DATA_TYPE_AND_METHOD = 31, + +}; + +struct cts_test_ioctl_data { + __u32 ntests; + struct cts_test_param __user *tests; +}; + + +#define CTS_TOOL_IOCTL_GET_DRIVER_VERSION _IOR('C', 0x00, u32 *) +#define CTS_TOOL_IOCTL_GET_DEVICE_TYPE _IOR('C', 0x01, u32 *) +#define CTS_TOOL_IOCTL_GET_FW_VERSION _IOR('C', 0x02, u16 *) +#define CTS_TOOL_IOCTL_GET_RESOLUTION _IOR('C', 0x03, u32 *) /* X in LSW, Y in MSW */ +#define CTS_TOOL_IOCTL_GET_ROW_COL _IOR('C', 0x04, u16 *) /* row in LSW, col in MSW */ +#define CTS_TOOL_IOCTL_GET_MODULE_ID _IOWR('C', 0x05, u32 *) + +#define CTS_TOOL_IOCTL_TEST _IOWR('C', 0x10, struct cts_test_ioctl_data *) +#define CTS_TOOL_IOCTL_RDWR_REG _IOWR('C', 0x20, struct cts_rdwr_reg_ioctl_data *) +#define CTS_TOOL_IOCTL_UPGRADE_FW _IOWR('C', 0x21, struct cts_upgrade_fw_ioctl_data *) + +#define CTS_TOOL_IOCTL_TCS_RW _IOWR('C', 0x30, struct cts_ioctl_tcs_rw_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_dbg("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_tcs_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_tcs_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_tcs_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); + 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_err("Get %s data row: %u col: %u len: %u, source: %x", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", + cmd->addr[1], cmd->addr[0], cmd->data_len, (cmd->data[1] & 0xf0)); + + if (cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA) { + ret = cts_tcs_tool_get_rawdata(cts_dev, (u8 *) cmd->data, (cmd->data[1] & 0xf0) << 8); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_DIFF_DATA) { + ret = cts_tcs_tool_get_manual_diff(cts_dev, (u8 *) cmd->data, (cmd->data[1] & 0xf0) << 8); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_BASE_DATA) { + ret = cts_tcs_tool_get_basedata(cts_dev, (u8 *) cmd->data, (cmd->data[1] & 0xf0) << 8); + } + 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_GET_INT_DATAS: + memcpy((u8 *)cmd->data, cts_dev->int_data, cts_dev->fwdata.int_data_size); + cmd->data_len = cts_dev->fwdata.int_data_size; + 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); + 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"); + 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_err("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, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), 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 (count > CTS_TOOL_CMD_HEADER_LENGTH) { + ret = copy_from_user(cmd->data, buffer + CTS_TOOL_CMD_HEADER_LENGTH, + count - CTS_TOOL_CMD_HEADER_LENGTH); + if (ret) { + cts_err("Copy command payload from user buffer len %u failed %d", + cmd->data_len, ret); + 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_tcs_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_tcs_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_tcs_write_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD: + case CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD: + cts_info("Do nothing"); + break; + case CTS_TOOL_CMD_SET_INT_DATA_TYPE_AND_METHOD: + cts_set_int_data_types(cts_dev, cmd->data[0] | (cmd->data[1] << 8)); + cts_set_int_data_method(cts_dev, cmd->data[2]); + break; + case CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM: + 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; + } + + ret = cts_update_firmware_from_file(cts_dev, + cts_tool_firmware_filepath); + 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_ioctl_tcs_rw(struct cts_device *cts_dev, + struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + struct cts_ioctl_tcs_rw_data rwdata; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + + if (copy_from_user(&rwdata, + (struct cts_ioctl_tcs_rw_data __user *)arg, + sizeof(rwdata))) { + cts_err("Copy ioctl tcs rw arg to kernel failed"); + return -EFAULT; + } + + if ((rwdata.opflags != CTS_TOOL_IOCTL_TCS_RW_OPFLAG_READ) && + (rwdata.opflags != CTS_TOOL_IOCTL_TCS_RW_OPFLAG_WRITE)) { + cts_err("ioctl tcs rw with invalid opflags %u", rwdata.opflags); + return -EINVAL; + } + + if (rwdata.txlen > CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ) { + cts_err("Send too long: %d", rwdata.txlen); + return -EINVAL; + } + + if (rwdata.rxlen > CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ) { + cts_err("Recv too long: %d", rwdata.rxlen); + return -EINVAL; + } + + if (!rwdata.txbuf || !rwdata.rxbuf) { + cts_err("Txbuf of Rxbuf is NULL!"); + return -EINVAL; + } + + txbuf = memdup_user(rwdata.txbuf, rwdata.txlen); + if (IS_ERR(txbuf)) { + ret = PTR_ERR(txbuf); + cts_err("Memdup txbuf failed %d", ret); + txbuf = NULL; + goto free_buf; + } + + rxbuf = memdup_user(rwdata.rxbuf, rwdata.rxlen); + if (IS_ERR(rxbuf)) { + ret = PTR_ERR(rxbuf); + cts_err("Memdup rxbuf failed %d", ret); + rxbuf = NULL; + goto free_buf; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_tool_xtrans(cts_dev, txbuf, rwdata.txlen, rxbuf, rwdata.rxlen); + cts_unlock_device(cts_dev); + if (ret < 0) { + cts_err("Trans failed %d", ret); + goto free_buf; + } + + ret = copy_to_user(rwdata.rxbuf, rxbuf, rwdata.rxlen); + if (ret < 0) { + cts_err("Copy to user failed %d", ret); + goto free_buf; + } + + ret = 0; + +free_buf: + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + + return ret; +} + +static int cts_ioctl_test(struct cts_device *cts_dev, + u32 ntests, struct cts_test_param *tests) +{ + u32 num_nodes = 0; + int i, ret = 0; + + cts_info("ioctl test total %u items", ntests); + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + + for (i = 0; i < ntests; i++) { + bool validate_data = false; + bool validate_data_per_node = false; + bool validate_min = false; + bool validate_max = false; + bool skip_invalid_node = false; + bool stop_test_if_validate_fail = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_file = false; + bool dump_test_data_to_file_append = false; + bool driver_log_to_user = false; + bool driver_log_to_file = false; + bool driver_log_to_file_append = false; + u32 __user *user_min_threshold = NULL; + u32 __user *user_max_threshold = NULL; + u32 __user *user_invalid_nodes = NULL; + int __user *user_test_result = NULL; + void __user *user_test_data = NULL; + int __user *user_test_data_wr_size = NULL; + const char __user *user_test_data_filepath = NULL; + void __user *user_driver_log_buf = NULL; + int __user *user_driver_log_wr_size = NULL; + const char __user *user_driver_log_filepath = NULL; + void __user *user_priv_param = NULL; + int test_result = 0; + int test_data_wr_size = 0; + int driver_log_wr_size = 0; + + cts_info("ioctl test item %d: %d(%s) flags: %08x priv param size: %d", + i, tests[i].test_item, cts_test_item_str(tests[i].test_item), + tests[i].flags, tests[i].priv_param_size); + /* + * Validate arguement + */ + validate_data = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + validate_min = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_MIN); + validate_max = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_MAX); + skip_invalid_node = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_SKIP_INVALID_NODE); + stop_test_if_validate_fail = + !!(tests[i].flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + dump_test_data_to_user = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_file = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + dump_test_data_to_file_append = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND); + driver_log_to_user = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_USERSPACE); + driver_log_to_file = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_FILE); + driver_log_to_file_append = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_FILE_APPEND); + + if (tests[i].test_result == NULL) { + cts_err("Result pointer = NULL"); + return -EFAULT; + } + + if (validate_data) { + cts_info(" - Flag: Validate data"); + + if (validate_data_per_node) { + cts_info(" - Flag: Validate data per-node"); + } + if (validate_min) { + cts_info(" - Flag: Validate min threshold"); + } + if (validate_max) { + cts_info(" - Flag: Validate max threshold"); + } + if (stop_test_if_validate_fail) { + cts_info(" - Flag: Stop test if validate fail"); + } + + if (validate_min && tests[i].min == NULL) { + cts_err("Min threshold pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (validate_max && tests[i].max == NULL) { + cts_err("Max threshold pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (skip_invalid_node) { + cts_info(" - Flag: Skip invalid node"); + + if (tests[i].num_invalid_node == 0 || + tests[i].num_invalid_node >= num_nodes) { + cts_err("Num invalid node %u out of range[0, %u]", + tests[i].num_invalid_node, num_nodes); + ret = -EINVAL; + goto store_result; + } + + if (tests[i].invalid_nodes == NULL) { + cts_err("Invalid nodes pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + } + } + + if (dump_test_data_to_console) { + cts_info(" - Flag: Dump test data to console"); + } + + if (dump_test_data_to_user) { + cts_info(" - Flag: Dump test data to user, size: %d", + tests[i].test_data_buf_size); + + if (tests[i].test_data_buf == NULL) { + cts_err("Test data pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].test_data_wr_size == NULL) { + cts_err("Test data write size pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].test_data_buf_size < num_nodes) { + cts_err("Test data size %d too small < %u", + tests[i].test_data_buf_size, num_nodes); + ret = -EINVAL; + goto store_result; + } + } + + if (dump_test_data_to_file) { + cts_info(" - Flag: Dump test data to file%s", + dump_test_data_to_file_append ? "[Append]" : ""); + + if (tests[i].test_data_filepath == NULL) { + cts_err("Test data filepath = NULL"); + ret = -EINVAL; + goto store_result; + } + } + + if (driver_log_to_user) { + cts_info(" - Flag: Dump driver log to user, size: %d", + tests[i].driver_log_buf_size); + + if (tests[i].driver_log_buf == NULL) { + cts_err("Driver log buf pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].driver_log_wr_size == NULL) { + cts_err("Driver log write size pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].driver_log_buf_size < 1024) { + cts_err("Driver log buf size %d too small < 1024", + tests[i].test_data_buf_size); + ret = -EINVAL; + goto store_result; + } + } + + if (driver_log_to_file) { + cts_info(" - Flag: Dump driver log to file %s", + driver_log_to_file_append ? "[Append]" : ""); + + if (tests[i].driver_log_filepath == NULL) { + cts_err("Driver log filepath = NULL"); + ret = -EINVAL; + goto store_result; + } + } + + /* + * Dump input parameter from user, + * Aallocate memory for output, + * Replace __user pointer with kernel pointer. + */ + user_test_result = (int __user *)tests[i].test_result; + tests[i].test_result = &test_result; + + if (validate_data) { + int num_threshold = validate_data_per_node ? num_nodes : 1; + int threshold_size = num_threshold * sizeof(tests[i].min[0]); + + if (validate_min) { + user_min_threshold = (int __user *)tests[i].min; + tests[i].min = memdup_user(user_min_threshold, threshold_size); + if (IS_ERR(tests[i].min)) { + ret = PTR_ERR(tests[i].min); + tests[i].min = NULL; + cts_err("Memdup min threshold from user failed %d", ret); + goto store_result; + } + } else { + tests[i].min = NULL; + } + if (validate_max) { + user_max_threshold = (int __user *)tests[i].max; + tests[i].max = memdup_user(user_max_threshold, threshold_size); + if (IS_ERR(tests[i].max)) { + ret = PTR_ERR(tests[i].max); + tests[i].max = NULL; + cts_err("Memdup max threshold from user failed %d", ret); + goto store_result; + } + } else { + tests[i].max = NULL; + } + if (skip_invalid_node) { + user_invalid_nodes = (u32 __user *)tests[i].invalid_nodes; + tests[i].invalid_nodes = memdup_user(user_invalid_nodes, + tests[i].num_invalid_node * sizeof(tests[i].invalid_nodes[0])); + if (IS_ERR(tests[i].invalid_nodes)) { + ret = PTR_ERR(tests[i].invalid_nodes); + tests[i].invalid_nodes = NULL; + cts_err("Memdup invalid node from user failed %d", ret); + goto store_result; + } + } + } + + if (dump_test_data_to_user) { + user_test_data = (void __user *)tests[i].test_data_buf; + tests[i].test_data_buf = vmalloc(tests[i].test_data_buf_size); + if (tests[i].test_data_buf == NULL) { + ret = -ENOMEM; + cts_err("Alloc test data mem failed"); + goto store_result; + } + user_test_data_wr_size = (int __user *)tests[i].test_data_wr_size; + tests[i].test_data_wr_size = &test_data_wr_size; + } + + if (dump_test_data_to_file) { +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): strndup_user is forbiddon with GKI Version!", __func__); +#else + user_test_data_filepath = (const char __user *)tests[i].test_data_filepath; + tests[i].test_data_filepath = strndup_user(user_test_data_filepath, PATH_MAX); + if (tests[i].test_data_filepath == NULL) { + cts_err("Strdup test data filepath failed"); + goto store_result; + } +#endif + } + + if (driver_log_to_user) { + user_driver_log_buf = (void __user *)tests[i].driver_log_buf; + tests[i].driver_log_buf = kmalloc(tests[i].driver_log_buf_size, GFP_KERNEL); + if (tests[i].driver_log_buf == NULL) { + ret = -ENOMEM; + cts_err("Alloc driver log mem failed"); + goto store_result; + } + user_driver_log_wr_size = (int __user *)tests[i].driver_log_wr_size; + tests[i].driver_log_wr_size = &driver_log_wr_size; + } + + if (driver_log_to_file) { +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): strndup_user is forbiddon with GKI Version!", __func__); +#else + user_driver_log_filepath = (const char __user *)tests[i].driver_log_filepath; + tests[i].driver_log_filepath = strndup_user(user_driver_log_filepath, PATH_MAX); + if (tests[i].driver_log_filepath == NULL) { + cts_err("Strdup driver log filepath failed"); + goto store_result; + } +#endif + cts_info("Log driver log to file '%s'", tests[i].driver_log_filepath); + } + + if (driver_log_to_file || driver_log_to_user) { + ret = cts_start_driver_log_redirect( + tests[i].driver_log_filepath, driver_log_to_file_append, + tests[i].driver_log_buf, tests[i].driver_log_buf_size, + tests[i].driver_log_level); + if (ret) { + cts_err("Start driver log redirect failed %d", ret); + goto store_result; + } + } + + if (tests[i].priv_param_size && tests[i].priv_param) { + user_priv_param = (void __user *)tests[i].priv_param; + tests[i].priv_param = memdup_user(user_priv_param, tests[i].priv_param_size); + if (IS_ERR(tests[i].priv_param)) { + ret = PTR_ERR(tests[i].priv_param); + tests[i].priv_param = NULL; + cts_err("Memdup priv param from user failed %d", ret); + goto store_result; + } + } + + switch (tests[i].test_item) { + case CTS_TEST_RESET_PIN: + ret = cts_test_reset_pin(cts_dev, &tests[i]); + break; + case CTS_TEST_INT_PIN: + ret = cts_test_int_pin(cts_dev, &tests[i]); + break; + case CTS_TEST_RAWDATA: + ret = cts_test_rawdata(cts_dev, &tests[i]); + break; + case CTS_TEST_NOISE: + ret = cts_test_noise(cts_dev, &tests[i]); + break; + case CTS_TEST_OPEN: + ret = cts_test_open(cts_dev, &tests[i]); + break; + case CTS_TEST_SHORT: + ret = cts_test_short(cts_dev, &tests[i]); + break; + case CTS_TEST_COMPENSATE_CAP: + ret = cts_test_compensate_cap(cts_dev, &tests[i]); + break; + case CTS_TEST_STYLUS_RAWDATA: + ret = cts_test_stylus_rawdata(cts_dev, &tests[i]); + break; + case CTS_TEST_STYLUS_NOISE: + ret = cts_test_stylus_noise(cts_dev, &tests[i]); + break; + default: + ret = ENOTSUPP; + cts_err("Un-supported test item"); + break; + } + +/* + * Copy result and test data back to userspace. + */ +store_result: + if (dump_test_data_to_user) { + if (user_test_data != NULL && test_data_wr_size > 0) { + cts_info("Copy test data to user, size: %d", test_data_wr_size); + if (copy_to_user(user_test_data, tests[i].test_data_buf, + test_data_wr_size)) { + cts_err("Copy test data to user failed"); + test_data_wr_size = 0; + // Skip this error + } + } + + if (user_test_data_wr_size != NULL) { + put_user(test_data_wr_size, user_test_data_wr_size); + } + } + + if (driver_log_to_user) { + driver_log_wr_size = cts_get_driver_log_redirect_size(); + if (user_driver_log_buf != NULL && driver_log_wr_size > 0) { + cts_info("Copy driver log to user, size: %d", driver_log_wr_size); + if (copy_to_user(user_driver_log_buf, tests[i].driver_log_buf, + driver_log_wr_size)) { + cts_err("Copy driver log to user failed"); + driver_log_wr_size = 0; + // Skip this error + } + } + + if (user_driver_log_wr_size != NULL) { + put_user(driver_log_wr_size, user_driver_log_wr_size); + } + } + + if (user_test_result != NULL) { + put_user(ret, user_test_result); + } else if (tests[i].test_result != NULL) { + put_user(ret, tests[i].test_result); + } + + /* + * Free memory + */ + if (dump_test_data_to_user) { + if (user_test_data != NULL && tests[i].test_data_buf != NULL) { + vfree(tests[i].test_data_buf); + } + } + if (validate_data) { + if (validate_min && user_min_threshold != NULL && tests[i].min != NULL) { + kfree(tests[i].min); + } + if (validate_max && user_max_threshold != NULL && tests[i].max != NULL) { + kfree(tests[i].max); + } + if (skip_invalid_node) { + if (user_invalid_nodes != NULL && tests[i].invalid_nodes != NULL) { + kfree(tests[i].invalid_nodes); + } + } + } + + if (dump_test_data_to_file && + user_test_data_filepath != NULL && + tests[i].test_data_filepath != NULL) { + kfree(tests[i].test_data_filepath); + } + + if (driver_log_to_user) { + if (user_driver_log_buf != NULL && + tests[i].driver_log_buf != NULL) { + kfree(tests[i].driver_log_buf); + } + } + + if (driver_log_to_file) { + if (user_driver_log_filepath != NULL && + tests[i].driver_log_filepath != NULL) { + kfree(tests[i].driver_log_filepath); + } + } + + if (driver_log_to_file || driver_log_to_user) { + cts_stop_driver_log_redirect(); + } + + if (user_priv_param && tests[i].priv_param) { + kfree(tests[i].priv_param); + } + + if (ret && stop_test_if_validate_fail) { + break; + } + } + + kfree(tests); + + return ret; +} + + +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; + unsigned int modid; + int ret; + + cts_info("ioctl, cmd=0x%016x, arg=0x%016lx", 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_GET_ROW_COL:{ + u16 rows_cols = (cts_dev->fwdata.rows << 8) | cts_dev->fwdata.cols; + return put_user(rows_cols, + (unsigned short __user *)arg); + } + case CTS_TOOL_IOCTL_GET_MODULE_ID: + ret = cts_tcs_get_module_id(cts_dev, &modid); + if (ret) { + cts_err("Get module Id failed"); + return -EFAULT; + } + return put_user(modid, + (unsigned int __user *)arg); + + case CTS_TOOL_IOCTL_TEST:{ + struct cts_test_ioctl_data test_arg; + struct cts_test_param *tests_pa; + + if (copy_from_user(&test_arg, (struct cts_test_ioctl_data __user *)arg, + sizeof(test_arg))) { + cts_err("Copy ioctl test arg to kernel failed"); + return -EFAULT; + } + + if (test_arg.ntests > 8) { + cts_err("ioctl test with too many tests %u", test_arg.ntests); + return -EINVAL; + } + + tests_pa = memdup_user(test_arg.tests, + test_arg.ntests * sizeof(struct cts_test_param)); + if (IS_ERR(tests_pa)) { + int ret = PTR_ERR(tests_pa); + cts_err("Memdump test param to kernel failed %d", ret); + return ret; + } + + return cts_ioctl_test(cts_dev, test_arg.ntests, tests_pa); + } + case CTS_TOOL_IOCTL_TCS_RW:{ + return cts_ioctl_tcs_rw(cts_dev, file, cmd, arg); + } + + default: + break; + } + + return -ENOTSUPP; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +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, +}; +#else +static struct proc_ops cts_tool_fops = { + .proc_lseek = no_llseek, + .proc_open = cts_tool_open, + .proc_read = cts_tool_read, + .proc_write = cts_tool_write, + .proc_ioctl = cts_tool_ioctl, +}; +#endif + +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, + 0660, 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 */