input: touchscreen: cyttsp5: add new touch driver
This is the reference driver source code for cyttsp5 touch driver.
Change-Id: I9b57cac83cfa1ea6cc7f3a655cd65cba499c137c
Signed-off-by: xigualu <luxh0702@thundersoft.com>
Git-commit: 797867bcbaf7a9c3b7ef9d8652e65e148d90bb07
Git-repo: https://source.codeaurora.org/external/thundersoft/ihvjointlab/sensor-driver/commit/?h=Parade_cyttsp5
Signed-off-by: Fei Mao <feim1@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/input/touchscreen/cyttsp5.txt b/Documentation/devicetree/bindings/input/touchscreen/cyttsp5.txt
new file mode 100644
index 0000000..28a0a2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/cyttsp5.txt
@@ -0,0 +1,88 @@
+* Cypress cyttsp5 touchscreen controller
+
+Required properties:
+ - compatible : must be "cy,cyttsp5_i2c_adapter"
+ - reg : Device I2C address or SPI chip select number
+ - interrupt-parent : the phandle for the gpio controller
+ (see interrupt binding[0]).
+ - interrupts : (gpio) interrupt to which the chip is connected
+ (see interrupt binding[0]).
+ - vcc_i2c-supply : power supply
+ - vdd-supply : power supply
+ - cy,mt : multi-touch
+
+Optional properties:
+ - cy,btn : button
+
+[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+[1]: Documentation/devicetree/bindings/gpio/gpio.txt
+
+Example:
+ &i2c1 {
+ /* ... */
+ tsc@24 {
+ compatible = "cy,cyttsp5_i2c_adapter";
+ reg = <0x24>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <13 0x2008>;
+ cy,adapter_id = "cyttsp5_i2c_adapter";
+ vcc_i2c-supply = <&pm8916_l6>;
+ vdd-supply = <&pm8916_l17>;
+
+ cy,core {
+ cy,name = "cyttsp5_core";
+ cy,irq_gpio = <13>;
+ cy,rst_gpio = <12>;
+ cy,hid_desc_register = <1>;
+ cy,flags = <4>;
+ cy,easy_wakeup_gesture = <1>;
+ cy,btn_keys = <172 139 158 217 114 115 212 116>;
+ cy,btn_keys-tag = <0>;
+
+ cy,mt {
+ cy,name = "cyttsp5_mt";
+ cy,inp_dev_name = "cyttsp5_mt";
+ cy,flags = <0x28>;
+ cy,abs =
+ <0x35 0 320 0 0
+ 0x36 0 360 0 0
+ 0x3a 0 255 0 0
+ 0xffff 0 255 0 0
+ 0x39 0 15 0 0
+ 0x30 0 255 0 0
+ 0x31 0 255 0 0
+ 0x34 0xffffff81 127 0 0
+ 0x37 0 1 0 0
+ 0x3b 0 255 0 0>;
+
+ cy,vkeys_x = <320>;
+ cy,vkeys_y = <360>;
+
+ cy,virtual_keys =
+ /* KEY_BACK */
+ <158 1360 90 160 180
+ /* KEY_MENU */
+ 139 1360 270 160 180
+ /* KEY_HOMEPAGE */
+ 172 1360 450 160 180
+ /* KEY SEARCH */
+ 217 1360 630 160 180>;
+ };
+
+ cy,btn {
+ cy,name = "cyttsp5_btn";
+
+ cy,inp_dev_name = "cyttsp5_btn";
+ };
+
+ cy,proximity {
+ cy,name = "cyttsp5_proximity";
+
+ cy,inp_dev_name = "cyttsp5_proximity";
+ cy,abs = <0x19 0 1 0 0>;
+ };
+ };
+ };
+
+ /* ... */
+ };
diff --git a/drivers/input/touchscreen/cyttsp5/Makefile b/drivers/input/touchscreen/cyttsp5/Makefile
new file mode 100644
index 0000000..4f50f5a
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/Makefile
@@ -0,0 +1,49 @@
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) += cyttsp5.o
+cyttsp5-y := cyttsp5_core.o cyttsp5_mt_common.o
+cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A) += cyttsp5_mta.o
+cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B) += cyttsp5_mtb.o
+cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON) += cyttsp5_btn.o
+cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY) += cyttsp5_proximity.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT) += cyttsp5_devtree.o
+ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5
+obj-y += cyttsp5_platform.o
+endif
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_I2C) += cyttsp5_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_SPI) += cyttsp5_spi.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG_MDL) += cyttsp5_debug.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER) += cyttsp5_loader.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS) += cyttsp5_device_access.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_TEST_DEVICE_ACCESS_API) += cyttsp5_test_device_access_api.o
+
+ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG),y)
+CFLAGS_cyttsp5_core.o += -DDEBUG
+CFLAGS_cyttsp5_i2c.o += -DDEBUG
+CFLAGS_cyttsp5_spi.o += -DDEBUG
+CFLAGS_cyttsp5_mta.o += -DDEBUG
+CFLAGS_cyttsp5_mtb.o += -DDEBUG
+CFLAGS_cyttsp5_mt_common.o += -DDEBUG
+CFLAGS_cyttsp5_btn.o += -DDEBUG
+CFLAGS_cyttsp5_proximity.o += -DDEBUG
+CFLAGS_cyttsp5_device_access.o += -DDEBUG
+CFLAGS_cyttsp5_loader.o += -DDEBUG
+CFLAGS_cyttsp5_debug.o += -DDEBUG
+CFLAGS_cyttsp5_devtree.o += -DDEBUG
+CFLAGS_cyttsp5_platform.o += -DDEBUG
+endif
+ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_VDEBUG),y)
+CFLAGS_cyttsp5_core.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_i2c.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_spi.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_mta.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_mtb.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_mt_common.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_btn.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_proximity.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_device_access.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_loader.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_debug.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_devtree.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp5_platform.o += -DVERBOSE_DEBUG
+endif
+
+
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_btn.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_btn.c
new file mode 100644
index 0000000..52c0eb8
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_btn.c
@@ -0,0 +1,369 @@
+/*
+ * cyttsp5_btn.c
+ * Parade TrueTouch(TM) Standard Product V5 CapSense Reports Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+#define CYTTSP5_BTN_NAME "cyttsp5_btn"
+
+static inline void cyttsp5_btn_key_action(struct cyttsp5_btn_data *bd,
+ int btn_no, int btn_state)
+{
+ struct device *dev = bd->dev;
+ struct cyttsp5_sysinfo *si = bd->si;
+
+ if (!si->btn[btn_no].enabled ||
+ si->btn[btn_no].state == btn_state)
+ return;
+
+ si->btn[btn_no].state = btn_state;
+ input_report_key(bd->input, si->btn[btn_no].key_code, btn_state);
+ input_sync(bd->input);
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: btn=%d key_code=%d %s\n",
+ __func__, btn_no, si->btn[btn_no].key_code,
+ btn_state == CY_BTN_PRESSED ?
+ "PRESSED" : "RELEASED");
+}
+
+static void cyttsp5_get_btn_touches(struct cyttsp5_btn_data *bd)
+{
+ struct cyttsp5_sysinfo *si = bd->si;
+ int num_btns = si->num_btns;
+ int cur_btn;
+ int cur_btn_state;
+
+ for (cur_btn = 0; cur_btn < num_btns; cur_btn++) {
+ /* Get current button state */
+ cur_btn_state = (si->xy_data[0] >> (cur_btn * CY_BITS_PER_BTN))
+ & CY_NUM_BTN_EVENT_ID;
+
+ cyttsp5_btn_key_action(bd, cur_btn, cur_btn_state);
+ }
+}
+
+static void cyttsp5_btn_lift_all(struct cyttsp5_btn_data *bd)
+{
+ struct cyttsp5_sysinfo *si = bd->si;
+ int i;
+
+ if (!si || si->num_btns == 0)
+ return;
+
+ for (i = 0; i < si->num_btns; i++)
+ cyttsp5_btn_key_action(bd, i, CY_BTN_RELEASED);
+}
+
+#ifdef VERBOSE_DEBUG
+static void cyttsp5_log_btn_data(struct cyttsp5_btn_data *bd)
+{
+ struct device *dev = bd->dev;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ u8 *pr_buf = cd->pr_buf;
+ struct cyttsp5_sysinfo *si = bd->si;
+ int cur;
+ int value;
+
+ for (cur = 0; cur < si->num_btns; cur++) {
+ pr_buf[0] = 0;
+ if (si->xy_data[0] & (1 << cur))
+ value = 1;
+ else
+ value = 0;
+ snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "btn_rec[%d]=0x", cur);
+ snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "%s%X (%02X)",
+ pr_buf, value,
+ le16_to_cpu(si->xy_data[1 + cur * 2]));
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: %s\n", __func__, pr_buf);
+ }
+}
+#endif
+
+/* read xy_data for all current CapSense button touches */
+static int cyttsp5_xy_worker(struct cyttsp5_btn_data *bd)
+{
+ struct cyttsp5_sysinfo *si = bd->si;
+
+ /* extract button press/release touch information */
+ if (si->num_btns > 0) {
+ cyttsp5_get_btn_touches(bd);
+#ifdef VERBOSE_DEBUG
+ /* log button press/release touch information */
+ cyttsp5_log_btn_data(bd);
+#endif
+ }
+
+ return 0;
+}
+
+static int cyttsp5_btn_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+ int rc;
+
+ if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id)
+ return 0;
+
+ /* core handles handshake */
+ mutex_lock(&bd->btn_lock);
+ rc = cyttsp5_xy_worker(bd);
+ mutex_unlock(&bd->btn_lock);
+ if (rc < 0)
+ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static int cyttsp5_startup_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+
+ mutex_lock(&bd->btn_lock);
+ cyttsp5_btn_lift_all(bd);
+ mutex_unlock(&bd->btn_lock);
+
+ return 0;
+}
+
+static int cyttsp5_btn_suspend_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+
+ mutex_lock(&bd->btn_lock);
+ cyttsp5_btn_lift_all(bd);
+ bd->is_suspended = true;
+ mutex_unlock(&bd->btn_lock);
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int cyttsp5_btn_resume_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+
+ pm_runtime_get(dev);
+
+ mutex_lock(&bd->btn_lock);
+ bd->is_suspended = false;
+ mutex_unlock(&bd->btn_lock);
+
+ return 0;
+}
+
+static int cyttsp5_btn_open(struct input_dev *input)
+{
+ struct device *dev = input->dev.parent;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&bd->btn_lock);
+ bd->is_suspended = false;
+ mutex_unlock(&bd->btn_lock);
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__);
+
+ /* set up touch call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME,
+ cyttsp5_btn_attention, CY_MODE_OPERATIONAL);
+
+ /* set up startup call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
+ cyttsp5_startup_attention, 0);
+
+ /* set up suspend call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME,
+ cyttsp5_btn_suspend_attention, 0);
+
+ /* set up resume call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME,
+ cyttsp5_btn_resume_attention, 0);
+
+ return 0;
+}
+
+static void cyttsp5_btn_close(struct input_dev *input)
+{
+ struct device *dev = input->dev.parent;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME,
+ cyttsp5_btn_attention, CY_MODE_OPERATIONAL);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
+ cyttsp5_startup_attention, 0);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME,
+ cyttsp5_btn_suspend_attention, 0);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME,
+ cyttsp5_btn_resume_attention, 0);
+
+ mutex_lock(&bd->btn_lock);
+ if (!bd->is_suspended) {
+ pm_runtime_put(dev);
+ bd->is_suspended = true;
+ }
+ mutex_unlock(&bd->btn_lock);
+}
+
+static int cyttsp5_setup_input_device(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+ int i;
+ int rc;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Initialize event signals\n",
+ __func__);
+ __set_bit(EV_KEY, bd->input->evbit);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Number of buttons %d\n",
+ __func__, bd->si->num_btns);
+ for (i = 0; i < bd->si->num_btns; i++) {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: btn:%d keycode:%d\n",
+ __func__, i, bd->si->btn[i].key_code);
+ __set_bit(bd->si->btn[i].key_code, bd->input->keybit);
+ }
+
+ rc = input_register_device(bd->input);
+ if (rc < 0)
+ dev_err(dev, "%s: Error, failed register input device r=%d\n",
+ __func__, rc);
+ else
+ bd->input_device_registered = true;
+
+ return rc;
+}
+
+static int cyttsp5_setup_input_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+ int rc;
+
+ bd->si = _cyttsp5_request_sysinfo(dev);
+ if (!bd->si)
+ return -1;
+
+ rc = cyttsp5_setup_input_device(dev);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
+ cyttsp5_setup_input_attention, 0);
+
+ return rc;
+}
+
+int cyttsp5_btn_probe(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+ struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
+ struct cyttsp5_btn_platform_data *btn_pdata;
+ int rc = 0;
+
+ if (!pdata || !pdata->btn_pdata) {
+ dev_err(dev, "%s: Missing platform data\n", __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+ btn_pdata = pdata->btn_pdata;
+
+ mutex_init(&bd->btn_lock);
+ bd->dev = dev;
+ bd->pdata = btn_pdata;
+
+ /* Create the input device and register it. */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Create the input device and register it\n",
+ __func__);
+ bd->input = input_allocate_device();
+ if (!bd->input) {
+ dev_err(dev, "%s: Error, failed to allocate input device\n",
+ __func__);
+ rc = -ENODEV;
+ goto error_alloc_failed;
+ }
+
+ if (bd->pdata->inp_dev_name)
+ bd->input->name = bd->pdata->inp_dev_name;
+ else
+ bd->input->name = CYTTSP5_BTN_NAME;
+ scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev),
+ cd->phys_num++);
+ bd->input->phys = bd->phys;
+ bd->input->dev.parent = bd->dev;
+ bd->input->open = cyttsp5_btn_open;
+ bd->input->close = cyttsp5_btn_close;
+ input_set_drvdata(bd->input, bd);
+
+ /* get sysinfo */
+ bd->si = _cyttsp5_request_sysinfo(dev);
+
+ if (bd->si) {
+ rc = cyttsp5_setup_input_device(dev);
+ if (rc)
+ goto error_init_input;
+ } else {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+ __func__, bd->si);
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0);
+ }
+
+ return 0;
+
+error_init_input:
+ input_free_device(bd->input);
+error_alloc_failed:
+error_no_pdata:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+int cyttsp5_btn_release(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_btn_data *bd = &cd->bd;
+
+ if (bd->input_device_registered) {
+ input_unregister_device(bd->input);
+ } else {
+ input_free_device(bd->input);
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0);
+ }
+
+ return 0;
+}
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c
new file mode 100644
index 0000000..4cc8b19
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_core.c
@@ -0,0 +1,6210 @@
+/*
+ * cyttsp5_core.c
+ * Parade TrueTouch(TM) Standard Product V5 Core Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+#include <linux/kthread.h>
+
+#define CY_CORE_STARTUP_RETRY_COUNT 3
+
+MODULE_FIRMWARE(CY_FW_FILE_NAME);
+
+static const char *cy_driver_core_name = CYTTSP5_CORE_NAME;
+static const char *cy_driver_core_version = CY_DRIVER_VERSION;
+static const char *cy_driver_core_date = CY_DRIVER_DATE;
+static bool cyttsp5_first_probe = true;
+static bool is_cyttsp5_probe_success;
+static const struct cyttsp5_bus_ops *cyttsp5_bus_ops_save;
+
+struct cyttsp5_hid_field {
+ int report_count;
+ int report_size;
+ int size; /* report_count * report_size */
+ int offset;
+ int data_type;
+ int logical_min;
+ int logical_max;
+ /* Usage Page (Hi 16 bit) + Usage (Lo 16 bit) */
+ u32 usage_page;
+ u32 collection_usage_pages[CY_HID_MAX_COLLECTIONS];
+ struct cyttsp5_hid_report *report;
+ bool record_field;
+};
+
+struct cyttsp5_hid_report {
+ u8 id;
+ u8 type;
+ int size;
+ struct cyttsp5_hid_field *fields[CY_HID_MAX_FIELDS];
+ int num_fields;
+ int record_field_index;
+ int header_size;
+ int record_size;
+ u32 usage_page;
+};
+
+struct atten_node {
+ struct list_head node;
+ char *id;
+ struct device *dev;
+
+ int (*func)(struct device *);
+ int mode;
+};
+
+struct param_node {
+ struct list_head node;
+ u8 id;
+ u32 value;
+ u8 size;
+};
+
+struct module_node {
+ struct list_head node;
+ struct cyttsp5_module *module;
+ void *data;
+};
+
+struct cyttsp5_hid_cmd {
+ u8 opcode;
+ u8 report_type;
+ union {
+ u8 report_id;
+ u8 power_state;
+ };
+ u8 has_data_register;
+ size_t write_length;
+ u8 *write_buf;
+ u8 *read_buf;
+ u8 wait_interrupt;
+ u8 reset_cmd;
+ u16 timeout_ms;
+};
+
+struct cyttsp5_hid_output {
+ u8 cmd_type;
+ u16 length;
+ u8 command_code;
+ size_t write_length;
+ u8 *write_buf;
+ u8 novalidate;
+ u8 reset_expected;
+ u16 timeout_ms;
+};
+
+#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode)
+#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4))
+#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id)
+
+#define HID_OUTPUT_APP_COMMAND(command) \
+ .cmd_type = HID_OUTPUT_CMD_APP, \
+ .command_code = command
+
+#define HID_OUTPUT_BL_COMMAND(command) \
+ .cmd_type = HID_OUTPUT_CMD_BL, \
+ .command_code = command
+
+#ifdef VERBOSE_DEBUG
+void cyttsp5_pr_buf(struct device *dev, u8 *dptr, int size,
+ const char *data_name)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ u8 *pr_buf = cd->pr_buf;
+ int i, k;
+ const char fmt[] = "%02X ";
+ int max;
+
+ if (!size)
+ return;
+
+ max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
+
+ pr_buf[0] = 0;
+ for (i = k = 0; i < size && k < max; i++, k += 3)
+ scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]);
+
+ if (size) {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: %s[0..%d]=%s%s\n",
+ __func__, data_name,
+ size - 1, pr_buf, size <= max ? "" : CY_PR_TRUNCATED);
+ } else {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: %s[]\n",
+ __func__, data_name);
+ }
+}
+EXPORT_SYMBOL_GPL(cyttsp5_pr_buf);
+#endif
+
+#ifdef TTHE_TUNER_SUPPORT
+static int tthe_print(struct cyttsp5_core_data *cd, u8 *buf, int buf_len,
+ const u8 *data_name)
+{
+ int len = strlen(data_name);
+ int i, n;
+ u8 *p;
+ int remain;
+ u8 data_name_with_time_stamp[100];
+
+ if (cd->show_timestamp) {
+ sprintf(data_name_with_time_stamp, "[%u] %s",
+ jiffies_to_msecs(jiffies), data_name);
+ data_name = data_name_with_time_stamp;
+ len = strlen(data_name);
+ }
+
+ mutex_lock(&cd->tthe_lock);
+ if (!cd->tthe_buf)
+ goto exit;
+
+ if (cd->tthe_buf_len + (len + buf_len) > CY_MAX_PRBUF_SIZE)
+ goto exit;
+
+ if (len + buf_len == 0)
+ goto exit;
+
+ remain = CY_MAX_PRBUF_SIZE - cd->tthe_buf_len;
+ if (remain < len)
+ len = remain;
+
+ p = cd->tthe_buf + cd->tthe_buf_len;
+ memcpy(p, data_name, len);
+ cd->tthe_buf_len += len;
+ p += len;
+ remain -= len;
+
+ *p = 0;
+ for (i = 0; i < buf_len; i++) {
+ n = scnprintf(p, remain, "%02X ", buf[i]);
+ if (!n)
+ break;
+ p += n;
+ remain -= n;
+ cd->tthe_buf_len += n;
+ }
+
+ n = scnprintf(p, remain, "\n");
+ if (!n)
+ cd->tthe_buf[cd->tthe_buf_len] = 0;
+ cd->tthe_buf_len += n;
+ wake_up(&cd->wait_q);
+exit:
+ mutex_unlock(&cd->tthe_lock);
+ return 0;
+}
+
+static int _cyttsp5_request_tthe_print(struct device *dev, u8 *buf,
+ int buf_len, const u8 *data_name)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return tthe_print(cd, buf, buf_len, data_name);
+}
+#endif
+
+/*
+ * cyttsp5_platform_detect_read()
+ *
+ * This function is passed to platform detect
+ * function to perform a read operation
+ */
+static int cyttsp5_platform_detect_read(struct device *dev, void *buf, int size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return cyttsp5_adap_read_default(cd, buf, size);
+}
+
+/* Must be called with cd->hid_report_lock acquired */
+static struct cyttsp5_hid_report *cyttsp5_get_hid_report_(
+ struct cyttsp5_core_data *cd, u8 report_type, u8 report_id,
+ bool create)
+{
+ struct cyttsp5_hid_report *report = NULL;
+ int i;
+
+ /* Look for created reports */
+ for (i = 0; i < cd->num_hid_reports; i++) {
+ if (cd->hid_reports[i]->type == report_type
+ && cd->hid_reports[i]->id == report_id) {
+ return cd->hid_reports[i];
+ }
+ }
+
+ /* Create a new report */
+ if (create && cd->num_hid_reports < CY_HID_MAX_REPORTS) {
+ report = kzalloc(sizeof(struct cyttsp5_hid_report),
+ GFP_KERNEL);
+ if (!report)
+ return NULL;
+
+ report->type = report_type;
+ report->id = report_id;
+ cd->hid_reports[cd->num_hid_reports++] = report;
+ }
+
+ return report;
+}
+
+/* Must be called with cd->hid_report_lock acquired */
+static void cyttsp5_free_hid_reports_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_report *report;
+ int i, j;
+
+ for (i = 0; i < cd->num_hid_reports; i++) {
+ report = cd->hid_reports[i];
+ for (j = 0; j < report->num_fields; j++)
+ kfree(report->fields[j]);
+ kfree(report);
+ cd->hid_reports[i] = NULL;
+ }
+
+ cd->num_hid_reports = 0;
+}
+
+static void cyttsp5_free_hid_reports(struct cyttsp5_core_data *cd)
+{
+ mutex_lock(&cd->hid_report_lock);
+ cyttsp5_free_hid_reports_(cd);
+ mutex_unlock(&cd->hid_report_lock);
+}
+
+/* Must be called with cd->hid_report_lock acquired */
+static struct cyttsp5_hid_field *cyttsp5_create_hid_field_(
+ struct cyttsp5_hid_report *report)
+{
+ struct cyttsp5_hid_field *field;
+
+ if (!report)
+ return NULL;
+
+ if (report->num_fields == CY_HID_MAX_FIELDS)
+ return NULL;
+
+ field = kzalloc(sizeof(struct cyttsp5_hid_field), GFP_KERNEL);
+ if (!field)
+ return NULL;
+
+ field->report = report;
+
+ report->fields[report->num_fields++] = field;
+
+ return field;
+}
+
+static int cyttsp5_add_parameter(struct cyttsp5_core_data *cd,
+ u8 param_id, u32 param_value, u8 param_size)
+{
+ struct param_node *param, *param_new;
+
+ /* Check if parameter exists */
+ spin_lock(&cd->spinlock);
+ list_for_each_entry(param, &cd->param_list, node) {
+ if (param->id == param_id) {
+ /* Update parameter */
+ param->value = param_value;
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Update parameter id:%d value:%d size:%d\n",
+ __func__, param_id, param_value, param_size);
+ goto exit_unlock;
+ }
+ }
+ spin_unlock(&cd->spinlock);
+
+ param_new = kzalloc(sizeof(*param_new), GFP_KERNEL);
+ if (!param_new)
+ return -ENOMEM;
+
+ param_new->id = param_id;
+ param_new->value = param_value;
+ param_new->size = param_size;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Add parameter id:%d value:%d size:%d\n",
+ __func__, param_id, param_value, param_size);
+
+ spin_lock(&cd->spinlock);
+ list_add(¶m_new->node, &cd->param_list);
+exit_unlock:
+ spin_unlock(&cd->spinlock);
+
+ return 0;
+}
+
+int request_exclusive(struct cyttsp5_core_data *cd, void *ownptr,
+ int timeout_ms)
+{
+ int t = msecs_to_jiffies(timeout_ms);
+ bool with_timeout = (timeout_ms != 0);
+
+ mutex_lock(&cd->system_lock);
+ if (!cd->exclusive_dev && cd->exclusive_waits == 0) {
+ cd->exclusive_dev = ownptr;
+ goto exit;
+ }
+
+ cd->exclusive_waits++;
+wait:
+ mutex_unlock(&cd->system_lock);
+ if (with_timeout) {
+ t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t);
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: tmo waiting exclusive access\n",
+ __func__);
+ return -ETIME;
+ }
+ } else {
+ wait_event(cd->wait_q, !cd->exclusive_dev);
+ }
+ mutex_lock(&cd->system_lock);
+ if (cd->exclusive_dev)
+ goto wait;
+ cd->exclusive_dev = ownptr;
+ cd->exclusive_waits--;
+exit:
+ mutex_unlock(&cd->system_lock);
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: request_exclusive ok=%p\n",
+ __func__, ownptr);
+
+ return 0;
+}
+
+static int release_exclusive_(struct cyttsp5_core_data *cd, void *ownptr)
+{
+ if (cd->exclusive_dev != ownptr)
+ return -EINVAL;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: exclusive_dev %p freed\n",
+ __func__, cd->exclusive_dev);
+ cd->exclusive_dev = NULL;
+ wake_up(&cd->wait_q);
+ return 0;
+}
+
+/*
+ * returns error if was not owned
+ */
+int release_exclusive(struct cyttsp5_core_data *cd, void *ownptr)
+{
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ rc = release_exclusive_(cd, ownptr);
+ mutex_unlock(&cd->system_lock);
+
+ return rc;
+}
+
+static int cyttsp5_hid_exec_cmd_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_cmd *hid_cmd)
+{
+ int rc;
+ u8 *cmd;
+ u8 cmd_length;
+ u8 cmd_offset = 0;
+
+ cmd_length = 2 /* command register */
+ + 2 /* command */
+ + (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */
+ + (hid_cmd->has_data_register ? 2 : 0) /* Data register */
+ + hid_cmd->write_length; /* Data length */
+
+ cmd = kzalloc(cmd_length, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ /* Set Command register */
+ memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register,
+ sizeof(cd->hid_desc.command_register));
+ cmd_offset += sizeof(cd->hid_desc.command_register);
+
+ /* Set Command */
+ SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type);
+
+ if (hid_cmd->report_id >= 0XF)
+ SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF);
+ else
+ SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id);
+ cmd_offset++;
+
+ SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode);
+ cmd_offset++;
+
+ if (hid_cmd->report_id >= 0XF) {
+ cmd[cmd_offset] = hid_cmd->report_id;
+ cmd_offset++;
+ }
+
+ /* Set Data register */
+ if (hid_cmd->has_data_register) {
+ memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register,
+ sizeof(cd->hid_desc.data_register));
+ cmd_offset += sizeof(cd->hid_desc.data_register);
+ }
+
+ /* Set Data */
+ if (hid_cmd->write_length && hid_cmd->write_buf) {
+ memcpy(&cmd[cmd_offset], hid_cmd->write_buf,
+ hid_cmd->write_length);
+ cmd_offset += hid_cmd->write_length;
+ }
+
+ rc = cyttsp5_adap_write_read_specific(cd, cmd_length, cmd,
+ hid_cmd->read_buf);
+ if (rc)
+ dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__);
+
+ kfree(cmd);
+ return rc;
+}
+
+static int cyttsp5_hid_exec_cmd_and_wait_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_cmd *hid_cmd)
+{
+ int rc;
+ int t;
+ u16 timeout_ms;
+ int *cmd_state;
+
+ if (hid_cmd->reset_cmd)
+ cmd_state = &cd->hid_reset_cmd_state;
+ else
+ cmd_state = &cd->hid_cmd_state;
+
+ if (hid_cmd->wait_interrupt) {
+ mutex_lock(&cd->system_lock);
+ *cmd_state = 1;
+ mutex_unlock(&cd->system_lock);
+ }
+
+ rc = cyttsp5_hid_exec_cmd_(cd, hid_cmd);
+ if (rc) {
+ if (hid_cmd->wait_interrupt)
+ goto error;
+
+ goto exit;
+ }
+
+ if (!hid_cmd->wait_interrupt)
+ goto exit;
+
+ if (hid_cmd->timeout_ms)
+ timeout_ms = hid_cmd->timeout_ms;
+ else
+ timeout_ms = CY_HID_RESET_TIMEOUT;
+
+ t = wait_event_timeout(cd->wait_q, (*cmd_state == 0),
+ msecs_to_jiffies(timeout_ms));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: HID output cmd execution timed out\n",
+ __func__);
+ rc = -ETIME;
+ goto error;
+ }
+
+ goto exit;
+
+error:
+ mutex_lock(&cd->system_lock);
+ *cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+
+exit:
+ return rc;
+}
+
+static int cyttsp5_hid_cmd_reset_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_cmd hid_cmd = {
+ .opcode = HID_CMD_RESET,
+ .wait_interrupt = 1,
+ .reset_cmd = 1,
+ .timeout_ms = CY_HID_RESET_TIMEOUT,
+ };
+
+ return cyttsp5_hid_exec_cmd_and_wait_(cd, &hid_cmd);
+}
+
+static int cyttsp5_hid_cmd_reset(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_cmd_reset_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int cyttsp5_hid_cmd_set_power_(struct cyttsp5_core_data *cd,
+ u8 power_state)
+{
+ int rc;
+ struct cyttsp5_hid_cmd hid_cmd = {
+ .opcode = HID_CMD_SET_POWER,
+ .wait_interrupt = 1,
+ .timeout_ms = CY_HID_SET_POWER_TIMEOUT,
+ };
+ hid_cmd.power_state = power_state;
+
+ rc = cyttsp5_hid_exec_cmd_and_wait_(cd, &hid_cmd);
+ if (rc) {
+ dev_err(cd->dev, "%s: Failed to set power to state:%d\n",
+ __func__, power_state);
+ return rc;
+ }
+
+ /* validate */
+ if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID)
+ || ((cd->response_buf[3] & 0x3) != power_state)
+ || ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int cyttsp5_hid_cmd_set_power(struct cyttsp5_core_data *cd,
+ u8 power_state)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_cmd_set_power_(cd, power_state);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static const u16 crc_table[16] = {
+ 0x0000, 0x1021, 0x2042, 0x3063,
+ 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b,
+ 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+};
+
+static u16 _cyttsp5_compute_crc(u8 *buf, u32 size)
+{
+ u16 remainder = 0xFFFF;
+ u16 xor_mask = 0x0000;
+ u32 index;
+ u32 byte_value;
+ u32 table_index;
+ u32 crc_bit_width = sizeof(u16) * 8;
+
+ /* Divide the message by polynomial, via the table. */
+ for (index = 0; index < size; index++) {
+ byte_value = buf[index];
+ table_index = ((byte_value >> 4) & 0x0F)
+ ^ (remainder >> (crc_bit_width - 4));
+ remainder = crc_table[table_index] ^ (remainder << 4);
+ table_index = (byte_value & 0x0F)
+ ^ (remainder >> (crc_bit_width - 4));
+ remainder = crc_table[table_index] ^ (remainder << 4);
+ }
+
+ /* Perform the final remainder CRC. */
+ return remainder ^ xor_mask;
+}
+
+static int cyttsp5_hid_output_validate_bl_response(
+ struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ u16 size;
+ u16 crc;
+ u8 status;
+
+ size = get_unaligned_le16(&cd->response_buf[0]);
+
+ if (hid_output->reset_expected && !size)
+ return 0;
+
+ if (cd->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET]
+ != HID_BL_RESPONSE_REPORT_ID) {
+ dev_err(cd->dev, "%s: HID output response, wrong report_id\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ if (cd->response_buf[4] != HID_OUTPUT_BL_SOP) {
+ dev_err(cd->dev, "%s: HID output response, wrong SOP\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ if (cd->response_buf[size - 1] != HID_OUTPUT_BL_EOP) {
+ dev_err(cd->dev, "%s: HID output response, wrong EOP\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ crc = _cyttsp5_compute_crc(&cd->response_buf[4], size - 7);
+ if (cd->response_buf[size - 3] != LOW_BYTE(crc)
+ || cd->response_buf[size - 2] != HI_BYTE(crc)) {
+ dev_err(cd->dev, "%s: HID output response, wrong CRC 0x%X\n",
+ __func__, crc);
+ return -EPROTO;
+ }
+
+ status = cd->response_buf[5];
+ if (status) {
+ dev_err(cd->dev, "%s: HID output response, ERROR:%d\n",
+ __func__, status);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int cyttsp5_hid_output_validate_app_response(
+ struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ int command_code;
+ u16 size;
+
+ size = get_unaligned_le16(&cd->response_buf[0]);
+
+ if (hid_output->reset_expected && !size)
+ return 0;
+
+ if (cd->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET]
+ != HID_APP_RESPONSE_REPORT_ID) {
+ dev_err(cd->dev, "%s: HID output response, wrong report_id\n",
+ __func__);
+ return -EPROTO;
+ }
+
+ command_code = cd->response_buf[HID_OUTPUT_RESPONSE_CMD_OFFSET]
+ & HID_OUTPUT_RESPONSE_CMD_MASK;
+ if (command_code != hid_output->command_code) {
+ dev_err(cd->dev,
+ "%s: HID output response, wrong command_code:%X\n",
+ __func__, command_code);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static void cyttsp5_check_set_parameter(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output, bool raw)
+{
+ u8 *param_buf;
+ u32 param_value = 0;
+ u8 param_size;
+ u8 param_id;
+ int i = 0;
+
+ if (!(cd->cpdata->flags & CY_CORE_FLAG_RESTORE_PARAMETERS))
+ return;
+
+ /* Check command input for Set Parameter command */
+ if (raw && hid_output->length >= 10 && hid_output->length <= 13
+ && !memcmp(&hid_output->write_buf[0],
+ &cd->hid_desc.output_register,
+ sizeof(cd->hid_desc.output_register))
+ && hid_output->write_buf[4] ==
+ HID_APP_OUTPUT_REPORT_ID
+ && hid_output->write_buf[6] ==
+ HID_OUTPUT_SET_PARAM)
+ param_buf = &hid_output->write_buf[7];
+ else if (!raw && hid_output->cmd_type == HID_OUTPUT_CMD_APP
+ && hid_output->command_code == HID_OUTPUT_SET_PARAM
+ && hid_output->write_length >= 3
+ && hid_output->write_length <= 6)
+ param_buf = &hid_output->write_buf[0];
+ else
+ return;
+
+ /* Get parameter ID, size and value */
+ param_id = param_buf[0];
+ param_size = param_buf[1];
+ if (param_size > 4) {
+ dev_err(cd->dev, "%s: Invalid parameter size\n", __func__);
+ return;
+ }
+
+ param_buf = ¶m_buf[2];
+ while (i < param_size)
+ param_value += *(param_buf++) << (8 * i++);
+
+ /* Check command response for Set Parameter command */
+ if (cd->response_buf[2] != HID_APP_RESPONSE_REPORT_ID
+ || (cd->response_buf[4] & HID_OUTPUT_CMD_MASK) !=
+ HID_OUTPUT_SET_PARAM
+ || cd->response_buf[5] != param_id
+ || cd->response_buf[6] != param_size) {
+ dev_err(cd->dev, "%s: Set Parameter command not successful\n",
+ __func__);
+ return;
+ }
+
+ cyttsp5_add_parameter(cd, param_id, param_value, param_size);
+}
+
+static void cyttsp5_check_command(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output, bool raw)
+{
+ cyttsp5_check_set_parameter(cd, hid_output, raw);
+}
+
+static int cyttsp5_hid_output_validate_response(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ if (hid_output->cmd_type == HID_OUTPUT_CMD_BL)
+ return cyttsp5_hid_output_validate_bl_response(cd, hid_output);
+
+ return cyttsp5_hid_output_validate_app_response(cd, hid_output);
+
+}
+
+static int cyttsp5_hid_send_output_user_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ int rc;
+
+ if (!hid_output->length || !hid_output->write_buf)
+ return -EINVAL;
+
+ rc = cyttsp5_adap_write_read_specific(cd, hid_output->length,
+ hid_output->write_buf, NULL);
+ if (rc)
+ dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__);
+
+ return rc;
+}
+
+static int cyttsp5_hid_send_output_user_and_wait_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ int rc;
+ int t;
+
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = HID_OUTPUT_USER_CMD + 1;
+ mutex_unlock(&cd->system_lock);
+
+ rc = cyttsp5_hid_send_output_user_(cd, hid_output);
+ if (rc)
+ goto error;
+
+ t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+ msecs_to_jiffies(CY_HID_OUTPUT_USER_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: HID output cmd execution timed out\n",
+ __func__);
+ rc = -ETIME;
+ goto error;
+ }
+
+ cyttsp5_check_command(cd, hid_output, true);
+
+ goto exit;
+
+error:
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+
+exit:
+ return rc;
+}
+
+static int cyttsp5_hid_send_output_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ int rc;
+ u8 *cmd;
+ u16 length;
+ u8 report_id;
+ u8 cmd_offset = 0;
+ u16 crc;
+ u8 cmd_allocated = 0;
+
+ switch (hid_output->cmd_type) {
+ case HID_OUTPUT_CMD_APP:
+ report_id = HID_APP_OUTPUT_REPORT_ID;
+ length = 5;
+ break;
+ case HID_OUTPUT_CMD_BL:
+ report_id = HID_BL_OUTPUT_REPORT_ID;
+ length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ length += hid_output->write_length;
+
+ if (length + 2 > CYTTSP5_PREALLOCATED_CMD_BUFFER) {
+ cmd = kzalloc(length + 2, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+ cmd_allocated = 1;
+ } else {
+ cmd = cd->cmd_buf;
+ }
+
+ /* Set Output register */
+ memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register,
+ sizeof(cd->hid_desc.output_register));
+ cmd_offset += sizeof(cd->hid_desc.output_register);
+
+ cmd[cmd_offset++] = LOW_BYTE(length);
+ cmd[cmd_offset++] = HI_BYTE(length);
+ cmd[cmd_offset++] = report_id;
+ cmd[cmd_offset++] = 0x0; /* reserved */
+ if (hid_output->cmd_type == HID_OUTPUT_CMD_BL)
+ cmd[cmd_offset++] = HID_OUTPUT_BL_SOP;
+ cmd[cmd_offset++] = hid_output->command_code;
+
+ /* Set Data Length for bootloader */
+ if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) {
+ cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length);
+ cmd[cmd_offset++] = HI_BYTE(hid_output->write_length);
+ }
+ /* Set Data */
+ if (hid_output->write_length && hid_output->write_buf) {
+ memcpy(&cmd[cmd_offset], hid_output->write_buf,
+ hid_output->write_length);
+ cmd_offset += hid_output->write_length;
+ }
+ if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) {
+ crc = _cyttsp5_compute_crc(&cmd[6],
+ hid_output->write_length + 4);
+ cmd[cmd_offset++] = LOW_BYTE(crc);
+ cmd[cmd_offset++] = HI_BYTE(crc);
+ cmd[cmd_offset++] = HID_OUTPUT_BL_EOP;
+ }
+
+ cyttsp5_pr_buf(cd->dev, cmd, length + 2, "command");
+ rc = cyttsp5_adap_write_read_specific(cd, length + 2, cmd, NULL);
+ if (rc)
+ dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__);
+
+ if (cmd_allocated)
+ kfree(cmd);
+ return rc;
+}
+
+static int cyttsp5_hid_send_output_and_wait_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_output *hid_output)
+{
+ int rc;
+ int t;
+#ifdef VERBOSE_DEBUG
+ u16 size;
+#endif
+ u16 timeout_ms;
+
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = hid_output->command_code + 1;
+ mutex_unlock(&cd->system_lock);
+
+ if (hid_output->timeout_ms)
+ timeout_ms = hid_output->timeout_ms;
+ else
+ timeout_ms = CY_HID_OUTPUT_TIMEOUT;
+
+ rc = cyttsp5_hid_send_output_(cd, hid_output);
+ if (rc)
+ goto error;
+
+
+ t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+ msecs_to_jiffies(timeout_ms));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: HID output cmd execution timed out\n",
+ __func__);
+ rc = -ETIME;
+ goto error;
+ }
+
+ if (!hid_output->novalidate)
+ rc = cyttsp5_hid_output_validate_response(cd, hid_output);
+
+ cyttsp5_check_command(cd, hid_output, false);
+
+#ifdef VERBOSE_DEBUG
+ size = get_unaligned_le16(&cd->response_buf[0]);
+ cyttsp5_pr_buf(cd->dev, cd->response_buf, size, "return_buf");
+#endif
+
+ goto exit;
+
+error:
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+exit:
+ return rc;
+}
+
+static int cyttsp5_hid_output_null_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_NULL),
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_null(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_null_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_start_bootloader_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_START_BOOTLOADER),
+ .timeout_ms = CY_HID_OUTPUT_START_BOOTLOADER_TIMEOUT,
+ .reset_expected = 1,
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_start_bootloader(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_start_bootloader_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_start_bl(struct device *dev, int protect)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_start_bootloader(cd);
+
+ return cyttsp5_hid_output_start_bootloader_(cd);
+}
+
+static void cyttsp5_si_get_cydata(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_cydata *cydata = &cd->sysinfo.cydata;
+ struct cyttsp5_cydata_dev *cydata_dev =
+ (struct cyttsp5_cydata_dev *)
+ &cd->response_buf[HID_SYSINFO_CYDATA_OFFSET];
+
+ cydata->pip_ver_major = cydata_dev->pip_ver_major;
+ cydata->pip_ver_minor = cydata_dev->pip_ver_minor;
+ cydata->bl_ver_major = cydata_dev->bl_ver_major;
+ cydata->bl_ver_minor = cydata_dev->bl_ver_minor;
+ cydata->fw_ver_major = cydata_dev->fw_ver_major;
+ cydata->fw_ver_minor = cydata_dev->fw_ver_minor;
+
+ cydata->fw_pid = get_unaligned_le16(&cydata_dev->fw_pid);
+ cydata->fw_ver_conf = get_unaligned_le16(&cydata_dev->fw_ver_conf);
+ cydata->post_code = get_unaligned_le16(&cydata_dev->post_code);
+ cydata->revctrl = get_unaligned_le32(&cydata_dev->revctrl);
+ cydata->jtag_id_l = get_unaligned_le16(&cydata_dev->jtag_si_id_l);
+ cydata->jtag_id_h = get_unaligned_le16(&cydata_dev->jtag_si_id_h);
+
+ memcpy(cydata->mfg_id, cydata_dev->mfg_id, CY_NUM_MFGID);
+
+ cyttsp5_pr_buf(cd->dev, (u8 *)cydata_dev,
+ sizeof(struct cyttsp5_cydata_dev), "sysinfo_cydata");
+}
+
+static void cyttsp5_si_get_sensing_conf_data(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data;
+ struct cyttsp5_sensing_conf_data_dev *scd_dev =
+ (struct cyttsp5_sensing_conf_data_dev *)
+ &cd->response_buf[HID_SYSINFO_SENSING_OFFSET];
+
+ scd->electrodes_x = scd_dev->electrodes_x;
+ scd->electrodes_y = scd_dev->electrodes_y;
+ scd->origin_x = scd_dev->origin_x;
+ scd->origin_y = scd_dev->origin_y;
+
+ /* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */
+ if (scd->origin_x & 0x02) {
+ scd->tx_num = scd->electrodes_x;
+ scd->rx_num = scd->electrodes_y;
+ } else {
+ scd->tx_num = scd->electrodes_y;
+ scd->rx_num = scd->electrodes_x;
+ }
+
+ scd->panel_id = scd_dev->panel_id;
+ scd->btn = scd_dev->btn;
+ scd->scan_mode = scd_dev->scan_mode;
+ scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
+
+ scd->res_x = get_unaligned_le16(&scd_dev->res_x);
+ scd->res_y = get_unaligned_le16(&scd_dev->res_y);
+ scd->max_z = get_unaligned_le16(&scd_dev->max_z);
+ scd->len_x = get_unaligned_le16(&scd_dev->len_x);
+ scd->len_y = get_unaligned_le16(&scd_dev->len_y);
+
+ cyttsp5_pr_buf(cd->dev, (u8 *)scd_dev,
+ sizeof(struct cyttsp5_sensing_conf_data_dev),
+ "sensing_conf_data");
+}
+
+static int cyttsp5_si_setup(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ int max_tch = si->sensing_conf_data.max_tch;
+
+ if (!si->xy_data)
+ si->xy_data = kzalloc(max_tch * si->desc.tch_record_size,
+ GFP_KERNEL);
+ if (!si->xy_data)
+ return -ENOMEM;
+
+ if (!si->xy_mode)
+ si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL);
+ if (!si->xy_mode) {
+ kfree(si->xy_data);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int cyttsp5_si_get_btn_data(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ int num_btns = 0;
+ int num_defined_keys;
+ u16 *key_table;
+ int btn;
+ int i;
+ int rc = 0;
+ unsigned int btns = cd->response_buf[HID_SYSINFO_BTN_OFFSET]
+ & HID_SYSINFO_BTN_MASK;
+ size_t btn_keys_size;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: get btn data\n", __func__);
+
+ for (i = 0; i < HID_SYSINFO_MAX_BTN; i++) {
+ if (btns & (1 << i))
+ num_btns++;
+ }
+ si->num_btns = num_btns;
+
+ if (num_btns) {
+ btn_keys_size = num_btns * sizeof(struct cyttsp5_btn);
+ if (!si->btn)
+ si->btn = kzalloc(btn_keys_size, GFP_KERNEL);
+ if (!si->btn)
+ return -ENOMEM;
+
+ if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL)
+ num_defined_keys = 0;
+ else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL)
+ num_defined_keys = 0;
+ else
+ num_defined_keys = cd->cpdata->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->size;
+
+ for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) {
+ key_table = (u16 *)cd->cpdata->sett
+ [CY_IC_GRPNUM_BTN_KEYS]->data;
+ si->btn[btn].key_code = key_table[btn];
+ si->btn[btn].enabled = true;
+ }
+ for (; btn < num_btns; btn++) {
+ si->btn[btn].key_code = KEY_RESERVED;
+ si->btn[btn].enabled = true;
+ }
+
+ return rc;
+ }
+
+ kfree(si->btn);
+ si->btn = NULL;
+ return rc;
+}
+
+static void cyttsp5_si_put_log_data(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ struct cyttsp5_cydata *cydata = &si->cydata;
+ struct cyttsp5_sensing_conf_data *scd = &si->sensing_conf_data;
+ int i;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: pip_ver_major =0x%02X (%d)\n",
+ __func__, cydata->pip_ver_major, cydata->pip_ver_major);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: pip_ver_minor =0x%02X (%d)\n",
+ __func__, cydata->pip_ver_minor, cydata->pip_ver_minor);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_pid =0x%04X (%d)\n",
+ __func__, cydata->fw_pid, cydata->fw_pid);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_major =0x%02X (%d)\n",
+ __func__, cydata->fw_ver_major, cydata->fw_ver_major);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_minor =0x%02X (%d)\n",
+ __func__, cydata->fw_ver_minor, cydata->fw_ver_minor);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: revctrl =0x%08X (%d)\n",
+ __func__, cydata->revctrl, cydata->revctrl);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_conf =0x%04X (%d)\n",
+ __func__, cydata->fw_ver_conf, cydata->fw_ver_conf);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: bl_ver_major =0x%02X (%d)\n",
+ __func__, cydata->bl_ver_major, cydata->bl_ver_major);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: bl_ver_minor =0x%02X (%d)\n",
+ __func__, cydata->bl_ver_minor, cydata->bl_ver_minor);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: jtag_id_h =0x%04X (%d)\n",
+ __func__, cydata->jtag_id_h, cydata->jtag_id_h);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: jtag_id_l =0x%04X (%d)\n",
+ __func__, cydata->jtag_id_l, cydata->jtag_id_l);
+ for (i = 0; i < CY_NUM_MFGID; i++)
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: mfg_id[%d] =0x%02X (%d)\n",
+ __func__, i, cydata->mfg_id[i], cydata->mfg_id[i]);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: post_code =0x%04X (%d)\n",
+ __func__, cydata->post_code, cydata->post_code);
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: electrodes_x =0x%02X (%d)\n",
+ __func__, scd->electrodes_x, scd->electrodes_x);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: electrodes_y =0x%02X (%d)\n",
+ __func__, scd->electrodes_y, scd->electrodes_y);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: len_x =0x%04X (%d)\n",
+ __func__, scd->len_x, scd->len_x);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: len_y =0x%04X (%d)\n",
+ __func__, scd->len_y, scd->len_y);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: res_x =0x%04X (%d)\n",
+ __func__, scd->res_x, scd->res_x);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: res_y =0x%04X (%d)\n",
+ __func__, scd->res_y, scd->res_y);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: max_z =0x%04X (%d)\n",
+ __func__, scd->max_z, scd->max_z);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: origin_x =0x%02X (%d)\n",
+ __func__, scd->origin_x, scd->origin_x);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: origin_y =0x%02X (%d)\n",
+ __func__, scd->origin_y, scd->origin_y);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: panel_id =0x%02X (%d)\n",
+ __func__, scd->panel_id, scd->panel_id);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: btn =0x%02X (%d)\n",
+ __func__, scd->btn, scd->btn);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: scan_mode =0x%02X (%d)\n",
+ __func__, scd->scan_mode, scd->scan_mode);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: max_num_of_tch_per_refresh_cycle =0x%02X (%d)\n",
+ __func__, scd->max_tch, scd->max_tch);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: xy_mode =%p\n",
+ __func__, si->xy_mode);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: xy_data =%p\n",
+ __func__, si->xy_data);
+}
+
+static int cyttsp5_get_sysinfo_regs(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ int rc;
+
+ rc = cyttsp5_si_get_btn_data(cd);
+ if (rc < 0)
+ return rc;
+
+ cyttsp5_si_get_cydata(cd);
+
+ cyttsp5_si_get_sensing_conf_data(cd);
+
+ cyttsp5_si_setup(cd);
+
+ cyttsp5_si_put_log_data(cd);
+
+ si->ready = true;
+ return rc;
+}
+
+static void cyttsp5_free_si_ptrs(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+
+ kfree(si->btn);
+ kfree(si->xy_mode);
+ kfree(si->xy_data);
+}
+
+static int cyttsp5_hid_output_get_sysinfo_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_SYSINFO),
+ .timeout_ms = CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT,
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ rc = cyttsp5_get_sysinfo_regs(cd);
+ if (rc)
+ cyttsp5_free_si_ptrs(cd);
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_get_sysinfo_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_suspend_scanning_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_SUSPEND_SCANNING),
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_suspend_scanning(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_suspend_scanning_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_suspend_scanning(struct device *dev,
+ int protect)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_suspend_scanning(cd);
+
+ return cyttsp5_hid_output_suspend_scanning_(cd);
+}
+
+static int cyttsp5_hid_output_resume_scanning_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RESUME_SCANNING),
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_resume_scanning(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_resume_scanning_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_resume_scanning(struct device *dev,
+ int protect)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_resume_scanning(cd);
+
+ return cyttsp5_hid_output_resume_scanning_(cd);
+}
+
+static int cyttsp5_hid_output_get_param_(struct cyttsp5_core_data *cd,
+ u8 param_id, u32 *value)
+{
+ int write_length = 1;
+ u8 param[1] = { param_id };
+ u8 read_param_id;
+ int param_size;
+ u8 *ptr;
+ int rc;
+ int i;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_PARAM),
+ .write_length = write_length,
+ .write_buf = param,
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ read_param_id = cd->response_buf[5];
+ if (read_param_id != param_id)
+ return -EPROTO;
+
+ param_size = cd->response_buf[6];
+ ptr = &cd->response_buf[7];
+ *value = 0;
+ for (i = 0; i < param_size; i++)
+ *value += ptr[i] << (i * 8);
+ return 0;
+}
+
+static int cyttsp5_hid_output_get_param(struct cyttsp5_core_data *cd,
+ u8 param_id, u32 *value)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_get_param_(cd, param_id, value);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+int _cyttsp5_request_hid_output_get_param(struct device *dev,
+ int protect, u8 param_id, u32 *value)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_get_param(cd, param_id, value);
+
+ return cyttsp5_hid_output_get_param_(cd, param_id, value);
+}
+
+static int cyttsp5_hid_output_set_param_(struct cyttsp5_core_data *cd,
+ u8 param_id, u32 value, u8 size)
+{
+ u8 write_buf[6];
+ u8 *ptr = &write_buf[2];
+ int rc;
+ int i;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_SET_PARAM),
+ .write_buf = write_buf,
+ };
+
+ write_buf[0] = param_id;
+ write_buf[1] = size;
+ for (i = 0; i < size; i++) {
+ ptr[i] = value & 0xFF;
+ value = value >> 8;
+ }
+
+ hid_output.write_length = 2 + size;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ if (param_id != cd->response_buf[5] || size != cd->response_buf[6])
+ return -EPROTO;
+
+ return 0;
+}
+
+static int cyttsp5_hid_output_set_param(struct cyttsp5_core_data *cd,
+ u8 param_id, u32 value, u8 size)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_set_param_(cd, param_id, value, size);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+int _cyttsp5_request_hid_output_set_param(struct device *dev,
+ int protect, u8 param_id, u32 value, u8 size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_set_param(cd, param_id, value, size);
+
+ return cyttsp5_hid_output_set_param_(cd, param_id, value, size);
+}
+
+static int cyttsp5_hid_output_enter_easywake_state_(
+ struct cyttsp5_core_data *cd, u8 data, u8 *return_data)
+{
+ int write_length = 1;
+ u8 param[1] = { data };
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_ENTER_EASYWAKE_STATE),
+ .write_length = write_length,
+ .write_buf = param,
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ *return_data = cd->response_buf[5];
+ return rc;
+}
+
+static int cyttsp5_hid_output_verify_config_block_crc_(
+ struct cyttsp5_core_data *cd, u8 ebid, u8 *status,
+ u16 *calculated_crc, u16 *stored_crc)
+{
+ int write_length = 1;
+ u8 param[1] = { ebid };
+ u8 *ptr;
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_VERIFY_CONFIG_BLOCK_CRC),
+ .write_length = write_length,
+ .write_buf = param,
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ ptr = &cd->response_buf[5];
+ *status = ptr[0];
+ *calculated_crc = get_unaligned_le16(&ptr[1]);
+ *stored_crc = get_unaligned_le16(&ptr[3]);
+ return 0;
+}
+
+static int cyttsp5_hid_output_verify_config_block_crc(
+ struct cyttsp5_core_data *cd, u8 ebid, u8 *status,
+ u16 *calculated_crc, u16 *stored_crc)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, status,
+ calculated_crc, stored_crc);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_verify_config_block_crc(
+ struct device *dev, int protect, u8 ebid, u8 *status,
+ u16 *calculated_crc, u16 *stored_crc)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_verify_config_block_crc(cd, ebid,
+ status, calculated_crc, stored_crc);
+
+ return cyttsp5_hid_output_verify_config_block_crc_(cd, ebid,
+ status, calculated_crc, stored_crc);
+}
+
+static int cyttsp5_hid_output_get_config_row_size_(struct cyttsp5_core_data *cd,
+ u16 *row_size)
+{
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_CONFIG_ROW_SIZE),
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ *row_size = get_unaligned_le16(&cd->response_buf[5]);
+ return 0;
+}
+
+static int cyttsp5_hid_output_get_config_row_size(struct cyttsp5_core_data *cd,
+ u16 *row_size)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_get_config_row_size_(cd, row_size);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_get_config_row_size(struct device *dev,
+ int protect, u16 *row_size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_get_config_row_size(cd, row_size);
+
+ return cyttsp5_hid_output_get_config_row_size_(cd, row_size);
+}
+
+static int cyttsp5_hid_output_read_conf_block_(struct cyttsp5_core_data *cd,
+ u16 row_number, u16 length, u8 ebid, u8 *read_buf, u16 *crc)
+{
+ int read_ebid;
+ int read_length;
+ int status;
+ int rc;
+ int write_length = 5;
+ u8 write_buf[5];
+ u8 cmd_offset = 0;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_READ_CONF_BLOCK),
+ .write_length = write_length,
+ .write_buf = write_buf,
+ };
+
+ write_buf[cmd_offset++] = LOW_BYTE(row_number);
+ write_buf[cmd_offset++] = HI_BYTE(row_number);
+ write_buf[cmd_offset++] = LOW_BYTE(length);
+ write_buf[cmd_offset++] = HI_BYTE(length);
+ write_buf[cmd_offset++] = ebid;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ status = cd->response_buf[5];
+ if (status)
+ return -EINVAL;
+
+ read_ebid = cd->response_buf[6];
+ if ((read_ebid != ebid) || (cd->response_buf[9] != 0))
+ return -EPROTO;
+
+ read_length = get_unaligned_le16(&cd->response_buf[7]);
+ if (length < read_length)
+ length = read_length;
+
+ memcpy(read_buf, &cd->response_buf[10], length);
+ *crc = get_unaligned_le16(&cd->response_buf[read_length + 10]);
+
+ return 0;
+}
+
+static int cyttsp5_hid_output_read_conf_ver_(struct cyttsp5_core_data *cd,
+ u16 *config_ver)
+{
+ int rc;
+ u8 read_buf[CY_TTCONFIG_VERSION_OFFSET + CY_TTCONFIG_VERSION_SIZE];
+ u16 crc;
+
+ rc = cyttsp5_hid_output_read_conf_block_(cd, CY_TTCONFIG_VERSION_ROW,
+ CY_TTCONFIG_VERSION_OFFSET + CY_TTCONFIG_VERSION_SIZE,
+ CY_TCH_PARM_EBID, read_buf, &crc);
+ if (rc)
+ return rc;
+
+ *config_ver = get_unaligned_le16(
+ &read_buf[CY_TTCONFIG_VERSION_OFFSET]);
+
+ return 0;
+}
+
+static int cyttsp5_hid_output_write_conf_block_(struct cyttsp5_core_data *cd,
+ u16 row_number, u16 write_length, u8 ebid, u8 *write_buf,
+ u8 *security_key, u16 *actual_write_len)
+{
+ /* row_number + write_len + ebid + security_key + crc */
+ int full_write_length = 2 + 2 + 1 + write_length + 8 + 2;
+ u8 *full_write_buf;
+ u8 cmd_offset = 0;
+ u16 crc;
+ int status;
+ int rc;
+ int read_ebid;
+ u8 *data;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_WRITE_CONF_BLOCK),
+ .write_length = full_write_length,
+ .timeout_ms = CY_HID_OUTPUT_WRITE_CONF_BLOCK_TIMEOUT,
+ };
+
+ full_write_buf = kzalloc(full_write_length, GFP_KERNEL);
+ if (!full_write_buf)
+ return -ENOMEM;
+
+ hid_output.write_buf = full_write_buf;
+ full_write_buf[cmd_offset++] = LOW_BYTE(row_number);
+ full_write_buf[cmd_offset++] = HI_BYTE(row_number);
+ full_write_buf[cmd_offset++] = LOW_BYTE(write_length);
+ full_write_buf[cmd_offset++] = HI_BYTE(write_length);
+ full_write_buf[cmd_offset++] = ebid;
+ data = &full_write_buf[cmd_offset];
+ memcpy(data, write_buf, write_length);
+ cmd_offset += write_length;
+ memcpy(&full_write_buf[cmd_offset], security_key, 8);
+ cmd_offset += 8;
+ crc = _cyttsp5_compute_crc(data, write_length);
+ full_write_buf[cmd_offset++] = LOW_BYTE(crc);
+ full_write_buf[cmd_offset++] = HI_BYTE(crc);
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ goto exit;
+
+ status = cd->response_buf[5];
+ if (status) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ read_ebid = cd->response_buf[6];
+ if (read_ebid != ebid) {
+ rc = -EPROTO;
+ goto exit;
+ }
+
+ *actual_write_len = get_unaligned_le16(&cd->response_buf[7]);
+
+exit:
+ kfree(full_write_buf);
+ return rc;
+}
+
+static int cyttsp5_hid_output_write_conf_block(struct cyttsp5_core_data *cd,
+ u16 row_number, u16 write_length, u8 ebid, u8 *write_buf,
+ u8 *security_key, u16 *actual_write_len)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_write_conf_block_(cd, row_number, write_length,
+ ebid, write_buf, security_key, actual_write_len);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_write_conf_block(struct device *dev,
+ int protect, u16 row_number, u16 write_length, u8 ebid,
+ u8 *write_buf, u8 *security_key, u16 *actual_write_len)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_write_conf_block(cd, row_number,
+ write_length, ebid, write_buf, security_key,
+ actual_write_len);
+
+ return cyttsp5_hid_output_write_conf_block_(cd, row_number,
+ write_length, ebid, write_buf, security_key,
+ actual_write_len);
+}
+
+static int cyttsp5_hid_output_get_data_structure_(
+ struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length,
+ u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
+ u8 *data)
+{
+ int rc;
+ u16 total_read_len = 0;
+ u16 read_len;
+ u16 off_buf = 0;
+ u8 write_buf[5];
+ u8 read_data_id;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_DATA_STRUCTURE),
+ .write_length = 5,
+ .write_buf = write_buf,
+ };
+
+again:
+ write_buf[0] = LOW_BYTE(read_offset);
+ write_buf[1] = HI_BYTE(read_offset);
+ write_buf[2] = LOW_BYTE(read_length);
+ write_buf[3] = HI_BYTE(read_length);
+ write_buf[4] = data_id;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ if (cd->response_buf[5] != CY_CMD_STATUS_SUCCESS)
+ goto set_status;
+
+ read_data_id = cd->response_buf[6];
+ if (read_data_id != data_id)
+ return -EPROTO;
+
+ read_len = get_unaligned_le16(&cd->response_buf[7]);
+ if (read_len && data) {
+ memcpy(&data[off_buf], &cd->response_buf[10], read_len);
+
+ total_read_len += read_len;
+
+ if (read_len < read_length) {
+ read_offset += read_len;
+ off_buf += read_len;
+ read_length -= read_len;
+ goto again;
+ }
+ }
+
+ if (data_format)
+ *data_format = cd->response_buf[9];
+ if (actual_read_len)
+ *actual_read_len = total_read_len;
+set_status:
+ if (status)
+ *status = cd->response_buf[5];
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_get_data_structure(
+ struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length,
+ u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
+ u8 *data)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_get_data_structure_(cd, read_offset,
+ read_length, data_id, status, data_format,
+ actual_read_len, data);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_get_data_structure(struct device *dev,
+ int protect, u16 read_offset, u16 read_length, u8 data_id,
+ u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_get_data_structure(cd,
+ read_offset, read_length, data_id, status,
+ data_format, actual_read_len, data);
+
+ return cyttsp5_hid_output_get_data_structure_(cd,
+ read_offset, read_length, data_id, status,
+ data_format, actual_read_len, data);
+}
+
+static int cyttsp5_hid_output_run_selftest_(
+ struct cyttsp5_core_data *cd, u8 test_id,
+ u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+ u8 *results_available)
+{
+ int rc;
+ u8 write_buf[2];
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RUN_SELF_TEST),
+ .write_length = 2,
+ .write_buf = write_buf,
+ .timeout_ms = CY_HID_OUTPUT_RUN_SELF_TEST_TIMEOUT,
+ };
+
+ write_buf[0] = test_id;
+ write_buf[1] = write_idacs_to_flash;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ if (status)
+ *status = cd->response_buf[5];
+ if (summary_result)
+ *summary_result = cd->response_buf[6];
+ if (results_available)
+ *results_available = cd->response_buf[7];
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_run_selftest(
+ struct cyttsp5_core_data *cd, u8 test_id,
+ u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+ u8 *results_available)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_run_selftest_(cd, test_id,
+ write_idacs_to_flash, status, summary_result,
+ results_available);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_run_selftest(struct device *dev,
+ int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status,
+ u8 *summary_result, u8 *results_available)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_run_selftest(cd, test_id,
+ write_idacs_to_flash, status, summary_result,
+ results_available);
+
+ return cyttsp5_hid_output_run_selftest_(cd, test_id,
+ write_idacs_to_flash, status, summary_result,
+ results_available);
+}
+
+static int cyttsp5_hid_output_get_selftest_result_(
+ struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length,
+ u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
+{
+ int rc;
+ u16 total_read_len = 0;
+ u16 read_len;
+ u16 off_buf = 0;
+ u8 write_buf[5];
+ u8 read_test_id;
+ bool repeat;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_SELF_TEST_RESULT),
+ .write_length = 5,
+ .write_buf = write_buf,
+ };
+
+ /*
+ * Do not repeat reading for Auto Shorts test
+ * when PIP version < 1.3
+ */
+ repeat = IS_PIP_VER_GE(&cd->sysinfo, 1, 3)
+ || test_id != CY_ST_ID_AUTOSHORTS;
+
+again:
+ write_buf[0] = LOW_BYTE(read_offset);
+ write_buf[1] = HI_BYTE(read_offset);
+ write_buf[2] = LOW_BYTE(read_length);
+ write_buf[3] = HI_BYTE(read_length);
+ write_buf[4] = test_id;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ if (cd->response_buf[5] != CY_CMD_STATUS_SUCCESS)
+ goto set_status;
+
+ read_test_id = cd->response_buf[6];
+ if (read_test_id != test_id)
+ return -EPROTO;
+
+ read_len = get_unaligned_le16(&cd->response_buf[7]);
+ if (read_len && data) {
+ memcpy(&data[off_buf], &cd->response_buf[10], read_len);
+
+ total_read_len += read_len;
+
+ if (repeat && read_len < read_length) {
+ read_offset += read_len;
+ off_buf += read_len;
+ read_length -= read_len;
+ goto again;
+ }
+ }
+
+ if (actual_read_len)
+ *actual_read_len = total_read_len;
+set_status:
+ if (status)
+ *status = cd->response_buf[5];
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_get_selftest_result(
+ struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length,
+ u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_get_selftest_result_(cd, read_offset,
+ read_length, test_id, status, actual_read_len, data);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_get_selftest_result(struct device *dev,
+ int protect, u16 read_offset, u16 read_length, u8 test_id,
+ u8 *status, u16 *actual_read_len, u8 *data)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_get_selftest_result(cd, read_offset,
+ read_length, test_id, status, actual_read_len,
+ data);
+
+ return cyttsp5_hid_output_get_selftest_result_(cd, read_offset,
+ read_length, test_id, status, actual_read_len,
+ data);
+}
+
+static int cyttsp5_hid_output_calibrate_idacs_(struct cyttsp5_core_data *cd,
+ u8 mode, u8 *status)
+{
+ int rc;
+ int write_length = 1;
+ u8 write_buf[1];
+ u8 cmd_offset = 0;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_CALIBRATE_IDACS),
+ .write_length = write_length,
+ .write_buf = write_buf,
+ .timeout_ms = CY_HID_OUTPUT_CALIBRATE_IDAC_TIMEOUT,
+ };
+
+ write_buf[cmd_offset++] = mode;
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ *status = cd->response_buf[5];
+ if (*status)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cyttsp5_hid_output_calibrate_idacs(struct cyttsp5_core_data *cd,
+ u8 mode, u8 *status)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_calibrate_idacs_(cd, mode, status);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_calibrate_idacs(struct device *dev,
+ int protect, u8 mode, u8 *status)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_calibrate_idacs(cd, mode, status);
+
+ return cyttsp5_hid_output_calibrate_idacs_(cd, mode, status);
+}
+
+static int cyttsp5_hid_output_initialize_baselines_(
+ struct cyttsp5_core_data *cd, u8 test_id, u8 *status)
+{
+ int rc;
+ int write_length = 1;
+ u8 write_buf[1];
+ u8 cmd_offset = 0;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_INITIALIZE_BASELINES),
+ .write_length = write_length,
+ .write_buf = write_buf,
+ };
+
+ write_buf[cmd_offset++] = test_id;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ *status = cd->response_buf[5];
+ if (*status)
+ return -EINVAL;
+
+ return rc;
+}
+
+static int cyttsp5_hid_output_initialize_baselines(struct cyttsp5_core_data *cd,
+ u8 test_id, u8 *status)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_initialize_baselines_(cd, test_id, status);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_initialize_baselines(struct device *dev,
+ int protect, u8 test_id, u8 *status)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_initialize_baselines(cd, test_id,
+ status);
+
+ return cyttsp5_hid_output_initialize_baselines_(cd, test_id, status);
+}
+
+static int cyttsp5_hid_output_exec_panel_scan_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_EXEC_PANEL_SCAN),
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_exec_panel_scan(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_exec_panel_scan_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_exec_panel_scan(struct device *dev,
+ int protect)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_exec_panel_scan(cd);
+
+ return cyttsp5_hid_output_exec_panel_scan_(cd);
+}
+
+/* @response: set none NULL only if all response required including header */
+static int cyttsp5_hid_output_retrieve_panel_scan_(
+ struct cyttsp5_core_data *cd, u16 read_offset, u16 read_count,
+ u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
+ u8 *read_buf)
+{
+ int status;
+ u8 read_data_id;
+ int rc;
+ int write_length = 5;
+ u8 write_buf[5];
+ u8 cmd_offset = 0;
+ u8 data_elem_size;
+ int size;
+ int data_size;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RETRIEVE_PANEL_SCAN),
+ .write_length = write_length,
+ .write_buf = write_buf,
+ };
+
+ write_buf[cmd_offset++] = LOW_BYTE(read_offset);
+ write_buf[cmd_offset++] = HI_BYTE(read_offset);
+ write_buf[cmd_offset++] = LOW_BYTE(read_count);
+ write_buf[cmd_offset++] = HI_BYTE(read_count);
+ write_buf[cmd_offset++] = data_id;
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ status = cd->response_buf[5];
+ if (status)
+ return -EINVAL;
+
+ read_data_id = cd->response_buf[6];
+ if (read_data_id != data_id)
+ return -EPROTO;
+
+ size = get_unaligned_le16(&cd->response_buf[0]);
+ *actual_read_len = get_unaligned_le16(&cd->response_buf[7]);
+ *config = cd->response_buf[9];
+
+ data_elem_size = *config & 0x07;
+ data_size = *actual_read_len * data_elem_size;
+
+ if (read_buf)
+ memcpy(read_buf, &cd->response_buf[10], data_size);
+ if (response)
+ memcpy(response, cd->response_buf, size);
+ return rc;
+}
+
+static int cyttsp5_hid_output_retrieve_panel_scan(
+ struct cyttsp5_core_data *cd, u16 read_offset, u16 read_count,
+ u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
+ u8 *read_buf)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_retrieve_panel_scan_(cd, read_offset,
+ read_count, data_id, response, config,
+ actual_read_len, read_buf);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_retrieve_panel_scan(struct device *dev,
+ int protect, u16 read_offset, u16 read_count, u8 data_id,
+ u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_retrieve_panel_scan(cd,
+ read_offset, read_count, data_id, response,
+ config, actual_read_len, read_buf);
+
+ return cyttsp5_hid_output_retrieve_panel_scan_(cd,
+ read_offset, read_count, data_id, response,
+ config, actual_read_len, read_buf);
+}
+
+static int cyttsp5_hid_output_user_cmd_(struct cyttsp5_core_data *cd,
+ u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+ u16 *actual_read_len)
+{
+ int rc;
+ u16 size;
+#ifdef TTHE_TUNER_SUPPORT
+ int command_code = 0;
+ int len;
+#endif
+ struct cyttsp5_hid_output hid_output = {
+ .length = write_len,
+ .write_buf = write_buf,
+ };
+
+ rc = cyttsp5_hid_send_output_user_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ size = get_unaligned_le16(&cd->response_buf[0]);
+ if (size == 0)
+ size = 2;
+
+ if (size > read_len) {
+ *actual_read_len = 0;
+ return -EINVAL;
+ }
+
+ memcpy(read_buf, cd->response_buf, size);
+ *actual_read_len = size;
+
+#ifdef TTHE_TUNER_SUPPORT
+ /* print up to cmd code */
+ len = HID_OUTPUT_CMD_OFFSET + 1;
+ if (write_len < len)
+ len = write_len;
+ else
+ command_code = write_buf[HID_OUTPUT_CMD_OFFSET]
+ & HID_OUTPUT_CMD_MASK;
+
+ /* Do not print for EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */
+ if (command_code != HID_OUTPUT_EXEC_PANEL_SCAN
+ && command_code != HID_OUTPUT_RETRIEVE_PANEL_SCAN)
+ tthe_print(cd, write_buf, len, "CMD=");
+#endif
+
+ return 0;
+}
+
+static int cyttsp5_get_config_ver_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ int rc;
+ u16 config_ver = 0;
+
+ rc = cyttsp5_hid_output_suspend_scanning_(cd);
+ if (rc)
+ goto error;
+
+ rc = cyttsp5_hid_output_read_conf_ver_(cd, &config_ver);
+ if (rc)
+ goto exit;
+
+ si->cydata.fw_ver_conf = config_ver;
+
+exit:
+ cyttsp5_hid_output_resume_scanning_(cd);
+error:
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: CONFIG_VER:%04X\n",
+ __func__, config_ver);
+ return rc;
+}
+
+static int cyttsp5_hid_output_user_cmd(struct cyttsp5_core_data *cd,
+ u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+ u16 *actual_read_len)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_user_cmd_(cd, read_len, read_buf,
+ write_len, write_buf, actual_read_len);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_user_cmd(struct device *dev,
+ int protect, u16 read_len, u8 *read_buf, u16 write_len,
+ u8 *write_buf, u16 *actual_read_len)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_user_cmd(cd, read_len, read_buf,
+ write_len, write_buf, actual_read_len);
+
+ return cyttsp5_hid_output_user_cmd_(cd, read_len, read_buf,
+ write_len, write_buf, actual_read_len);
+}
+
+static int cyttsp5_hid_output_bl_get_information_(struct cyttsp5_core_data *cd,
+ u8 *return_data)
+{
+ int rc;
+ int data_len;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_GET_INFO),
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc)
+ return rc;
+
+ data_len = get_unaligned_le16(&cd->input_buf[6]);
+ if (!data_len)
+ return -EPROTO;
+
+ memcpy(return_data, &cd->response_buf[8], data_len);
+
+ return 0;
+}
+
+static int cyttsp5_hid_output_bl_get_information(struct cyttsp5_core_data *cd,
+ u8 *return_data)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_bl_get_information_(cd, return_data);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_bl_get_information(struct device *dev,
+ int protect, u8 *return_data)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_bl_get_information(cd, return_data);
+
+ return cyttsp5_hid_output_bl_get_information_(cd, return_data);
+}
+
+static int cyttsp5_hid_output_bl_initiate_bl_(struct cyttsp5_core_data *cd,
+ u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
+{
+ u16 write_length = key_size + row_size;
+ u8 *write_buf;
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_INITIATE_BL),
+ .write_length = write_length,
+ .timeout_ms = CY_HID_OUTPUT_BL_INITIATE_BL_TIMEOUT,
+ };
+
+ write_buf = kzalloc(write_length, GFP_KERNEL);
+ if (!write_buf)
+ return -ENOMEM;
+
+ hid_output.write_buf = write_buf;
+
+ if (key_size)
+ memcpy(write_buf, key_buf, key_size);
+
+ if (row_size)
+ memcpy(&write_buf[key_size], metadata_row_buf, row_size);
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+
+ kfree(write_buf);
+ return rc;
+}
+
+static int cyttsp5_hid_output_bl_initiate_bl(struct cyttsp5_core_data *cd,
+ u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
+ row_size, metadata_row_buf);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_bl_initiate_bl(struct device *dev,
+ int protect, u16 key_size, u8 *key_buf, u16 row_size,
+ u8 *metadata_row_buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_bl_initiate_bl(cd, key_size, key_buf,
+ row_size, metadata_row_buf);
+
+ return cyttsp5_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
+ row_size, metadata_row_buf);
+}
+
+static int cyttsp5_hid_output_bl_program_and_verify_(
+ struct cyttsp5_core_data *cd, u16 data_len, u8 *data_buf)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_PROGRAM_AND_VERIFY),
+ .write_length = data_len,
+ .write_buf = data_buf,
+ .timeout_ms = CY_HID_OUTPUT_BL_PROGRAM_AND_VERIFY_TIMEOUT,
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_bl_program_and_verify(
+ struct cyttsp5_core_data *cd, u16 data_len, u8 *data_buf)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_bl_program_and_verify_(cd, data_len, data_buf);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_bl_program_and_verify(
+ struct device *dev, int protect, u16 data_len, u8 *data_buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_bl_program_and_verify(cd, data_len,
+ data_buf);
+
+ return cyttsp5_hid_output_bl_program_and_verify_(cd, data_len,
+ data_buf);
+}
+
+static int cyttsp5_hid_output_bl_verify_app_integrity_(
+ struct cyttsp5_core_data *cd, u8 *result)
+{
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_VERIFY_APP_INTEGRITY),
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc) {
+ *result = 0;
+ return rc;
+ }
+
+ *result = cd->response_buf[8];
+ return 0;
+}
+
+static int cyttsp5_hid_output_bl_verify_app_integrity(
+ struct cyttsp5_core_data *cd, u8 *result)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_bl_verify_app_integrity_(cd, result);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_bl_verify_app_integrity(
+ struct device *dev, int protect, u8 *result)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_bl_verify_app_integrity(cd, result);
+
+ return cyttsp5_hid_output_bl_verify_app_integrity_(cd, result);
+}
+
+static int cyttsp5_hid_output_bl_launch_app_(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_LAUNCH_APP),
+ .reset_expected = 1,
+ };
+
+ return cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+}
+
+static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_bl_launch_app_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_launch_app(struct device *dev,
+ int protect)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_bl_launch_app(cd);
+
+ return cyttsp5_hid_output_bl_launch_app_(cd);
+}
+
+static int cyttsp5_hid_output_bl_get_panel_id_(
+ struct cyttsp5_core_data *cd, u8 *panel_id)
+{
+ int rc;
+ struct cyttsp5_hid_output hid_output = {
+ HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_GET_PANEL_ID),
+ };
+
+ rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output);
+ if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) {
+ parade_debug(cd->dev, DEBUG_LEVEL_1,
+ "%s: Get Panel ID command not supported\n",
+ __func__);
+ *panel_id = PANEL_ID_NOT_ENABLED;
+ return 0;
+ } else if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on Get Panel ID command\n",
+ __func__);
+ return rc;
+ }
+
+ *panel_id = cd->response_buf[8];
+ return 0;
+}
+
+static int cyttsp5_hid_output_bl_get_panel_id(
+ struct cyttsp5_core_data *cd, u8 *panel_id)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_hid_output_bl_get_panel_id_(cd, panel_id);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_hid_output_bl_get_panel_id(
+ struct device *dev, int protect, u8 *panel_id)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_hid_output_bl_get_panel_id(cd, panel_id);
+
+ return cyttsp5_hid_output_bl_get_panel_id_(cd, panel_id);
+}
+
+static int cyttsp5_get_hid_descriptor_(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_desc *desc)
+{
+ struct device *dev = cd->dev;
+ int rc;
+ int t;
+ u8 cmd[2];
+
+ /* Read HID descriptor length and version */
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 1;
+ mutex_unlock(&cd->system_lock);
+
+ /* Set HID descriptor register */
+ memcpy(cmd, &cd->hid_core.hid_desc_register,
+ sizeof(cd->hid_core.hid_desc_register));
+
+ rc = cyttsp5_adap_write_read_specific(cd, 2, cmd, NULL);
+ if (rc) {
+ dev_err(dev, "%s: failed to get HID descriptor length and version, rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+ msecs_to_jiffies(CY_HID_GET_HID_DESCRIPTOR_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: HID get descriptor timed out\n",
+ __func__);
+ rc = -ETIME;
+ goto error;
+ }
+
+ memcpy((u8 *)desc, cd->response_buf, sizeof(struct cyttsp5_hid_desc));
+
+ /* Check HID descriptor length and version */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: HID len:%X HID ver:%X\n",
+ __func__,
+ le16_to_cpu(desc->hid_desc_len),
+ le16_to_cpu(desc->bcd_version));
+
+ if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) ||
+ le16_to_cpu(desc->bcd_version) != CY_HID_VERSION) {
+ dev_err(dev, "%s: Unsupported HID version\n", __func__);
+ return -ENODEV;
+ }
+
+ goto exit;
+
+error:
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+exit:
+ return rc;
+}
+
+static int cyttsp5_get_hid_descriptor(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_desc *desc)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_get_hid_descriptor_(cd, desc);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+ return rc;
+}
+
+static int _cyttsp5_request_get_hid_desc(struct device *dev, int protect)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (protect)
+ return cyttsp5_get_hid_descriptor(cd, &cd->hid_desc);
+
+ return cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+}
+
+static int cyttsp5_hw_soft_reset(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ if (cd->hid_desc.hid_desc_len == 0) {
+ rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = cyttsp5_hid_cmd_reset_(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n",
+ __func__);
+ return rc;
+ }
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: execute SOFT reset\n",
+ __func__);
+ return 0;
+}
+
+static int cyttsp5_hw_hard_reset(struct cyttsp5_core_data *cd)
+{
+ if (cd->cpdata->xres) {
+ cd->cpdata->xres(cd->cpdata, cd->dev);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: execute HARD reset\n",
+ __func__);
+ return 0;
+ }
+ dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__);
+ return -ENODEV;
+}
+
+static int cyttsp5_hw_reset(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ rc = cyttsp5_hw_hard_reset(cd);
+ mutex_unlock(&cd->system_lock);
+ if (rc == -ENODEV)
+ rc = cyttsp5_hw_soft_reset(cd);
+ return rc;
+}
+
+static inline int get_hid_item_data(u8 *data, int item_size)
+{
+ if (item_size == 1)
+ return (int)*data;
+ else if (item_size == 2)
+ return (int)get_unaligned_le16(data);
+ else if (item_size == 4)
+ return (int)get_unaligned_le32(data);
+ return 0;
+}
+
+static int parse_report_descriptor(struct cyttsp5_core_data *cd,
+ u8 *report_desc, size_t len)
+{
+ struct cyttsp5_hid_report *report;
+ struct cyttsp5_hid_field *field;
+ u8 *buf = report_desc;
+ u8 *end = buf + len;
+ int rc = 0;
+ int offset = 0;
+ int i;
+ u8 report_type;
+ u32 up_usage;
+ /* Global items */
+ u8 report_id = 0;
+ u16 usage_page = 0;
+ int report_count = 0;
+ int report_size = 0;
+ int logical_min = 0;
+ int logical_max = 0;
+ /* Local items */
+ u16 usage = 0;
+ /* Main items - Collection stack */
+ u32 collection_usages[CY_HID_MAX_NESTED_COLLECTIONS] = {0};
+ u8 collection_types[CY_HID_MAX_NESTED_COLLECTIONS] = {0};
+ /* First collection for header, second for report */
+ int logical_collection_count = 0;
+ int collection_nest = 0;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Report descriptor length: %u\n",
+ __func__, (u32)len);
+
+ mutex_lock(&cd->hid_report_lock);
+ cyttsp5_free_hid_reports_(cd);
+
+ while (buf < end) {
+ int item_type;
+ int item_size;
+ int item_tag;
+ u8 *data;
+
+ /* Get Item */
+ item_size = HID_GET_ITEM_SIZE(buf[0]);
+ if (item_size == 3)
+ item_size = 4;
+ item_type = HID_GET_ITEM_TYPE(buf[0]);
+ item_tag = HID_GET_ITEM_TAG(buf[0]);
+
+ data = ++buf;
+ buf += item_size;
+
+ /* Process current item */
+ switch (item_type) {
+ case HID_ITEM_TYPE_GLOBAL:
+ switch (item_tag) {
+ case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+ if (item_size != 1) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ report_id = get_hid_item_data(data, item_size);
+ offset = 0;
+ logical_collection_count = 0;
+ break;
+ case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+ if (item_size == 0 || item_size == 4) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ usage_page = (u16)get_hid_item_data(data,
+ item_size);
+ break;
+ case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+ if (item_size == 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ logical_min = get_hid_item_data(data,
+ item_size);
+ break;
+ case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+ if (item_size == 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ logical_max = get_hid_item_data(data,
+ item_size);
+ break;
+ case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+ if (item_size == 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ report_count = get_hid_item_data(data,
+ item_size);
+ break;
+ case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+ if (item_size == 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ report_size = get_hid_item_data(data,
+ item_size);
+ break;
+ default:
+ dev_info(cd->dev,
+ "%s: Unrecognized Global tag %d\n",
+ __func__, item_tag);
+ }
+ break;
+ case HID_ITEM_TYPE_LOCAL:
+ switch (item_tag) {
+ case HID_LOCAL_ITEM_TAG_USAGE:
+ if (item_size == 0 || item_size == 4) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ usage = (u16)get_hid_item_data(data,
+ item_size);
+ break;
+ case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+ if (item_size == 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ /* usage_min = */
+ get_hid_item_data(data, item_size);
+ break;
+ case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+ if (item_size == 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ /* usage_max = */
+ get_hid_item_data(data, item_size);
+ break;
+ default:
+ dev_info(cd->dev,
+ "%s: Unrecognized Local tag %d\n",
+ __func__, item_tag);
+ }
+ break;
+ case HID_ITEM_TYPE_MAIN:
+ switch (item_tag) {
+ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+ if (item_size != 1) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ if (CY_HID_MAX_NESTED_COLLECTIONS ==
+ collection_nest) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ up_usage = usage_page << 16 | usage;
+
+ /* Update collection stack */
+ collection_usages[collection_nest] = up_usage;
+ collection_types[collection_nest] =
+ get_hid_item_data(data, item_size);
+
+ if (collection_types[collection_nest] ==
+ HID_COLLECTION_LOGICAL)
+ logical_collection_count++;
+
+ collection_nest++;
+ break;
+ case HID_MAIN_ITEM_TAG_END_COLLECTION:
+ if (item_size != 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ if (--collection_nest < 0) {
+ rc = -EINVAL;
+ goto exit;
+ }
+ break;
+ case HID_MAIN_ITEM_TAG_INPUT:
+ report_type = HID_INPUT_REPORT;
+ goto continue_main_item;
+ case HID_MAIN_ITEM_TAG_OUTPUT:
+ report_type = HID_OUTPUT_REPORT;
+ goto continue_main_item;
+ case HID_MAIN_ITEM_TAG_FEATURE:
+ report_type = HID_FEATURE_REPORT;
+continue_main_item:
+ if (item_size != 1) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ up_usage = usage_page << 16 | usage;
+
+ /* Get or create report */
+ report = cyttsp5_get_hid_report_(cd,
+ report_type, report_id, true);
+ if (!report) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+ if (!report->usage_page && collection_nest > 0)
+ report->usage_page =
+ collection_usages
+ [collection_nest - 1];
+
+ /* Create field */
+ field = cyttsp5_create_hid_field_(report);
+ if (!field) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ field->report_count = report_count;
+ field->report_size = report_size;
+ field->size = report_count * report_size;
+ field->offset = offset;
+ field->data_type =
+ get_hid_item_data(data, item_size);
+ field->logical_min = logical_min;
+ field->logical_max = logical_max;
+ field->usage_page = up_usage;
+
+ for (i = 0; i < collection_nest; i++) {
+ field->collection_usage_pages
+ [collection_types[i]] =
+ collection_usages[i];
+ }
+
+ /* Update report's header or record size */
+ if (logical_collection_count == 1) {
+ report->header_size += field->size;
+ } else if (logical_collection_count == 2) {
+ field->record_field = true;
+ field->offset -= report->header_size;
+ /* Set record field index */
+ if (report->record_field_index == 0)
+ report->record_field_index =
+ report->num_fields - 1;
+ report->record_size += field->size;
+ }
+
+ report->size += field->size;
+
+ offset += field->size;
+ break;
+ default:
+ dev_info(cd->dev, "%s: Unrecognized Main tag %d\n",
+ __func__, item_tag);
+ }
+
+ /* Reset all local items */
+ usage = 0;
+ break;
+ }
+ }
+
+ if (buf != end) {
+ dev_err(cd->dev, "%s: Report descriptor length invalid\n",
+ __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (collection_nest) {
+ dev_err(cd->dev, "%s: Unbalanced collection items (%d)\n",
+ __func__, collection_nest);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+exit:
+ if (rc)
+ cyttsp5_free_hid_reports_(cd);
+ mutex_unlock(&cd->hid_report_lock);
+ return rc;
+}
+
+static struct cyttsp5_hid_field *find_report_desc_field(
+ struct cyttsp5_core_data *cd, u32 usage_page,
+ u32 collection_usage_page)
+{
+ struct cyttsp5_hid_report *report = NULL;
+ struct cyttsp5_hid_field *field = NULL;
+ int i;
+ int j;
+ u32 field_cup;
+ u32 field_up;
+
+ for (i = 0; i < cd->num_hid_reports; i++) {
+ report = cd->hid_reports[i];
+ for (j = 0; j < report->num_fields; j++) {
+ field = report->fields[j];
+ field_cup = field->collection_usage_pages
+ [HID_COLLECTION_LOGICAL];
+ field_up = field->usage_page;
+ if (field_cup == collection_usage_page
+ && field_up == usage_page) {
+ return field;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int fill_tch_abs(struct cyttsp5_tch_abs_params *tch_abs,
+ struct cyttsp5_hid_field *field)
+{
+ tch_abs->ofs = field->offset / 8;
+ tch_abs->size = field->report_size / 8;
+ if (field->report_size % 8)
+ tch_abs->size += 1;
+ tch_abs->min = 0;
+ tch_abs->max = 1 << field->report_size;
+ tch_abs->bofs = field->offset - (tch_abs->ofs << 3);
+
+ return 0;
+}
+
+static struct cyttsp5_hid_report *find_report_desc(struct cyttsp5_core_data *cd,
+ u32 usage_page)
+{
+ struct cyttsp5_hid_report *report = NULL;
+ int i;
+
+ for (i = 0; i < cd->num_hid_reports; i++) {
+ if (cd->hid_reports[i]->usage_page == usage_page) {
+ report = cd->hid_reports[i];
+ break;
+ }
+ }
+
+ return report;
+}
+
+static int setup_report_descriptor(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ struct cyttsp5_hid_report *report;
+ struct cyttsp5_hid_field *field;
+ int i;
+ u32 tch_collection_usage_page = HID_CY_TCH_COL_USAGE_PG;
+ u32 btn_collection_usage_page = HID_CY_BTN_COL_USAGE_PG;
+
+ for (i = CY_TCH_X; i < CY_TCH_NUM_ABS; i++) {
+ field = find_report_desc_field(cd,
+ cyttsp5_tch_abs_field_map[i],
+ tch_collection_usage_page);
+ if (field) {
+ parade_debug(cd->dev, DEBUG_LEVEL_2,
+ " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+ field, field->report_count, field->report_size,
+ field->offset, field->data_type,
+ field->logical_min, field->logical_max,
+ field->usage_page);
+ fill_tch_abs(&si->tch_abs[i], field);
+ si->tch_abs[i].report = 1;
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d",
+ cyttsp5_tch_abs_string[i],
+ (u32)si->tch_abs[i].ofs,
+ (u32)si->tch_abs[i].size,
+ (u32)si->tch_abs[i].min,
+ (u32)si->tch_abs[i].max,
+ (u32)si->tch_abs[i].bofs,
+ si->tch_abs[i].report);
+
+ } else {
+ si->tch_abs[i].report = 0;
+ }
+ }
+ for (i = CY_TCH_TIME; i < CY_TCH_NUM_HDR; i++) {
+ field = find_report_desc_field(cd,
+ cyttsp5_tch_hdr_field_map[i],
+ tch_collection_usage_page);
+ if (field) {
+ parade_debug(cd->dev, DEBUG_LEVEL_2,
+ " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+ field, field->report_count, field->report_size,
+ field->offset, field->data_type,
+ field->logical_min, field->logical_max,
+ field->usage_page);
+ fill_tch_abs(&si->tch_hdr[i], field);
+ si->tch_hdr[i].report = 1;
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d",
+ cyttsp5_tch_hdr_string[i],
+ (u32)si->tch_hdr[i].ofs,
+ (u32)si->tch_hdr[i].size,
+ (u32)si->tch_hdr[i].min,
+ (u32)si->tch_hdr[i].max,
+ (u32)si->tch_hdr[i].bofs,
+ si->tch_hdr[i].report);
+
+ } else {
+ si->tch_hdr[i].report = 0;
+ }
+ }
+
+ report = find_report_desc(cd, tch_collection_usage_page);
+ if (report) {
+ si->desc.tch_report_id = report->id;
+ si->desc.tch_record_size = report->record_size / 8;
+ si->desc.tch_header_size = (report->header_size / 8) + 3;
+ } else {
+ si->desc.tch_report_id = HID_TOUCH_REPORT_ID;
+ si->desc.tch_record_size = TOUCH_REPORT_SIZE;
+ si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE;
+ }
+
+ report = find_report_desc(cd, btn_collection_usage_page);
+ if (report)
+ si->desc.btn_report_id = report->id;
+ else
+ si->desc.btn_report_id = HID_BTN_REPORT_ID;
+
+ for (i = 0; i < cd->num_hid_reports; i++) {
+ struct cyttsp5_hid_report *report = cd->hid_reports[i];
+
+ switch (report->id) {
+ case HID_WAKEUP_REPORT_ID:
+ cd->features.easywake = 1;
+ break;
+ case HID_NOISE_METRIC_REPORT_ID:
+ cd->features.noise_metric = 1;
+ break;
+ case HID_TRACKING_HEATMAP_REPOR_ID:
+ cd->features.tracking_heatmap = 1;
+ break;
+ case HID_SENSOR_DATA_REPORT_ID:
+ cd->features.sensor_data = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "Features: easywake:%d noise_metric:%d tracking_heatmap:%d sensor_data:%d\n",
+ cd->features.easywake, cd->features.noise_metric,
+ cd->features.tracking_heatmap,
+ cd->features.sensor_data);
+
+ return 0;
+}
+
+static int cyttsp5_get_report_descriptor_(struct cyttsp5_core_data *cd)
+{
+ struct device *dev = cd->dev;
+ u8 cmd[2];
+ int rc;
+ int t;
+
+ /* Read report descriptor length and version */
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 1;
+ mutex_unlock(&cd->system_lock);
+
+ /* Set report descriptor register */
+ memcpy(cmd, &cd->hid_desc.report_desc_register,
+ sizeof(cd->hid_desc.report_desc_register));
+
+ rc = cyttsp5_adap_write_read_specific(cd, 2, cmd, NULL);
+ if (rc) {
+ dev_err(dev, "%s: failed to get HID descriptor length and version, rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+ msecs_to_jiffies(CY_HID_GET_REPORT_DESCRIPTOR_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: HID get descriptor timed out\n",
+ __func__);
+ rc = -ETIME;
+ goto error;
+ }
+
+ cyttsp5_pr_buf(cd->dev, cd->response_buf,
+ cd->hid_core.hid_report_desc_len, "Report Desc");
+
+ rc = parse_report_descriptor(cd, cd->response_buf + 3,
+ get_unaligned_le16(&cd->response_buf[0]) - 3);
+ if (rc) {
+ dev_err(cd->dev, "%s: Error parsing report descriptor r=%d\n",
+ __func__, rc);
+ }
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: %d reports found in descriptor\n",
+ __func__, cd->num_hid_reports);
+
+ for (t = 0; t < cd->num_hid_reports; t++) {
+ struct cyttsp5_hid_report *report = cd->hid_reports[t];
+ int j;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2,
+ "Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n",
+ t, report->type, report->id,
+ report->size, report->num_fields,
+ report->record_field_index, report->header_size,
+ report->record_size, report->usage_page);
+
+ for (j = 0; j < report->num_fields; j++) {
+ struct cyttsp5_hid_field *field = report->fields[j];
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2,
+ " Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
+ j, field->report_count, field->report_size,
+ field->offset, field->data_type,
+ field->logical_min, field->logical_max,
+ field->usage_page);
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, " Collections Phys:%08X App:%08X Log:%08X\n",
+ field->collection_usage_pages
+ [HID_COLLECTION_PHYSICAL],
+ field->collection_usage_pages
+ [HID_COLLECTION_APPLICATION],
+ field->collection_usage_pages
+ [HID_COLLECTION_LOGICAL]);
+ }
+ }
+
+ rc = setup_report_descriptor(cd);
+
+ /* Free it for now */
+ cyttsp5_free_hid_reports_(cd);
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: %d reports found in descriptor\n",
+ __func__, cd->num_hid_reports);
+
+ goto exit;
+
+error:
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+exit:
+ return rc;
+}
+
+static int cyttsp5_get_mode(struct cyttsp5_core_data *cd,
+ struct cyttsp5_hid_desc *desc)
+{
+ if (desc->packet_id == CY_HID_APP_REPORT_ID)
+ return CY_MODE_OPERATIONAL;
+ else if (desc->packet_id == CY_HID_BL_REPORT_ID)
+ return CY_MODE_BOOTLOADER;
+
+ return CY_MODE_UNKNOWN;
+}
+
+static int _cyttsp5_request_get_mode(struct device *dev, int protect, u8 *mode)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int rc;
+
+ if (protect)
+ rc = cyttsp5_get_hid_descriptor(cd, &cd->hid_desc);
+ else
+ rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+
+ if (rc)
+ *mode = CY_MODE_UNKNOWN;
+ else
+ *mode = cyttsp5_get_mode(cd, &cd->hid_desc);
+
+ return rc;
+}
+
+static void cyttsp5_queue_startup_(struct cyttsp5_core_data *cd)
+{
+ if (cd->startup_state == STARTUP_NONE) {
+ cd->startup_state = STARTUP_QUEUED;
+ schedule_work(&cd->startup_work);
+ dev_info(cd->dev, "%s: cyttsp5_startup queued\n", __func__);
+ } else {
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: startup_state = %d\n",
+ __func__, cd->startup_state);
+ }
+}
+
+static void cyttsp5_queue_startup(struct cyttsp5_core_data *cd)
+{
+ mutex_lock(&cd->system_lock);
+ cyttsp5_queue_startup_(cd);
+ mutex_unlock(&cd->system_lock);
+}
+
+static void call_atten_cb(struct cyttsp5_core_data *cd,
+ enum cyttsp5_atten_type type, int mode)
+{
+ struct atten_node *atten, *atten_n;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: check list type=%d mode=%d\n",
+ __func__, type, mode);
+ spin_lock(&cd->spinlock);
+ list_for_each_entry_safe(atten, atten_n,
+ &cd->atten_list[type], node) {
+ if (!mode || atten->mode & mode) {
+ spin_unlock(&cd->spinlock);
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: attention for '%s'",
+ __func__, dev_name(atten->dev));
+ atten->func(atten->dev);
+ spin_lock(&cd->spinlock);
+ }
+ }
+ spin_unlock(&cd->spinlock);
+}
+
+static void cyttsp5_start_wd_timer(struct cyttsp5_core_data *cd)
+{
+ if (!cd->watchdog_interval)
+ return;
+
+ mod_timer(&cd->watchdog_timer, jiffies +
+ msecs_to_jiffies(cd->watchdog_interval));
+}
+
+static void cyttsp5_stop_wd_timer(struct cyttsp5_core_data *cd)
+{
+ if (!cd->watchdog_interval)
+ return;
+
+ /*
+ * Ensure we wait until the watchdog timer
+ * running on a different CPU finishes
+ */
+ del_timer_sync(&cd->watchdog_timer);
+ cancel_work_sync(&cd->watchdog_work);
+ del_timer_sync(&cd->watchdog_timer);
+}
+
+static int start_fw_upgrade(void *data)
+{
+ struct cyttsp5_core_data *cd = (struct cyttsp5_core_data *)data;
+
+ call_atten_cb(cd, CY_ATTEN_LOADER, 0);
+ return 0;
+}
+
+static void cyttsp5_watchdog_work(struct work_struct *work)
+{
+ struct cyttsp5_core_data *cd =
+ container_of(work, struct cyttsp5_core_data,
+ watchdog_work);
+ int rc;
+ /*fix CDT207254
+ *if found the current sleep_state is SS_SLEEPING
+ *then no need to request_exclusive, directly return
+ */
+ if (cd->sleep_state == SS_SLEEPING)
+ return;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ goto queue_startup;
+ }
+
+ rc = cyttsp5_hid_output_null_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+
+queue_startup:
+ if (rc) {
+ dev_err(cd->dev,
+ "%s: failed to access device in watchdog timer r=%d\n",
+ __func__, rc);
+
+ /* Already tried FW upgrade because of watchdog but failed */
+ if (cd->startup_retry_count > CY_WATCHDOG_RETRY_COUNT)
+ return;
+
+ if (cd->startup_retry_count++ < CY_WATCHDOG_RETRY_COUNT)
+ cyttsp5_queue_startup(cd);
+ else
+ kthread_run(start_fw_upgrade, cd, "cyttp5_loader");
+
+ return;
+ }
+
+ cyttsp5_start_wd_timer(cd);
+}
+
+static void cyttsp5_watchdog_timer(unsigned long handle)
+{
+ struct cyttsp5_core_data *cd = (struct cyttsp5_core_data *)handle;
+
+ if (!cd)
+ return;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Watchdog timer triggered\n",
+ __func__);
+
+ if (!work_pending(&cd->watchdog_work))
+ schedule_work(&cd->watchdog_work);
+}
+
+static int cyttsp5_put_device_into_easy_wakeup_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+ u8 status = 0;
+
+ mutex_lock(&cd->system_lock);
+ cd->wait_until_wake = 0;
+ mutex_unlock(&cd->system_lock);
+
+ rc = cyttsp5_hid_output_enter_easywake_state_(cd,
+ cd->easy_wakeup_gesture, &status);
+ if (rc || status == 0)
+ return -EBUSY;
+
+ return rc;
+}
+
+static int cyttsp5_put_device_into_deep_sleep_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = cyttsp5_hid_cmd_set_power_(cd, HID_POWER_SLEEP);
+ if (rc)
+ rc = -EBUSY;
+ return rc;
+}
+
+static int cyttsp5_put_device_into_sleep_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture))
+ rc = cyttsp5_put_device_into_deep_sleep_(cd);
+ else
+ rc = cyttsp5_put_device_into_easy_wakeup_(cd);
+
+ return rc;
+}
+
+static int cyttsp5_core_poweroff_device_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ if (cd->irq_enabled) {
+ cd->irq_enabled = false;
+ disable_irq_nosync(cd->irq);
+ }
+
+ rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, 0);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: HW Power down fails r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+static int cyttsp5_core_sleep_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ if (cd->sleep_state == SS_SLEEP_OFF) {
+ cd->sleep_state = SS_SLEEPING;
+ } else {
+ mutex_unlock(&cd->system_lock);
+ return 1;
+ }
+ mutex_unlock(&cd->system_lock);
+
+ /* Ensure watchdog and startup works stopped */
+ cyttsp5_stop_wd_timer(cd);
+ cancel_work_sync(&cd->startup_work);
+ cyttsp5_stop_wd_timer(cd);
+
+ if (cd->cpdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP)
+ rc = cyttsp5_core_poweroff_device_(cd);
+ else
+ rc = cyttsp5_put_device_into_sleep_(cd);
+
+ mutex_lock(&cd->system_lock);
+ cd->sleep_state = SS_SLEEP_ON;
+ mutex_unlock(&cd->system_lock);
+
+ return rc;
+}
+
+static int cyttsp5_core_sleep(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_core_sleep_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+ else
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n",
+ __func__);
+
+ return rc;
+}
+
+static int cyttsp5_wakeup_host(struct cyttsp5_core_data *cd)
+{
+#ifndef EASYWAKE_TSG6
+ /* TSG5 EasyWake */
+ int rc = 0;
+ int event_id;
+ int size = get_unaligned_le16(&cd->input_buf[0]);
+
+ /* Validate report */
+ if (size != 4 || cd->input_buf[2] != 4)
+ rc = -EINVAL;
+
+ cd->wake_initiated_by_device = 1;
+ event_id = cd->input_buf[3];
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: e=%d, rc=%d\n",
+ __func__, event_id, rc);
+
+ if (rc) {
+ cyttsp5_core_sleep_(cd);
+ goto exit;
+ }
+
+ /* attention WAKE */
+ call_atten_cb(cd, CY_ATTEN_WAKE, 0);
+exit:
+ return rc;
+#else
+ /* TSG6 FW1.3 EasyWake */
+ int rc = 0;
+ int i = 0;
+ int report_length;
+
+ /* Validate report */
+ if (cd->input_buf[2] != 4)
+ rc = -EINVAL;
+
+ cd->wake_initiated_by_device = 1;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: rc=%d\n", __func__, rc);
+
+ if (rc) {
+ cyttsp5_core_sleep_(cd);
+ goto exit;
+ }
+
+ /* Get gesture id and gesture data length */
+ cd->gesture_id = cd->input_buf[3];
+ report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]);
+ cd->gesture_data_length = report_length - 4;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: gesture_id = %d, gesture_data_length = %d\n",
+ __func__, cd->gesture_id, cd->gesture_data_length);
+
+ for (i = 0; i < cd->gesture_data_length; i++)
+ cd->gesture_data[i] = cd->input_buf[4 + i];
+
+ /* attention WAKE */
+ call_atten_cb(cd, CY_ATTEN_WAKE, 0);
+exit:
+ return rc;
+#endif
+}
+
+static void cyttsp5_get_touch_axis(struct cyttsp5_core_data *cd,
+ int *axis, int size, int max, u8 *data, int bofs)
+{
+ int nbyte;
+ int next;
+
+ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+ *axis = *axis + ((data[next] >> bofs) << (nbyte * 8));
+ next++;
+ }
+
+ *axis &= max - 1;
+}
+
+static int move_tracking_hetmap_data(struct cyttsp5_core_data *cd,
+ struct cyttsp5_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+ int size = get_unaligned_le16(&cd->input_buf[0]);
+
+ if (size)
+ tthe_print(cd, cd->input_buf, size, "THM=");
+#endif
+ memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
+ return 0;
+}
+
+static int move_sensor_data(struct cyttsp5_core_data *cd,
+ struct cyttsp5_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+ int size = get_unaligned_le16(&cd->input_buf[0]);
+
+ if (size)
+ tthe_print(cd, cd->input_buf, size, "sensor_monitor=");
+#endif
+ memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
+ return 0;
+}
+
+static int move_button_data(struct cyttsp5_core_data *cd,
+ struct cyttsp5_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+ int size = get_unaligned_le16(&cd->input_buf[0]);
+
+ if (size)
+ tthe_print(cd, cd->input_buf, size, "OpModeData=");
+#endif
+ memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE);
+ cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE,
+ "xy_mode");
+
+ memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE],
+ BTN_REPORT_SIZE);
+ cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_data, BTN_REPORT_SIZE, "xy_data");
+ return 0;
+}
+
+static int move_touch_data(struct cyttsp5_core_data *cd,
+ struct cyttsp5_sysinfo *si)
+{
+ int max_tch = si->sensing_conf_data.max_tch;
+ int num_cur_tch;
+ int length;
+ struct cyttsp5_tch_abs_params *tch = &si->tch_hdr[CY_TCH_NUM];
+#ifdef TTHE_TUNER_SUPPORT
+ int size = get_unaligned_le16(&cd->input_buf[0]);
+
+ if (size)
+ tthe_print(cd, cd->input_buf, size, "OpModeData=");
+#endif
+
+ memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size);
+ cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_mode, si->desc.tch_header_size,
+ "xy_mode");
+
+ cyttsp5_get_touch_axis(cd, &num_cur_tch, tch->size,
+ tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs);
+ if (unlikely(num_cur_tch > max_tch))
+ num_cur_tch = max_tch;
+
+ length = num_cur_tch * si->desc.tch_record_size;
+
+ memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length);
+ cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_data, length, "xy_data");
+ return 0;
+}
+
+static int parse_touch_input(struct cyttsp5_core_data *cd, int size)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ int report_id = cd->input_buf[2];
+ int rc = -EINVAL;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Received touch report\n",
+ __func__);
+ if (!si->ready) {
+ dev_err(cd->dev,
+ "%s: Need system information to parse touches\n",
+ __func__);
+ return 0;
+ }
+
+ if (!si->xy_mode || !si->xy_data)
+ return rc;
+
+ if (report_id == si->desc.tch_report_id)
+ rc = move_touch_data(cd, si);
+ else if (report_id == si->desc.btn_report_id)
+ rc = move_button_data(cd, si);
+ else if (report_id == HID_SENSOR_DATA_REPORT_ID)
+ rc = move_sensor_data(cd, si);
+ else if (report_id == HID_TRACKING_HEATMAP_REPOR_ID)
+ rc = move_tracking_hetmap_data(cd, si);
+
+ if (rc)
+ return rc;
+
+ /* attention IRQ */
+ call_atten_cb(cd, CY_ATTEN_IRQ, cd->mode);
+
+ return 0;
+}
+
+static int parse_command_input(struct cyttsp5_core_data *cd, int size)
+{
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Received cmd interrupt\n",
+ __func__);
+
+ memcpy(cd->response_buf, cd->input_buf, size);
+
+ mutex_lock(&cd->system_lock);
+ cd->hid_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+ wake_up(&cd->wait_q);
+
+ return 0;
+}
+
+static int cyttsp5_parse_input(struct cyttsp5_core_data *cd)
+{
+ int report_id;
+ int is_command = 0;
+ int size;
+
+ size = get_unaligned_le16(&cd->input_buf[0]);
+
+ /* check reset */
+ if (size == 0) {
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Reset complete\n",
+ __func__);
+ memcpy(cd->response_buf, cd->input_buf, 2);
+ mutex_lock(&cd->system_lock);
+ if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) {
+ mutex_unlock(&cd->system_lock);
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Device Initiated Reset\n",
+ __func__);
+ return 0;
+ }
+
+ cd->hid_reset_cmd_state = 0;
+ if (cd->hid_cmd_state == HID_OUTPUT_START_BOOTLOADER + 1
+ || cd->hid_cmd_state ==
+ HID_OUTPUT_BL_LAUNCH_APP + 1
+ || cd->hid_cmd_state ==
+ HID_OUTPUT_USER_CMD + 1)
+ cd->hid_cmd_state = 0;
+ wake_up(&cd->wait_q);
+ mutex_unlock(&cd->system_lock);
+ return 0;
+ } else if (size == 2 || size >= CY_PIP_1P7_EMPTY_BUF)
+ /*
+ * Before PIP 1.7, empty buffer is 0x0002;
+ * From PIP 1.7, empty buffer is 0xFFXX
+ */
+ return 0;
+
+ report_id = cd->input_buf[2];
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: report_id:%X\n",
+ __func__, report_id);
+
+ /* Check wake-up report */
+ if (report_id == HID_WAKEUP_REPORT_ID) {
+ cyttsp5_wakeup_host(cd);
+ return 0;
+ }
+
+ /* update watchdog expire time */
+ mod_timer_pending(&cd->watchdog_timer, jiffies +
+ msecs_to_jiffies(cd->watchdog_interval));
+
+ if (report_id != cd->sysinfo.desc.tch_report_id
+ && report_id != cd->sysinfo.desc.btn_report_id
+ && report_id != HID_SENSOR_DATA_REPORT_ID
+ && report_id != HID_TRACKING_HEATMAP_REPOR_ID)
+ is_command = 1;
+
+ if (unlikely(is_command)) {
+ parse_command_input(cd, size);
+ return 0;
+ }
+ parse_touch_input(cd, size);
+ return 0;
+}
+
+static int cyttsp5_read_input(struct cyttsp5_core_data *cd)
+{
+ struct device *dev = cd->dev;
+ int rc;
+ int t;
+
+ /* added as workaround to CDT170960: easywake failure */
+ /* Interrupt for easywake, wait for bus controller to wake */
+ mutex_lock(&cd->system_lock);
+ if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) {
+ if (cd->sleep_state == SS_SLEEP_ON) {
+ mutex_unlock(&cd->system_lock);
+ if (!dev->power.is_suspended)
+ goto read;
+ t = wait_event_timeout(cd->wait_q,
+ (cd->wait_until_wake == 1),
+ msecs_to_jiffies(2000));
+ if (IS_TMO(t))
+ cyttsp5_queue_startup(cd);
+ goto read;
+ }
+ }
+ mutex_unlock(&cd->system_lock);
+
+read:
+ rc = cyttsp5_adap_read_default_nosize(cd, cd->input_buf, CY_MAX_INPUT);
+ if (rc) {
+ dev_err(dev, "%s: Error getting report, r=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Read input successfully\n",
+ __func__);
+ return rc;
+}
+
+static bool cyttsp5_check_irq_asserted(struct cyttsp5_core_data *cd)
+{
+#ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP
+ /*
+ * Workaround for FW defect, CDT165308
+ * bl_launch app creates a glitch in IRQ line
+ */
+ if (cd->hid_cmd_state == HID_OUTPUT_BL_LAUNCH_APP + 1
+ && cd->cpdata->irq_stat){
+ /*
+ * in X1S panel and GC1546 panel, the width for the INT
+ * glitch is about 4us,the normal INT width of response
+ * will last more than 200us, so use 10us delay
+ * for distinguish the glitch the normal INT is enough.
+ */
+ udelay(10);
+ if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
+ != CY_IRQ_ASSERTED_VALUE)
+ return false;
+ }
+#endif
+ return true;
+}
+
+
+static irqreturn_t cyttsp5_irq(int irq, void *handle)
+{
+ struct cyttsp5_core_data *cd = handle;
+ int rc;
+
+ if (!cyttsp5_check_irq_asserted(cd))
+ return IRQ_HANDLED;
+
+ rc = cyttsp5_read_input(cd);
+ if (!rc)
+ cyttsp5_parse_input(cd);
+
+ return IRQ_HANDLED;
+}
+
+int _cyttsp5_subscribe_attention(struct device *dev,
+ enum cyttsp5_atten_type type, char *id, int (*func)(struct device *),
+ int mode)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct atten_node *atten, *atten_new;
+
+ atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL);
+ if (!atten_new)
+ return -ENOMEM;
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s from '%s'\n", __func__,
+ dev_name(cd->dev));
+
+ spin_lock(&cd->spinlock);
+ list_for_each_entry(atten, &cd->atten_list[type], node) {
+ if (atten->id == id && atten->mode == mode) {
+ spin_unlock(&cd->spinlock);
+ kfree(atten_new);
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: %s=%p %s=%d\n",
+ __func__,
+ "already subscribed attention",
+ dev, "mode", mode);
+
+ return 0;
+ }
+ }
+
+ atten_new->id = id;
+ atten_new->dev = dev;
+ atten_new->mode = mode;
+ atten_new->func = func;
+
+ list_add(&atten_new->node, &cd->atten_list[type]);
+ spin_unlock(&cd->spinlock);
+
+ return 0;
+}
+
+int _cyttsp5_unsubscribe_attention(struct device *dev,
+ enum cyttsp5_atten_type type, char *id, int (*func)(struct device *),
+ int mode)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct atten_node *atten, *atten_n;
+
+ spin_lock(&cd->spinlock);
+ list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) {
+ if (atten->id == id && atten->mode == mode) {
+ list_del(&atten->node);
+ spin_unlock(&cd->spinlock);
+ kfree(atten);
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: %s=%p %s=%d\n",
+ __func__,
+ "unsub for atten->dev", atten->dev,
+ "atten->mode", atten->mode);
+ return 0;
+ }
+ }
+ spin_unlock(&cd->spinlock);
+
+ return -ENODEV;
+}
+
+static int _cyttsp5_request_exclusive(struct device *dev,
+ int timeout_ms)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return request_exclusive(cd, (void *)dev, timeout_ms);
+}
+
+static int _cyttsp5_release_exclusive(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return release_exclusive(cd, (void *)dev);
+}
+
+static int cyttsp5_reset(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ /* reset hardware */
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: reset hw...\n", __func__);
+ rc = cyttsp5_hw_reset(cd);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: %s dev='%s' r=%d\n", __func__,
+ "Fail hw reset", dev_name(cd->dev), rc);
+ return rc;
+}
+
+static int cyttsp5_reset_and_wait(struct cyttsp5_core_data *cd)
+{
+ int rc;
+ int t;
+
+ mutex_lock(&cd->system_lock);
+ cd->hid_reset_cmd_state = 1;
+ mutex_unlock(&cd->system_lock);
+
+ rc = cyttsp5_reset(cd);
+ if (rc < 0)
+ goto error;
+
+ t = wait_event_timeout(cd->wait_q, (cd->hid_reset_cmd_state == 0),
+ msecs_to_jiffies(CY_HID_RESET_TIMEOUT));
+ if (IS_TMO(t)) {
+ dev_err(cd->dev, "%s: reset timed out\n",
+ __func__);
+ rc = -ETIME;
+ goto error;
+ }
+
+ goto exit;
+
+error:
+ mutex_lock(&cd->system_lock);
+ cd->hid_reset_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+exit:
+ return rc;
+}
+
+/*
+ * returns err if refused or timeout(core uses fixed timeout period) occurs;
+ * blocks until ISR occurs
+ */
+static int _cyttsp5_request_reset(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ cd->hid_reset_cmd_state = 1;
+ mutex_unlock(&cd->system_lock);
+
+ rc = cyttsp5_reset(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on h/w reset r=%d\n",
+ __func__, rc);
+ mutex_lock(&cd->system_lock);
+ cd->hid_reset_cmd_state = 0;
+ mutex_unlock(&cd->system_lock);
+ }
+
+ return rc;
+}
+
+/*
+ * returns err if refused ; if no error then restart has completed
+ * and system is in normal operating mode
+ */
+static int _cyttsp5_request_restart(struct device *dev, bool wait)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ cyttsp5_queue_startup(cd);
+
+ if (wait)
+ wait_event(cd->wait_q, cd->startup_state == STARTUP_NONE);
+
+ return 0;
+}
+
+/*
+ * returns NULL if sysinfo has not been acquired from the device yet
+ */
+struct cyttsp5_sysinfo *_cyttsp5_request_sysinfo(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (cd->sysinfo.ready)
+ return &cd->sysinfo;
+
+ return NULL;
+}
+
+static struct cyttsp5_loader_platform_data *_cyttsp5_request_loader_pdata(
+ struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return cd->pdata->loader_pdata;
+}
+
+static int _cyttsp5_request_start_wd(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ cyttsp5_start_wd_timer(cd);
+ return 0;
+}
+
+static int _cyttsp5_request_stop_wd(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ cyttsp5_stop_wd_timer(cd);
+ return 0;
+}
+
+static int cyttsp5_core_wake_device_from_deep_sleep_(
+ struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = cyttsp5_hid_cmd_set_power_(cd, HID_POWER_ON);
+ if (rc)
+ rc = -EAGAIN;
+
+ /* Prevent failure on sequential wake/sleep requests from OS */
+ msleep(20);
+
+ return rc;
+}
+
+static int cyttsp5_core_wake_device_(struct cyttsp5_core_data *cd)
+{
+ if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) {
+ mutex_lock(&cd->system_lock);
+ cd->wait_until_wake = 1;
+ mutex_unlock(&cd->system_lock);
+ wake_up(&cd->wait_q);
+ msleep(20);
+
+ if (cd->wake_initiated_by_device) {
+ cd->wake_initiated_by_device = 0;
+ return 0;
+ }
+ }
+
+ return cyttsp5_core_wake_device_from_deep_sleep_(cd);
+}
+
+static int cyttsp5_restore_parameters_(struct cyttsp5_core_data *cd)
+{
+ struct param_node *param;
+ int rc = 0;
+
+ if (!(cd->cpdata->flags & CY_CORE_FLAG_RESTORE_PARAMETERS))
+ goto exit;
+
+ spin_lock(&cd->spinlock);
+ list_for_each_entry(param, &cd->param_list, node) {
+ spin_unlock(&cd->spinlock);
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Parameter id:%d value:%d\n",
+ __func__, param->id, param->value);
+ rc = cyttsp5_hid_output_set_param_(cd, param->id,
+ param->value, param->size);
+ if (rc)
+ goto exit;
+ spin_lock(&cd->spinlock);
+ }
+ spin_unlock(&cd->spinlock);
+exit:
+ return rc;
+}
+
+static int _fast_startup(struct cyttsp5_core_data *cd)
+{
+ int retry = CY_CORE_STARTUP_RETRY_COUNT;
+ int rc;
+
+reset:
+ if (retry != CY_CORE_STARTUP_RETRY_COUNT)
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Retry %d\n",
+ __func__, CY_CORE_STARTUP_RETRY_COUNT - retry);
+
+ rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc);
+
+ if (cd->mode == CY_MODE_BOOTLOADER) {
+ dev_info(cd->dev, "%s: Bootloader mode\n", __func__);
+ rc = cyttsp5_hid_output_bl_launch_app_(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on launch app r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+ if (rc < 0) {
+ dev_err(cd->dev,
+ "%s: Error on getting HID descriptor r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc);
+ if (cd->mode == CY_MODE_BOOTLOADER) {
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ }
+
+ rc = cyttsp5_restore_parameters_(cd);
+ if (rc)
+ dev_err(cd->dev, "%s: failed to restore parameters rc=%d\n",
+ __func__, rc);
+
+exit:
+ return rc;
+}
+
+static int cyttsp5_core_poweron_device_(struct cyttsp5_core_data *cd)
+{
+ struct device *dev = cd->dev;
+ int rc;
+
+ rc = cd->cpdata->power(cd->cpdata, 1, dev, 0);
+ if (rc < 0) {
+ dev_err(dev, "%s: HW Power up fails r=%d\n", __func__, rc);
+ goto exit;
+ }
+
+ if (!cd->irq_enabled) {
+ cd->irq_enabled = true;
+ enable_irq(cd->irq);
+ }
+
+ rc = _fast_startup(cd);
+exit:
+ return rc;
+}
+
+static int cyttsp5_core_wake_(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ if (cd->sleep_state == SS_SLEEP_ON) {
+ cd->sleep_state = SS_WAKING;
+ } else {
+ mutex_unlock(&cd->system_lock);
+ return 1;
+ }
+ mutex_unlock(&cd->system_lock);
+
+ if (cd->cpdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP)
+ rc = cyttsp5_core_poweron_device_(cd);
+ else
+ rc = cyttsp5_core_wake_device_(cd);
+
+ mutex_lock(&cd->system_lock);
+ cd->sleep_state = SS_SLEEP_OFF;
+ mutex_unlock(&cd->system_lock);
+
+ cyttsp5_start_wd_timer(cd);
+ return rc;
+}
+
+static int cyttsp5_core_wake(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ return rc;
+ }
+
+ rc = cyttsp5_core_wake_(cd);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+ else
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n",
+ __func__);
+
+ return rc;
+}
+
+static int cyttsp5_get_ic_crc_(struct cyttsp5_core_data *cd, u8 ebid)
+{
+ struct cyttsp5_sysinfo *si = &cd->sysinfo;
+ int rc;
+ u8 status;
+ u16 calculated_crc = 0;
+ u16 stored_crc = 0;
+
+ rc = cyttsp5_hid_output_suspend_scanning_(cd);
+ if (rc)
+ goto error;
+
+ rc = cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, &status,
+ &calculated_crc, &stored_crc);
+ if (rc)
+ goto exit;
+
+ if (status) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ si->ttconfig.crc = stored_crc;
+
+exit:
+ cyttsp5_hid_output_resume_scanning_(cd);
+error:
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: CRC: ebid:%d, crc:0x%04X\n",
+ __func__, ebid, si->ttconfig.crc);
+ return rc;
+}
+
+static int cyttsp5_check_and_deassert_int(struct cyttsp5_core_data *cd)
+{
+ u16 size;
+ u8 buf[2];
+ u8 *p;
+ u8 retry = 3;
+ int rc;
+
+ do {
+ rc = cyttsp5_adap_read_default(cd, buf, 2);
+ if (rc < 0)
+ return rc;
+ size = get_unaligned_le16(&buf[0]);
+
+ if (size == 2 || size == 0 || size >= CY_PIP_1P7_EMPTY_BUF)
+ return 0;
+
+ p = kzalloc(size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ rc = cyttsp5_adap_read_default(cd, p, size);
+ kfree(p);
+ if (rc < 0)
+ return rc;
+ } while (retry--);
+
+ return -EINVAL;
+}
+
+static int cyttsp5_startup_(struct cyttsp5_core_data *cd, bool reset)
+{
+ int retry = CY_CORE_STARTUP_RETRY_COUNT;
+ int rc;
+ bool detected = false;
+
+#ifdef TTHE_TUNER_SUPPORT
+ tthe_print(cd, NULL, 0, "enter startup");
+#endif
+
+ cyttsp5_stop_wd_timer(cd);
+
+reset:
+ if (retry != CY_CORE_STARTUP_RETRY_COUNT)
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Retry %d\n",
+ __func__, CY_CORE_STARTUP_RETRY_COUNT - retry);
+
+ rc = cyttsp5_check_and_deassert_int(cd);
+
+ if (reset || retry != CY_CORE_STARTUP_RETRY_COUNT) {
+ /* reset hardware */
+ rc = cyttsp5_reset_and_wait(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on h/w reset r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ }
+
+ rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc);
+
+ detected = true;
+
+ /* Switch to bootloader mode to get Panel ID */
+ if (cd->mode == CY_MODE_OPERATIONAL) {
+ rc = cyttsp5_hid_output_start_bootloader_(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on start bootloader r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ dev_info(cd->dev, "%s: Bootloader mode\n", __func__);
+ }
+
+ cyttsp5_hid_output_bl_get_panel_id_(cd, &cd->panel_id);
+
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Panel ID: 0x%02X\n",
+ __func__, cd->panel_id);
+
+ rc = cyttsp5_hid_output_bl_launch_app_(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on launch app r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc);
+ if (rc < 0) {
+ dev_err(cd->dev,
+ "%s: Error on getting HID descriptor r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc);
+ if (cd->mode == CY_MODE_BOOTLOADER) {
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ mutex_lock(&cd->system_lock);
+ /* Read descriptor lengths */
+ cd->hid_core.hid_report_desc_len =
+ le16_to_cpu(cd->hid_desc.report_desc_len);
+ cd->hid_core.hid_max_input_len =
+ le16_to_cpu(cd->hid_desc.max_input_len);
+ cd->hid_core.hid_max_output_len =
+ le16_to_cpu(cd->hid_desc.max_output_len);
+
+ cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc);
+ if (cd->mode == CY_MODE_OPERATIONAL)
+ dev_info(cd->dev, "%s: Operational mode\n", __func__);
+ else if (cd->mode == CY_MODE_BOOTLOADER)
+ dev_info(cd->dev, "%s: Bootloader mode\n", __func__);
+ else if (cd->mode == CY_MODE_UNKNOWN) {
+ dev_err(cd->dev, "%s: Unknown mode\n", __func__);
+ rc = -ENODEV;
+ mutex_unlock(&cd->system_lock);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+ mutex_unlock(&cd->system_lock);
+
+ dev_info(cd->dev, "%s: Reading report descriptor\n", __func__);
+ rc = cyttsp5_get_report_descriptor_(cd);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: Error on getting report descriptor r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ if (!cd->features.easywake)
+ cd->easy_wakeup_gesture = CY_CORE_EWG_NONE;
+
+ rc = cyttsp5_hid_output_get_sysinfo_(cd);
+ if (rc) {
+ dev_err(cd->dev, "%s: Error on getting sysinfo r=%d\n",
+ __func__, rc);
+ if (retry--)
+ goto reset;
+ goto exit;
+ }
+
+ dev_info(cd->dev, "cyttsp5 Protocol Version: %d.%d\n",
+ cd->sysinfo.cydata.pip_ver_major,
+ cd->sysinfo.cydata.pip_ver_minor);
+
+ /* Read config version directly if PIP version < 1.2 */
+ if (!IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) {
+ rc = cyttsp5_get_config_ver_(cd);
+ if (rc)
+ dev_err(cd->dev, "%s: failed to read config version rc=%d\n",
+ __func__, rc);
+ }
+
+ rc = cyttsp5_get_ic_crc_(cd, CY_TCH_PARM_EBID);
+ if (rc)
+ dev_err(cd->dev, "%s: failed to crc data rc=%d\n",
+ __func__, rc);
+
+ rc = cyttsp5_restore_parameters_(cd);
+ if (rc)
+ dev_err(cd->dev, "%s: failed to restore parameters rc=%d\n",
+ __func__, rc);
+
+ /* attention startup */
+ call_atten_cb(cd, CY_ATTEN_STARTUP, 0);
+
+exit:
+ if (!rc)
+ cd->startup_retry_count = 0;
+
+ cyttsp5_start_wd_timer(cd);
+
+ if (!detected)
+ rc = -ENODEV;
+
+#ifdef TTHE_TUNER_SUPPORT
+ tthe_print(cd, NULL, 0, "exit startup");
+#endif
+
+ return rc;
+}
+
+static int cyttsp5_startup(struct cyttsp5_core_data *cd, bool reset)
+{
+ int rc;
+
+ mutex_lock(&cd->system_lock);
+ cd->startup_state = STARTUP_RUNNING;
+ mutex_unlock(&cd->system_lock);
+
+ rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+ __func__, cd->exclusive_dev, cd->dev);
+ goto exit;
+ }
+
+ rc = cyttsp5_startup_(cd, reset);
+
+ if (release_exclusive(cd, cd->dev) < 0)
+ /* Don't return fail code, mode is already changed. */
+ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+ else
+ parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n",
+ __func__);
+
+exit:
+ mutex_lock(&cd->system_lock);
+ cd->startup_state = STARTUP_NONE;
+ mutex_unlock(&cd->system_lock);
+
+ /* Wake the waiters for end of startup */
+ wake_up(&cd->wait_q);
+
+ return rc;
+}
+
+static void cyttsp5_startup_work_function(struct work_struct *work)
+{
+ struct cyttsp5_core_data *cd = container_of(work,
+ struct cyttsp5_core_data, startup_work);
+ int rc;
+
+ rc = cyttsp5_startup(cd, true);
+ if (rc < 0)
+ dev_err(cd->dev, "%s: Fail queued startup r=%d\n",
+ __func__, rc);
+}
+
+/*
+ * CONFIG_PM_RUNTIME option is removed in 3.19.0.
+ */
+#if defined(CONFIG_PM_RUNTIME) || \
+ (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE)
+static int cyttsp5_core_rt_suspend(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int rc;
+
+ rc = cyttsp5_core_sleep(cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on sleep\n", __func__);
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static int cyttsp5_core_rt_resume(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int rc;
+
+ rc = cyttsp5_core_wake(cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on wake\n", __func__);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_PM_SLEEP)
+static int cyttsp5_core_suspend(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ cyttsp5_core_sleep(cd);
+
+ if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture))
+ return 0;
+
+ /*
+ * This will not prevent resume
+ * Required to prevent interrupts before i2c awake
+ */
+ disable_irq(cd->irq);
+ cd->irq_disabled = 1;
+
+ if (device_may_wakeup(dev)) {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s Device MAY wakeup\n",
+ __func__);
+ if (!enable_irq_wake(cd->irq))
+ cd->irq_wake = 1;
+ } else {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s Device MAY NOT wakeup\n",
+ __func__);
+ }
+
+ return 0;
+}
+
+static int cyttsp5_core_resume(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture))
+ goto exit;
+
+ /*
+ * I2C bus pm does not call suspend if device runtime suspended
+ * This flag is cover that case
+ */
+ if (cd->irq_disabled) {
+ enable_irq(cd->irq);
+ cd->irq_disabled = 0;
+ }
+
+ if (device_may_wakeup(dev)) {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s Device MAY wakeup\n",
+ __func__);
+ if (cd->irq_wake) {
+ disable_irq_wake(cd->irq);
+ cd->irq_wake = 0;
+ }
+ } else {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s Device MAY NOT wakeup\n",
+ __func__);
+ }
+
+exit:
+ cyttsp5_core_wake(cd);
+
+ return 0;
+}
+#endif
+
+#if NEED_SUSPEND_NOTIFIER
+static int cyttsp5_pm_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct cyttsp5_core_data *cd = container_of(nb,
+ struct cyttsp5_core_data, pm_notifier);
+
+ if (action == PM_SUSPEND_PREPARE) {
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Suspend prepare\n",
+ __func__);
+
+ /*
+ * If not runtime PM suspended, either call runtime
+ * PM suspend callback or wait until it finishes
+ */
+ if (!pm_runtime_suspended(cd->dev))
+ pm_runtime_suspend(cd->dev);
+
+ (void) cyttsp5_core_suspend(cd->dev);
+ }
+
+ return NOTIFY_DONE;
+}
+#endif
+
+const struct dev_pm_ops cyttsp5_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cyttsp5_core_suspend, cyttsp5_core_resume)
+ SET_RUNTIME_PM_OPS(cyttsp5_core_rt_suspend, cyttsp5_core_rt_resume,
+ NULL)
+};
+EXPORT_SYMBOL_GPL(cyttsp5_pm_ops);
+
+/*
+ * Show Firmware version via sysfs
+ */
+static ssize_t cyttsp5_ic_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_cydata *cydata = &cd->sysinfo.cydata;
+
+ return sprintf(buf,
+ "%s: 0x%02X\n"
+ "%s: 0x%02X\n"
+ "%s: 0x%08X\n"
+ "%s: 0x%04X\n"
+ "%s: 0x%02X\n"
+ "%s: 0x%02X\n"
+ "%s: 0x%02X\n"
+ "%s: 0x%02X\n",
+ "Firmware Major Version", cydata->fw_ver_major,
+ "Firmware Minor Version", cydata->fw_ver_minor,
+ "Revision Control Number", cydata->revctrl,
+ "Firmware Configuration Version", cydata->fw_ver_conf,
+ "Bootloader Major Version", cydata->bl_ver_major,
+ "Bootloader Minor Version", cydata->bl_ver_minor,
+ "Protocol Major Version", cydata->pip_ver_major,
+ "Protocol Minor Version", cydata->pip_ver_minor);
+}
+
+/*
+ * Show Driver version via sysfs
+ */
+static ssize_t cyttsp5_drv_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Driver: %s\nVersion: %s\nDate: %s\n",
+ cy_driver_core_name, cy_driver_core_version,
+ cy_driver_core_date);
+}
+
+/*
+ * HW reset via sysfs
+ */
+static ssize_t cyttsp5_hw_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int rc;
+
+ rc = cyttsp5_startup(cd, true);
+ if (rc < 0)
+ dev_err(dev, "%s: HW reset failed r=%d\n",
+ __func__, rc);
+
+ return size;
+}
+
+/*
+ * Show IRQ status via sysfs
+ */
+static ssize_t cyttsp5_hw_irq_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int retval;
+
+ if (cd->cpdata->irq_stat) {
+ retval = cd->cpdata->irq_stat(cd->cpdata, dev);
+ switch (retval) {
+ case 0:
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Interrupt line is LOW.\n");
+ case 1:
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Interrupt line is HIGH.\n");
+ default:
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Function irq_stat() returned %d.\n", retval);
+ }
+ }
+
+ return snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Function irq_stat() undefined.\n");
+}
+
+/*
+ * Show IRQ enable/disable status via sysfs
+ */
+static ssize_t cyttsp5_drv_irq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ mutex_lock(&cd->system_lock);
+ if (cd->irq_enabled)
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Driver interrupt is ENABLED\n");
+ else
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Driver interrupt is DISABLED\n");
+ mutex_unlock(&cd->system_lock);
+
+ return ret;
+}
+
+/*
+ * Enable/disable IRQ via sysfs
+ */
+static ssize_t cyttsp5_drv_irq_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ unsigned long value;
+ int retval = 0;
+
+ retval = kstrtoul(buf, 10, &value);
+ if (retval < 0) {
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ goto cyttsp5_drv_irq_store_error_exit;
+ }
+
+ mutex_lock(&cd->system_lock);
+ switch (value) {
+ case 0:
+ if (cd->irq_enabled) {
+ cd->irq_enabled = false;
+ /* Disable IRQ */
+ disable_irq_nosync(cd->irq);
+ dev_info(dev, "%s: Driver IRQ now disabled\n",
+ __func__);
+ } else
+ dev_info(dev, "%s: Driver IRQ already disabled\n",
+ __func__);
+ break;
+
+ case 1:
+ if (cd->irq_enabled == false) {
+ cd->irq_enabled = true;
+ /* Enable IRQ */
+ enable_irq(cd->irq);
+ dev_info(dev, "%s: Driver IRQ now enabled\n",
+ __func__);
+ } else
+ dev_info(dev, "%s: Driver IRQ already enabled\n",
+ __func__);
+ break;
+
+ default:
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ }
+ mutex_unlock(&(cd->system_lock));
+
+cyttsp5_drv_irq_store_error_exit:
+
+ return size;
+}
+
+/*
+ * Gets user input from sysfs and parse it
+ * return size of parsed output buffer
+ */
+
+#define CY_MAX_CONFIG_BYTES_DEC 256
+#define CYTTSP5_INPUT_ELEM_SZ_DEC 10
+
+static int cyttsp5_ic_parse_input_dec(struct device *dev, const char *buf,
+ size_t buf_size, u32 *ic_buf, size_t ic_buf_size)
+{
+ const char *pbuf = buf;
+ unsigned long value;
+ char scan_buf[CYTTSP5_INPUT_ELEM_SZ_DEC];
+ u32 i = 0;
+ u32 j;
+ int last = 0;
+ int ret;
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: pbuf=%p buf=%p size=%zu %s=%zu buf=%s\n",
+ __func__, pbuf, buf, buf_size, "scan buf size",
+ (size_t)CYTTSP5_INPUT_ELEM_SZ_DEC, buf);
+
+ while (pbuf <= (buf + buf_size)) {
+ if (i >= CY_MAX_CONFIG_BYTES_DEC) {
+ dev_err(dev, "%s: %s size=%d max=%d\n", __func__,
+ "Max cmd size exceeded", i,
+ CY_MAX_CONFIG_BYTES_DEC);
+ return -EINVAL;
+ }
+ if (i >= ic_buf_size) {
+ dev_err(dev, "%s: %s size=%d buf_size=%zu\n", __func__,
+ "Buffer size exceeded", i, ic_buf_size);
+ return -EINVAL;
+ }
+ while (((*pbuf == ' ') || (*pbuf == ','))
+ && (pbuf < (buf + buf_size))) {
+ last = *pbuf;
+ pbuf++;
+ }
+
+ if (pbuf >= (buf + buf_size))
+ break;
+
+ memset(scan_buf, 0, CYTTSP5_INPUT_ELEM_SZ_DEC);
+ if ((last == ',') && (*pbuf == ',')) {
+ dev_err(dev, "%s: %s \",,\" not allowed.\n", __func__,
+ "Invalid data format.");
+ return -EINVAL;
+ }
+ for (j = 0; j < (CYTTSP5_INPUT_ELEM_SZ_DEC - 1)
+ && (pbuf < (buf + buf_size))
+ && (*pbuf != ' ')
+ && (*pbuf != ','); j++) {
+ last = *pbuf;
+ scan_buf[j] = *pbuf++;
+ }
+ ret = kstrtoul(scan_buf, 10, &value);
+ if (ret < 0) {
+ dev_err(dev, "%s: Invalid data format.\n", __func__);
+ return ret;
+ }
+
+ ic_buf[i] = value;
+ i++;
+ }
+
+ return i;
+}
+
+/*
+ * Debugging options via sysfs
+ */
+static ssize_t cyttsp5_drv_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ unsigned long value;
+ int rc;
+ u8 return_data[8];
+ static u8 wd_disabled;
+ u32 input_data[2];
+ int length;
+
+ /*maximal input two data*/
+ length = cyttsp5_ic_parse_input_dec(dev, buf, size, input_data,
+ 3);
+ if (length <= 0) {
+ dev_err(dev, "%s: %s failed\n", __func__,
+ "cyttsp5_ic_parse_input_dec");
+ goto cyttsp5_drv_debug_store_exit;
+ }
+ value = input_data[0];
+
+
+ /* Start watchdog timer command */
+ if (value == CY_DBG_HID_START_WD) {
+ dev_info(dev, "%s: start watchdog (cd=%p)\n", __func__, cd);
+ wd_disabled = 0;
+ cyttsp5_start_wd_timer(cd);
+ goto cyttsp5_drv_debug_store_exit;
+ }
+
+ /* Stop watchdog timer temporarily */
+ cyttsp5_stop_wd_timer(cd);
+
+ if (value == CY_DBG_HID_STOP_WD) {
+ dev_info(dev, "%s: stop watchdog (cd=%p)\n", __func__, cd);
+ wd_disabled = 1;
+ goto cyttsp5_drv_debug_store_exit;
+ }
+
+ switch (value) {
+ case CY_DBG_SUSPEND:
+ dev_info(dev, "%s: SUSPEND (cd=%p)\n", __func__, cd);
+ rc = cyttsp5_core_sleep(cd);
+ if (rc)
+ dev_err(dev, "%s: Suspend failed rc=%d\n",
+ __func__, rc);
+ else
+ dev_info(dev, "%s: Suspend succeeded\n", __func__);
+ break;
+
+ case CY_DBG_RESUME:
+ dev_info(dev, "%s: RESUME (cd=%p)\n", __func__, cd);
+ rc = cyttsp5_core_wake(cd);
+ if (rc)
+ dev_err(dev, "%s: Resume failed rc=%d\n",
+ __func__, rc);
+ else
+ dev_info(dev, "%s: Resume succeeded\n", __func__);
+ break;
+ case CY_DBG_SOFT_RESET:
+ dev_info(dev, "%s: SOFT RESET (cd=%p)\n", __func__, cd);
+ rc = cyttsp5_hw_soft_reset(cd);
+ break;
+ case CY_DBG_RESET:
+ dev_info(dev, "%s: HARD RESET (cd=%p)\n", __func__, cd);
+ rc = cyttsp5_hw_hard_reset(cd);
+ break;
+ case CY_DBG_HID_RESET:
+ dev_info(dev, "%s: hid_reset (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_cmd_reset(cd);
+ break;
+ case CY_DBG_HID_SET_POWER_ON:
+ dev_info(dev, "%s: hid_set_power_on (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_cmd_set_power(cd, HID_POWER_ON);
+ wd_disabled = 0;
+ break;
+ case CY_DBG_HID_SET_POWER_SLEEP:
+ dev_info(dev, "%s: hid_set_power_off (cd=%p)\n", __func__, cd);
+ wd_disabled = 1;
+ cyttsp5_hid_cmd_set_power(cd, HID_POWER_SLEEP);
+ break;
+ case CY_DBG_HID_NULL:
+ dev_info(dev, "%s: hid_null (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_null(cd);
+ break;
+ case CY_DBG_HID_ENTER_BL:
+ dev_info(dev, "%s: start_bootloader (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_start_bootloader(cd);
+ break;
+ case CY_DBG_HID_SYSINFO:
+ dev_info(dev, "%s: get_sysinfo (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_get_sysinfo(cd);
+ break;
+ case CY_DBG_HID_SUSPEND_SCAN:
+ dev_info(dev, "%s: suspend_scanning (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_suspend_scanning(cd);
+ break;
+ case CY_DBG_HID_RESUME_SCAN:
+ dev_info(dev, "%s: resume_scanning (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_resume_scanning(cd);
+ break;
+ case HID_OUTPUT_BL_VERIFY_APP_INTEGRITY:
+ dev_info(dev, "%s: verify app integ (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_bl_verify_app_integrity(cd, &return_data[0]);
+ break;
+ case HID_OUTPUT_BL_GET_INFO:
+ dev_info(dev, "%s: bl get info (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_bl_get_information(cd, return_data);
+ break;
+ case HID_OUTPUT_BL_PROGRAM_AND_VERIFY:
+ dev_info(dev, "%s: program and verify (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_bl_program_and_verify(cd, 0, NULL);
+ break;
+ case HID_OUTPUT_BL_LAUNCH_APP:
+ dev_info(dev, "%s: launch app (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_bl_launch_app(cd);
+ break;
+ case HID_OUTPUT_BL_INITIATE_BL:
+ dev_info(dev, "%s: initiate bl (cd=%p)\n", __func__, cd);
+ cyttsp5_hid_output_bl_initiate_bl(cd, 0, NULL, 0, NULL);
+ break;
+#ifdef TTHE_TUNER_SUPPORT
+ case CY_TTHE_TUNER_EXIT:
+ cd->tthe_exit = 1;
+ wake_up(&cd->wait_q);
+ kfree(cd->tthe_buf);
+ cd->tthe_buf = NULL;
+ cd->tthe_exit = 0;
+ break;
+ case CY_TTHE_BUF_CLEAN:
+ if (cd->tthe_buf)
+ memset(cd->tthe_buf, 0, CY_MAX_PRBUF_SIZE);
+ else
+ dev_info(dev, "%s : tthe_buf not existed\n", __func__);
+ break;
+#endif
+ case CY_DBG_REPORT_LEVEL:
+ mutex_lock(&cd->system_lock);
+ cd->debug_level = input_data[1];
+ dev_info(dev, "%s: Set debug_level: %d\n",
+ __func__, cd->debug_level);
+ mutex_unlock(&(cd->system_lock));
+ break;
+ case CY_DBG_WATCHDOG_INTERVAL:
+ mutex_lock(&cd->system_lock);
+ if (input_data[1] > 0)
+ cd->watchdog_interval = input_data[1];
+ dev_info(dev, "%s: Set watchdog_interval: %d\n",
+ __func__, cd->watchdog_interval);
+ mutex_unlock(&(cd->system_lock));
+ break;
+ case CY_DBG_SHOW_TIMESTAMP:
+ mutex_lock(&cd->system_lock);
+ cd->show_timestamp = input_data[1];
+ dev_info(dev, "%s: Set show_timestamp: %d\n",
+ __func__, cd->show_timestamp);
+ mutex_unlock(&(cd->system_lock));
+ break;
+
+ default:
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ }
+
+ /* Enable watchdog timer */
+ if (!wd_disabled)
+ cyttsp5_start_wd_timer(cd);
+
+cyttsp5_drv_debug_store_exit:
+ return size;
+}
+
+/*
+ * Show system status on deep sleep status via sysfs
+ */
+static ssize_t cyttsp5_sleep_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ mutex_lock(&cd->system_lock);
+ if (cd->sleep_state == SS_SLEEP_ON)
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "off\n");
+ else
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "on\n");
+ mutex_unlock(&cd->system_lock);
+
+ return ret;
+}
+
+static ssize_t cyttsp5_easy_wakeup_gesture_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ mutex_lock(&cd->system_lock);
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n",
+ cd->easy_wakeup_gesture);
+ mutex_unlock(&cd->system_lock);
+ return ret;
+}
+
+static ssize_t cyttsp5_easy_wakeup_gesture_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ unsigned long value;
+ int ret;
+
+ if (!cd->features.easywake)
+ return -EINVAL;
+
+ ret = kstrtoul(buf, 10, &value);
+ if (ret < 0)
+ return ret;
+
+ if (value > 0xFF)
+ return -EINVAL;
+
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&cd->system_lock);
+ if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 2))
+ cd->easy_wakeup_gesture = (u8)value;
+ else
+ ret = -ENODEV;
+ mutex_unlock(&cd->system_lock);
+
+ pm_runtime_put(dev);
+
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+#ifdef EASYWAKE_TSG6
+/*
+ * Show easywake gesture id via sysfs
+ */
+static ssize_t cyttsp5_easy_wakeup_gesture_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ mutex_lock(&cd->system_lock);
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n",
+ cd->gesture_id);
+ mutex_unlock(&cd->system_lock);
+ return ret;
+}
+
+/*
+ * Show easywake gesture data via sysfs
+ * The format:
+ * x1(LSB), x1(MSB),y1(LSB), y1(MSB),x2(LSB), x2(MSB),y2(LSB), y2(MSB),...
+ */
+static ssize_t cyttsp5_easy_wakeup_gesture_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ int i;
+
+ mutex_lock(&cd->system_lock);
+
+ for (i = 0; i < cd->gesture_data_length; i++)
+ ret += snprintf(buf + ret, CY_MAX_PRBUF_SIZE - ret,
+ "0x%02X\n", cd->gesture_data[i]);
+
+ ret += snprintf(buf + ret, CY_MAX_PRBUF_SIZE - ret,
+ "(%d bytes)\n", cd->gesture_data_length);
+
+ mutex_unlock(&cd->system_lock);
+ return ret;
+}
+#endif
+
+/* Show Panel ID via sysfs */
+static ssize_t cyttsp5_panel_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n",
+ cd->panel_id);
+ return ret;
+}
+
+/* Show platform data via sysfs */
+static ssize_t cyttsp5_platform_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
+ ssize_t ret;
+
+ ret = sprintf(buf,
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n"
+ "%s: %d\n",
+ "Interrupt GPIO", pdata->core_pdata->irq_gpio,
+ "Reset GPIO", pdata->core_pdata->rst_gpio,
+ "Level trigger delay (us)", pdata->core_pdata->level_irq_udelay,
+ "HID descriptor register", pdata->core_pdata->hid_desc_register,
+ "Vendor ID", pdata->core_pdata->vendor_id,
+ "Product ID", pdata->core_pdata->product_id,
+ "Easy wakeup gesture", pdata->core_pdata->easy_wakeup_gesture,
+ "Vkeys x", pdata->mt_pdata->vkeys_x,
+ "Vkeys y", pdata->mt_pdata->vkeys_y,
+ "Core data flags", pdata->core_pdata->flags,
+ "MT data flags", pdata->mt_pdata->flags,
+ "Loader data flags", pdata->loader_pdata->flags);
+ return ret;
+}
+
+static struct device_attribute attributes[] = {
+ __ATTR(ic_ver, S_IRUGO, cyttsp5_ic_ver_show, NULL),
+ __ATTR(drv_ver, S_IRUGO, cyttsp5_drv_ver_show, NULL),
+ __ATTR(hw_reset, S_IWUSR, NULL, cyttsp5_hw_reset_store),
+ __ATTR(hw_irq_stat, S_IRUSR, cyttsp5_hw_irq_stat_show, NULL),
+ __ATTR(drv_irq, S_IRUSR | S_IWUSR, cyttsp5_drv_irq_show,
+ cyttsp5_drv_irq_store),
+ __ATTR(drv_debug, S_IWUSR, NULL, cyttsp5_drv_debug_store),
+ __ATTR(sleep_status, S_IRUSR, cyttsp5_sleep_status_show, NULL),
+ __ATTR(easy_wakeup_gesture, S_IRUSR | S_IWUSR,
+ cyttsp5_easy_wakeup_gesture_show,
+ cyttsp5_easy_wakeup_gesture_store),
+#ifdef EASYWAKE_TSG6
+ __ATTR(easy_wakeup_gesture_id, S_IRUSR,
+ cyttsp5_easy_wakeup_gesture_id_show, NULL),
+ __ATTR(easy_wakeup_gesture_data, S_IRUSR,
+ cyttsp5_easy_wakeup_gesture_data_show, NULL),
+#endif
+ __ATTR(panel_id, S_IRUGO, cyttsp5_panel_id_show, NULL),
+ __ATTR(platform_data, S_IRUGO, cyttsp5_platform_data_show, NULL),
+};
+
+static int add_sysfs_interfaces(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(attributes); i++)
+ if (device_create_file(dev, attributes + i))
+ goto undo;
+ return 0;
+undo:
+ for (i--; i >= 0; i--)
+ device_remove_file(dev, attributes + i);
+ dev_err(dev, "%s: failed to create sysfs interface\n", __func__);
+ return -ENODEV;
+}
+
+static void remove_sysfs_interfaces(struct device *dev)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(attributes); i++)
+ device_remove_file(dev, attributes + i);
+}
+
+/*
+ * ttdl_restart via sysfs
+ */
+static ssize_t cyttsp5_ttdl_restart_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int rc;
+ struct i2c_client *client =
+ (struct i2c_client *)container_of(dev, struct i2c_client, dev);
+
+ if (is_cyttsp5_probe_success) {
+ dev_err(dev, "%s: previous cyttsp5_probe is successful, do nothing\n",
+ __func__);
+ return size;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(dev, "%s I2C functionality not Supported\n", __func__);
+ return -EIO;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ rc = cyttsp5_devtree_create_and_get_pdata(dev);
+ if (rc < 0)
+ return rc;
+#endif
+
+ rc = cyttsp5_probe(cyttsp5_bus_ops_save, &client->dev, client->irq,
+ 512);
+
+ if (!rc) {
+ is_cyttsp5_probe_success = true;
+ dev_err(dev, "%s restart successful\n", __func__);
+ } else {
+ is_cyttsp5_probe_success = false;
+ dev_err(dev, "%s: ttdl restart failed r=%d\n",
+ __func__, rc);
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ if (rc)
+ cyttsp5_devtree_clean_pdata(dev);
+#endif
+
+ return size;
+
+}
+static DEVICE_ATTR(ttdl_restart, S_IWUSR, NULL, cyttsp5_ttdl_restart_store);
+
+
+#ifdef TTHE_TUNER_SUPPORT
+static int tthe_debugfs_open(struct inode *inode, struct file *filp)
+{
+ struct cyttsp5_core_data *cd = inode->i_private;
+
+ filp->private_data = inode->i_private;
+
+ if (cd->tthe_buf)
+ return -EBUSY;
+
+ cd->tthe_buf = kzalloc(CY_MAX_PRBUF_SIZE, GFP_KERNEL);
+ if (!cd->tthe_buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int tthe_debugfs_close(struct inode *inode, struct file *filp)
+{
+ struct cyttsp5_core_data *cd = filp->private_data;
+
+ filp->private_data = NULL;
+
+ kfree(cd->tthe_buf);
+ cd->tthe_buf = NULL;
+
+ return 0;
+}
+
+static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_core_data *cd = filp->private_data;
+ int size;
+ int ret;
+
+ wait_event_interruptible(cd->wait_q,
+ cd->tthe_buf_len != 0 || cd->tthe_exit);
+ mutex_lock(&cd->tthe_lock);
+ if (cd->tthe_exit) {
+ mutex_unlock(&cd->tthe_lock);
+ return 0;
+ }
+ if (count > cd->tthe_buf_len)
+ size = cd->tthe_buf_len;
+ else
+ size = count;
+ if (!size) {
+ mutex_unlock(&cd->tthe_lock);
+ return 0;
+ }
+
+ ret = copy_to_user(buf, cd->tthe_buf, cd->tthe_buf_len);
+ if (ret == size)
+ return -EFAULT;
+ size -= ret;
+ cd->tthe_buf_len -= size;
+ mutex_unlock(&cd->tthe_lock);
+ *ppos += size;
+ return size;
+}
+
+static const struct file_operations tthe_debugfs_fops = {
+ .open = tthe_debugfs_open,
+ .release = tthe_debugfs_close,
+ .read = tthe_debugfs_read,
+};
+#endif
+
+static struct cyttsp5_core_nonhid_cmd _cyttsp5_core_nonhid_cmd = {
+ .start_bl = _cyttsp5_request_hid_output_start_bl,
+ .suspend_scanning = _cyttsp5_request_hid_output_suspend_scanning,
+ .resume_scanning = _cyttsp5_request_hid_output_resume_scanning,
+ .get_param = _cyttsp5_request_hid_output_get_param,
+ .set_param = _cyttsp5_request_hid_output_set_param,
+ .verify_config_block_crc =
+ _cyttsp5_request_hid_output_verify_config_block_crc,
+ .get_config_row_size = _cyttsp5_request_hid_output_get_config_row_size,
+ .get_data_structure = _cyttsp5_request_hid_output_get_data_structure,
+ .run_selftest = _cyttsp5_request_hid_output_run_selftest,
+ .get_selftest_result = _cyttsp5_request_hid_output_get_selftest_result,
+ .calibrate_idacs = _cyttsp5_request_hid_output_calibrate_idacs,
+ .initialize_baselines =
+ _cyttsp5_request_hid_output_initialize_baselines,
+ .exec_panel_scan = _cyttsp5_request_hid_output_exec_panel_scan,
+ .retrieve_panel_scan = _cyttsp5_request_hid_output_retrieve_panel_scan,
+ .write_conf_block = _cyttsp5_request_hid_output_write_conf_block,
+ .user_cmd = _cyttsp5_request_hid_output_user_cmd,
+ .get_bl_info = _cyttsp5_request_hid_output_bl_get_information,
+ .initiate_bl = _cyttsp5_request_hid_output_bl_initiate_bl,
+ .launch_app = _cyttsp5_request_hid_output_launch_app,
+ .prog_and_verify = _cyttsp5_request_hid_output_bl_program_and_verify,
+ .verify_app_integrity =
+ _cyttsp5_request_hid_output_bl_verify_app_integrity,
+ .get_panel_id = _cyttsp5_request_hid_output_bl_get_panel_id,
+};
+
+static struct cyttsp5_core_commands _cyttsp5_core_commands = {
+ .subscribe_attention = _cyttsp5_subscribe_attention,
+ .unsubscribe_attention = _cyttsp5_unsubscribe_attention,
+ .request_exclusive = _cyttsp5_request_exclusive,
+ .release_exclusive = _cyttsp5_release_exclusive,
+ .request_reset = _cyttsp5_request_reset,
+ .request_restart = _cyttsp5_request_restart,
+ .request_sysinfo = _cyttsp5_request_sysinfo,
+ .request_loader_pdata = _cyttsp5_request_loader_pdata,
+ .request_stop_wd = _cyttsp5_request_stop_wd,
+ .request_start_wd = _cyttsp5_request_start_wd,
+ .request_get_hid_desc = _cyttsp5_request_get_hid_desc,
+ .request_get_mode = _cyttsp5_request_get_mode,
+#ifdef TTHE_TUNER_SUPPORT
+ .request_tthe_print = _cyttsp5_request_tthe_print,
+#endif
+ .nonhid_cmd = &_cyttsp5_core_nonhid_cmd,
+};
+
+struct cyttsp5_core_commands *cyttsp5_get_commands(void)
+{
+ return &_cyttsp5_core_commands;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_get_commands);
+
+static DEFINE_MUTEX(core_list_lock);
+static LIST_HEAD(core_list);
+static DEFINE_MUTEX(module_list_lock);
+static LIST_HEAD(module_list);
+static int core_number;
+
+static int cyttsp5_probe_module(struct cyttsp5_core_data *cd,
+ struct cyttsp5_module *module)
+{
+ struct module_node *module_node;
+ int rc = 0;
+
+ module_node = kzalloc(sizeof(*module_node), GFP_KERNEL);
+ if (!module_node)
+ return -ENOMEM;
+
+ module_node->module = module;
+
+ mutex_lock(&cd->module_list_lock);
+ list_add(&module_node->node, &cd->module_list);
+ mutex_unlock(&cd->module_list_lock);
+
+ rc = module->probe(cd->dev, &module_node->data);
+ if (rc) {
+ /*
+ * Remove from the list when probe fails
+ * in order not to call release
+ */
+ mutex_lock(&cd->module_list_lock);
+ list_del(&module_node->node);
+ mutex_unlock(&cd->module_list_lock);
+ kfree(module_node);
+ goto exit;
+ }
+
+exit:
+ return rc;
+}
+
+static void cyttsp5_release_module(struct cyttsp5_core_data *cd,
+ struct cyttsp5_module *module)
+{
+ struct module_node *m, *m_n;
+
+ mutex_lock(&cd->module_list_lock);
+ list_for_each_entry_safe(m, m_n, &cd->module_list, node)
+ if (m->module == module) {
+ module->release(cd->dev, m->data);
+ list_del(&m->node);
+ kfree(m);
+ break;
+ }
+ mutex_unlock(&cd->module_list_lock);
+}
+
+static void cyttsp5_probe_modules(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_module *m;
+ int rc = 0;
+
+ mutex_lock(&module_list_lock);
+ list_for_each_entry(m, &module_list, node) {
+ rc = cyttsp5_probe_module(cd, m);
+ if (rc)
+ dev_err(cd->dev, "%s: Probe fails for module %s\n",
+ __func__, m->name);
+ }
+ mutex_unlock(&module_list_lock);
+}
+
+static void cyttsp5_release_modules(struct cyttsp5_core_data *cd)
+{
+ struct cyttsp5_module *m;
+
+ mutex_lock(&module_list_lock);
+ list_for_each_entry(m, &module_list, node)
+ cyttsp5_release_module(cd, m);
+ mutex_unlock(&module_list_lock);
+}
+
+struct cyttsp5_core_data *cyttsp5_get_core_data(char *id)
+{
+ struct cyttsp5_core_data *d;
+
+ list_for_each_entry(d, &core_list, node)
+ if (!strncmp(d->core_id, id, 20))
+ return d;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_get_core_data);
+
+static void cyttsp5_add_core(struct device *dev)
+{
+ struct cyttsp5_core_data *d;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ mutex_lock(&core_list_lock);
+ list_for_each_entry(d, &core_list, node)
+ if (d->dev == dev)
+ goto unlock;
+
+ list_add(&cd->node, &core_list);
+unlock:
+ mutex_unlock(&core_list_lock);
+}
+
+static void cyttsp5_del_core(struct device *dev)
+{
+ struct cyttsp5_core_data *d, *d_n;
+
+ mutex_lock(&core_list_lock);
+ list_for_each_entry_safe(d, d_n, &core_list, node)
+ if (d->dev == dev) {
+ list_del(&d->node);
+ goto unlock;
+ }
+unlock:
+ mutex_unlock(&core_list_lock);
+}
+
+int cyttsp5_register_module(struct cyttsp5_module *module)
+{
+ struct cyttsp5_module *m;
+ struct cyttsp5_core_data *cd;
+
+ int rc = 0;
+
+ if (!module || !module->probe || !module->release)
+ return -EINVAL;
+
+ mutex_lock(&module_list_lock);
+ list_for_each_entry(m, &module_list, node)
+ if (m == module) {
+ rc = -EEXIST;
+ goto unlock;
+ }
+
+ list_add(&module->node, &module_list);
+
+ /* Probe the module for each core */
+ mutex_lock(&core_list_lock);
+ list_for_each_entry(cd, &core_list, node)
+ cyttsp5_probe_module(cd, module);
+ mutex_unlock(&core_list_lock);
+
+unlock:
+ mutex_unlock(&module_list_lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_register_module);
+
+void cyttsp5_unregister_module(struct cyttsp5_module *module)
+{
+ struct cyttsp5_module *m, *m_n;
+ struct cyttsp5_core_data *cd;
+
+ if (!module)
+ return;
+
+ mutex_lock(&module_list_lock);
+
+ /* Release the module for each core */
+ mutex_lock(&core_list_lock);
+ list_for_each_entry(cd, &core_list, node)
+ cyttsp5_release_module(cd, module);
+ mutex_unlock(&core_list_lock);
+
+ list_for_each_entry_safe(m, m_n, &module_list, node)
+ if (m == module) {
+ list_del(&m->node);
+ break;
+ }
+
+ mutex_unlock(&module_list_lock);
+}
+EXPORT_SYMBOL_GPL(cyttsp5_unregister_module);
+
+void *cyttsp5_get_module_data(struct device *dev, struct cyttsp5_module *module)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct module_node *m;
+ void *data = NULL;
+
+ mutex_lock(&cd->module_list_lock);
+ list_for_each_entry(m, &cd->module_list, node)
+ if (m->module == module) {
+ data = m->data;
+ break;
+ }
+ mutex_unlock(&cd->module_list_lock);
+
+ return data;
+}
+EXPORT_SYMBOL(cyttsp5_get_module_data);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cyttsp5_early_suspend(struct early_suspend *h)
+{
+ struct cyttsp5_core_data *cd =
+ container_of(h, struct cyttsp5_core_data, es);
+
+ call_atten_cb(cd, CY_ATTEN_SUSPEND, 0);
+}
+
+static void cyttsp5_late_resume(struct early_suspend *h)
+{
+ struct cyttsp5_core_data *cd =
+ container_of(h, struct cyttsp5_core_data, es);
+
+ call_atten_cb(cd, CY_ATTEN_RESUME, 0);
+}
+
+static void cyttsp5_setup_early_suspend(struct cyttsp5_core_data *cd)
+{
+ cd->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ cd->es.suspend = cyttsp5_early_suspend;
+ cd->es.resume = cyttsp5_late_resume;
+
+ register_early_suspend(&cd->es);
+}
+#elif defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct cyttsp5_core_data *cd =
+ container_of(self, struct cyttsp5_core_data, fb_notifier);
+ struct fb_event *evdata = data;
+ int *blank;
+
+ if (event != FB_EVENT_BLANK || !evdata)
+ goto exit;
+
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK) {
+ dev_info(cd->dev, "%s: UNBLANK!\n", __func__);
+ if (cd->fb_state != FB_ON) {
+ call_atten_cb(cd, CY_ATTEN_RESUME, 0);
+ cd->fb_state = FB_ON;
+ }
+ } else if (*blank == FB_BLANK_POWERDOWN) {
+ dev_info(cd->dev, "%s: POWERDOWN!\n", __func__);
+ if (cd->fb_state != FB_OFF) {
+ call_atten_cb(cd, CY_ATTEN_SUSPEND, 0);
+ cd->fb_state = FB_OFF;
+ }
+ }
+
+exit:
+ return 0;
+}
+
+static void cyttsp5_setup_fb_notifier(struct cyttsp5_core_data *cd)
+{
+ int rc;
+
+ cd->fb_state = FB_ON;
+
+ cd->fb_notifier.notifier_call = fb_notifier_callback;
+
+ rc = fb_register_client(&cd->fb_notifier);
+ if (rc)
+ dev_err(cd->dev, "Unable to register fb_notifier: %d\n", rc);
+}
+#endif
+
+static int cyttsp5_setup_irq_gpio(struct cyttsp5_core_data *cd)
+{
+ struct device *dev = cd->dev;
+ unsigned long irq_flags;
+ int rc;
+
+ /* Initialize IRQ */
+ cd->irq = gpio_to_irq(cd->cpdata->irq_gpio);
+ if (cd->irq < 0)
+ return -EINVAL;
+
+ cd->irq_enabled = true;
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: initialize threaded irq=%d\n",
+ __func__, cd->irq);
+ if (cd->cpdata->level_irq_udelay > 0)
+ /* use level triggered interrupts */
+ irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+ else
+ /* use edge triggered interrupts */
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+
+ rc = request_threaded_irq(cd->irq, NULL, cyttsp5_irq, irq_flags,
+ dev_name(dev), cd);
+ if (rc < 0)
+ dev_err(dev, "%s: Error, could not request irq\n", __func__);
+
+ return rc;
+}
+
+int cyttsp5_probe(const struct cyttsp5_bus_ops *ops, struct device *dev,
+ u16 irq, size_t xfer_buf_size)
+{
+ struct cyttsp5_core_data *cd;
+ struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
+ enum cyttsp5_atten_type type;
+ int rc = 0;
+
+ /* Set default values on first probe */
+ if (cyttsp5_first_probe) {
+ cyttsp5_first_probe = false;
+ is_cyttsp5_probe_success = false;
+ cyttsp5_bus_ops_save = NULL;
+ }
+
+ if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) {
+ dev_err(dev, "%s: Missing platform data\n", __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+
+ if (pdata->core_pdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) {
+ if (!pdata->core_pdata->power) {
+ dev_err(dev, "%s: Missing platform data function\n",
+ __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+ }
+
+ /* get context and debug print buffers */
+ cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+ if (!cd) {
+ rc = -ENOMEM;
+ goto error_alloc_data;
+ }
+
+ /* Initialize device info */
+ cd->dev = dev;
+ cd->pdata = pdata;
+ cd->cpdata = pdata->core_pdata;
+ cd->bus_ops = ops;
+ cd->debug_level = CY_INITIAL_DEBUG_LEVEL;
+ cd->watchdog_interval = CY_WATCHDOG_TIMEOUT;
+ cd->show_timestamp = CY_INITIAL_SHOW_TIME_STAMP;
+ scnprintf(cd->core_id, 20, "%s%d", CYTTSP5_CORE_NAME, core_number++);
+
+ /* Initialize mutexes and spinlocks */
+ mutex_init(&cd->module_list_lock);
+ mutex_init(&cd->system_lock);
+ mutex_init(&cd->adap_lock);
+ mutex_init(&cd->hid_report_lock);
+ spin_lock_init(&cd->spinlock);
+
+ /* Initialize module list */
+ INIT_LIST_HEAD(&cd->module_list);
+
+ /* Initialize attention lists */
+ for (type = 0; type < CY_ATTEN_NUM_ATTEN; type++)
+ INIT_LIST_HEAD(&cd->atten_list[type]);
+
+ /* Initialize parameter list */
+ INIT_LIST_HEAD(&cd->param_list);
+
+ /* Initialize wait queue */
+ init_waitqueue_head(&cd->wait_q);
+
+ /* Initialize works */
+ INIT_WORK(&cd->startup_work, cyttsp5_startup_work_function);
+ INIT_WORK(&cd->watchdog_work, cyttsp5_watchdog_work);
+
+ /* Initialize HID specific data */
+ cd->hid_core.hid_vendor_id = (cd->cpdata->vendor_id) ?
+ cd->cpdata->vendor_id : CY_HID_VENDOR_ID;
+ cd->hid_core.hid_product_id = (cd->cpdata->product_id) ?
+ cd->cpdata->product_id : CY_HID_APP_PRODUCT_ID;
+ cd->hid_core.hid_desc_register =
+ cpu_to_le16(cd->cpdata->hid_desc_register);
+
+ /* Set platform easywake value */
+ cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture;
+
+ /* Set Panel ID to Not Enabled */
+ cd->panel_id = PANEL_ID_NOT_ENABLED;
+
+ dev_set_drvdata(dev, cd);
+ cyttsp5_add_core(dev);
+
+ /*create ttdl_restart sysfs node is probe failed*/
+ if (!is_cyttsp5_probe_success)
+ device_create_file(dev, &dev_attr_ttdl_restart);
+
+ /*
+ * Save the pointer to a global value, which will be used
+ * in ttdl_restart function
+ */
+ cyttsp5_bus_ops_save = ops;
+
+ /* Call platform init function */
+ if (cd->cpdata->init) {
+ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Init HW\n", __func__);
+ rc = cd->cpdata->init(cd->cpdata, 1, cd->dev);
+ } else {
+ dev_info(cd->dev, "%s: No HW INIT function\n", __func__);
+ rc = 0;
+ }
+ if (rc < 0)
+ dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc);
+
+ /* Call platform detect function */
+ if (cd->cpdata->detect) {
+ dev_info(cd->dev, "%s: Detect HW\n", __func__);
+ rc = cd->cpdata->detect(cd->cpdata, cd->dev,
+ cyttsp5_platform_detect_read);
+ if (rc) {
+ dev_info(cd->dev, "%s: No HW detected\n", __func__);
+ rc = -ENODEV;
+ goto error_detect;
+ }
+ }
+
+ /* Setup watchdog timer */
+ setup_timer(&cd->watchdog_timer, cyttsp5_watchdog_timer,
+ (unsigned long)cd);
+
+ rc = cyttsp5_setup_irq_gpio(cd);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, could not setup IRQ\n", __func__);
+ goto error_setup_irq;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: add sysfs interfaces\n",
+ __func__);
+ rc = add_sysfs_interfaces(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, fail sysfs init\n", __func__);
+ goto error_attr_create;
+ }
+
+#ifdef TTHE_TUNER_SUPPORT
+ mutex_init(&cd->tthe_lock);
+ cd->tthe_debugfs = debugfs_create_file(CYTTSP5_TTHE_TUNER_FILE_NAME,
+ 0644, NULL, cd, &tthe_debugfs_fops);
+#endif
+ rc = device_init_wakeup(dev, 1);
+ if (rc < 0)
+ dev_err(dev, "%s: Error, device_init_wakeup rc:%d\n",
+ __func__, rc);
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /*
+ * call startup directly to ensure that the device
+ * is tested before leaving the probe
+ */
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: call startup\n", __func__);
+ rc = cyttsp5_startup(cd, false);
+
+ pm_runtime_put_sync(dev);
+
+ /* Do not fail probe if startup fails but the device is detected */
+ if (rc == -ENODEV) {
+ dev_err(cd->dev, "%s: Fail initial startup r=%d\n",
+ __func__, rc);
+ goto error_startup;
+ }
+
+ rc = cyttsp5_mt_probe(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, fail mt probe\n", __func__);
+ goto error_startup;
+ }
+
+ rc = cyttsp5_btn_probe(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, fail btn probe\n", __func__);
+ goto error_startup_mt;
+ }
+
+ rc = cyttsp5_proximity_probe(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, fail proximity probe\n", __func__);
+ goto error_startup_btn;
+ }
+
+ /* Probe registered modules */
+ cyttsp5_probe_modules(cd);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ cyttsp5_setup_early_suspend(cd);
+#elif defined(CONFIG_FB)
+ cyttsp5_setup_fb_notifier(cd);
+#endif
+
+#if NEED_SUSPEND_NOTIFIER
+ cd->pm_notifier.notifier_call = cyttsp5_pm_notifier;
+ register_pm_notifier(&cd->pm_notifier);
+#endif
+ is_cyttsp5_probe_success = true;
+ return 0;
+
+error_startup_btn:
+ cyttsp5_btn_release(dev);
+error_startup_mt:
+ cyttsp5_mt_release(dev);
+error_startup:
+ pm_runtime_disable(dev);
+#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
+ device_wakeup_disable(dev);
+#endif
+ device_init_wakeup(dev, 0);
+ cancel_work_sync(&cd->startup_work);
+ cyttsp5_stop_wd_timer(cd);
+ cyttsp5_free_si_ptrs(cd);
+ remove_sysfs_interfaces(dev);
+error_attr_create:
+ free_irq(cd->irq, cd);
+ del_timer(&cd->watchdog_timer);
+error_setup_irq:
+error_detect:
+ if (cd->cpdata->init)
+ cd->cpdata->init(cd->cpdata, 0, dev);
+ cyttsp5_del_core(dev);
+ dev_set_drvdata(dev, NULL);
+ kfree(cd);
+error_alloc_data:
+error_no_pdata:
+ dev_err(dev, "%s failed.\n", __func__);
+ is_cyttsp5_probe_success = false;
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_probe);
+
+int cyttsp5_release(struct cyttsp5_core_data *cd)
+{
+ struct device *dev = cd->dev;
+
+ /* Release successfully probed modules */
+ cyttsp5_release_modules(cd);
+
+ cyttsp5_proximity_release(dev);
+ cyttsp5_btn_release(dev);
+ cyttsp5_mt_release(dev);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&cd->es);
+#elif defined(CONFIG_FB)
+ fb_unregister_client(&cd->fb_notifier);
+#endif
+
+#if NEED_SUSPEND_NOTIFIER
+ unregister_pm_notifier(&cd->pm_notifier);
+#endif
+
+ /*
+ * Suspend the device before freeing the startup_work and stopping
+ * the watchdog since sleep function restarts watchdog on failure
+ */
+ pm_runtime_suspend(dev);
+ pm_runtime_disable(dev);
+
+ cancel_work_sync(&cd->startup_work);
+
+ cyttsp5_stop_wd_timer(cd);
+
+#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
+ device_wakeup_disable(dev);
+#endif
+ device_init_wakeup(dev, 0);
+
+#ifdef TTHE_TUNER_SUPPORT
+ mutex_lock(&cd->tthe_lock);
+ cd->tthe_exit = 1;
+ wake_up(&cd->wait_q);
+ mutex_unlock(&cd->tthe_lock);
+ debugfs_remove(cd->tthe_debugfs);
+#endif
+ remove_sysfs_interfaces(dev);
+ free_irq(cd->irq, cd);
+ if (cd->cpdata->init)
+ cd->cpdata->init(cd->cpdata, 0, dev);
+ dev_set_drvdata(dev, NULL);
+ cyttsp5_del_core(dev);
+ cyttsp5_free_si_ptrs(cd);
+ cyttsp5_free_hid_reports(cd);
+ kfree(cd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_release);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_debug.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_debug.c
new file mode 100644
index 0000000..f4ebba7
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_debug.c
@@ -0,0 +1,393 @@
+/*
+ * cyttsp5_debug.c
+ * Parade TrueTouch(TM) Standard Product V5 Debug Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+#define CYTTSP5_DEBUG_NAME "cyttsp5_debug"
+
+struct cyttsp5_debug_data {
+ struct device *dev;
+ struct cyttsp5_sysinfo *si;
+ uint32_t interrupt_count;
+ uint32_t formated_output;
+ struct mutex sysfs_lock;
+ u8 pr_buf[CY_MAX_PRBUF_SIZE];
+};
+
+static struct cyttsp5_core_commands *cmd;
+
+static struct cyttsp5_module debug_module;
+
+static inline struct cyttsp5_debug_data *cyttsp5_get_debug_data(
+ struct device *dev)
+{
+ return cyttsp5_get_module_data(dev, &debug_module);
+}
+
+/*
+ * This function provide output of combined xy_mode and xy_data.
+ * Required by TTHE.
+ */
+static void cyttsp5_pr_buf_op_mode(struct cyttsp5_debug_data *dd, u8 *pr_buf,
+ struct cyttsp5_sysinfo *si, u8 cur_touch)
+{
+ const char fmt[] = "%02X ";
+ int max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
+ u8 report_id = si->xy_mode[2];
+ int header_size = 0;
+ int report_size = 0;
+ int total_size = 0;
+ int i, k;
+
+ if (report_id == si->desc.tch_report_id) {
+ header_size = si->desc.tch_header_size;
+ report_size = cur_touch * si->desc.tch_record_size;
+ } else if (report_id == si->desc.btn_report_id) {
+ header_size = BTN_INPUT_HEADER_SIZE;
+ report_size = BTN_REPORT_SIZE;
+ }
+ total_size = header_size + report_size;
+
+ pr_buf[0] = 0;
+ for (i = k = 0; i < header_size && i < max; i++, k += 3)
+ scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]);
+
+ for (i = 0; i < report_size && i < max; i++, k += 3)
+ scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_data[i]);
+
+ pr_info("%s=%s%s\n", "cyttsp5_OpModeData", pr_buf,
+ total_size <= max ? "" : CY_PR_TRUNCATED);
+}
+
+static void cyttsp5_debug_print(struct device *dev, u8 *pr_buf, u8 *sptr,
+ int size, const char *data_name)
+{
+ int i, j;
+ int elem_size = sizeof("XX ") - 1;
+ int max = (CY_MAX_PRBUF_SIZE - 1) / elem_size;
+ int limit = size < max ? size : max;
+
+ if (limit < 0)
+ limit = 0;
+
+ pr_buf[0] = 0;
+ for (i = j = 0; i < limit; i++, j += elem_size)
+ scnprintf(pr_buf + j, CY_MAX_PRBUF_SIZE - j, "%02X ", sptr[i]);
+
+ if (size)
+ pr_info("%s[0..%d]=%s%s\n", data_name, size - 1, pr_buf,
+ size <= max ? "" : CY_PR_TRUNCATED);
+ else
+ pr_info("%s[]\n", data_name);
+}
+
+static void cyttsp5_debug_formated(struct device *dev, u8 *pr_buf,
+ struct cyttsp5_sysinfo *si, u8 num_cur_tch)
+{
+ u8 report_id = si->xy_mode[2];
+ int header_size = 0;
+ int report_size = 0;
+ u8 data_name[] = "touch[99]";
+ int max_print_length = 20;
+ int i;
+
+ if (report_id == si->desc.tch_report_id) {
+ header_size = si->desc.tch_header_size;
+ report_size = num_cur_tch * si->desc.tch_record_size;
+ } else if (report_id == si->desc.btn_report_id) {
+ header_size = BTN_INPUT_HEADER_SIZE;
+ report_size = BTN_REPORT_SIZE;
+ }
+
+ /* xy_mode */
+ cyttsp5_debug_print(dev, pr_buf, si->xy_mode, header_size, "xy_mode");
+
+ /* xy_data */
+ if (report_size > max_print_length) {
+ pr_info("xy_data[0..%d]:\n", report_size);
+ for (i = 0; i < report_size - max_print_length;
+ i += max_print_length) {
+ cyttsp5_debug_print(dev, pr_buf, si->xy_data + i,
+ max_print_length, " ");
+ }
+ if (report_size - i)
+ cyttsp5_debug_print(dev, pr_buf, si->xy_data + i,
+ report_size - i, " ");
+ } else {
+ cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size,
+ "xy_data");
+ }
+
+ /* touches */
+ if (report_id == si->desc.tch_report_id) {
+ for (i = 0; i < num_cur_tch; i++) {
+ scnprintf(data_name, sizeof(data_name) - 1,
+ "touch[%u]", i);
+ cyttsp5_debug_print(dev, pr_buf,
+ si->xy_data + (i * si->desc.tch_record_size),
+ si->desc.tch_record_size, data_name);
+ }
+ }
+
+ /* buttons */
+ if (report_id == si->desc.btn_report_id)
+ cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size,
+ "button");
+}
+
+/* read xy_data for all touches for debug */
+static int cyttsp5_xy_worker(struct cyttsp5_debug_data *dd)
+{
+ struct device *dev = dd->dev;
+ struct cyttsp5_sysinfo *si = dd->si;
+ u8 report_reg = si->xy_mode[TOUCH_COUNT_BYTE_OFFSET];
+ u8 num_cur_tch = GET_NUM_TOUCHES(report_reg);
+ uint32_t formated_output;
+
+ mutex_lock(&dd->sysfs_lock);
+ dd->interrupt_count++;
+ formated_output = dd->formated_output;
+ mutex_unlock(&dd->sysfs_lock);
+
+ /* Interrupt */
+ pr_info("Interrupt(%u)\n", dd->interrupt_count);
+
+ if (formated_output)
+ cyttsp5_debug_formated(dev, dd->pr_buf, si, num_cur_tch);
+ else
+ /* print data for TTHE */
+ cyttsp5_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch);
+
+ pr_info("\n");
+
+ return 0;
+}
+
+static int cyttsp5_debug_attention(struct device *dev)
+{
+ struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
+ struct cyttsp5_sysinfo *si = dd->si;
+ u8 report_id = si->xy_mode[2];
+ int rc = 0;
+
+ if (report_id != si->desc.tch_report_id
+ && report_id != si->desc.btn_report_id)
+ return 0;
+
+ /* core handles handshake */
+ rc = cyttsp5_xy_worker(dd);
+ if (rc < 0)
+ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static ssize_t cyttsp5_interrupt_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
+ int val;
+
+ mutex_lock(&dd->sysfs_lock);
+ val = dd->interrupt_count;
+ mutex_unlock(&dd->sysfs_lock);
+
+ return scnprintf(buf, CY_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val);
+}
+
+static ssize_t cyttsp5_interrupt_count_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
+
+ mutex_lock(&dd->sysfs_lock);
+ dd->interrupt_count = 0;
+ mutex_unlock(&dd->sysfs_lock);
+ return size;
+}
+
+static DEVICE_ATTR(int_count, S_IRUSR | S_IWUSR,
+ cyttsp5_interrupt_count_show, cyttsp5_interrupt_count_store);
+
+static ssize_t cyttsp5_formated_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
+ int val;
+
+ mutex_lock(&dd->sysfs_lock);
+ val = dd->formated_output;
+ mutex_unlock(&dd->sysfs_lock);
+
+ return scnprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Formated debug output: %x\n", val);
+}
+
+static ssize_t cyttsp5_formated_output_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
+ unsigned long value;
+ int rc;
+
+ rc = kstrtoul(buf, 10, &value);
+ if (rc < 0) {
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ return size;
+ }
+
+ /* Expecting only 0 or 1 */
+ if (value != 0 && value != 1) {
+ dev_err(dev, "%s: Invalid value %lu\n", __func__, value);
+ return size;
+ }
+
+ mutex_lock(&dd->sysfs_lock);
+ dd->formated_output = value;
+ mutex_unlock(&dd->sysfs_lock);
+ return size;
+}
+
+static DEVICE_ATTR(formated_output, S_IRUSR | S_IWUSR,
+ cyttsp5_formated_output_show, cyttsp5_formated_output_store);
+
+static int cyttsp5_debug_probe(struct device *dev, void **data)
+{
+ struct cyttsp5_debug_data *dd;
+ int rc;
+
+ /* get context and debug print buffers */
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd) {
+ rc = -ENOMEM;
+ goto cyttsp5_debug_probe_alloc_failed;
+ }
+
+ rc = device_create_file(dev, &dev_attr_int_count);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create int_count\n",
+ __func__);
+ goto cyttsp5_debug_probe_create_int_count_failed;
+ }
+
+ rc = device_create_file(dev, &dev_attr_formated_output);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create formated_output\n",
+ __func__);
+ goto cyttsp5_debug_probe_create_formated_failed;
+ }
+
+ mutex_init(&dd->sysfs_lock);
+ dd->dev = dev;
+ *data = dd;
+
+ dd->si = cmd->request_sysinfo(dev);
+ if (!dd->si) {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core\n",
+ __func__);
+ rc = -ENODEV;
+ goto cyttsp5_debug_probe_sysinfo_failed;
+ }
+
+ rc = cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME,
+ cyttsp5_debug_attention, CY_MODE_OPERATIONAL);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, could not subscribe attention cb\n",
+ __func__);
+ goto cyttsp5_debug_probe_subscribe_failed;
+ }
+
+ return 0;
+
+cyttsp5_debug_probe_subscribe_failed:
+cyttsp5_debug_probe_sysinfo_failed:
+ device_remove_file(dev, &dev_attr_formated_output);
+cyttsp5_debug_probe_create_formated_failed:
+ device_remove_file(dev, &dev_attr_int_count);
+cyttsp5_debug_probe_create_int_count_failed:
+ kfree(dd);
+cyttsp5_debug_probe_alloc_failed:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+static void cyttsp5_debug_release(struct device *dev, void *data)
+{
+ struct cyttsp5_debug_data *dd = data;
+ int rc;
+
+ rc = cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME,
+ cyttsp5_debug_attention, CY_MODE_OPERATIONAL);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error, could not un-subscribe attention\n",
+ __func__);
+ goto cyttsp5_debug_release_exit;
+ }
+
+cyttsp5_debug_release_exit:
+ device_remove_file(dev, &dev_attr_formated_output);
+ device_remove_file(dev, &dev_attr_int_count);
+ kfree(dd);
+}
+
+static struct cyttsp5_module debug_module = {
+ .name = CYTTSP5_DEBUG_NAME,
+ .probe = cyttsp5_debug_probe,
+ .release = cyttsp5_debug_release,
+};
+
+static int __init cyttsp5_debug_init(void)
+{
+ int rc;
+
+ cmd = cyttsp5_get_commands();
+ if (!cmd)
+ return -EINVAL;
+
+ rc = cyttsp5_register_module(&debug_module);
+ if (rc < 0) {
+ pr_err("%s: Error, failed registering module\n",
+ __func__);
+ return rc;
+ }
+
+ pr_info("%s: Parade TTSP Debug Driver (Built %s) rc=%d\n",
+ __func__, CY_DRIVER_VERSION, rc);
+ return 0;
+}
+module_init(cyttsp5_debug_init);
+
+static void __exit cyttsp5_debug_exit(void)
+{
+ cyttsp5_unregister_module(&debug_module);
+}
+module_exit(cyttsp5_debug_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_device_access.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_device_access.c
new file mode 100644
index 0000000..52dd630
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_device_access.c
@@ -0,0 +1,5201 @@
+/*
+ * cyttsp5_device_access.c
+ * Parade TrueTouch(TM) Standard Product V5 Device Access Module.
+ * Configuration and Test command/status user interface.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+#include <linux/firmware.h>
+
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+#define CY_CMCP_THRESHOLD_FILE_NAME "cyttsp5_thresholdfile.csv"
+#define CMCP_THRESHOLD_FILE_NAME "ttdl_cmcp_thresholdfile.csv"
+
+/* Max test case number */
+#define MAX_CASE_NUM (22)
+
+/* ASCII */
+#define ASCII_LF (0x0A)
+#define ASCII_CR (0x0D)
+#define ASCII_COMMA (0x2C)
+#define ASCII_ZERO (0x30)
+#define ASCII_NINE (0x39)
+
+/* Max characters of test case name */
+#define NAME_SIZE_MAX (50)
+
+/* Max sensor and button number */
+#define MAX_BUTTONS (HID_SYSINFO_MAX_BTN)
+#define MAX_SENSORS (1024)
+#define MAX_TX_SENSORS (128)
+#define MAX_RX_SENSORS (128)
+
+/* Multiply by 2 for double (min, max) values */
+#define TABLE_BUTTON_MAX_SIZE (MAX_BUTTONS * 2)
+#define TABLE_SENSOR_MAX_SIZE (MAX_SENSORS * 2)
+#define TABLE_TX_MAX_SIZE (MAX_TX_SENSORS*2)
+#define TABLE_RX_MAX_SIZE (MAX_RX_SENSORS*2)
+
+#define CM_PANEL_DATA_OFFSET (6)
+#define CM_BTN_DATA_OFFSET (6)
+#define CP_PANEL_DATA_OFFSET (6)
+#define CP_BTN_DATA_OFFSET (6)
+#define MAX_BUF_LEN (50000)
+
+/* cmcp csv file information */
+struct configuration {
+ u32 cm_range_limit_row;
+ u32 cm_range_limit_col;
+ u32 cm_min_limit_cal;
+ u32 cm_max_limit_cal;
+ u32 cm_max_delta_sensor_percent;
+ u32 cm_max_delta_button_percent;
+ u32 min_sensor_rx;
+ u32 max_sensor_rx;
+ u32 min_sensor_tx;
+ u32 max_sensor_tx;
+ u32 min_button;
+ u32 max_button;
+ u32 max_delta_sensor;
+ u32 cp_max_delta_sensor_rx_percent;
+ u32 cp_max_delta_sensor_tx_percent;
+ u32 cm_min_max_table_button[TABLE_BUTTON_MAX_SIZE];
+ u32 cp_min_max_table_button[TABLE_BUTTON_MAX_SIZE];
+ u32 cm_min_max_table_sensor[TABLE_SENSOR_MAX_SIZE];
+ u32 cp_min_max_table_rx[TABLE_RX_MAX_SIZE];
+ u32 cp_min_max_table_tx[TABLE_TX_MAX_SIZE];
+ u32 cm_min_max_table_button_size;
+ u32 cp_min_max_table_button_size;
+ u32 cm_min_max_table_sensor_size;
+ u32 cp_min_max_table_rx_size;
+ u32 cp_min_max_table_tx_size;
+ u32 cp_max_delta_button_percent;
+ u32 cm_max_table_gradient_cols_percent[TABLE_TX_MAX_SIZE];
+ u32 cm_max_table_gradient_cols_percent_size;
+ u32 cm_max_table_gradient_rows_percent[TABLE_RX_MAX_SIZE];
+ u32 cm_max_table_gradient_rows_percent_size;
+ u32 cm_excluding_row_edge;
+ u32 cm_excluding_col_edge;
+ u32 rx_num;
+ u32 tx_num;
+ u32 btn_num;
+ u32 cm_enabled;
+ u32 cp_enabled;
+ u32 is_valid_or_not;
+};
+
+/* Test case search definition */
+struct test_case_search {
+ char name[NAME_SIZE_MAX]; /* Test case name */
+ u32 name_size; /* Test case name size */
+ u32 offset; /* Test case offset */
+};
+
+/* Test case field definition */
+struct test_case_field {
+ char *name; /* Test case name */
+ u32 name_size; /* Test case name size */
+ u32 type; /* Test case type */
+ u32 *bufptr; /* Buffer to store value information */
+ u32 exist_or_not;/* Test case exist or not */
+ u32 data_num; /* Buffer data number */
+ u32 line_num; /* Buffer line number */
+};
+
+/* Test case type */
+enum test_case_type {
+ TEST_CASE_TYPE_NO,
+ TEST_CASE_TYPE_ONE,
+ TEST_CASE_TYPE_MUL,
+ TEST_CASE_TYPE_MUL_LINES,
+};
+
+/* Test case order in test_case_field_array */
+enum case_order {
+ CM_TEST_INPUTS,
+ CM_EXCLUDING_COL_EDGE,
+ CM_EXCLUDING_ROW_EDGE,
+ CM_GRADIENT_CHECK_COL,
+ CM_GRADIENT_CHECK_ROW,
+ CM_RANGE_LIMIT_ROW,
+ CM_RANGE_LIMIT_COL,
+ CM_MIN_LIMIT_CAL,
+ CM_MAX_LIMIT_CAL,
+ CM_MAX_DELTA_SENSOR_PERCENT,
+ CM_MAX_DELTA_BUTTON_PERCENT,
+ PER_ELEMENT_MIN_MAX_TABLE_BUTTON,
+ PER_ELEMENT_MIN_MAX_TABLE_SENSOR,
+ CP_TEST_INPUTS,
+ CP_MAX_DELTA_SENSOR_RX_PERCENT,
+ CP_MAX_DELTA_SENSOR_TX_PERCENT,
+ CP_MAX_DELTA_BUTTON_PERCENT,
+ CP_PER_ELEMENT_MIN_MAX_BUTTON,
+ MIN_BUTTON,
+ MAX_BUTTON,
+ PER_ELEMENT_MIN_MAX_RX,
+ PER_ELEMENT_MIN_MAX_TX,
+ CASE_ORDER_MAX,
+};
+
+enum cmcp_test_item {
+ CMCP_FULL = 0,
+ CMCP_CM_PANEL,
+ CMCP_CP_PANEL,
+ CMCP_CM_BTN,
+ CMCP_CP_BTN,
+};
+
+#define CM_ENABLED 0x10
+#define CP_ENABLED 0x20
+#define CM_PANEL (0x01 | CM_ENABLED)
+#define CP_PANEL (0x02 | CP_ENABLED)
+#define CM_BTN (0x04 | CM_ENABLED)
+#define CP_BTN (0x08 | CP_ENABLED)
+#define CMCP_FULL_CASE\
+ (CM_PANEL | CP_PANEL | CM_BTN | CP_BTN | CM_ENABLED | CP_ENABLED)
+
+#define CYTTSP5_DEVICE_ACCESS_NAME "cyttsp5_device_access"
+#define CYTTSP5_INPUT_ELEM_SZ (sizeof("0xHH") + 1)
+
+#define STATUS_SUCCESS 0
+#define STATUS_FAIL -1
+#define PIP_CMD_MAX_LENGTH ((1 << 16) - 1)
+
+#ifdef TTHE_TUNER_SUPPORT
+struct heatmap_param {
+ bool scan_start;
+ enum scan_data_type_list data_type; /* raw, base, diff */
+ int num_element;
+};
+#endif
+#define ABS(x) (((x) < 0) ? -(x) : (x))
+
+#define CY_MAX_CONFIG_BYTES 256
+#define CYTTSP5_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME "get_panel_data"
+#define TTHE_TUNER_MAX_BUF (CY_MAX_PRBUF_SIZE * 3)
+
+struct cyttsp5_device_access_data {
+ struct device *dev;
+ struct cyttsp5_sysinfo *si;
+ struct mutex sysfs_lock;
+ u8 status;
+ u16 response_length;
+ bool sysfs_nodes_created;
+ struct kobject mfg_test;
+ u8 panel_scan_data_id;
+ u8 get_idac_data_id;
+ u8 calibrate_sensing_mode;
+ u8 calibrate_initialize_baselines;
+ u8 baseline_sensing_mode;
+#ifdef TTHE_TUNER_SUPPORT
+ struct heatmap_param heatmap;
+ struct dentry *tthe_get_panel_data_debugfs;
+ struct mutex debugfs_lock;
+ u8 tthe_get_panel_data_buf[TTHE_TUNER_MAX_BUF];
+ u8 tthe_get_panel_data_is_open;
+#endif
+ struct dentry *cmcp_results_debugfs;
+
+ struct dentry *base_dentry;
+ struct dentry *mfg_test_dentry;
+ u8 ic_buf[CY_MAX_PRBUF_SIZE];
+ u8 response_buf[CY_MAX_PRBUF_SIZE];
+ struct mutex cmcp_threshold_lock;
+ u8 *cmcp_threshold_data;
+ int cmcp_threshold_size;
+ bool cmcp_threshold_loading;
+ struct work_struct cmcp_threshold_update;
+ struct completion builtin_cmcp_threshold_complete;
+ int builtin_cmcp_threshold_status;
+ bool is_manual_upgrade_enabled;
+ struct configuration *configs;
+ struct cmcp_data *cmcp_info;
+ struct result *result;
+ struct test_case_search *test_search_array;
+ struct test_case_field *test_field_array;
+ int cmcp_test_items;
+ int test_executed;
+ int cmcp_range_check;
+ int cmcp_force_calibrate;
+ int cmcp_test_in_progress;
+};
+
+struct cmcp_data {
+ struct gd_sensor *gd_sensor_col;
+ struct gd_sensor *gd_sensor_row;
+ int32_t *cm_data_panel;
+ int32_t *cp_tx_data_panel;
+ int32_t *cp_rx_data_panel;
+ int32_t *cp_tx_cal_data_panel;
+ int32_t *cp_rx_cal_data_panel;
+ int32_t cp_sensor_rx_delta;
+ int32_t cp_sensor_tx_delta;
+ int32_t cp_button_delta;
+ int32_t *cm_btn_data;
+ int32_t *cp_btn_data;
+ int32_t *cm_sensor_column_delta;
+ int32_t *cm_sensor_row_delta;
+ int32_t cp_btn_cal;
+ int32_t cm_btn_cal;
+ int32_t cp_button_ave;
+ int32_t cm_ave_data_panel;
+ int32_t cp_tx_ave_data_panel;
+ int32_t cp_rx_ave_data_panel;
+ int32_t cm_cal_data_panel;
+ int32_t cm_ave_data_btn;
+ int32_t cm_cal_data_btn;
+ int32_t cm_delta_data_btn;
+ int32_t cm_sensor_delta;
+
+ int32_t tx_num;
+ int32_t rx_num;
+ int32_t btn_num;
+};
+
+struct result {
+ int32_t sensor_assignment;
+ int32_t config_ver;
+ int32_t revision_ctrl;
+ int32_t device_id_high;
+ int32_t device_id_low;
+ bool cm_test_run;
+ bool cp_test_run;
+ /* Sensor Cm validation */
+ bool cm_test_pass;
+ bool cm_sensor_validation_pass;
+ bool cm_sensor_row_delta_pass;
+ bool cm_sensor_col_delta_pass;
+ bool cm_sensor_gd_row_pass;
+ bool cm_sensor_gd_col_pass;
+ bool cm_sensor_calibration_pass;
+ bool cm_sensor_delta_pass;
+ bool cm_button_validation_pass;
+ bool cm_button_delta_pass;
+
+ int32_t *cm_sensor_raw_data;
+ int32_t cm_sensor_calibration;
+ int32_t cm_sensor_delta;
+ int32_t *cm_button_raw_data;
+ int32_t cm_button_delta;
+
+ /* Sensor Cp validation */
+ bool cp_test_pass;
+ bool cp_sensor_delta_pass;
+ bool cp_sensor_rx_delta_pass;
+ bool cp_sensor_tx_delta_pass;
+ bool cp_sensor_average_pass;
+ bool cp_button_delta_pass;
+ bool cp_button_average_pass;
+ bool cp_rx_validation_pass;
+ bool cp_tx_validation_pass;
+ bool cp_button_validation_pass;
+
+ int32_t *cp_sensor_rx_raw_data;
+ int32_t *cp_sensor_tx_raw_data;
+ int32_t cp_sensor_rx_delta;
+ int32_t cp_sensor_tx_delta;
+ int32_t cp_sensor_rx_calibration;
+ int32_t cp_sensor_tx_calibration;
+ int32_t *cp_button_raw_data;
+ int32_t cp_button_delta;
+
+ /*other validation*/
+ bool short_test_pass;
+ bool test_summary;
+ uint8_t *cm_open_pwc;
+};
+
+static struct cyttsp5_core_commands *cmd;
+
+static struct cyttsp5_module device_access_module;
+
+static ssize_t cyttsp5_run_and_get_selftest_result_noprint(struct device *dev,
+ char *buf, size_t buf_len, u8 test_id, u16 read_length,
+ bool get_result_on_pass);
+
+static int _cyttsp5_calibrate_idacs_cmd(struct device *dev,
+ u8 sensing_mode, u8 *status);
+
+static inline struct cyttsp5_device_access_data *cyttsp5_get_device_access_data(
+ struct device *dev)
+{
+ return cyttsp5_get_module_data(dev, &device_access_module);
+}
+
+static ssize_t cyttsp5_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ u8 val;
+
+ mutex_lock(&dad->sysfs_lock);
+ val = dad->status;
+ mutex_unlock(&dad->sysfs_lock);
+
+ return scnprintf(buf, CY_MAX_PRBUF_SIZE, "%d\n", val);
+}
+
+static DEVICE_ATTR(status, S_IRUSR, cyttsp5_status_show, NULL);
+
+static ssize_t cyttsp5_response_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ int i;
+ ssize_t num_read;
+ int index;
+
+ mutex_lock(&dad->sysfs_lock);
+ index = scnprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status %d\n", dad->status);
+ if (!dad->status)
+ goto error;
+
+ num_read = dad->response_length;
+
+ for (i = 0; i < num_read; i++)
+ index += scnprintf(buf + index, CY_MAX_PRBUF_SIZE - index,
+ "0x%02X\n", dad->response_buf[i]);
+
+ index += scnprintf(buf + index, CY_MAX_PRBUF_SIZE - index,
+ "(%zd bytes)\n", num_read);
+
+error:
+ mutex_unlock(&dad->sysfs_lock);
+ return index;
+}
+
+static DEVICE_ATTR(response, S_IRUSR, cyttsp5_response_show, NULL);
+
+/*
+ * Gets user input from sysfs and parse it
+ * return size of parsed output buffer
+ */
+static int cyttsp5_ic_parse_input(struct device *dev, const char *buf,
+ size_t buf_size, u8 *ic_buf, size_t ic_buf_size)
+{
+ const char *pbuf = buf;
+ unsigned long value;
+ char scan_buf[CYTTSP5_INPUT_ELEM_SZ];
+ u32 i = 0;
+ u32 j;
+ int last = 0;
+ int ret;
+
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: pbuf=%p buf=%p size=%zu %s=%zu buf=%s\n",
+ __func__, pbuf, buf, buf_size, "scan buf size",
+ CYTTSP5_INPUT_ELEM_SZ, buf);
+
+ while (pbuf <= (buf + buf_size)) {
+ if (i >= CY_MAX_CONFIG_BYTES) {
+ dev_err(dev, "%s: %s size=%d max=%d\n", __func__,
+ "Max cmd size exceeded", i,
+ CY_MAX_CONFIG_BYTES);
+ return -EINVAL;
+ }
+ if (i >= ic_buf_size) {
+ dev_err(dev, "%s: %s size=%d buf_size=%zu\n", __func__,
+ "Buffer size exceeded", i, ic_buf_size);
+ return -EINVAL;
+ }
+ while (((*pbuf == ' ') || (*pbuf == ','))
+ && (pbuf < (buf + buf_size))) {
+ last = *pbuf;
+ pbuf++;
+ }
+
+ if (pbuf >= (buf + buf_size))
+ break;
+
+ memset(scan_buf, 0, CYTTSP5_INPUT_ELEM_SZ);
+ if ((last == ',') && (*pbuf == ',')) {
+ dev_err(dev, "%s: %s \",,\" not allowed.\n", __func__,
+ "Invalid data format.");
+ return -EINVAL;
+ }
+ for (j = 0; j < (CYTTSP5_INPUT_ELEM_SZ - 1)
+ && (pbuf < (buf + buf_size))
+ && (*pbuf != ' ')
+ && (*pbuf != ','); j++) {
+ last = *pbuf;
+ scan_buf[j] = *pbuf++;
+ }
+
+ ret = kstrtoul(scan_buf, 16, &value);
+ if (ret < 0) {
+ dev_err(dev, "%s: %s '%s' %s%s i=%d r=%d\n", __func__,
+ "Invalid data format. ", scan_buf,
+ "Use \"0xHH,...,0xHH\"", " instead.",
+ i, ret);
+ return ret;
+ }
+
+ ic_buf[i] = value;
+ i++;
+ }
+
+ return i;
+}
+
+static ssize_t cyttsp5_command_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ ssize_t length;
+ int rc;
+
+ mutex_lock(&dad->sysfs_lock);
+ dad->status = 0;
+ dad->response_length = 0;
+ length = cyttsp5_ic_parse_input(dev, buf, size, dad->ic_buf,
+ CY_MAX_PRBUF_SIZE);
+ if (length <= 0) {
+ dev_err(dev, "%s: %s Group Data store\n", __func__,
+ "Malformed input for");
+ goto exit;
+ }
+
+ /* write ic_buf to log */
+ cyttsp5_pr_buf(dev, dad->ic_buf, length, "ic_buf");
+
+ pm_runtime_get_sync(dev);
+ rc = cmd->nonhid_cmd->user_cmd(dev, 1, CY_MAX_PRBUF_SIZE,
+ dad->response_buf, length, dad->ic_buf,
+ &dad->response_length);
+ pm_runtime_put(dev);
+ if (rc) {
+ dad->response_length = 0;
+ dev_err(dev, "%s: Failed to store command\n", __func__);
+ } else {
+ dad->status = 1;
+ }
+
+exit:
+ mutex_unlock(&dad->sysfs_lock);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: return size=%zu\n",
+ __func__, size);
+ return size;
+}
+
+static DEVICE_ATTR(command, S_IWUSR, NULL, cyttsp5_command_store);
+
+static int cmcp_check_config_fw_match(struct device *dev,
+ struct configuration *configuration)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ int32_t tx_num = dad->configs->tx_num;
+ int32_t rx_num = dad->configs->rx_num;
+ int32_t button_num = dad->configs->btn_num;
+ int ret = 0;
+
+ if (tx_num != dad->si->sensing_conf_data.tx_num) {
+ dev_err(dev, "%s: TX number mismatch!\n", __func__);
+ ret = -EINVAL;
+ }
+
+ if (rx_num != dad->si->sensing_conf_data.rx_num) {
+ dev_err(dev, "%s: RX number mismatch!\n", __func__);
+ ret = -EINVAL;
+ }
+
+ if (button_num != dad->si->num_btns) {
+ dev_err(dev, "%s: Button number mismatch!\n", __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int validate_cm_test_results(struct device *dev,
+ struct configuration *configuration, struct cmcp_data *cmcp_info,
+ struct result *result, bool *pass, int test_item)
+{
+ int32_t tx_num = cmcp_info->tx_num;
+ int32_t rx_num = cmcp_info->rx_num;
+ int32_t button_num = cmcp_info->btn_num;
+ uint32_t sensor_num = tx_num * rx_num;
+ int32_t *cm_sensor_data = cmcp_info->cm_data_panel;
+ int32_t cm_button_delta;
+ int32_t cm_sensor_calibration;
+ int32_t *cm_button_data = cmcp_info->cm_btn_data;
+ struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col;
+ struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row;
+ int32_t *cm_sensor_column_delta = cmcp_info->cm_sensor_column_delta;
+ int32_t *cm_sensor_row_delta = cmcp_info->cm_sensor_row_delta;
+ int ret = 0;
+ int i, j;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: start\n", __func__);
+
+ if ((test_item & CM_PANEL) == CM_PANEL) {
+ parade_debug(dev, DEBUG_LEVEL_2, "Check each sensor Cm data for min max value\n ");
+
+ /* Check each sensor Cm data for min/max values */
+ result->cm_sensor_validation_pass = true;
+
+ for (i = 0; i < sensor_num; i++) {
+ int row = i % rx_num;
+ int col = i / rx_num;
+ int32_t cm_sensor_min =
+ configuration->cm_min_max_table_sensor[(row*tx_num+col)*2];
+ int32_t cm_sensor_max =
+ configuration->cm_min_max_table_sensor[(row*tx_num+col)*2+1];
+ if ((cm_sensor_data[i] < cm_sensor_min)
+ || (cm_sensor_data[i] > cm_sensor_max)) {
+ dev_err(dev, "%s: Sensor[%d,%d]:%d (%d,%d)\n",
+ "Cm sensor min/max test",
+ row, col,
+ cm_sensor_data[i],
+ cm_sensor_min, cm_sensor_max);
+ result->cm_sensor_validation_pass = false;
+ }
+ }
+
+ /*check cm gradient column data*/
+ result->cm_sensor_gd_col_pass = true;
+ for (i = 0;
+ i < configuration->cm_max_table_gradient_cols_percent_size;
+ i++) {
+ if ((gd_sensor_col + i)->gradient_val >
+ 10 * configuration->cm_max_table_gradient_cols_percent[i]){
+ dev_err(dev,
+ "%s: cm_max_table_gradient_cols_percent[%d]:%d, gradient_val:%d\n",
+ __func__,
+ i,
+ configuration->cm_max_table_gradient_cols_percent[i],
+ (gd_sensor_col + i)->gradient_val);
+ result->cm_sensor_gd_col_pass = false;
+ }
+ }
+
+ /*check cm gradient row data*/
+ result->cm_sensor_gd_row_pass = true;
+ for (j = 0;
+ j < configuration->cm_max_table_gradient_rows_percent_size;
+ j++) {
+ if ((gd_sensor_row + j)->gradient_val >
+ 10 * configuration->cm_max_table_gradient_rows_percent[j]) {
+ dev_err(dev,
+ "%s: cm_max_table_gradient_rows_percent[%d]:%d, gradient_val:%d\n",
+ __func__,
+ j, configuration->cm_max_table_gradient_rows_percent[j],
+ (gd_sensor_row + j)->gradient_val);
+ result->cm_sensor_gd_row_pass = false;
+ }
+ }
+
+ result->cm_sensor_row_delta_pass = true;
+ result->cm_sensor_col_delta_pass = true;
+ result->cm_sensor_calibration_pass = true;
+ result->cm_sensor_delta_pass = true;
+
+
+ /*
+ * Check each row Cm data
+ * with neighbor for difference
+ */
+ for (i = 0; i < tx_num; i++) {
+ for (j = 1; j < rx_num; j++) {
+ int32_t cm_sensor_row_diff =
+ ABS(cm_sensor_data[i * rx_num + j] -
+ cm_sensor_data[i * rx_num + j - 1]);
+ cm_sensor_row_delta[i * rx_num + j - 1] =
+ cm_sensor_row_diff;
+ if (cm_sensor_row_diff
+ > configuration->cm_range_limit_row) {
+ dev_err(dev,
+ "%s: Sensor[%d,%d]:%d (%d)\n",
+ "Cm sensor row range limit test",
+ j, i,
+ cm_sensor_row_diff,
+ configuration->cm_range_limit_row);
+ result->cm_sensor_row_delta_pass = false;
+ }
+ }
+ }
+
+ /*
+ * Check each column Cm data
+ * with neighbor for difference
+ */
+ for (i = 1; i < tx_num; i++) {
+ for (j = 0; j < rx_num; j++) {
+ int32_t cm_sensor_col_diff =
+ ABS((int)cm_sensor_data[i * rx_num + j] -
+ (int)cm_sensor_data[(i - 1) * rx_num + j]);
+ cm_sensor_column_delta[(i - 1) * rx_num + j] =
+ cm_sensor_col_diff;
+ if (cm_sensor_col_diff >
+ configuration->cm_range_limit_col) {
+ dev_err(dev,
+ "%s: Sensor[%d,%d]:%d (%d)\n",
+ "Cm sensor column range limit test",
+ j, i,
+ cm_sensor_col_diff,
+ configuration->cm_range_limit_col);
+ result->cm_sensor_col_delta_pass = false;
+ }
+ }
+ }
+
+ /* Check sensor calculated Cm for min/max values */
+ cm_sensor_calibration = cmcp_info->cm_cal_data_panel;
+ if (cm_sensor_calibration <
+ configuration->cm_min_limit_cal
+ || cm_sensor_calibration >
+ configuration->cm_max_limit_cal) {
+ dev_err(dev, "%s: Cm_cal:%d (%d,%d)\n",
+ "Cm sensor Cm_cal min/max test",
+ cm_sensor_calibration,
+ configuration->cm_min_limit_cal,
+ configuration->cm_max_limit_cal);
+ result->cm_sensor_calibration_pass = false;
+ }
+
+ /* Check sensor Cm delta for range limit */
+ if (cmcp_info->cm_sensor_delta
+ > 10 * configuration->cm_max_delta_sensor_percent) {
+ dev_err(dev,
+ "%s: Cm_sensor_delta:%d (%d)\n",
+ "Cm sensor delta range limit test",
+ cmcp_info->cm_sensor_delta,
+ configuration->cm_max_delta_sensor_percent);
+ result->cm_sensor_delta_pass = false;
+ }
+
+ result->cm_test_pass = result->cm_sensor_gd_col_pass
+ && result->cm_sensor_gd_row_pass
+ && result->cm_sensor_validation_pass
+ && result->cm_sensor_row_delta_pass
+ && result->cm_sensor_col_delta_pass
+ && result->cm_sensor_calibration_pass
+ && result->cm_sensor_delta_pass;
+ }
+
+ if (((test_item & CM_BTN) == CM_BTN) && (cmcp_info->btn_num)) {
+ /* Check each button Cm data for min/max values */
+ result->cm_button_validation_pass = true;
+ for (i = 0; i < button_num; i++) {
+ int32_t cm_button_min =
+ configuration->cm_min_max_table_button[i * 2];
+ int32_t cm_button_max =
+ configuration->cm_min_max_table_button[i * 2 + 1];
+ if ((cm_button_data[i] <= cm_button_min)
+ || (cm_button_data[i] >= cm_button_max)) {
+ dev_err(dev,
+ "%s: Button[%d]:%d (%d,%d)\n",
+ "Cm button min/max test",
+ i,
+ cm_button_data[i],
+ cm_button_min, cm_button_max);
+ result->cm_button_validation_pass = false;
+ }
+ }
+
+ /* Check button Cm delta for range limit */
+ result->cm_button_delta_pass = true;
+
+ cm_button_delta = ABS((cmcp_info->cm_ave_data_btn -
+ cmcp_info->cm_cal_data_btn) * 100 /
+ cmcp_info->cm_ave_data_btn);
+ if (cm_button_delta >
+ configuration->cm_max_delta_button_percent) {
+ dev_err(dev,
+ "%s: Cm_button_delta:%d (%d)\n",
+ "Cm button delta range limit test",
+ cm_button_delta,
+ configuration->cm_max_delta_button_percent);
+ result->cm_button_delta_pass = false;
+ }
+
+ result->cm_test_pass = result->cm_test_pass
+ && result->cm_button_validation_pass
+ && result->cm_button_delta_pass;
+ }
+
+ if (pass)
+ *pass = result->cm_test_pass;
+
+ return ret;
+}
+static int validate_cp_test_results(struct device *dev,
+ struct configuration *configuration, struct cmcp_data *cmcp_info,
+ struct result *result, bool *pass, int test_item)
+{
+ int i = 0;
+ uint32_t configuration_rx_num;
+ uint32_t configuration_tx_num;
+ int32_t *cp_sensor_tx_data = cmcp_info->cp_tx_data_panel;
+ int32_t *cp_sensor_rx_data = cmcp_info->cp_rx_data_panel;
+ int32_t cp_button_delta;
+ int32_t cp_button_average;
+
+ result->cp_test_pass = true;
+ configuration_rx_num = configuration->cp_min_max_table_rx_size/2;
+ configuration_tx_num = configuration->cp_min_max_table_tx_size/2;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s start\n", __func__);
+
+ if ((test_item & CP_PANEL) == CP_PANEL) {
+ int32_t cp_sensor_tx_delta;
+ int32_t cp_sensor_rx_delta;
+
+ /* Check Sensor Cp delta for range limit */
+ result->cp_sensor_delta_pass = true;
+ /*check cp_sensor_tx_delta */
+ for (i = 0; i < configuration_tx_num; i++) {
+ cp_sensor_tx_delta =
+ ABS((cmcp_info->cp_tx_cal_data_panel[i]-
+ cmcp_info->cp_tx_data_panel[i]) * 100 /
+ cmcp_info->cp_tx_data_panel[i]);
+
+ if (cp_sensor_tx_delta >
+ configuration->cp_max_delta_sensor_tx_percent) {
+ dev_err(dev,
+ "%s: Cp_sensor_tx_delta:%d (%d)\n",
+ "Cp sensor delta range limit test",
+ cp_sensor_tx_delta,
+ configuration->cp_max_delta_sensor_tx_percent);
+ result->cp_sensor_delta_pass = false;
+ }
+ }
+
+ /*check cp_sensor_rx_delta */
+ for (i = 0; i < configuration_rx_num; i++) {
+ cp_sensor_rx_delta =
+ ABS((cmcp_info->cp_rx_cal_data_panel[i] -
+ cmcp_info->cp_rx_data_panel[i]) * 100 /
+ cmcp_info->cp_rx_data_panel[i]);
+ if (cp_sensor_rx_delta >
+ configuration->cp_max_delta_sensor_rx_percent) {
+ dev_err(dev,
+ "%s: Cp_sensor_rx_delta:%d(%d)\n",
+ "Cp sensor delta range limit test",
+ cp_sensor_rx_delta,
+ configuration->cp_max_delta_sensor_rx_percent);
+ result->cp_sensor_delta_pass = false;
+ }
+ }
+
+ /* Check sensor Cp rx for min/max values */
+ result->cp_rx_validation_pass = true;
+ for (i = 0; i < configuration_rx_num; i++) {
+ int32_t cp_rx_min =
+ configuration->cp_min_max_table_rx[i * 2];
+ int32_t cp_rx_max =
+ configuration->cp_min_max_table_rx[i * 2 + 1];
+ if ((cp_sensor_rx_data[i] <= cp_rx_min)
+ || (cp_sensor_rx_data[i] >= cp_rx_max)) {
+ dev_err(dev,
+ "%s: Cp Rx[%d]:%d (%d,%d)\n",
+ "Cp Rx min/max test",
+ i,
+ (int)cp_sensor_rx_data[i],
+ cp_rx_min, cp_rx_max);
+ result->cp_rx_validation_pass = false;
+ }
+ }
+
+ /* Check sensor Cp tx for min/max values */
+ result->cp_tx_validation_pass = true;
+ for (i = 0; i < configuration_tx_num; i++) {
+ int32_t cp_tx_min =
+ configuration->cp_min_max_table_tx[i * 2];
+ int32_t cp_tx_max =
+ configuration->cp_min_max_table_tx[i * 2 + 1];
+ if ((cp_sensor_tx_data[i] <= cp_tx_min)
+ || (cp_sensor_tx_data[i] >= cp_tx_max)) {
+ dev_err(dev,
+ "%s: Cp Tx[%d]:%d(%d,%d)\n",
+ "Cp Tx min/max test",
+ i,
+ cp_sensor_tx_data[i],
+ cp_tx_min, cp_tx_max);
+ result->cp_tx_validation_pass = false;
+ }
+ }
+
+ result->cp_test_pass = result->cp_test_pass
+ && result->cp_sensor_delta_pass
+ && result->cp_rx_validation_pass
+ && result->cp_tx_validation_pass;
+ }
+
+ if (((test_item & CP_BTN) == CP_BTN) && (cmcp_info->btn_num)) {
+ result->cp_button_delta_pass = true;
+
+ /* Check button Cp delta for range limit */
+ cp_button_delta = ABS((cmcp_info->cp_btn_cal
+ - cmcp_info->cp_button_ave) * 100 /
+ cmcp_info->cp_button_ave);
+ if (cp_button_delta >
+ configuration->cp_max_delta_button_percent) {
+ dev_err(dev,
+ "%s: Cp_button_delta:%d (%d)\n",
+ "Cp button delta range limit test",
+ cp_button_delta,
+ configuration->cp_max_delta_button_percent);
+ result->cp_button_delta_pass = false;
+ }
+
+ /* Check button Cp average for min/max values */
+ result->cp_button_average_pass = true;
+ cp_button_average = cmcp_info->cp_button_ave;
+ if (cp_button_average < configuration->min_button
+ || cp_button_average >
+ configuration->max_button) {
+ dev_err(dev,
+ "%s: Button Cp average fails min/max test\n",
+ __func__);
+ dev_err(dev,
+ "%s: Cp_button_average:%d (%d,%d)\n",
+ "Cp button average min/max test",
+ cp_button_average,
+ configuration->min_button,
+ configuration->max_button);
+ result->cp_button_average_pass = false;
+ }
+
+ /* Check each button Cp data for min/max values */
+ result->cp_button_validation_pass = true;
+ for (i = 0; i < cmcp_info->btn_num; i++) {
+ int32_t cp_button_min =
+ configuration->cp_min_max_table_button[i * 2];
+ int32_t cp_button_max =
+ configuration->cp_min_max_table_button[i * 2 + 1];
+ if ((cmcp_info->cp_btn_data[i] <= cp_button_min)
+ || (cmcp_info->cp_btn_data[i] >= cp_button_max)) {
+ dev_err(dev,
+ "%s: Button[%d]:%d (%d,%d)\n",
+ "Cp button min/max test",
+ i,
+ cmcp_info->cp_btn_data[i],
+ cp_button_min, cp_button_max);
+ result->cp_button_validation_pass = false;
+ }
+ }
+
+ result->cp_test_pass = result->cp_test_pass
+ && result->cp_button_delta_pass
+ && result->cp_button_average_pass
+ && result->cp_button_validation_pass;
+ }
+
+ if (pass)
+ *pass = result->cp_test_pass;
+
+ return 0;
+}
+
+static void calculate_gradient_row(struct gd_sensor *gd_sensor_row_head,
+ uint16_t row_num, int exclude_row_edge, int exclude_col_edge)
+{
+ int i = 0;
+ uint16_t cm_min_cur = 0;
+ uint16_t cm_max_cur = 0;
+ uint16_t cm_ave_cur = 0;
+ uint16_t cm_ave_next = 0;
+ uint16_t cm_ave_prev = 0;
+ struct gd_sensor *p = gd_sensor_row_head;
+
+ if (exclude_row_edge) {
+ for (i = 0; i < row_num; i++) {
+ if (!exclude_col_edge) {
+ cm_ave_cur = (p + i)->cm_ave;
+ cm_min_cur = (p + i)->cm_min;
+ cm_max_cur = (p + i)->cm_max;
+ if (i < (row_num-1))
+ cm_ave_next = (p + i+1)->cm_ave;
+ if (i > 0)
+ cm_ave_prev = (p + i-1)->cm_ave;
+ } else {
+ cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+ cm_min_cur = (p + i)->cm_min_exclude_edge;
+ cm_max_cur = (p + i)->cm_max_exclude_edge;
+ if (i < (row_num-1))
+ cm_ave_next =
+ (p + i+1)->cm_ave_exclude_edge;
+ if (i > 0)
+ cm_ave_prev =
+ (p + i-1)->cm_ave_exclude_edge;
+ }
+
+ if (cm_ave_cur == 0)
+ cm_ave_cur = 1;
+
+ /*multiple 1000 to increate accuracy*/
+ if ((i == 0) || (i == (row_num-1))) {
+ (p + i)->gradient_val =
+ (cm_max_cur - cm_min_cur) * 1000 /
+ cm_ave_cur;
+ } else if (i == 1) {
+ (p + i)->gradient_val = (cm_max_cur - cm_min_cur
+ + ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+ cm_ave_cur;
+ } else {
+ (p + i)->gradient_val = (cm_max_cur - cm_min_cur
+ + ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+ cm_ave_cur;
+ }
+ }
+ } else if (!exclude_row_edge) {
+ for (i = 0; i < row_num; i++) {
+ if (!exclude_col_edge) {
+ cm_ave_cur = (p + i)->cm_ave;
+ cm_min_cur = (p + i)->cm_min;
+ cm_max_cur = (p + i)->cm_max;
+ if (i < (row_num-1))
+ cm_ave_next = (p + i+1)->cm_ave;
+ if (i > 0)
+ cm_ave_prev = (p + i-1)->cm_ave;
+ } else {
+ cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+ cm_min_cur = (p + i)->cm_min_exclude_edge;
+ cm_max_cur = (p + i)->cm_max_exclude_edge;
+ if (i < (row_num-1))
+ cm_ave_next =
+ (p + i+1)->cm_ave_exclude_edge;
+ if (i > 0)
+ cm_ave_prev =
+ (p + i-1)->cm_ave_exclude_edge;
+ }
+
+ if (cm_ave_cur == 0)
+ cm_ave_cur = 1;
+ /*multiple 1000 to increate accuracy*/
+ if (i <= 1)
+ (p + i)->gradient_val = (cm_max_cur - cm_min_cur
+ + ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+ cm_ave_cur;
+ else
+ (p + i)->gradient_val = (cm_max_cur - cm_min_cur
+ + ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+ cm_ave_cur;
+ }
+ }
+}
+
+static void calculate_gradient_col(struct gd_sensor *gd_sensor_row_head,
+ uint16_t col_num, int exclude_row_edge, int exclude_col_edge)
+{
+ int i = 0;
+ int32_t cm_min_cur = 0;
+ int32_t cm_max_cur = 0;
+ int32_t cm_ave_cur = 0;
+ int32_t cm_ave_next = 0;
+ int32_t cm_ave_prev = 0;
+ struct gd_sensor *p = gd_sensor_row_head;
+
+ if (!exclude_col_edge) {
+ for (i = 0; i < col_num; i++) {
+ if (!exclude_row_edge) {
+ cm_ave_cur = (p + i)->cm_ave;
+ cm_min_cur = (p + i)->cm_min;
+ cm_max_cur = (p + i)->cm_max;
+ if (i < (col_num-1))
+ cm_ave_next = (p + i+1)->cm_ave;
+ if (i > 0)
+ cm_ave_prev = (p + i-1)->cm_ave;
+ } else {
+ cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+ cm_min_cur = (p + i)->cm_min_exclude_edge;
+ cm_max_cur = (p + i)->cm_max_exclude_edge;
+ if (i < (col_num-1))
+ cm_ave_next =
+ (p + i+1)->cm_ave_exclude_edge;
+ if (i > 0)
+ cm_ave_prev =
+ (p + i-1)->cm_ave_exclude_edge;
+ }
+ if (cm_ave_cur == 0)
+ cm_ave_cur = 1;
+ /*multiple 1000 to increate accuracy*/
+ if (i <= 1)
+ (p + i)->gradient_val = (cm_max_cur - cm_min_cur
+ + ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+ cm_ave_cur;
+ else
+ (p + i)->gradient_val = (cm_max_cur - cm_min_cur
+ + ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+ cm_ave_cur;
+ }
+ } else if (exclude_col_edge) {
+ for (i = 0; i < col_num; i++) {
+ if (!exclude_row_edge) {
+ cm_ave_cur = (p + i)->cm_ave;
+ cm_min_cur = (p + i)->cm_min;
+ cm_max_cur = (p + i)->cm_max;
+ if (i < (col_num-1))
+ cm_ave_next = (p + i+1)->cm_ave;
+ if (i > 0)
+ cm_ave_prev = (p + i-1)->cm_ave;
+ } else {
+ cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+ cm_min_cur = (p + i)->cm_min_exclude_edge;
+ cm_max_cur = (p + i)->cm_max_exclude_edge;
+ if (i < (col_num-1))
+ cm_ave_next =
+ (p + i+1)->cm_ave_exclude_edge;
+ if (i > 0)
+ cm_ave_prev =
+ (p + i-1)->cm_ave_exclude_edge;
+ }
+
+ if (cm_ave_cur == 0)
+ cm_ave_cur = 1;
+ /*multiple 1000 to increate accuracy*/
+ if ((i == 0) || (i == (col_num - 1)))
+ (p + i)->gradient_val =
+ (cm_max_cur - cm_min_cur) * 1000 /
+ cm_ave_cur;
+ else if (i == 1)
+ (p + i)->gradient_val =
+ (cm_max_cur - cm_min_cur +
+ ABS(cm_ave_cur - cm_ave_next))
+ * 1000 / cm_ave_cur;
+ else
+ (p + i)->gradient_val =
+ (cm_max_cur - cm_min_cur +
+ ABS(cm_ave_cur - cm_ave_prev))
+ * 1000 / cm_ave_cur;
+ }
+ }
+}
+
+static void fill_gd_sensor_table(struct gd_sensor *head, int32_t index,
+ int32_t cm_max, int32_t cm_min, int32_t cm_ave,
+ int32_t cm_max_exclude_edge, int32_t cm_min_exclude_edge,
+ int32_t cm_ave_exclude_edge)
+{
+ (head + index)->cm_max = cm_max;
+ (head + index)->cm_min = cm_min;
+ (head + index)->cm_ave = cm_ave;
+ (head + index)->cm_ave_exclude_edge = cm_ave_exclude_edge;
+ (head + index)->cm_max_exclude_edge = cm_max_exclude_edge;
+ (head + index)->cm_min_exclude_edge = cm_min_exclude_edge;
+}
+
+static void calculate_gd_info(struct gd_sensor *gd_sensor_col,
+ struct gd_sensor *gd_sensor_row, int tx_num, int rx_num,
+ int32_t *cm_sensor_data, int cm_excluding_row_edge,
+ int cm_excluding_col_edge)
+{
+ int32_t cm_max;
+ int32_t cm_min;
+ int32_t cm_ave;
+ int32_t cm_max_exclude_edge;
+ int32_t cm_min_exclude_edge;
+ int32_t cm_ave_exclude_edge;
+ int32_t cm_data;
+ int i;
+ int j;
+
+ /*calculate all the gradient related info for column*/
+ for (i = 0; i < tx_num; i++) {
+ /*re-initialize for a new col*/
+ cm_max = cm_sensor_data[i * rx_num];
+ cm_min = cm_max;
+ cm_ave = 0;
+ cm_max_exclude_edge = cm_sensor_data[i * rx_num + 1];
+ cm_min_exclude_edge = cm_max_exclude_edge;
+ cm_ave_exclude_edge = 0;
+
+ for (j = 0; j < rx_num; j++) {
+ cm_data = cm_sensor_data[i * rx_num + j];
+ if (cm_data > cm_max)
+ cm_max = cm_data;
+ if (cm_data < cm_min)
+ cm_min = cm_data;
+ cm_ave += cm_data;
+ /*calculate exclude edge data*/
+ if ((j > 0) && (j < (rx_num-1))) {
+ if (cm_data > cm_max_exclude_edge)
+ cm_max_exclude_edge = cm_data;
+ if (cm_data < cm_min_exclude_edge)
+ cm_min_exclude_edge = cm_data;
+ cm_ave_exclude_edge += cm_data;
+ }
+ }
+ cm_ave /= rx_num;
+ cm_ave_exclude_edge /= (rx_num-2);
+ fill_gd_sensor_table(gd_sensor_col, i, cm_max, cm_min, cm_ave,
+ cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge);
+ }
+
+ calculate_gradient_col(gd_sensor_col, tx_num, cm_excluding_row_edge,
+ cm_excluding_col_edge);
+
+ /*calculate all the gradient related info for row*/
+ for (j = 0; j < rx_num; j++) {
+ /*re-initialize for a new row*/
+ cm_max = cm_sensor_data[j];
+ cm_min = cm_max;
+ cm_ave = 0;
+ cm_max_exclude_edge = cm_sensor_data[rx_num + j];
+ cm_min_exclude_edge = cm_max_exclude_edge;
+ cm_ave_exclude_edge = 0;
+ for (i = 0; i < tx_num; i++) {
+ cm_data = cm_sensor_data[i * rx_num + j];
+ if (cm_data > cm_max)
+ cm_max = cm_data;
+ if (cm_data < cm_min)
+ cm_min = cm_data;
+ cm_ave += cm_data;
+ /*calculate exclude edge data*/
+ if ((i > 0) && (i < (tx_num-1))) {
+ if (cm_data > cm_max_exclude_edge)
+ cm_max_exclude_edge = cm_data;
+ if (cm_data < cm_min_exclude_edge)
+ cm_min_exclude_edge = cm_data;
+ cm_ave_exclude_edge += cm_data;
+ }
+ }
+ cm_ave /= tx_num;
+ cm_ave_exclude_edge /= (tx_num-2);
+ fill_gd_sensor_table(gd_sensor_row, j, cm_max, cm_min, cm_ave,
+ cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge);
+ }
+ calculate_gradient_row(gd_sensor_row, rx_num, cm_excluding_row_edge,
+ cm_excluding_col_edge);
+}
+
+static int cyttsp5_get_cmcp_info(struct cyttsp5_device_access_data *dad,
+ struct cmcp_data *cmcp_info)
+{
+ struct device *dev;
+ int32_t *cm_data_panel = cmcp_info->cm_data_panel;
+ int32_t *cp_tx_data_panel = cmcp_info->cp_tx_data_panel;
+ int32_t *cp_rx_data_panel = cmcp_info->cp_rx_data_panel;
+ int32_t *cp_tx_cal_data_panel = cmcp_info->cp_tx_cal_data_panel;
+ int32_t *cp_rx_cal_data_panel = cmcp_info->cp_rx_cal_data_panel;
+ int32_t *cm_btn_data = cmcp_info->cm_btn_data;
+ int32_t *cp_btn_data = cmcp_info->cp_btn_data;
+ struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col;
+ struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row;
+ struct result *result = dad->result;
+ int32_t cp_btn_cal = 0;
+ int32_t cm_btn_cal = 0;
+ int32_t cp_btn_ave = 0;
+ int32_t cm_ave_data_panel = 0;
+ int32_t cm_ave_data_btn = 0;
+ int32_t cm_delta_data_btn = 0;
+ int32_t cp_tx_ave_data_panel = 0;
+ int32_t cp_rx_ave_data_panel = 0;
+ u8 tmp_buf[3];
+ int tx_num;
+ int rx_num;
+ int btn_num;
+ int rc = 0;
+ int i;
+
+ dev = dad->dev;
+ cmcp_info->tx_num = dad->si->sensing_conf_data.tx_num;
+ cmcp_info->rx_num = dad->si->sensing_conf_data.rx_num;
+ cmcp_info->btn_num = dad->si->num_btns;
+
+ tx_num = cmcp_info->tx_num;
+ rx_num = cmcp_info->rx_num;
+ btn_num = cmcp_info->btn_num;
+ parade_debug(dev, DEBUG_LEVEL_2, "%s tx_num=%d", __func__, tx_num);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s rx_num=%d", __func__, rx_num);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s btn_num=%d", __func__, btn_num);
+
+ /*short test*/
+ result->short_test_pass = true;
+ rc = cyttsp5_run_and_get_selftest_result_noprint(
+ dev, tmp_buf, sizeof(tmp_buf),
+ CY_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, false);
+ if (rc) {
+ dev_err(dev, "short test not supported");
+ goto exit;
+ }
+ if (dad->ic_buf[1] != 0)
+ result->short_test_pass = false;
+
+ /*Get cm_panel data*/
+ rc = cyttsp5_run_and_get_selftest_result_noprint(
+ dev, tmp_buf, sizeof(tmp_buf),
+ CY_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, true);
+ if (rc) {
+ dev_err(dev, "Get CM Panel not supported");
+ goto exit;
+ }
+ if (cm_data_panel != NULL) {
+ for (i = 0; i < tx_num * rx_num; i++) {
+ cm_data_panel[i] =
+ 10*(dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2] + 256
+ * dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2+1]);
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "cm_data_panel[%d]=%d\n",
+ i, cm_data_panel[i]);
+ cm_ave_data_panel += cm_data_panel[i];
+ }
+ cm_ave_data_panel /= (tx_num * rx_num);
+ cmcp_info->cm_ave_data_panel = cm_ave_data_panel;
+ cmcp_info->cm_cal_data_panel =
+ 10*(dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2]
+ +256 * dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2+1]);
+ /*multiple 1000 to increate accuracy*/
+ cmcp_info->cm_sensor_delta = ABS((cmcp_info->cm_ave_data_panel -
+ cmcp_info->cm_cal_data_panel) * 1000 /
+ cmcp_info->cm_ave_data_panel);
+ }
+
+ /*calculate gradient panel sensor column/row here*/
+ calculate_gd_info(gd_sensor_col, gd_sensor_row, tx_num, rx_num,
+ cm_data_panel, 1, 1);
+ for (i = 0; i < tx_num; i++) {
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "i=%d max=%d,min=%d,ave=%d, gradient=%d",
+ i, gd_sensor_col[i].cm_max, gd_sensor_col[i].cm_min,
+ gd_sensor_col[i].cm_ave, gd_sensor_col[i].gradient_val);
+ }
+
+ for (i = 0; i < rx_num; i++) {
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "i=%d max=%d,min=%d,ave=%d, gradient=%d",
+ i, gd_sensor_row[i].cm_max, gd_sensor_row[i].cm_min,
+ gd_sensor_row[i].cm_ave, gd_sensor_row[i].gradient_val);
+ }
+
+ /*Get cp data*/
+ rc = cyttsp5_run_and_get_selftest_result_noprint(
+ dev, tmp_buf, sizeof(tmp_buf),
+ CY_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, true);
+ if (rc) {
+ dev_err(dev, "Get CP Panel not supported");
+ goto exit;
+ }
+ /*Get cp_tx_data_panel*/
+ if (cp_tx_data_panel != NULL) {
+ for (i = 0; i < tx_num; i++) {
+ cp_tx_data_panel[i] =
+ 10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2]
+ + 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2+1]);
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "cp_tx_data_panel[%d]=%d\n",
+ i, cp_tx_data_panel[i]);
+ cp_tx_ave_data_panel += cp_tx_data_panel[i];
+ }
+ cp_tx_ave_data_panel /= tx_num;
+ cmcp_info->cp_tx_ave_data_panel = cp_tx_ave_data_panel;
+ }
+
+ /*Get cp_tx_cal_data_panel*/
+ if (cp_tx_cal_data_panel != NULL) {
+ for (i = 0; i < tx_num; i++) {
+ cp_tx_cal_data_panel[i] =
+ 10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2]
+ + 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2+1]);
+ parade_debug(dev, DEBUG_LEVEL_2, " cp_tx_cal_data_panel[%d]=%d\n",
+ i, cp_tx_cal_data_panel[i]);
+ }
+ }
+
+ /*get cp_sensor_tx_delta,using the first sensor cal value for temp */
+ /*multiple 1000 to increase accuracy*/
+ cmcp_info->cp_sensor_tx_delta = ABS((cp_tx_cal_data_panel[0]
+ - cp_tx_ave_data_panel) * 1000 / cp_tx_ave_data_panel);
+
+ /*Get cp_rx_data_panel*/
+ if (cp_rx_data_panel != NULL) {
+ for (i = 0; i < rx_num; i++) {
+ cp_rx_data_panel[i] =
+ 10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2] +
+ 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2+1]);
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "cp_rx_data_panel[%d]=%d\n", i,
+ cp_rx_data_panel[i]);
+ cp_rx_ave_data_panel += cp_rx_data_panel[i];
+ }
+ cp_rx_ave_data_panel /= rx_num;
+ cmcp_info->cp_rx_ave_data_panel = cp_rx_ave_data_panel;
+ }
+
+ /*Get cp_rx_cal_data_panel*/
+ if (cp_rx_cal_data_panel != NULL) {
+ for (i = 0; i < rx_num; i++) {
+ cp_rx_cal_data_panel[i] =
+ 10 * (dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2] +
+ 256 *
+ dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2+1]);
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "cp_rx_cal_data_panel[%d]=%d\n", i,
+ cp_rx_cal_data_panel[i]);
+ }
+ }
+
+ /*get cp_sensor_rx_delta,using the first sensor cal value for temp */
+ /*multiple 1000 to increase accuracy*/
+ cmcp_info->cp_sensor_rx_delta = ABS((cp_rx_cal_data_panel[0]
+ - cp_rx_ave_data_panel) * 1000 / cp_rx_ave_data_panel);
+
+ if (btn_num == 0)
+ goto skip_button_test;
+
+ /*get cm btn data*/
+ rc = cyttsp5_run_and_get_selftest_result_noprint(
+ dev, tmp_buf, sizeof(tmp_buf),
+ CY_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, true);
+ if (rc) {
+ dev_err(dev, "Get CM BTN not supported");
+ goto exit;
+ }
+ if (cm_btn_data != NULL) {
+ for (i = 0; i < btn_num; i++) {
+ cm_btn_data[i] =
+ 10 * (dad->ic_buf[CM_BTN_DATA_OFFSET+i*2] +
+ 256 * dad->ic_buf[CM_BTN_DATA_OFFSET+i*2+1]);
+ parade_debug(dev, DEBUG_LEVEL_2,
+ " cm_btn_data[%d]=%d\n",
+ i, cm_btn_data[i]);
+ cm_ave_data_btn += cm_btn_data[i];
+ }
+ cm_ave_data_btn /= btn_num;
+ cm_btn_cal = 10*(dad->ic_buf[CM_BTN_DATA_OFFSET+i*2]
+ + 256 * dad->ic_buf[CM_BTN_DATA_OFFSET+i*2+1]);
+ /*multiple 1000 to increase accuracy*/
+ cm_delta_data_btn = ABS((cm_ave_data_btn-cm_btn_cal)
+ * 1000 / cm_ave_data_btn);
+ parade_debug(dev, DEBUG_LEVEL_2, " cm_btn_cal=%d\n",
+ cm_btn_cal);
+
+ cmcp_info->cm_ave_data_btn = cm_ave_data_btn;
+ cmcp_info->cm_cal_data_btn = cm_btn_cal;
+ cmcp_info->cm_delta_data_btn = cm_delta_data_btn;
+ }
+
+ /*get cp btn data*/
+ rc = cyttsp5_run_and_get_selftest_result_noprint(
+ dev, tmp_buf, sizeof(tmp_buf),
+ CY_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH, true);
+ if (rc) {
+ dev_err(dev, "Get CP BTN not supported");
+ goto exit;
+ }
+ if (cp_btn_data != NULL) {
+ for (i = 0; i < btn_num; i++) {
+ cp_btn_data[i] =
+ 10 * (dad->ic_buf[CP_BTN_DATA_OFFSET+i*2] +
+ 256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]);
+ cp_btn_ave += cp_btn_data[i];
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "cp_btn_data[%d]=%d\n",
+ i, cp_btn_data[i]);
+ }
+ cp_btn_ave /= btn_num;
+ cp_btn_cal = 10*(dad->ic_buf[CP_BTN_DATA_OFFSET+i*2]
+ + 256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]);
+ cmcp_info->cp_button_ave = cp_btn_ave;
+ cmcp_info->cp_btn_cal = cp_btn_cal;
+ /*multiple 1000 to increase accuracy*/
+ cmcp_info->cp_button_delta = ABS((cp_btn_cal
+ - cp_btn_ave) * 1000 / cp_btn_ave);
+ parade_debug(dev, DEBUG_LEVEL_2, " cp_btn_cal=%d\n",
+ cp_btn_cal);
+ parade_debug(dev, DEBUG_LEVEL_2, " cp_btn_ave=%d\n",
+ cp_btn_ave);
+ }
+skip_button_test:
+exit:
+ return rc;
+}
+
+static void cyttsp5_free_cmcp_buf(struct cmcp_data *cmcp_info)
+{
+ if (cmcp_info->gd_sensor_col != NULL)
+ kfree(cmcp_info->gd_sensor_col);
+ if (cmcp_info->gd_sensor_row != NULL)
+ kfree(cmcp_info->gd_sensor_row);
+ if (cmcp_info->cm_data_panel != NULL)
+ kfree(cmcp_info->cm_data_panel);
+ if (cmcp_info->cp_tx_data_panel != NULL)
+ kfree(cmcp_info->cp_tx_data_panel);
+ if (cmcp_info->cp_rx_data_panel != NULL)
+ kfree(cmcp_info->cp_rx_data_panel);
+ if (cmcp_info->cp_tx_cal_data_panel != NULL)
+ kfree(cmcp_info->cp_tx_cal_data_panel);
+ if (cmcp_info->cp_rx_cal_data_panel != NULL)
+ kfree(cmcp_info->cp_rx_cal_data_panel);
+ if (cmcp_info->cm_btn_data != NULL)
+ kfree(cmcp_info->cm_btn_data);
+ if (cmcp_info->cp_btn_data != NULL)
+ kfree(cmcp_info->cp_btn_data);
+ if (cmcp_info->cm_sensor_column_delta != NULL)
+ kfree(cmcp_info->cm_sensor_column_delta);
+ if (cmcp_info->cm_sensor_row_delta != NULL)
+ kfree(cmcp_info->cm_sensor_row_delta);
+}
+
+static int cyttsp5_cmcp_get_test_item(int item_input)
+{
+ int test_item = 0;
+
+ switch (item_input) {
+ case CMCP_FULL:
+ test_item = CMCP_FULL_CASE;
+ break;
+ case CMCP_CM_PANEL:
+ test_item = CM_PANEL;
+ break;
+ case CMCP_CP_PANEL:
+ test_item = CP_PANEL;
+ break;
+ case CMCP_CM_BTN:
+ test_item = CM_BTN;
+ break;
+ case CMCP_CP_BTN:
+ test_item = CP_BTN;
+ break;
+ }
+ return test_item;
+}
+
+static ssize_t cyttsp5_cmcp_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ struct cmcp_data *cmcp_info = dad->cmcp_info;
+ struct result *result = dad->result;
+ struct configuration *configuration = dad->configs;
+ bool final_pass = true;
+ static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test",
+ "Cm panel test", "Cp panel test",
+ "Cm button test", "Cp button test"};
+ int index = 0;
+ int test_item = 0;
+ int no_builtin_file = 0;
+ int rc;
+ u8 status;
+ int self_test_id_supported = 0;
+
+ dev = dad->dev;
+ if ((configuration == NULL) || (cmcp_info == NULL))
+ goto exit;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ if (dad->cmcp_test_in_progress) {
+ mutex_unlock(&dad->sysfs_lock);
+ goto cmcp_not_ready;
+ }
+ dad->cmcp_test_in_progress = 1;
+
+ dad->test_executed = 0;
+ test_item = cyttsp5_cmcp_get_test_item(dad->cmcp_test_items);
+
+ if (dad->builtin_cmcp_threshold_status < 0) {
+ dev_err(dev, "%s: No cmcp threshold file.\n", __func__);
+ no_builtin_file = 1;
+ mutex_unlock(&dad->sysfs_lock);
+ goto start_testing;
+ }
+
+ if (dad->cmcp_test_items < 0) {
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Invalid test item! Should be 0~4!\n", __func__);
+ mutex_unlock(&dad->sysfs_lock);
+ goto invalid_item;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Test item is %s, %d\n",
+ __func__, cmcp_test_case_array[dad->cmcp_test_items],
+ test_item);
+
+ if ((dad->si->num_btns == 0)
+ && ((dad->cmcp_test_items == CMCP_CM_BTN)
+ || (dad->cmcp_test_items == CMCP_CP_BTN))) {
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: FW doesn't support button!\n", __func__);
+ mutex_unlock(&dad->sysfs_lock);
+ goto invalid_item_btn;
+ }
+
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (cmcp_check_config_fw_match(dev, configuration))
+ goto mismatch;
+
+start_testing:
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Start Cm/Cp test!\n", __func__);
+ result->cm_test_pass = true;
+ result->cp_test_pass = true;
+ /*stop watchdog*/
+ rc = cmd->request_stop_wd(dev);
+ if (rc)
+ dev_err(dev, "stop watchdog failed");
+ /*force single tx*/
+ rc = cmd->nonhid_cmd->set_param(dev, 0, 0x1F, 1, 1);
+ if (rc)
+ dev_err(dev, "force single tx failed");
+ /*suspend_scanning */
+ rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+ if (rc)
+ dev_err(dev, "suspend_scanning failed");
+ /*do calibration*/
+ if (!dad->cmcp_force_calibrate) {
+ parade_debug(dev, DEBUG_LEVEL_2, "do calibration in single tx mode");
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, 0, &status);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs for mutual r=%d\n",
+ __func__, rc);
+ }
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, 1, &status);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs for buttons r=%d\n",
+ __func__, rc);
+ }
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, 2, &status);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs for self r=%d\n",
+ __func__, rc);
+ }
+ }
+ /*resume_scanning */
+ rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+ if (rc)
+ dev_err(dev, "resume_scanning failed");
+
+ /*get all cmcp data from FW*/
+ self_test_id_supported =
+ cyttsp5_get_cmcp_info(dad, cmcp_info);
+ if (self_test_id_supported)
+ dev_err(dev, "cyttsp5_get_cmcp_info failed");
+
+ /*restore to multi tx*/
+ rc = cmd->nonhid_cmd->set_param(dev, 0, 0x1F, 0, 1);
+ if (rc)
+ dev_err(dev, "restore multi tx failed");
+
+ /*suspend_scanning */
+ rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+ if (rc)
+ dev_err(dev, "suspend_scanning failed");
+ /*do calibration*/
+ if (!dad->cmcp_force_calibrate) {
+ parade_debug(dev, DEBUG_LEVEL_2, "do calibration in multi tx mode");
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, 0, &status);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs for mutual r=%d\n",
+ __func__, rc);
+ }
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, 1, &status);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs for buttons r=%d\n",
+ __func__, rc);
+ }
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, 2, &status);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs for self r=%d\n",
+ __func__, rc);
+ }
+
+ }
+ /*resume_scanning */
+ rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+ if (rc)
+ dev_err(dev, "resume_scanning failed");
+
+ /*start watchdog*/
+ rc = cmd->request_start_wd(dev);
+ if (rc)
+ dev_err(dev, "start watchdog failed");
+
+ if (self_test_id_supported)
+ goto self_test_id_failed;
+
+ if (no_builtin_file)
+ goto no_builtin;
+
+ if (test_item && CM_ENABLED)
+ validate_cm_test_results(dev, configuration, cmcp_info,
+ result, &final_pass, test_item);
+
+ if (test_item && CP_ENABLED)
+ validate_cp_test_results(dev, configuration, cmcp_info,
+ result, &final_pass, test_item);
+no_builtin:
+ if ((dad->cmcp_test_items == CMCP_FULL)
+ && (dad->cmcp_range_check == 0)) {
+ /*full test and full check*/
+ result->test_summary =
+ result->cm_test_pass
+ && result->cp_test_pass
+ && result->short_test_pass;
+ } else if ((dad->cmcp_test_items == CMCP_FULL)
+ && (dad->cmcp_range_check == 1)) {
+ /*full test and basic check*/
+ result->test_summary =
+ result->cm_sensor_gd_col_pass
+ && result->cm_sensor_gd_row_pass
+ && result->cm_sensor_validation_pass
+ && result->cp_rx_validation_pass
+ && result->cp_tx_validation_pass
+ && result->short_test_pass;
+ } else if (dad->cmcp_test_items == CMCP_CM_PANEL) {
+ /*cm panel test result only*/
+ result->test_summary =
+ result->cm_sensor_gd_col_pass
+ && result->cm_sensor_gd_row_pass
+ && result->cm_sensor_validation_pass
+ && result->cm_sensor_row_delta_pass
+ && result->cm_sensor_col_delta_pass
+ && result->cm_sensor_calibration_pass
+ && result->cm_sensor_delta_pass;
+ } else if (dad->cmcp_test_items == CMCP_CP_PANEL) {
+ /*cp panel test result only*/
+ result->test_summary =
+ result->cp_sensor_delta_pass
+ && result->cp_rx_validation_pass
+ && result->cp_tx_validation_pass;
+ } else if (dad->cmcp_test_items == CMCP_CM_BTN) {
+ /*cm button test result only*/
+ result->test_summary =
+ result->cm_button_validation_pass
+ && result->cm_button_delta_pass;
+ } else if (dad->cmcp_test_items == CMCP_CP_BTN) {
+ /*cp button test result only*/
+ result->test_summary =
+ result->cp_button_delta_pass
+ && result->cp_button_average_pass
+ && result->cp_button_validation_pass;
+ }
+ mutex_lock(&dad->sysfs_lock);
+ dad->test_executed = 1;
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (result->test_summary) {
+ dev_vdbg(dev, "%s: Finish Cm/Cp test! All Test Passed\n",
+ __func__);
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 1\n");
+ } else {
+ dev_vdbg(dev, "%s: Finish Cm/Cp test! Range Check Failure\n",
+ __func__);
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 6\n");
+ }
+
+ goto cmcp_ready;
+mismatch:
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 2\nInput cmcp threshold file mismatches with FW\n");
+ goto cmcp_ready;
+invalid_item_btn:
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 3\nFW doesn't support button!\n");
+ goto cmcp_ready;
+invalid_item:
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 4\nWrong test item or range check input!\nOnly support below items:\n0 - Cm/Cp Panel & Button with Gradient (Typical)\n1 - Cm Panel with Gradient\n2 - Cp Panel\n3 - Cm Button\n4 - Cp Button\nOnly support below range check:\n0 - Full Range Checking (default)\n1 - Basic Range Checking(TSG5 style)\n");
+ goto cmcp_ready;
+self_test_id_failed:
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 5\nget self test ID not supported!");
+ goto cmcp_ready;
+cmcp_not_ready:
+ index = snprintf(buf, CY_MAX_PRBUF_SIZE,
+ "Status 0\n");
+ goto cmcp_ready;
+cmcp_ready:
+ mutex_lock(&dad->sysfs_lock);
+ dad->cmcp_test_in_progress = 0;
+ mutex_unlock(&dad->sysfs_lock);
+exit:
+ return index;
+}
+
+static ssize_t cyttsp5_cmcp_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ u8 test_item = 0;
+ u8 range_check = 0;
+ u8 force_calibrate = 0;
+ int ret;
+ static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test",
+ "Cm panel test", "Cp panel test",
+ "Cm button test", "Cp button test"};
+ static const char * const cmcp_test_range_check_array[] = {
+ "Full (default)", "Basic"};
+ static const char * const cmcp_test_force_cal_array[] = {
+ "Calibrate When Testing (default)", "No Calibration"};
+ ssize_t length = 0;
+
+ pm_runtime_get_sync(dev);
+ mutex_lock(&dad->sysfs_lock);
+
+ length = cyttsp5_ic_parse_input(dev, buf, size, dad->ic_buf,
+ CY_MAX_PRBUF_SIZE);
+ if (length <= 0 || length > 3) {
+ dev_err(dev, "%s: Input format error!\n", __func__);
+ dad->cmcp_test_items = -EINVAL;
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Get test item */
+ test_item = dad->ic_buf[0];
+ /* Get range check */
+ if (length >= 2)
+ range_check = dad->ic_buf[1];
+ /* Get force calibration */
+ if (length == 3)
+ force_calibrate = dad->ic_buf[2];
+
+ /*
+ * Test item limitation:
+ * 0: Perform all Tests
+ * 1: CM Panel with Gradient
+ * 2: CP Panel
+ * 3: CM Button
+ * 4: CP Button
+ * Ranage check limitation:
+ * 0: full check
+ * 1: basic check
+ * Force calibrate limitation:
+ * 0: do calibration
+ * 1: don't do calibration
+ */
+ if ((test_item < 0) || (test_item > 4) || (range_check > 1)
+ || (force_calibrate > 1)) {
+ dev_err(dev,
+ "%s: Test item should be 0~4; Range check should be 0~1; Force calibrate should be 0~1\n",
+ __func__);
+ dad->cmcp_test_items = -EINVAL;
+ ret = -EINVAL;
+ goto error;
+ }
+ /*
+ * If it is not all Test, then range_check should be 0
+ * because other test does not has concept of basic check
+ */
+ if (test_item > 0 && test_item < 5)
+ range_check = 0;
+ dad->cmcp_test_items = test_item;
+ dad->cmcp_range_check = range_check;
+ dad->cmcp_force_calibrate = force_calibrate;
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Test item is %s; Range check is %s; Force calibrate is %s.\n",
+ __func__,
+ cmcp_test_case_array[test_item],
+ cmcp_test_range_check_array[range_check],
+ cmcp_test_force_cal_array[force_calibrate]);
+
+error:
+ mutex_unlock(&dad->sysfs_lock);
+ pm_runtime_put(dev);
+
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static DEVICE_ATTR(cmcp_test, S_IRUSR | S_IWUSR,
+ cyttsp5_cmcp_test_show, cyttsp5_cmcp_test_store);
+
+int prepare_print_string(char *out_buf, char *in_buf, int index)
+{
+ if ((out_buf == NULL) || (in_buf == NULL))
+ return index;
+ index += scnprintf(&out_buf[index], MAX_BUF_LEN - index,
+ "%s", in_buf);
+ return index;
+}
+
+int prepare_print_data(char *out_buf, int32_t *in_buf, int index, int data_num)
+{
+ int i;
+
+ if ((out_buf == NULL) || (in_buf == NULL))
+ return index;
+ for (i = 0; i < data_num; i++)
+ index += scnprintf(&out_buf[index], MAX_BUF_LEN - index,
+ "%d,", in_buf[i]);
+ return index;
+}
+
+int save_header(char *out_buf, int index, struct result *result)
+{
+ struct timex txc;
+ struct rtc_time tm;
+ char time_buf[100] = {0};
+
+ do_gettimeofday(&(txc.time));
+ rtc_time_to_tm(txc.time.tv_sec, &tm);
+ scnprintf(time_buf, 100, "%d/%d/%d,TIME,%d:%d:%d,", tm.tm_year+1900,
+ tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ index = prepare_print_string(out_buf, ",.header,\n", index);
+ index = prepare_print_string(out_buf, ",DATE,", index);
+ index = prepare_print_string(out_buf, &time_buf[0], index);
+ index = prepare_print_string(out_buf, ",\n", index);
+ index = prepare_print_string(out_buf, ",SW_VERSION,", index);
+ index = prepare_print_string(out_buf, CY_DRIVER_VERSION, index);
+ index = prepare_print_string(out_buf, ",\n", index);
+ index = prepare_print_string(out_buf, ",.end,\n", index);
+ index = prepare_print_string(out_buf, ",.engineering data,\n", index);
+
+ return index;
+}
+
+static int print_silicon_id(char *out_buf, char *in_buf, int index)
+{
+ index = prepare_print_string(out_buf, ",1,", index);
+ index = prepare_print_string(out_buf, &in_buf[0], index);
+ return index;
+}
+
+int save_engineering_data(struct device *dev, char *out_buf, int index,
+ struct cmcp_data *cmcp_info, struct configuration *configuration,
+ struct result *result, int test_item, int no_builtin_file)
+{
+ int i;
+ int j;
+ int tx_num = cmcp_info->tx_num;
+ int rx_num = cmcp_info->rx_num;
+ int btn_num = cmcp_info->btn_num;
+ int tmp = 0;
+ uint32_t fw_revision_control;
+ uint32_t fw_config_ver;
+ char device_id[20] = {0};
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+
+ fw_revision_control = dad->si->cydata.revctrl;
+ fw_config_ver = dad->si->cydata.fw_ver_conf;
+ /*calculate silicon id*/
+ result->device_id_low = 0;
+ result->device_id_high = 0;
+
+ for (i = 0; i < 4; i++)
+ result->device_id_low =
+ (result->device_id_low << 8) + dad->si->cydata.mfg_id[i];
+
+ for (i = 4; i < 8; i++)
+ result->device_id_high =
+ (result->device_id_high << 8) + dad->si->cydata.mfg_id[i];
+
+ scnprintf(device_id, 20, "%x%x",
+ result->device_id_high, result->device_id_low);
+
+ /*print test summary*/
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ if (result->test_summary)
+ index = prepare_print_string(out_buf, ",PASS,\n", index);
+ else
+ index = prepare_print_string(out_buf, ",FAIL,\n", index);
+
+ /*revision ctrl number*/
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ index = prepare_print_string(out_buf, ",FW revision Control,", index);
+ index = prepare_print_data(out_buf, &fw_revision_control, index, 1);
+ index = prepare_print_string(out_buf, "\n", index);
+
+ /*config version*/
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ index = prepare_print_string(out_buf, ",CONFIG_VER,", index);
+ index = prepare_print_data(out_buf, &fw_config_ver, index, 1);
+ index = prepare_print_string(out_buf, "\n", index);
+
+ /*short test*/
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ if (result->short_test_pass)
+ index = prepare_print_string(out_buf, ",Shorts,PASS,\n", index);
+ else
+ index = prepare_print_string(out_buf, ",Shorts,FAIL,\n", index);
+
+ if ((test_item & CM_ENABLED) == CM_ENABLED) {
+ /*print BUTNS_CM_DATA_ROW00*/
+ if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,BUTNS_CM_DATA_ROW00,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cm_btn_data[0],
+ index,
+ btn_num);
+ index = prepare_print_string(out_buf, "\n", index);
+ }
+
+ if ((test_item & CM_PANEL) == CM_PANEL) {
+ /*print CM_DATA_ROW*/
+ for (i = 0; i < rx_num; i++) {
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,CM_DATA_ROW",
+ index);
+ index = prepare_print_data(out_buf, &i,
+ index, 1);
+ for (j = 0; j < tx_num; j++)
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cm_data_panel[j*rx_num+i],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+
+ if (!no_builtin_file) {
+ /*print CM_MAX_GRADIENT_COLS_PERCENT*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,CM_MAX_GRADIENT_COLS_PERCENT,",
+ index);
+ for (i = 0; i < tx_num; i++) {
+ char tmp_buf[10] = {0};
+
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->gd_sensor_col[i].gradient_val / 10,
+ cmcp_info->gd_sensor_col[i].gradient_val % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ }
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print CM_MAX_GRADIENT_ROWS_PERCENT*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,CM_MAX_GRADIENT_ROWS_PERCENT,",
+ index);
+ for (i = 0; i < rx_num; i++) {
+ char tmp_buf[10] = {0};
+
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->gd_sensor_row[i].gradient_val / 10,
+ cmcp_info->gd_sensor_row[i].gradient_val % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ }
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ if (!dad->cmcp_range_check) {
+ /*print CM_DELTA_COLUMN*/
+ for (i = 0; i < rx_num; i++) {
+ index = print_silicon_id(
+ out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(
+ out_buf,
+ ",Sensor Cm Validation,DELTA_COLUMNS_ROW",
+ index);
+ index = prepare_print_data(
+ out_buf,
+ &i, index, 1);
+ index = prepare_print_data(
+ out_buf,
+ &tmp, index, 1);
+ for (j = 1; j < tx_num; j++)
+ index = prepare_print_data(
+ out_buf,
+ &cmcp_info->cm_sensor_column_delta[(j-1)*rx_num+i],
+ index, 1);
+ index = prepare_print_string(
+ out_buf,
+ "\n", index);
+ }
+
+ /*print CM_DELTA_ROW*/
+ index = print_silicon_id(out_buf,
+ &device_id[0],
+ index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,DELTA_ROWS_ROW",
+ index);
+ index = prepare_print_data(out_buf,
+ &tmp, index, 1);
+ for (j = 0; j < tx_num; j++)
+ index = prepare_print_data(
+ out_buf,
+ &tmp, index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ for (i = 1; i < rx_num; i++) {
+ index = print_silicon_id(
+ out_buf,
+ &device_id[0],
+ index);
+ index = prepare_print_string(
+ out_buf,
+ ",Sensor Cm Validation,DELTA_ROWS_ROW",
+ index);
+ index = prepare_print_data(
+ out_buf, &i,
+ index, 1);
+ for (j = 0; j < tx_num; j++)
+ index = prepare_print_data(
+ out_buf,
+ &cmcp_info->cm_sensor_row_delta[j*rx_num+i-1],
+ index, 1);
+ index = prepare_print_string(
+ out_buf,
+ "\n", index);
+ }
+
+ /*print pass/fail Sensor Cm Validation*/
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ if (result->cm_test_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,PASS,\n",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,FAIL,\n",
+ index);
+ }
+ }
+ }
+
+ if (!no_builtin_file) {
+ if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)
+ && (!dad->cmcp_range_check)) {
+ char tmp_buf[10] = {0};
+ /*print Button Element by Element */
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ if (result->cm_button_validation_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Button Element by Element,PASS\n",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Button Element by Element,FAIL\n",
+ index);
+
+ /*
+ *print Sensor Cm Validation
+ *- Buttons Range Buttons Range
+ */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Buttons Range,Buttons Range,",
+ index);
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->cm_delta_data_btn / 10,
+ cmcp_info->cm_delta_data_btn % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Sensor Cm Validation
+ *-Buttons Range Cm_button_avg
+ */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Buttons Range,Cm_button_avg,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cm_ave_data_btn,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Sensor Cm Validation
+ * -Buttons Range Cm_button_avg
+ */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Buttons Range,Cm_button_cal,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cm_cal_data_btn,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Sensor Cm Validation
+ *-Buttons Range pass/fail
+ */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_button_delta_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Buttons Range,PASS,LIMITS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Buttons Range,FAIL,LIMITS,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_max_delta_button_percent,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+
+ if ((test_item & CM_PANEL) == CM_PANEL &&
+ !dad->cmcp_range_check) {
+ char tmp_buf[10] = {0};
+ /*print Cm_sensor_cal */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Calibration,Cm_sensor_cal,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cm_cal_data_panel,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cm_sensor_cal limit*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_calibration_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Calibration,PASS,LIMITS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Calibration,FAIL,LIMITS,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_min_limit_cal,
+ index, 1);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_max_limit_cal,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Columns Delta Matrix*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_col_delta_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Columns Delta Matrix,PASS,LIMITS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Columns Delta Matrix,FAIL,LIMITS,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_range_limit_col,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cm Validation - Element by Element*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_validation_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Element by Element,PASS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Element by Element,FAIL,",
+ index);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cm Validation -Gradient Cols*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_gd_col_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Gradient Cols,PASS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Gradient Cols,FAIL,",
+ index);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cm Validation -Gradient Rows*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_gd_row_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Gradient Rows,PASS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Gradient Rows,FAIL,",
+ index);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+
+ /*
+ * Print Sensor Cm Validation
+ * -Rows Delta Matrix
+ */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_row_delta_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Rows Delta Matrix,PASS,LIMITS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Rows Delta Matrix,FAIL,LIMITS,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_range_limit_row,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cm_sensor_avg */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Sensor Range,Cm_sensor_avg,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cm_ave_data_panel,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*printSensor Cm Validation -
+ * Sensor Range, Sensor Range
+ */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Sensor Range,Sensor Range,",
+ index);
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->cm_sensor_delta / 10,
+ cmcp_info->cm_sensor_delta % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Sensor Cm Validation - Sensor Range*/
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ if (result->cm_sensor_delta_pass)
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Sensor Range,PASS,LIMITS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation - Sensor Range,FAIL,LIMITS,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_max_delta_sensor_percent,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ }
+ }
+
+ if ((test_item & CP_ENABLED) == CP_ENABLED) {
+ if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+ /*print BUTNS_CP_DATA_ROW00 */
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,BUTNS_CP_DATA_ROW00,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_btn_data[0],
+ index, btn_num);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ if (!no_builtin_file && !dad->cmcp_range_check) {
+ /*print Cp Button Element by Element */
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ if (result->cp_button_validation_pass)
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check - Button Element by Element,PASS\n",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check - Button Element by Element,FAIL\n",
+ index);
+
+ /*print cp_button_ave */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_button_avg,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_button_ave,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cp_button_cal */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_button_cal,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_btn_cal,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ }
+
+ if ((test_item & CP_PANEL) == CP_PANEL) {
+ /*print CP_DATA_RX */
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,CP_DATA_RX,", index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_rx_data_panel[0], index, rx_num);
+ index = prepare_print_string(out_buf, "\n", index);
+
+ /*print CP_DATA_TX */
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,CP_DATA_TX,", index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_tx_data_panel[0], index, tx_num);
+ index = prepare_print_string(out_buf, "\n", index);
+ }
+ if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)
+ && !dad->cmcp_range_check) {
+ if (!no_builtin_file) {
+ char tmp_buf[10] = {0};
+ /*print Cp_delta_button */
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_delta_button,",
+ index);
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->cp_button_delta / 10,
+ cmcp_info->cp_button_delta % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ index = prepare_print_string(out_buf, "\n",
+ index);
+ }
+ }
+ if ((test_item & CP_PANEL) == CP_PANEL &&
+ !dad->cmcp_range_check) {
+ if (!no_builtin_file) {
+ char tmp_buf[10] = {0};
+ /*print Cp_delta_rx */
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_delta_rx,",
+ index);
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->cp_sensor_rx_delta / 10,
+ cmcp_info->cp_sensor_rx_delta % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ index = prepare_print_string(out_buf, "\n",
+ index);
+
+ /*print Cp_delta_tx */
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_delta_tx,",
+ index);
+ scnprintf(tmp_buf, 10, "%d.%d,",
+ cmcp_info->cp_sensor_tx_delta / 10,
+ cmcp_info->cp_sensor_tx_delta % 10);
+ index = prepare_print_string(out_buf,
+ &tmp_buf[0], index);
+ index = prepare_print_string(out_buf, "\n",
+ index);
+
+ /*print Cp_sensor_avg_rx */
+ index = print_silicon_id(out_buf, &device_id[0],
+ index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_sensor_avg_rx,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_rx_ave_data_panel,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cp_sensor_avg_tx */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_sensor_avg_tx,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_tx_ave_data_panel,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cp_sensor_cal_rx */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_sensor_cal_rx,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_rx_cal_data_panel[0],
+ index, rx_num);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print Cp_sensor_cal_tx */
+ index = print_silicon_id(out_buf,
+ &device_id[0], index);
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,Cp_sensor_cal_tx,",
+ index);
+ index = prepare_print_data(out_buf,
+ &cmcp_info->cp_tx_cal_data_panel[0],
+ index, tx_num);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ }
+
+ if (!no_builtin_file && !dad->cmcp_range_check) {
+ /*print cp test limits */
+ index = print_silicon_id(out_buf, &device_id[0], index);
+ if (result->cp_test_pass)
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,PASS, LIMITS,",
+ index);
+ else
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,FAIL, LIMITS,",
+ index);
+
+ index = prepare_print_string(out_buf,
+ "CP_MAX_DELTA_SENSOR_RX_PERCENT,", index);
+ index = prepare_print_data(out_buf,
+ &configuration->cp_max_delta_sensor_rx_percent,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "CP_MAX_DELTA_SENSOR_TX_PERCENT,", index);
+ index = prepare_print_data(out_buf,
+ &configuration->cp_max_delta_sensor_tx_percent,
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "CP_MAX_DELTA_BUTTON_PERCENT,", index);
+ index = prepare_print_data(out_buf,
+ &configuration->cp_max_delta_button_percent,
+ index, 1);
+ index = prepare_print_string(out_buf, "\n", index);
+ }
+ }
+
+ if (!no_builtin_file) {
+ if ((test_item & CM_ENABLED) == CM_ENABLED) {
+ if ((test_item & CM_PANEL) == CM_PANEL) {
+ /*print columns gradient limit*/
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_COLS_PERCENT,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_max_table_gradient_cols_percent[0],
+ index,
+ configuration->cm_max_table_gradient_cols_percent_size);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ /*print rows gradient limit*/
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_ROWS_PERCENT,",
+ index);
+ index = prepare_print_data(out_buf,
+ &configuration->cm_max_table_gradient_rows_percent[0],
+ index,
+ configuration->cm_max_table_gradient_rows_percent_size);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print cm max limit*/
+ for (i = 0; i < rx_num; i++) {
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,MAX_LIMITS,CM_DATA_ROW",
+ index);
+ index = prepare_print_data(out_buf,
+ &i, index, 1);
+ for (j = 0; j < tx_num; j++)
+ index = prepare_print_data(
+ out_buf,
+ &configuration->cm_min_max_table_sensor[i*tx_num*2+j*2+1],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ }
+
+ if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,MAX LIMITS,M_BUTNS,",
+ index);
+ for (j = 0; j < btn_num; j++) {
+ index = prepare_print_data(out_buf,
+ &configuration->cm_min_max_table_button[2*j+1],
+ index, 1);
+ }
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation MAX LIMITS\n", index);
+
+ if ((test_item & CM_PANEL) == CM_PANEL) {
+ /*print cm min limit*/
+ for (i = 0; i < rx_num; i++) {
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,MIN_LIMITS,CM_DATA_ROW",
+ index);
+ index = prepare_print_data(out_buf, &i,
+ index, 1);
+ for (j = 0; j < tx_num; j++)
+ index = prepare_print_data(
+ out_buf,
+ &configuration->cm_min_max_table_sensor[i*tx_num*2 + j*2],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ }
+
+ if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation,MIN LIMITS,M_BUTNS,",
+ index);
+ for (j = 0; j < btn_num; j++) {
+ index = prepare_print_data(out_buf,
+ &configuration->cm_min_max_table_button[2*j],
+ index, 1);
+ }
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ index = prepare_print_string(out_buf,
+ ",Sensor Cm Validation MIN LIMITS\n", index);
+ }
+
+ if ((test_item & CP_ENABLED) == CP_ENABLED) {
+ if ((test_item & CP_PANEL) == CP_PANEL) {
+ /*print cp tx max limit*/
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,MAX_LIMITS,TX,",
+ index);
+ for (i = 0; i < tx_num; i++)
+ index = prepare_print_data(out_buf,
+ &configuration->cp_min_max_table_tx[i*2+1],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print cp rx max limit*/
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,MAX_LIMITS,RX,",
+ index);
+ for (i = 0; i < rx_num; i++)
+ index = prepare_print_data(out_buf,
+ &configuration->cp_min_max_table_rx[i*2+1],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+
+ /*print cp btn max limit*/
+ if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,MAX_LIMITS,S_BUTNS,",
+ index);
+ for (i = 0; i < btn_num; i++)
+ index = prepare_print_data(out_buf,
+ &configuration->cp_min_max_table_button[i*2+1],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+
+ if ((test_item & CP_PANEL) == CP_PANEL) {
+ /*print cp tx min limit*/
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,MIN_LIMITS,TX,",
+ index);
+ for (i = 0; i < tx_num; i++)
+ index = prepare_print_data(out_buf,
+ &configuration->cp_min_max_table_tx[i*2],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+
+ /*print cp rx min limit*/
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,MIN_LIMITS,RX,",
+ index);
+ for (i = 0; i < rx_num; i++)
+ index = prepare_print_data(out_buf,
+ &configuration->cp_min_max_table_rx[i*2],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+
+ /*print cp btn min limit*/
+ if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+ index = prepare_print_string(out_buf,
+ ",Self-cap Calibration Check,MIN_LIMITS,S_BUTNS,",
+ index);
+ for (i = 0; i < btn_num; i++)
+ index = prepare_print_data(out_buf,
+ &configuration->cp_min_max_table_button[i*2],
+ index, 1);
+ index = prepare_print_string(out_buf,
+ "\n", index);
+ }
+ }
+ }
+ return index;
+}
+
+int result_save(struct device *dev, char *buf,
+ struct configuration *configuration, struct result *result,
+ struct cmcp_data *cmcp_info, loff_t *ppos, size_t count, int test_item,
+ int no_builtin_file)
+{
+ u8 *out_buf = NULL;
+ int index = 0;
+ int byte_left;
+
+ out_buf = kzalloc(MAX_BUF_LEN, GFP_KERNEL);
+ if (configuration == NULL)
+ dev_err(dev, "config is NULL");
+ if (result == NULL)
+ dev_err(dev, "result is NULL");
+ if (cmcp_info == NULL)
+ dev_err(dev, "cmcp_info is NULL");
+
+ index = save_header(out_buf, index, result);
+ index = save_engineering_data(dev, out_buf, index,
+ cmcp_info, configuration, result,
+ test_item, no_builtin_file);
+ byte_left = simple_read_from_buffer(buf, count, ppos, out_buf, index);
+
+ kfree(out_buf);
+ return byte_left;
+}
+
+static int cmcp_results_debugfs_open(struct inode *inode,
+ struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+static int cmcp_results_debugfs_close(struct inode *inode,
+ struct file *filp)
+{
+ filp->private_data = NULL;
+ return 0;
+}
+
+static ssize_t cmcp_results_debugfs_read(struct file *filp,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_data *dad = filp->private_data;
+ struct device *dev;
+ struct cmcp_data *cmcp_info = dad->cmcp_info;
+ struct result *result = dad->result;
+ struct configuration *configuration = dad->configs;
+ int ret = 0;
+ int test_item;
+ int no_builtin_file = 0;
+ int test_executed = 0;
+
+ dev = dad->dev;
+
+ mutex_lock(&dad->sysfs_lock);
+ test_executed = dad->test_executed;
+ test_item = cyttsp5_cmcp_get_test_item(dad->cmcp_test_items);
+ if (dad->builtin_cmcp_threshold_status < 0) {
+ dev_err(dev, "%s: No cmcp threshold file.\n", __func__);
+ no_builtin_file = 1;
+ }
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (test_executed)
+ /*save result to buf*/
+ ret = result_save(dev, buf, configuration, result, cmcp_info,
+ ppos, count, test_item, no_builtin_file);
+ else {
+ char warning_info[] =
+ "No test result available!\n";
+ dev_err(dev, "%s: No test result available!\n", __func__);
+
+ return simple_read_from_buffer(buf, count, ppos, warning_info,
+ strlen(warning_info));
+ }
+
+ return ret;
+}
+
+static const struct file_operations cmcp_results_debugfs_fops = {
+ .open = cmcp_results_debugfs_open,
+ .release = cmcp_results_debugfs_close,
+ .read = cmcp_results_debugfs_read,
+ .write = NULL,
+};
+
+static ssize_t cyttsp5_cmcp_threshold_loading_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ bool cmcp_threshold_loading;
+
+ mutex_lock(&dad->cmcp_threshold_lock);
+ cmcp_threshold_loading = dad->cmcp_threshold_loading;
+ mutex_unlock(&dad->cmcp_threshold_lock);
+
+ return sprintf(buf, "%d\n", cmcp_threshold_loading);
+}
+
+/* Return the buffer offset of new test case */
+u32 cmcp_return_offset_of_new_case(const char *bufPtr, u32 first_time)
+{
+ static u32 offset, first_search;
+
+ if (first_time == 0) {
+ first_search = 0;
+ offset = 0;
+ }
+
+ if (first_search != 0) {
+ /* Search one case */
+ for (;;) {
+ /* Search ASCII_LF */
+ while (*bufPtr++ != ASCII_LF)
+ offset++;
+
+ offset++;
+ /*
+ * Single line: end loop
+ * Multiple lines: continue loop
+ */
+ if (*bufPtr != ASCII_COMMA)
+ break;
+ }
+ } else
+ first_search = 1;
+
+ return offset;
+}
+
+/* Get test case information from cmcp threshold file */
+u32 cmcp_get_case_info_from_threshold_file(struct device *dev, const char *buf,
+ struct test_case_search *search_array, u32 file_size)
+{
+ u32 case_num = 0, buffer_offset = 0, name_count = 0, first_search = 0;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Search cmcp threshold file\n",
+ __func__);
+
+ /* Get all the test cases */
+ for (case_num = 0; case_num < MAX_CASE_NUM; case_num++) {
+ buffer_offset =
+ cmcp_return_offset_of_new_case(&buf[buffer_offset],
+ first_search);
+ first_search = 1;
+
+ if (buf[buffer_offset] == 0)
+ break;
+
+ for (name_count = 0; name_count < NAME_SIZE_MAX; name_count++) {
+ /* File end */
+ if (buf[buffer_offset + name_count] == ASCII_COMMA)
+ break;
+
+ search_array[case_num].name[name_count] =
+ buf[buffer_offset + name_count];
+ }
+
+ /* Exit when buffer offset is larger than file size */
+ if (buffer_offset >= file_size)
+ break;
+
+ search_array[case_num].name_size = name_count;
+ search_array[case_num].offset = buffer_offset;
+ /*
+ * parade_debug(dev, DEBUG_LEVEL_2, "Find case %d: Name is %s;
+ * Name size is %d; Case offset is %d\n",
+ * case_num,
+ * search_array[case_num].name,
+ * search_array[case_num].name_size,
+ * search_array[case_num].offset);
+ */
+ }
+
+ return case_num;
+}
+
+/* Compose one value based on data of each bit */
+int cmcp_compose_data(char *buf, u32 count)
+{
+ u32 base_array[] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9};
+ int value = 0;
+ u32 index = 0;
+
+ for (index = 0; index < count; index++)
+ value += buf[index] * base_array[count - 1 - index];
+
+ return value;
+}
+
+/* Return one value */
+int cmcp_return_one_value(struct device *dev,
+ const char *buf, u32 *offset, u32 *line_num)
+{
+ int value = -1;
+ char tmp_buffer[10];
+ u32 count = 0;
+ u32 tmp_offset = *offset;
+ static u32 line_count = 1;
+
+ /* Bypass extra commas */
+ while (buf[tmp_offset] == ASCII_COMMA
+ && buf[tmp_offset + 1] == ASCII_COMMA)
+ tmp_offset++;
+
+ /* Windows and Linux difference at the end of one line */
+ if (buf[tmp_offset] == ASCII_COMMA
+ && buf[tmp_offset + 1] == ASCII_CR
+ && buf[tmp_offset + 2] == ASCII_LF)
+ tmp_offset += 2;
+ else if (buf[tmp_offset] == ASCII_COMMA
+ && buf[tmp_offset + 1] == ASCII_LF)
+ tmp_offset += 1;
+ else if (buf[tmp_offset] == ASCII_COMMA
+ && buf[tmp_offset + 1] == ASCII_CR)
+ tmp_offset += 1;
+
+ /* New line for multiple lines */
+ if (buf[tmp_offset] == ASCII_LF && buf[tmp_offset + 1] == ASCII_COMMA) {
+ tmp_offset++;
+ line_count++;
+ /*parade_debug(dev, DEBUG_LEVEL_2, "\n");*/
+ }
+
+ /* Beginning */
+ if (buf[tmp_offset] == ASCII_COMMA) {
+ tmp_offset++;
+ for (;;) {
+ if ((buf[tmp_offset] >= ASCII_ZERO)
+ && (buf[tmp_offset] <= ASCII_NINE)) {
+ tmp_buffer[count++] =
+ buf[tmp_offset] - ASCII_ZERO;
+ tmp_offset++;
+ } else {
+ if (count != 0) {
+ value = cmcp_compose_data(tmp_buffer,
+ count);
+ /*parade_debug(dev, DEBUG_LEVEL_2, */
+ /* ",%d", value);*/
+ } else {
+ /* 0 indicates no data available */
+ value = -1;
+ }
+ break;
+ }
+ }
+ } else {
+ /* Multiple line: line count */
+ *line_num = line_count;
+ /* Reset for next case */
+ line_count = 1;
+ }
+
+ *offset = tmp_offset;
+
+ return value;
+}
+
+/* Get configuration information */
+void cmcp_get_configuration_info(struct device *dev,
+ const char *buf, struct test_case_search *search_array,
+ u32 case_count, struct test_case_field *field_array,
+ struct configuration *config)
+{
+ u32 count = 0, sub_count = 0;
+ u32 exist_or_not = 0;
+ u32 value_offset = 0;
+ int retval = 0;
+ u32 data_num = 0;
+ u32 line_num = 1;
+
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Fill configuration struct per cmcp threshold file\n",
+ __func__);
+
+ /* Search cases */
+ for (count = 0; count < MAX_CASE_NUM; count++) {
+ exist_or_not = 0;
+ for (sub_count = 0; sub_count < case_count; sub_count++) {
+ if (!strncmp(field_array[count].name,
+ search_array[sub_count].name,
+ field_array[count].name_size)) {
+ exist_or_not = 1;
+ break;
+ }
+ }
+
+ field_array[count].exist_or_not = exist_or_not;
+
+ /* Clear data number */
+ data_num = 0;
+
+ if (exist_or_not == 1) {
+ switch (field_array[count].type) {
+ case TEST_CASE_TYPE_NO:
+ field_array[count].data_num = 0;
+ field_array[count].line_num = 1;
+ break;
+ case TEST_CASE_TYPE_ONE:
+ value_offset = search_array[sub_count].offset
+ + search_array[sub_count].name_size;
+ *field_array[count].bufptr =
+ cmcp_return_one_value(dev, buf,
+ &value_offset, 0);
+ field_array[count].data_num = 1;
+ field_array[count].line_num = 1;
+ break;
+ case TEST_CASE_TYPE_MUL:
+ case TEST_CASE_TYPE_MUL_LINES:
+ line_num = 1;
+ value_offset = search_array[sub_count].offset
+ + search_array[sub_count].name_size;
+ for (;;) {
+ retval = cmcp_return_one_value(dev,
+ buf, &value_offset, &line_num);
+ if (retval >= 0) {
+ *field_array[count].bufptr++ =
+ retval;
+ data_num++;
+ } else
+ break;
+ }
+
+ field_array[count].data_num = data_num;
+ field_array[count].line_num = line_num;
+ break;
+ default:
+ break;
+ }
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: %s: Data number is %d, line number is %d\n",
+ __func__,
+ field_array[count].name,
+ field_array[count].data_num,
+ field_array[count].line_num);
+ } else
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: !!! %s doesn't exist\n",
+ __func__, field_array[count].name);
+ }
+}
+
+/* Get basic information, like tx, rx, button number */
+void cmcp_get_basic_info(struct device *dev,
+ struct test_case_field *field_array, struct configuration *config)
+{
+#define CMCP_DEBUG 0
+ u32 tx_num = 0;
+#if CMCP_DEBUG
+ u32 index = 0;
+#endif
+
+ config->is_valid_or_not = 1; /* Set to valid by default */
+ config->cm_enabled = 0;
+ config->cp_enabled = 0;
+
+ if (field_array[CM_TEST_INPUTS].exist_or_not)
+ config->cm_enabled = 1;
+ if (field_array[CP_TEST_INPUTS].exist_or_not)
+ config->cp_enabled = 1;
+
+ /* Get basic information only when CM and CP are enabled */
+ if (config->cm_enabled && config->cp_enabled) {
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Find CM and CP thresholds\n", __func__);
+
+ config->rx_num =
+ field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num;
+ tx_num =
+ (field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >> 1)
+ /field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num;
+ config->tx_num = tx_num;
+
+ config->btn_num =
+ field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num >> 1;
+
+ config->cm_min_max_table_button_size =
+ field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num;
+ config->cm_min_max_table_sensor_size =
+ field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num;
+ config->cp_min_max_table_rx_size =
+ field_array[PER_ELEMENT_MIN_MAX_RX].data_num;
+ config->cp_min_max_table_tx_size =
+ field_array[PER_ELEMENT_MIN_MAX_TX].data_num;
+ config->cm_max_table_gradient_cols_percent_size =
+ field_array[CM_GRADIENT_CHECK_COL].data_num;
+ config->cm_max_table_gradient_rows_percent_size =
+ field_array[CM_GRADIENT_CHECK_ROW].data_num;
+ config->cp_min_max_table_button_size =
+ field_array[CP_PER_ELEMENT_MIN_MAX_BUTTON].data_num;
+
+#if CMCP_DEBUG
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_excluding_col_edge);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_excluding_row_edge);
+ for (index = 0;
+ index < config->cm_max_table_gradient_cols_percent_size;
+ index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_max_table_gradient_cols_percent[index]);
+ for (index = 0;
+ index < config->cm_max_table_gradient_rows_percent_size;
+ index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_max_table_gradient_rows_percent[index]);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_range_limit_row);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_range_limit_col);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_min_limit_cal);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_max_limit_cal);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_max_delta_sensor_percent);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_max_delta_button_percent);
+ for (index = 0;
+ index < config->cm_min_max_table_button_size; index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_min_max_table_button[index]);
+ for (index = 0;
+ index < config->cm_min_max_table_sensor_size; index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cm_min_max_table_sensor[index]);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cp_max_delta_sensor_rx_percent);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cp_max_delta_sensor_tx_percent);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cp_max_delta_button_percent);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->min_button);
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->max_button);
+
+ for (index = 0;
+ index < config->cp_min_max_table_button_size; index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cp_min_max_table_button[index]);
+ for (index = 0;
+ index < config->cp_min_max_table_rx_size; index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cp_min_max_table_rx[index]);
+ for (index = 0;
+ index < config->cp_min_max_table_tx_size; index++)
+ parade_debug(dev, DEBUG_LEVEL_2, "%d\n",
+ config->cp_min_max_table_tx[index]);
+#endif
+ /* Invalid mutual data length */
+ if ((field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >>
+ 1) % field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num) {
+ config->is_valid_or_not = 0;
+ parade_debug(dev, DEBUG_LEVEL_2, "Invalid mutual data length\n");
+ }
+ } else {
+ if (!config->cm_enabled)
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Miss CM thresholds or CM data format is wrong!\n",
+ __func__);
+
+ if (!config->cp_enabled)
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Miss CP thresholds or CP data format is wrong!\n",
+ __func__);
+
+ config->rx_num = 0;
+ config->tx_num = 0;
+ config->btn_num = 0;
+ config->is_valid_or_not = 0;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s:\n"
+ "Input file is %s!\n"
+ "CM test: %s\n"
+ "CP test: %s\n"
+ "rx_num is %d\n"
+ "tx_num is %d\n"
+ "btn_num is %d\n",
+ __func__,
+ config->is_valid_or_not == 1 ? "VALID" : "!!! INVALID !!!",
+ config->cm_enabled == 1 ? "Found" : "Not found",
+ config->cp_enabled == 1 ? "Found" : "Not found",
+ config->rx_num,
+ config->tx_num,
+ config->btn_num);
+}
+
+void cmcp_test_case_field_init(struct test_case_field *test_field_array,
+ struct configuration *configs)
+{
+ struct test_case_field test_case_field_array[MAX_CASE_NUM] = {
+ {"CM TEST INPUTS", 14, TEST_CASE_TYPE_NO,
+ NULL, 0, 0, 0},
+ {"CM_EXCLUDING_COL_EDGE", 21, TEST_CASE_TYPE_ONE,
+ &configs->cm_excluding_col_edge, 0, 0, 0},
+ {"CM_EXCLUDING_ROW_EDGE", 21, TEST_CASE_TYPE_ONE,
+ &configs->cm_excluding_row_edge, 0, 0, 0},
+ {"CM_GRADIENT_CHECK_COL", 21, TEST_CASE_TYPE_MUL,
+ &configs->cm_max_table_gradient_cols_percent[0],
+ 0, 0, 0},
+ {"CM_GRADIENT_CHECK_ROW", 21, TEST_CASE_TYPE_MUL,
+ &configs->cm_max_table_gradient_rows_percent[0],
+ 0, 0, 0},
+ {"CM_RANGE_LIMIT_ROW", 18, TEST_CASE_TYPE_ONE,
+ &configs->cm_range_limit_row, 0, 0, 0},
+ {"CM_RANGE_LIMIT_COL", 18, TEST_CASE_TYPE_ONE,
+ &configs->cm_range_limit_col, 0, 0, 0},
+ {"CM_MIN_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE,
+ &configs->cm_min_limit_cal, 0, 0, 0},
+ {"CM_MAX_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE,
+ &configs->cm_max_limit_cal, 0, 0, 0},
+ {"CM_MAX_DELTA_SENSOR_PERCENT", 27, TEST_CASE_TYPE_ONE,
+ &configs->cm_max_delta_sensor_percent, 0, 0, 0},
+ {"CM_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE,
+ &configs->cm_max_delta_button_percent, 0, 0, 0},
+ {"PER_ELEMENT_MIN_MAX_TABLE_BUTTON", 32, TEST_CASE_TYPE_MUL,
+ &configs->cm_min_max_table_button[0], 0, 0, 0},
+ {"PER_ELEMENT_MIN_MAX_TABLE_SENSOR", 32,
+ TEST_CASE_TYPE_MUL_LINES,
+ &configs->cm_min_max_table_sensor[0], 0, 0, 0},
+ {"CP TEST INPUTS", 14, TEST_CASE_TYPE_NO,
+ NULL, 0, 0, 0},
+ {"CP_PER_ELEMENT_MIN_MAX_BUTTON", 29, TEST_CASE_TYPE_MUL,
+ &configs->cp_min_max_table_button[0], 0, 0, 0},
+ {"CP_MAX_DELTA_SENSOR_RX_PERCENT", 30, TEST_CASE_TYPE_ONE,
+ &configs->cp_max_delta_sensor_rx_percent,
+ 0, 0, 0},
+ {"CP_MAX_DELTA_SENSOR_TX_PERCENT", 30, TEST_CASE_TYPE_ONE,
+ &configs->cp_max_delta_sensor_tx_percent,
+ 0, 0, 0},
+ {"CP_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE,
+ &configs->cp_max_delta_button_percent, 0, 0, 0},
+ {"MIN_BUTTON", 10, TEST_CASE_TYPE_ONE,
+ &configs->min_button, 0, 0, 0},
+ {"MAX_BUTTON", 10, TEST_CASE_TYPE_ONE,
+ &configs->max_button, 0, 0, 0},
+ {"PER_ELEMENT_MIN_MAX_RX", 22, TEST_CASE_TYPE_MUL,
+ &configs->cp_min_max_table_rx[0], 0, 0, 0},
+ {"PER_ELEMENT_MIN_MAX_TX", 22, TEST_CASE_TYPE_MUL,
+ &configs->cp_min_max_table_tx[0], 0, 0, 0},
+ };
+
+ memcpy(test_field_array, test_case_field_array,
+ sizeof(struct test_case_field) * MAX_CASE_NUM);
+}
+
+static ssize_t cyttsp5_parse_cmcp_threshold_file_common(
+ struct device *dev, const char *buf, u32 file_size)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ ssize_t rc = 0;
+ u32 case_count = 0;
+
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Start parsing cmcp threshold file. File size is %d\n",
+ __func__, file_size);
+
+ cmcp_test_case_field_init(dad->test_field_array, dad->configs);
+
+ /* Get all the cases from .csv file */
+ case_count = cmcp_get_case_info_from_threshold_file(dev,
+ buf, dad->test_search_array, file_size);
+
+ /* Search cases */
+ cmcp_get_configuration_info(dev,
+ buf,
+ dad->test_search_array, case_count, dad->test_field_array,
+ dad->configs);
+
+ /* Get basic information */
+ cmcp_get_basic_info(dev, dad->test_field_array, dad->configs);
+
+ return rc;
+}
+
+static ssize_t cyttsp5_cmcp_threshold_loading_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ long value;
+ int rc;
+
+ rc = kstrtol(buf, 10, &value);
+ if (rc < 0 || value < -1 || value > 1) {
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ return size;
+ }
+
+ mutex_lock(&dad->cmcp_threshold_lock);
+
+ if (value == 1)
+ dad->cmcp_threshold_loading = true;
+ else if (value == -1)
+ dad->cmcp_threshold_loading = false;
+ else if (value == 0 && dad->cmcp_threshold_loading) {
+ dad->cmcp_threshold_loading = false;
+
+ if (dad->cmcp_threshold_size == 0) {
+ dev_err(dev, "%s: No cmcp threshold data\n", __func__);
+ goto exit_free;
+ }
+
+ /* Clear test executed flag */
+ dad->test_executed = 0;
+
+ cyttsp5_parse_cmcp_threshold_file_common(dev,
+ &dad->cmcp_threshold_data[0], dad->cmcp_threshold_size);
+
+ /* Mark valid */
+ dad->builtin_cmcp_threshold_status = 0;
+ /* Restore test item to default value when new file input */
+ dad->cmcp_test_items = 0;
+ }
+
+exit_free:
+ kfree(dad->cmcp_threshold_data);
+ dad->cmcp_threshold_data = NULL;
+ dad->cmcp_threshold_size = 0;
+
+ mutex_unlock(&dad->cmcp_threshold_lock);
+
+ if (rc)
+ return rc;
+
+ return size;
+}
+
+static DEVICE_ATTR(cmcp_threshold_loading, S_IRUSR | S_IWUSR,
+ cyttsp5_cmcp_threshold_loading_show,
+ cyttsp5_cmcp_threshold_loading_store);
+
+/*
+* cmcp threshold data write
+*/
+static ssize_t cyttsp5_cmcp_threshold_data_write(struct file *filp,
+ struct kobject *kobj, struct bin_attribute *bin_attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ u8 *p;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: offset:%lld count:%zu\n",
+ __func__, offset, count);
+
+ mutex_lock(&dad->cmcp_threshold_lock);
+
+ if (!dad->cmcp_threshold_loading) {
+ mutex_unlock(&dad->cmcp_threshold_lock);
+ return -ENODEV;
+ }
+
+ p = krealloc(dad->cmcp_threshold_data, offset + count, GFP_KERNEL);
+ if (!p) {
+ kfree(dad->cmcp_threshold_data);
+ dad->cmcp_threshold_data = NULL;
+ mutex_unlock(&dad->cmcp_threshold_lock);
+ return -ENOMEM;
+ }
+ dad->cmcp_threshold_data = p;
+
+ memcpy(&dad->cmcp_threshold_data[offset], buf, count);
+ dad->cmcp_threshold_size += count;
+
+ mutex_unlock(&dad->cmcp_threshold_lock);
+
+ return count;
+}
+
+static struct bin_attribute bin_attr_cmcp_threshold_data = {
+ .attr = {
+ .name = "cmcp_threshold_data",
+ .mode = S_IWUSR,
+ },
+ .size = 0,
+ .write = cyttsp5_cmcp_threshold_data_write,
+};
+
+/*
+ * Suspend scan command
+ */
+static int cyttsp5_suspend_scan_cmd_(struct device *dev)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+ if (rc < 0)
+ dev_err(dev, "%s: Suspend scan failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Resume scan command
+ */
+static int cyttsp5_resume_scan_cmd_(struct device *dev)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+ if (rc < 0)
+ dev_err(dev, "%s: Resume scan failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Execute scan command
+ */
+static int cyttsp5_exec_scan_cmd_(struct device *dev)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->exec_panel_scan(dev, 0);
+ if (rc < 0)
+ dev_err(dev, "%s: Heatmap start scan failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Retrieve panel data command
+ */
+static int cyttsp5_ret_scan_data_cmd_(struct device *dev, u16 read_offset,
+ u16 read_count, u8 data_id, u8 *response, u8 *config,
+ u16 *actual_read_len, u8 *return_buf)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->retrieve_panel_scan(dev, 0, read_offset,
+ read_count, data_id, response, config, actual_read_len,
+ return_buf);
+ if (rc < 0)
+ dev_err(dev, "%s: Retrieve scan data failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Get data structure command
+ */
+static int cyttsp5_get_data_structure_cmd_(struct device *dev, u16 read_offset,
+ u16 read_length, u8 data_id, u8 *status, u8 *data_format,
+ u16 *actual_read_len, u8 *data)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->get_data_structure(dev, 0, read_offset,
+ read_length, data_id, status, data_format,
+ actual_read_len, data);
+ if (rc < 0)
+ dev_err(dev, "%s: Get data structure failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Run self test command
+ */
+static int cyttsp5_run_selftest_cmd_(struct device *dev, u8 test_id,
+ u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+ u8 *results_available)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->run_selftest(dev, 0, test_id,
+ write_idacs_to_flash, status, summary_result,
+ results_available);
+ if (rc < 0)
+ dev_err(dev, "%s: Run self test failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Get self test result command
+ */
+static int cyttsp5_get_selftest_result_cmd_(struct device *dev,
+ u16 read_offset, u16 read_length, u8 test_id, u8 *status,
+ u16 *actual_read_len, u8 *data)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->get_selftest_result(dev, 0, read_offset,
+ read_length, test_id, status, actual_read_len, data);
+ if (rc < 0)
+ dev_err(dev, "%s: Get self test result failed r=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+/*
+ * Calibrate IDACs command
+ */
+static int _cyttsp5_calibrate_idacs_cmd(struct device *dev,
+ u8 sensing_mode, u8 *status)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, sensing_mode, status);
+ return rc;
+}
+
+/*
+ * Initialize Baselines command
+ */
+static int _cyttsp5_initialize_baselines_cmd(struct device *dev,
+ u8 sensing_mode, u8 *status)
+{
+ int rc;
+
+ rc = cmd->nonhid_cmd->initialize_baselines(dev, 0, sensing_mode,
+ status);
+ return rc;
+}
+
+static int prepare_print_buffer(int status, u8 *in_buf, int length,
+ u8 *out_buf, size_t out_buf_size)
+{
+ int index = 0;
+ int i;
+
+ index += scnprintf(out_buf, out_buf_size, "status %d\n", status);
+
+ for (i = 0; i < length; i++) {
+ index += scnprintf(&out_buf[index], out_buf_size - index,
+ "%02X\n", in_buf[i]);
+ }
+
+ return index;
+}
+static ssize_t cyttsp5_run_and_get_selftest_result_noprint(struct device *dev,
+ char *buf, size_t buf_len, u8 test_id, u16 read_length,
+ bool get_result_on_pass)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ int status = STATUS_FAIL;
+ u8 cmd_status = 0;
+ u8 summary_result = 0;
+ u16 act_length = 0;
+ int length = 0;
+ int rc;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto put_pm_runtime;
+ }
+
+ rc = cyttsp5_suspend_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on suspend scan r=%d\n",
+ __func__, rc);
+ goto release_exclusive;
+ }
+
+ rc = cyttsp5_run_selftest_cmd_(dev, test_id, 0,
+ &cmd_status, &summary_result, NULL);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on run self test for test_id:%d r=%d\n",
+ __func__, test_id, rc);
+ goto resume_scan;
+ }
+
+ /* Form response buffer */
+ dad->ic_buf[0] = cmd_status;
+ dad->ic_buf[1] = summary_result;
+
+ length = 2;
+
+ /* Get data if command status is success */
+ if (cmd_status != CY_CMD_STATUS_SUCCESS)
+ goto status_success;
+
+ /* Get data unless test result is pass */
+ if (summary_result == CY_ST_RESULT_PASS && !get_result_on_pass)
+ goto status_success;
+
+ rc = cyttsp5_get_selftest_result_cmd_(dev, 0, read_length,
+ test_id, &cmd_status, &act_length, &dad->ic_buf[6]);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on get self test result r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ dad->ic_buf[2] = cmd_status;
+ dad->ic_buf[3] = test_id;
+ dad->ic_buf[4] = LOW_BYTE(act_length);
+ dad->ic_buf[5] = HI_BYTE(act_length);
+
+ length = 6 + act_length;
+
+status_success:
+ status = STATUS_SUCCESS;
+
+resume_scan:
+ cyttsp5_resume_scan_cmd_(dev);
+
+release_exclusive:
+ cmd->release_exclusive(dev);
+
+put_pm_runtime:
+ pm_runtime_put(dev);
+ mutex_unlock(&dad->sysfs_lock);
+
+ return status;
+}
+
+static ssize_t cyttsp5_run_and_get_selftest_result(struct device *dev,
+ char *buf, size_t buf_len, u8 test_id, u16 read_length,
+ bool get_result_on_pass)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ int status = STATUS_FAIL;
+ u8 cmd_status = 0;
+ u8 summary_result = 0;
+ u16 act_length = 0;
+ int length = 0;
+ int size;
+ int rc;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto put_pm_runtime;
+ }
+
+ rc = cyttsp5_suspend_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on suspend scan r=%d\n",
+ __func__, rc);
+ goto release_exclusive;
+ }
+
+ rc = cyttsp5_run_selftest_cmd_(dev, test_id, 0,
+ &cmd_status, &summary_result, NULL);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on run self test for test_id:%d r=%d\n",
+ __func__, test_id, rc);
+ goto resume_scan;
+ }
+
+ /* Form response buffer */
+ dad->ic_buf[0] = cmd_status;
+ dad->ic_buf[1] = summary_result;
+
+ length = 2;
+
+ /* Get data if command status is success */
+ if (cmd_status != CY_CMD_STATUS_SUCCESS)
+ goto status_success;
+
+ /* Get data unless test result is pass */
+ if (summary_result == CY_ST_RESULT_PASS && !get_result_on_pass)
+ goto status_success;
+
+ rc = cyttsp5_get_selftest_result_cmd_(dev, 0, read_length,
+ test_id, &cmd_status, &act_length, &dad->ic_buf[6]);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on get self test result r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ dad->ic_buf[2] = cmd_status;
+ dad->ic_buf[3] = test_id;
+ dad->ic_buf[4] = LOW_BYTE(act_length);
+ dad->ic_buf[5] = HI_BYTE(act_length);
+
+ length = 6 + act_length;
+
+status_success:
+ status = STATUS_SUCCESS;
+
+resume_scan:
+ cyttsp5_resume_scan_cmd_(dev);
+
+release_exclusive:
+ cmd->release_exclusive(dev);
+
+put_pm_runtime:
+ pm_runtime_put(dev);
+
+ if (status == STATUS_FAIL)
+ length = 0;
+
+ size = prepare_print_buffer(status, dad->ic_buf, length, buf, buf_len);
+
+ mutex_unlock(&dad->sysfs_lock);
+
+ return size;
+}
+
+struct cyttsp5_device_access_debugfs_data {
+ struct cyttsp5_device_access_data *dad;
+ ssize_t pr_buf_len;
+ u8 pr_buf[3 * CY_MAX_PRBUF_SIZE];
+};
+
+static int cyttsp5_device_access_debugfs_open(struct inode *inode,
+ struct file *filp)
+{
+ struct cyttsp5_device_access_data *dad = inode->i_private;
+ struct cyttsp5_device_access_debugfs_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dad = dad;
+
+ filp->private_data = data;
+
+ return nonseekable_open(inode, filp);
+}
+
+static int cyttsp5_device_access_debugfs_release(struct inode *inode,
+ struct file *filp)
+{
+ kfree(filp->private_data);
+
+ return 0;
+}
+
+#define CY_DEBUGFS_FOPS(_name, _read, _write) \
+static const struct file_operations _name##_debugfs_fops = { \
+ .open = cyttsp5_device_access_debugfs_open, \
+ .release = cyttsp5_device_access_debugfs_release, \
+ .read = _read, \
+ .write = _write, \
+}
+
+static ssize_t panel_scan_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ struct device *dev = dad->dev;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ int status = STATUS_FAIL;
+ u8 config;
+ u16 actual_read_len;
+ int length = 0;
+ u8 element_size = 0;
+ u8 *buf_offset;
+ int elem_offset = 0;
+ int rc;
+
+ if (*ppos)
+ goto exit;
+
+
+ mutex_lock(&dad->sysfs_lock);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto put_pm_runtime;
+ }
+
+ rc = cyttsp5_suspend_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on suspend scan r=%d\n",
+ __func__, rc);
+ goto release_exclusive;
+ }
+
+ rc = cyttsp5_exec_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on execute panel scan r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ /* Set length to max to read all */
+ rc = cyttsp5_ret_scan_data_cmd_(dev, 0, 0xFFFF,
+ dad->panel_scan_data_id, dad->ic_buf, &config,
+ &actual_read_len, NULL);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on retrieve panel scan r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ length = get_unaligned_le16(&dad->ic_buf[0]);
+ buf_offset = dad->ic_buf + length;
+ element_size = config & 0x07;
+ elem_offset = actual_read_len;
+ while (actual_read_len > 0) {
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, 0xFFFF,
+ dad->panel_scan_data_id, NULL, &config,
+ &actual_read_len, buf_offset);
+ if (rc < 0)
+ goto resume_scan;
+
+ length += actual_read_len * element_size;
+ buf_offset = dad->ic_buf + length;
+ elem_offset += actual_read_len;
+ }
+ /* Reconstruct cmd header */
+ put_unaligned_le16(length, &dad->ic_buf[0]);
+ put_unaligned_le16(elem_offset, &dad->ic_buf[7]);
+
+ /* Do not print command header */
+ length -= 5;
+
+ status = STATUS_SUCCESS;
+
+resume_scan:
+ cyttsp5_resume_scan_cmd_(dev);
+
+release_exclusive:
+ cmd->release_exclusive(dev);
+
+put_pm_runtime:
+ pm_runtime_put(dev);
+
+ if (status == STATUS_FAIL)
+ length = 0;
+ if (cd->show_timestamp) {
+ int index = 0;
+
+ index += scnprintf(data->pr_buf, sizeof(data->pr_buf),
+ "[%u] ", jiffies_to_msecs(jiffies));
+ data->pr_buf_len = prepare_print_buffer(status,
+ &dad->ic_buf[5], length, &data->pr_buf[index],
+ sizeof(data->pr_buf)-index);
+ } else {
+ data->pr_buf_len = prepare_print_buffer(status,
+ &dad->ic_buf[5], length, data->pr_buf,
+ sizeof(data->pr_buf));
+ }
+
+ mutex_unlock(&dad->sysfs_lock);
+
+exit:
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+static ssize_t panel_scan_debugfs_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ ssize_t length;
+ int rc = 0;
+
+ rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+ buf, count);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ length = cyttsp5_ic_parse_input(dad->dev, data->pr_buf, count,
+ dad->ic_buf, CY_MAX_PRBUF_SIZE);
+
+ if (length != 1) {
+ dev_err(dad->dev, "%s: Malformed input\n", __func__);
+ rc = -EINVAL;
+ goto exit_unlock;
+ }
+
+ dad->panel_scan_data_id = dad->ic_buf[0];
+
+exit_unlock:
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+CY_DEBUGFS_FOPS(panel_scan, panel_scan_debugfs_read, panel_scan_debugfs_write);
+
+static ssize_t get_idac_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ struct device *dev = dad->dev;
+ int status = STATUS_FAIL;
+ u8 cmd_status = 0;
+ u8 data_format = 0;
+ u16 act_length = 0;
+ int length = 0;
+ int rc;
+
+ if (*ppos)
+ goto exit;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto put_pm_runtime;
+ }
+
+ rc = cyttsp5_suspend_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on suspend scan r=%d\n",
+ __func__, rc);
+ goto release_exclusive;
+ }
+
+ rc = cyttsp5_get_data_structure_cmd_(dev, 0, PIP_CMD_MAX_LENGTH,
+ dad->get_idac_data_id, &cmd_status, &data_format,
+ &act_length, &dad->ic_buf[5]);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on get data structure r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ dad->ic_buf[0] = cmd_status;
+ dad->ic_buf[1] = dad->get_idac_data_id;
+ dad->ic_buf[2] = LOW_BYTE(act_length);
+ dad->ic_buf[3] = HI_BYTE(act_length);
+ dad->ic_buf[4] = data_format;
+
+ length = 5 + act_length;
+
+ status = STATUS_SUCCESS;
+
+resume_scan:
+ cyttsp5_resume_scan_cmd_(dev);
+
+release_exclusive:
+ cmd->release_exclusive(dev);
+
+put_pm_runtime:
+ pm_runtime_put(dev);
+
+ if (status == STATUS_FAIL)
+ length = 0;
+
+ data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+ data->pr_buf, sizeof(data->pr_buf));
+
+ mutex_unlock(&dad->sysfs_lock);
+
+exit:
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+static ssize_t get_idac_debugfs_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ ssize_t length;
+ int rc = 0;
+
+ rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+ buf, count);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ length = cyttsp5_ic_parse_input(dad->dev, data->pr_buf, count,
+ dad->ic_buf, CY_MAX_PRBUF_SIZE);
+ if (length != 1) {
+ dev_err(dad->dev, "%s: Malformed input\n", __func__);
+ rc = -EINVAL;
+ goto exit_unlock;
+ }
+
+ dad->get_idac_data_id = dad->ic_buf[0];
+
+exit_unlock:
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+CY_DEBUGFS_FOPS(get_idac, get_idac_debugfs_read, get_idac_debugfs_write);
+
+static ssize_t calibrate_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ struct device *dev = dad->dev;
+ int status = STATUS_FAIL;
+ int length = 0;
+ int rc;
+
+ if (*ppos)
+ goto exit;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto put_pm_runtime;
+ }
+
+ rc = cyttsp5_suspend_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on suspend scan r=%d\n",
+ __func__, rc);
+ goto release_exclusive;
+ }
+
+ rc = _cyttsp5_calibrate_idacs_cmd(dev, dad->calibrate_sensing_mode,
+ &dad->ic_buf[0]);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on calibrate idacs r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ length = 1;
+
+ /* Check if baseline initialization is requested */
+ if (dad->calibrate_initialize_baselines) {
+ /* Perform baseline initialization for all modes */
+ rc = _cyttsp5_initialize_baselines_cmd(dev, CY_IB_SM_MUTCAP |
+ CY_IB_SM_SELFCAP | CY_IB_SM_BUTTON,
+ &dad->ic_buf[length]);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on initialize baselines r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ length++;
+ }
+
+ status = STATUS_SUCCESS;
+
+resume_scan:
+ cyttsp5_resume_scan_cmd_(dev);
+
+release_exclusive:
+ cmd->release_exclusive(dev);
+
+put_pm_runtime:
+ pm_runtime_put(dev);
+
+ if (status == STATUS_FAIL)
+ length = 0;
+
+ data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+ data->pr_buf, sizeof(data->pr_buf));
+
+ mutex_unlock(&dad->sysfs_lock);
+
+exit:
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+static ssize_t calibrate_debugfs_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ ssize_t length;
+ int rc = 0;
+
+ rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+ buf, count);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ length = cyttsp5_ic_parse_input(dad->dev, data->pr_buf, count,
+ dad->ic_buf, CY_MAX_PRBUF_SIZE);
+ if (length != 2) {
+ dev_err(dad->dev, "%s: Malformed input\n", __func__);
+ rc = -EINVAL;
+ goto exit_unlock;
+ }
+
+ dad->calibrate_sensing_mode = dad->ic_buf[0];
+ dad->calibrate_initialize_baselines = dad->ic_buf[1];
+
+exit_unlock:
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+CY_DEBUGFS_FOPS(calibrate, calibrate_debugfs_read, calibrate_debugfs_write);
+
+static ssize_t baseline_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ struct device *dev = dad->dev;
+ int status = STATUS_FAIL;
+ int length = 0;
+ int rc;
+
+ if (*ppos)
+ goto exit;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto put_pm_runtime;
+ }
+
+ rc = cyttsp5_suspend_scan_cmd_(dev);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on suspend scan r=%d\n",
+ __func__, rc);
+ goto release_exclusive;
+ }
+
+ rc = _cyttsp5_initialize_baselines_cmd(dev, dad->baseline_sensing_mode,
+ &dad->ic_buf[0]);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on initialize baselines r=%d\n",
+ __func__, rc);
+ goto resume_scan;
+ }
+
+ length = 1;
+
+ status = STATUS_SUCCESS;
+
+resume_scan:
+ cyttsp5_resume_scan_cmd_(dev);
+
+release_exclusive:
+ cmd->release_exclusive(dev);
+
+put_pm_runtime:
+ pm_runtime_put(dev);
+
+ if (status == STATUS_FAIL)
+ length = 0;
+
+ data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+ data->pr_buf, sizeof(data->pr_buf));
+
+ mutex_unlock(&dad->sysfs_lock);
+
+exit:
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+static ssize_t baseline_debugfs_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+ struct cyttsp5_device_access_data *dad = data->dad;
+ ssize_t length;
+ int rc = 0;
+
+ rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+ buf, count);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ mutex_lock(&dad->sysfs_lock);
+
+ length = cyttsp5_ic_parse_input(dad->dev, buf, count, dad->ic_buf,
+ CY_MAX_PRBUF_SIZE);
+ if (length != 1) {
+ dev_err(dad->dev, "%s: Malformed input\n", __func__);
+ rc = -EINVAL;
+ goto exit_unlock;
+ }
+
+ dad->baseline_sensing_mode = dad->ic_buf[0];
+
+exit_unlock:
+ mutex_unlock(&dad->sysfs_lock);
+
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+CY_DEBUGFS_FOPS(baseline, baseline_debugfs_read, baseline_debugfs_write);
+
+static ssize_t auto_shorts_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+
+ if (!*ppos)
+ /* Set length to PIP_CMD_MAX_LENGTH to read all */
+ data->pr_buf_len = cyttsp5_run_and_get_selftest_result(
+ data->dad->dev, data->pr_buf, sizeof(data->pr_buf),
+ CY_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, false);
+
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+CY_DEBUGFS_FOPS(auto_shorts, auto_shorts_debugfs_read, NULL);
+
+static ssize_t opens_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+
+ if (!*ppos)
+ /* Set length to PIP_CMD_MAX_LENGTH to read all */
+ data->pr_buf_len = cyttsp5_run_and_get_selftest_result(
+ data->dad->dev, data->pr_buf, sizeof(data->pr_buf),
+ CY_ST_ID_OPENS, PIP_CMD_MAX_LENGTH, false);
+
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+CY_DEBUGFS_FOPS(opens, opens_debugfs_read, NULL);
+
+static ssize_t cm_panel_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+
+ if (!*ppos)
+ /* Set length to PIP_CMD_MAX_LENGTH to read all */
+ data->pr_buf_len = cyttsp5_run_and_get_selftest_result(
+ data->dad->dev, data->pr_buf, sizeof(data->pr_buf),
+ CY_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, true);
+
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+CY_DEBUGFS_FOPS(cm_panel, cm_panel_debugfs_read, NULL);
+
+static ssize_t cp_panel_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+
+ if (!*ppos)
+ /* Set length to PIP_CMD_MAX_LENGTH to read all */
+ data->pr_buf_len = cyttsp5_run_and_get_selftest_result(
+ data->dad->dev, data->pr_buf, sizeof(data->pr_buf),
+ CY_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, true);
+
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+CY_DEBUGFS_FOPS(cp_panel, cp_panel_debugfs_read, NULL);
+
+static ssize_t cm_button_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+
+ if (!*ppos)
+ /* Set length to PIP_CMD_MAX_LENGTH to read all */
+ data->pr_buf_len = cyttsp5_run_and_get_selftest_result(
+ data->dad->dev, data->pr_buf, sizeof(data->pr_buf),
+ CY_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, true);
+
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+CY_DEBUGFS_FOPS(cm_button, cm_button_debugfs_read, NULL);
+
+static ssize_t cp_button_debugfs_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_debugfs_data *data = filp->private_data;
+
+ if (!*ppos)
+ /* Set length to PIP_CMD_MAX_LENGTH to read all */
+ data->pr_buf_len = cyttsp5_run_and_get_selftest_result(
+ data->dad->dev, data->pr_buf, sizeof(data->pr_buf),
+ CY_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH, true);
+
+ return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+ data->pr_buf_len);
+}
+
+CY_DEBUGFS_FOPS(cp_button, cp_button_debugfs_read, NULL);
+
+#ifdef TTHE_TUNER_SUPPORT
+static ssize_t tthe_get_panel_data_debugfs_read(struct file *filp,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_data *dad = filp->private_data;
+ struct device *dev;
+ struct cyttsp5_core_data *cd;
+ u8 config;
+ u16 actual_read_len;
+ u16 length = 0;
+ u8 element_size = 0;
+ u8 *buf_offset;
+ u8 *buf_out;
+ int elem;
+ int elem_offset = 0;
+ int print_idx = 0;
+ int rc;
+ int rc1;
+ int i;
+
+ mutex_lock(&dad->debugfs_lock);
+ dev = dad->dev;
+ cd = dev_get_drvdata(dev);
+ buf_out = dad->tthe_get_panel_data_buf;
+ if (!buf_out)
+ goto release_mutex;
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0)
+ goto put_runtime;
+
+ if (dad->heatmap.scan_start) {
+ /*
+ * To fix CDT206291: avoid multiple scans when
+ * return data is larger than 4096 bytes in one cycle
+ */
+ dad->heatmap.scan_start = 0;
+
+ /* Start scan */
+ rc = cyttsp5_exec_scan_cmd_(dev);
+ if (rc < 0)
+ goto release_exclusive;
+ }
+
+ elem = dad->heatmap.num_element;
+
+#if defined(CY_ENABLE_MAX_ELEN)
+ if (elem > CY_MAX_ELEN) {
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset,
+ CY_MAX_ELEN, dad->heatmap.data_type, dad->ic_buf,
+ &config, &actual_read_len, NULL);
+ } else{
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem,
+ dad->heatmap.data_type, dad->ic_buf, &config,
+ &actual_read_len, NULL);
+ }
+#else
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem,
+ dad->heatmap.data_type, dad->ic_buf, &config,
+ &actual_read_len, NULL);
+#endif
+ if (rc < 0)
+ goto release_exclusive;
+
+ length = get_unaligned_le16(&dad->ic_buf[0]);
+ buf_offset = dad->ic_buf + length;
+
+ element_size = config & CY_CMD_RET_PANEL_ELMNT_SZ_MASK;
+
+ elem -= actual_read_len;
+ elem_offset = actual_read_len;
+ while (elem > 0) {
+#ifdef CY_ENABLE_MAX_ELEN
+ if (elem > CY_MAX_ELEN) {
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset,
+ CY_MAX_ELEN, dad->heatmap.data_type, NULL, &config,
+ &actual_read_len, buf_offset);
+ } else{
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem,
+ dad->heatmap.data_type, NULL, &config,
+ &actual_read_len, buf_offset);
+ }
+#else
+
+ rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem,
+ dad->heatmap.data_type, NULL, &config,
+ &actual_read_len, buf_offset);
+#endif
+ if (rc < 0)
+ goto release_exclusive;
+
+ if (!actual_read_len)
+ break;
+
+ length += actual_read_len * element_size;
+ buf_offset = dad->ic_buf + length;
+ elem -= actual_read_len;
+ elem_offset += actual_read_len;
+ }
+
+ /* Reconstruct cmd header */
+ put_unaligned_le16(length, &dad->ic_buf[0]);
+ put_unaligned_le16(elem_offset, &dad->ic_buf[7]);
+
+release_exclusive:
+ rc1 = cmd->release_exclusive(dev);
+put_runtime:
+ pm_runtime_put(dev);
+
+ if (rc < 0)
+ goto release_mutex;
+ if (cd->show_timestamp)
+ print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF,
+ "[%u] CY_DATA:", jiffies_to_msecs(jiffies));
+ else
+ print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF,
+ "CY_DATA:");
+ for (i = 0; i < length; i++)
+ print_idx += scnprintf(buf_out + print_idx,
+ TTHE_TUNER_MAX_BUF - print_idx,
+ "%02X ", dad->ic_buf[i]);
+ print_idx += scnprintf(buf_out + print_idx,
+ TTHE_TUNER_MAX_BUF - print_idx,
+ ":(%d bytes)\n", length);
+ rc = simple_read_from_buffer(buf, count, ppos, buf_out, print_idx);
+ print_idx = rc;
+
+release_mutex:
+ mutex_unlock(&dad->debugfs_lock);
+ return print_idx;
+}
+
+static ssize_t tthe_get_panel_data_debugfs_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct cyttsp5_device_access_data *dad = filp->private_data;
+ struct device *dev = dad->dev;
+ ssize_t length;
+ int max_read;
+ u8 *buf_in = dad->tthe_get_panel_data_buf;
+ int ret;
+
+ mutex_lock(&dad->debugfs_lock);
+ ret = copy_from_user(buf_in + (*ppos), buf, count);
+ if (ret)
+ goto exit;
+ buf_in[count] = 0;
+
+ length = cyttsp5_ic_parse_input(dev, buf_in, count, dad->ic_buf,
+ CY_MAX_PRBUF_SIZE);
+ if (length <= 0) {
+ dev_err(dev, "%s: %s Group Data store\n", __func__,
+ "Malformed input for");
+ goto exit;
+ }
+
+ /* update parameter value */
+ dad->heatmap.num_element = get_unaligned_le16(&dad->ic_buf[3]);
+ dad->heatmap.data_type = dad->ic_buf[5];
+
+ if (dad->ic_buf[6] > 0)
+ dad->heatmap.scan_start = true;
+ else
+ dad->heatmap.scan_start = false;
+
+ /* elem can not be bigger then buffer size */
+ max_read = CY_CMD_RET_PANEL_HDR;
+ max_read += dad->heatmap.num_element * CY_CMD_RET_PANEL_ELMNT_SZ_MAX;
+
+ if (max_read >= CY_MAX_PRBUF_SIZE) {
+ dad->heatmap.num_element =
+ (CY_MAX_PRBUF_SIZE - CY_CMD_RET_PANEL_HDR)
+ / CY_CMD_RET_PANEL_ELMNT_SZ_MAX;
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Will get %d element\n",
+ __func__, dad->heatmap.num_element);
+ }
+
+exit:
+ mutex_unlock(&dad->debugfs_lock);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: return count=%zu\n",
+ __func__, count);
+ return count;
+}
+
+static int tthe_get_panel_data_debugfs_open(struct inode *inode,
+ struct file *filp)
+{
+ struct cyttsp5_device_access_data *dad = inode->i_private;
+
+ mutex_lock(&dad->debugfs_lock);
+
+ if (dad->tthe_get_panel_data_is_open) {
+ mutex_unlock(&dad->debugfs_lock);
+ return -EBUSY;
+ }
+
+ filp->private_data = inode->i_private;
+
+ dad->tthe_get_panel_data_is_open = 1;
+ mutex_unlock(&dad->debugfs_lock);
+ return 0;
+}
+
+static int tthe_get_panel_data_debugfs_close(struct inode *inode,
+ struct file *filp)
+{
+ struct cyttsp5_device_access_data *dad = filp->private_data;
+
+ mutex_lock(&dad->debugfs_lock);
+ filp->private_data = NULL;
+ dad->tthe_get_panel_data_is_open = 0;
+ mutex_unlock(&dad->debugfs_lock);
+
+ return 0;
+}
+
+static const struct file_operations tthe_get_panel_data_fops = {
+ .open = tthe_get_panel_data_debugfs_open,
+ .release = tthe_get_panel_data_debugfs_close,
+ .read = tthe_get_panel_data_debugfs_read,
+ .write = tthe_get_panel_data_debugfs_write,
+};
+#endif
+
+static int cyttsp5_setup_sysfs(struct device *dev)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ int rc;
+
+ rc = device_create_file(dev, &dev_attr_command);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create command\n",
+ __func__);
+ goto exit;
+ }
+
+ rc = device_create_file(dev, &dev_attr_status);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create status\n",
+ __func__);
+ goto unregister_command;
+ }
+
+ rc = device_create_file(dev, &dev_attr_response);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create response\n",
+ __func__);
+ goto unregister_status;
+ }
+
+ dad->base_dentry = debugfs_create_dir(dev_name(dev), NULL);
+ if (IS_ERR_OR_NULL(dad->base_dentry)) {
+ dev_err(dev, "%s: Error, could not create base directory\n",
+ __func__);
+ goto unregister_response;
+ }
+
+ dad->mfg_test_dentry = debugfs_create_dir("mfg_test",
+ dad->base_dentry);
+ if (IS_ERR_OR_NULL(dad->mfg_test_dentry)) {
+ dev_err(dev, "%s: Error, could not create mfg_test directory\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("panel_scan", 0600,
+ dad->mfg_test_dentry, dad,
+ &panel_scan_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create panel_scan\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("get_idac", 0600,
+ dad->mfg_test_dentry, dad, &get_idac_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create get_idac\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("auto_shorts", 0400,
+ dad->mfg_test_dentry, dad,
+ &auto_shorts_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create auto_shorts\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("opens", 0400,
+ dad->mfg_test_dentry, dad, &opens_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create opens\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("calibrate", 0600,
+ dad->mfg_test_dentry, dad, &calibrate_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create calibrate\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("baseline", 0600,
+ dad->mfg_test_dentry, dad, &baseline_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create baseline\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("cm_panel", 0400,
+ dad->mfg_test_dentry, dad, &cm_panel_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create cm_panel\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("cp_panel", 0400,
+ dad->mfg_test_dentry, dad, &cp_panel_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create cp_panel\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("cm_button", 0400,
+ dad->mfg_test_dentry, dad, &cm_button_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create cm_button\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("cp_button", 0400,
+ dad->mfg_test_dentry, dad, &cp_button_debugfs_fops))) {
+ dev_err(dev, "%s: Error, could not create cp_button\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ dad->cmcp_results_debugfs = debugfs_create_file("cmcp_results", 0644,
+ dad->mfg_test_dentry, dad, &cmcp_results_debugfs_fops);
+ if (IS_ERR_OR_NULL(dad->cmcp_results_debugfs)) {
+ dev_err(dev, "%s: Error, could not create cmcp_results\n",
+ __func__);
+ dad->cmcp_results_debugfs = NULL;
+ goto unregister_base_dir;
+ }
+
+#ifdef TTHE_TUNER_SUPPORT
+ dad->tthe_get_panel_data_debugfs = debugfs_create_file(
+ CYTTSP5_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME,
+ 0644, NULL, dad, &tthe_get_panel_data_fops);
+ if (IS_ERR_OR_NULL(dad->tthe_get_panel_data_debugfs)) {
+ dev_err(dev, "%s: Error, could not create get_panel_data\n",
+ __func__);
+ dad->tthe_get_panel_data_debugfs = NULL;
+ goto unregister_base_dir;
+ }
+#endif
+
+ rc = device_create_file(dev, &dev_attr_cmcp_test);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create cmcp_test\n",
+ __func__);
+ goto unregister_base_dir;
+ }
+
+ rc = device_create_file(dev, &dev_attr_cmcp_threshold_loading);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create cmcp_thresold_loading\n",
+ __func__);
+ goto unregister_cmcp_test;
+ }
+
+ rc = device_create_bin_file(dev, &bin_attr_cmcp_threshold_data);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create cmcp_thresold_data\n",
+ __func__);
+ goto unregister_cmcp_thresold_loading;
+ }
+
+ dad->sysfs_nodes_created = true;
+ return rc;
+
+unregister_cmcp_thresold_loading:
+ device_remove_file(dev, &dev_attr_cmcp_threshold_loading);
+unregister_cmcp_test:
+ device_remove_file(dev, &dev_attr_cmcp_test);
+unregister_base_dir:
+ debugfs_remove_recursive(dad->base_dentry);
+unregister_response:
+ device_remove_file(dev, &dev_attr_response);
+unregister_status:
+ device_remove_file(dev, &dev_attr_status);
+unregister_command:
+ device_remove_file(dev, &dev_attr_command);
+exit:
+ return rc;
+}
+
+static int cyttsp5_setup_sysfs_attention(struct device *dev)
+{
+ struct cyttsp5_device_access_data *dad
+ = cyttsp5_get_device_access_data(dev);
+ int rc = 0;
+
+ dad->si = cmd->request_sysinfo(dev);
+ if (!dad->si)
+ return -EINVAL;
+
+ rc = cyttsp5_setup_sysfs(dev);
+
+ cmd->unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_DEVICE_ACCESS_NAME, cyttsp5_setup_sysfs_attention,
+ 0);
+
+ return rc;
+}
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API
+int cyttsp5_device_access_user_command(const char *core_name, u16 read_len,
+ u8 *read_buf, u16 write_len, u8 *write_buf,
+ u16 *actual_read_len)
+{
+ struct cyttsp5_core_data *cd;
+ int rc;
+
+ might_sleep();
+
+ /* Check parameters */
+ if (!read_buf || !write_buf || !actual_read_len)
+ return -EINVAL;
+
+ if (!core_name)
+ core_name = CY_DEFAULT_CORE_ID;
+
+ /* Find device */
+ cd = cyttsp5_get_core_data((char *)core_name);
+ if (!cd) {
+ pr_err("%s: No device.\n", __func__);
+ return -ENODEV;
+ }
+
+ pm_runtime_get_sync(cd->dev);
+ rc = cmd->nonhid_cmd->user_cmd(cd->dev, 1, read_len, read_buf,
+ write_len, write_buf, actual_read_len);
+ pm_runtime_put(cd->dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_device_access_user_command);
+
+struct command_work {
+ struct work_struct work;
+ const char *core_name;
+ u16 read_len;
+ u8 *read_buf;
+ u16 write_len;
+ u8 *write_buf;
+
+ void (*cont)(const char *core_name, u16 read_len, u8 *read_buf,
+ u16 write_len, u8 *write_buf, u16 actual_read_length,
+ int rc);
+};
+
+static void cyttsp5_device_access_user_command_work_func(
+ struct work_struct *work)
+{
+ struct command_work *cmd_work =
+ container_of(work, struct command_work, work);
+ u16 actual_read_length;
+ int rc;
+
+ rc = cyttsp5_device_access_user_command(cmd_work->core_name,
+ cmd_work->read_len, cmd_work->read_buf,
+ cmd_work->write_len, cmd_work->write_buf,
+ &actual_read_length);
+
+ if (cmd_work->cont)
+ cmd_work->cont(cmd_work->core_name,
+ cmd_work->read_len, cmd_work->read_buf,
+ cmd_work->write_len, cmd_work->write_buf,
+ actual_read_length, rc);
+
+ kfree(cmd_work);
+}
+
+int cyttsp5_device_access_user_command_async(const char *core_name,
+ u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+ void (*cont)(const char *core_name, u16 read_len, u8 *read_buf,
+ u16 write_len, u8 *write_buf, u16 actual_read_length,
+ int rc))
+{
+ struct command_work *cmd_work;
+
+ cmd_work = kzalloc(sizeof(*cmd_work), GFP_ATOMIC);
+ if (!cmd_work)
+ return -ENOMEM;
+
+ cmd_work->core_name = core_name;
+ cmd_work->read_len = read_len;
+ cmd_work->read_buf = read_buf;
+ cmd_work->write_len = write_len;
+ cmd_work->write_buf = write_buf;
+ cmd_work->cont = cont;
+
+ INIT_WORK(&cmd_work->work,
+ cyttsp5_device_access_user_command_work_func);
+ schedule_work(&cmd_work->work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_device_access_user_command_async);
+#endif
+
+static void cyttsp5_cmcp_parse_threshold_file(const struct firmware *fw,
+ void *context)
+{
+ struct device *dev = context;
+ struct cyttsp5_device_access_data *dad =
+ cyttsp5_get_device_access_data(dev);
+
+ if (!fw) {
+ dev_info(dev, "%s: No builtin cmcp threshold file\n", __func__);
+ goto exit;
+ }
+
+ if (!fw->data || !fw->size) {
+ dev_err(dev, "%s: Invalid builtin cmcp threshold file\n",
+ __func__);
+ goto exit;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Found cmcp threshold file.\n",
+ __func__);
+
+ cyttsp5_parse_cmcp_threshold_file_common(dev, &fw->data[0], fw->size);
+
+ dad->builtin_cmcp_threshold_status = 0;
+ complete(&dad->builtin_cmcp_threshold_complete);
+ return;
+
+exit:
+ release_firmware(fw);
+
+ dad->builtin_cmcp_threshold_status = -EINVAL;
+ complete(&dad->builtin_cmcp_threshold_complete);
+}
+
+static void cyttsp5_parse_cmcp_threshold_builtin(
+ struct work_struct *cmcp_threshold_update)
+{
+ struct cyttsp5_device_access_data *dad =
+ container_of(cmcp_threshold_update,
+ struct cyttsp5_device_access_data,
+ cmcp_threshold_update);
+ struct device *dev = dad->dev;
+ int retval;
+
+ dad->si = cmd->request_sysinfo(dev);
+ if (!dad->si) {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core\n",
+ __func__);
+ return;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Enabling cmcp threshold class loader built-in\n",
+ __func__);
+
+ /* Open threshold file */
+ retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ CMCP_THRESHOLD_FILE_NAME, dev, GFP_KERNEL, dev,
+ cyttsp5_cmcp_parse_threshold_file);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed loading cmcp threshold file, attempting legacy file\n",
+ __func__);
+ /* Try legacy file name */
+ retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ CY_CMCP_THRESHOLD_FILE_NAME, dev, GFP_KERNEL,
+ dev, cyttsp5_cmcp_parse_threshold_file);
+ if (retval < 0) {
+ dev_err(dev, "%s: Fail request cmcp threshold class file load\n",
+ __func__);
+ goto exit;
+ }
+ }
+
+ /* wait until cmcp threshold upgrade finishes */
+ wait_for_completion(&dad->builtin_cmcp_threshold_complete);
+
+ retval = dad->builtin_cmcp_threshold_status;
+
+exit:
+ return;
+}
+
+static int cyttsp5_device_access_probe(struct device *dev, void **data)
+{
+ struct cyttsp5_device_access_data *dad;
+ struct configuration *configurations;
+ struct cmcp_data *cmcp_info;
+ struct result *result;
+
+ int tx_num = MAX_TX_SENSORS;
+ int rx_num = MAX_RX_SENSORS;
+ int btn_num = MAX_BUTTONS;
+
+ struct test_case_field *test_case_field_array;
+ struct test_case_search *test_case_search_array;
+ int rc = 0;
+
+ dad = kzalloc(sizeof(*dad), GFP_KERNEL);
+ if (!dad) {
+ rc = -ENOMEM;
+ goto cyttsp5_device_access_probe_data_failed;
+ }
+
+ configurations =
+ kzalloc(sizeof(*configurations), GFP_KERNEL);
+ if (!configurations) {
+ rc = -ENOMEM;
+ goto cyttsp5_device_access_probe_configs_failed;
+ }
+ dad->configs = configurations;
+
+ cmcp_info = kzalloc(sizeof(*cmcp_info), GFP_KERNEL);
+ if (!cmcp_info) {
+ rc = -ENOMEM;
+ goto cyttsp5_device_access_probe_cmcp_info_failed;
+ }
+ dad->cmcp_info = cmcp_info;
+
+ cmcp_info->tx_num = tx_num;
+ cmcp_info->rx_num = rx_num;
+ cmcp_info->btn_num = btn_num;
+
+ result = kzalloc(sizeof(*result), GFP_KERNEL);
+ if (!result) {
+ rc = -ENOMEM;
+ goto cyttsp5_device_access_probe_result_failed;
+ }
+ dad->result = result;
+
+ test_case_field_array =
+ kzalloc(sizeof(*test_case_field_array) * MAX_CASE_NUM,
+ GFP_KERNEL);
+ if (!test_case_field_array) {
+ rc = -ENOMEM;
+ goto cyttsp5_device_access_probe_field_array_failed;
+ }
+
+ test_case_search_array =
+ kzalloc(sizeof(*test_case_search_array) * MAX_CASE_NUM,
+ GFP_KERNEL);
+ if (!test_case_search_array) {
+ rc = -ENOMEM;
+ goto cyttsp5_device_access_probe_search_array_failed;
+ }
+
+ cmcp_info->gd_sensor_col = (struct gd_sensor *)
+ kzalloc(tx_num * sizeof(struct gd_sensor), GFP_KERNEL);
+ if (cmcp_info->gd_sensor_col == NULL)
+ goto cyttsp5_device_access_probe_gd_sensor_col_failed;
+
+ cmcp_info->gd_sensor_row = (struct gd_sensor *)
+ kzalloc(rx_num * sizeof(struct gd_sensor), GFP_KERNEL);
+ if (cmcp_info->gd_sensor_row == NULL)
+ goto cyttsp5_device_access_probe_gd_sensor_row_failed;
+
+ cmcp_info->cm_data_panel =
+ kzalloc((tx_num * rx_num + 1) * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cm_data_panel == NULL)
+ goto cyttsp5_device_access_probe_cm_data_panel_failed;
+
+ cmcp_info->cp_tx_data_panel =
+ kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cp_tx_data_panel == NULL)
+ goto cyttsp5_device_access_probe_cp_tx_data_panel_failed;
+
+ cmcp_info->cp_tx_cal_data_panel =
+ kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cp_tx_cal_data_panel == NULL)
+ goto cyttsp5_device_access_probe_cp_tx_cal_data_panel_failed;
+
+ cmcp_info->cp_rx_data_panel =
+ kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cp_rx_data_panel == NULL)
+ goto cyttsp5_device_access_probe_cp_rx_data_panel_failed;
+
+ cmcp_info->cp_rx_cal_data_panel =
+ kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cp_rx_cal_data_panel == NULL)
+ goto cyttsp5_device_access_probe_cp_rx_cal_data_panel_failed;
+
+ cmcp_info->cm_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cm_btn_data == NULL)
+ goto cyttsp5_device_access_probe_cm_btn_data_failed;
+
+ cmcp_info->cp_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cp_btn_data == NULL)
+ goto cyttsp5_device_access_probe_cp_btn_data_failed;
+
+ cmcp_info->cm_sensor_column_delta =
+ kzalloc(rx_num * tx_num * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cm_sensor_column_delta == NULL)
+ goto cyttsp5_device_access_probe_cm_sensor_column_delta_failed;
+
+ cmcp_info->cm_sensor_row_delta =
+ kzalloc(tx_num * rx_num * sizeof(int32_t), GFP_KERNEL);
+ if (cmcp_info->cm_sensor_row_delta == NULL)
+ goto cyttsp5_device_access_probe_cm_sensor_row_delta_failed;
+
+ mutex_init(&dad->sysfs_lock);
+ mutex_init(&dad->cmcp_threshold_lock);
+ dad->dev = dev;
+#ifdef TTHE_TUNER_SUPPORT
+ mutex_init(&dad->debugfs_lock);
+ dad->heatmap.num_element = 200;
+#endif
+ *data = dad;
+
+ dad->test_field_array = test_case_field_array;
+ dad->test_search_array = test_case_search_array;
+ dad->test_executed = 0;
+
+ init_completion(&dad->builtin_cmcp_threshold_complete);
+
+ /* get sysinfo */
+ dad->si = cmd->request_sysinfo(dev);
+ if (dad->si) {
+ rc = cyttsp5_setup_sysfs(dev);
+ if (rc)
+ goto cyttsp5_device_access_setup_sysfs_failed;
+ } else {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+ __func__, dad->si);
+ cmd->subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_DEVICE_ACCESS_NAME,
+ cyttsp5_setup_sysfs_attention, 0);
+ }
+
+ INIT_WORK(&dad->cmcp_threshold_update,
+ cyttsp5_parse_cmcp_threshold_builtin);
+ schedule_work(&dad->cmcp_threshold_update);
+
+ return 0;
+
+cyttsp5_device_access_setup_sysfs_failed:
+ kfree(cmcp_info->cm_sensor_row_delta);
+cyttsp5_device_access_probe_cm_sensor_row_delta_failed:
+ kfree(cmcp_info->cm_sensor_column_delta);
+cyttsp5_device_access_probe_cm_sensor_column_delta_failed:
+ kfree(cmcp_info->cp_btn_data);
+cyttsp5_device_access_probe_cp_btn_data_failed:
+ kfree(cmcp_info->cm_btn_data);
+cyttsp5_device_access_probe_cm_btn_data_failed:
+ kfree(cmcp_info->cp_rx_cal_data_panel);
+cyttsp5_device_access_probe_cp_rx_cal_data_panel_failed:
+ kfree(cmcp_info->cp_rx_data_panel);
+cyttsp5_device_access_probe_cp_rx_data_panel_failed:
+ kfree(cmcp_info->cp_tx_cal_data_panel);
+cyttsp5_device_access_probe_cp_tx_cal_data_panel_failed:
+ kfree(cmcp_info->cp_tx_data_panel);
+cyttsp5_device_access_probe_cp_tx_data_panel_failed:
+ kfree(cmcp_info->cm_data_panel);
+cyttsp5_device_access_probe_cm_data_panel_failed:
+ kfree(cmcp_info->gd_sensor_row);
+cyttsp5_device_access_probe_gd_sensor_row_failed:
+ kfree(cmcp_info->gd_sensor_col);
+cyttsp5_device_access_probe_gd_sensor_col_failed:
+ kfree(test_case_search_array);
+cyttsp5_device_access_probe_search_array_failed:
+ kfree(test_case_field_array);
+cyttsp5_device_access_probe_field_array_failed:
+ kfree(result);
+cyttsp5_device_access_probe_result_failed:
+ kfree(cmcp_info);
+cyttsp5_device_access_probe_cmcp_info_failed:
+ kfree(configurations);
+cyttsp5_device_access_probe_configs_failed:
+ kfree(dad);
+cyttsp5_device_access_probe_data_failed:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+static void cyttsp5_device_access_release(struct device *dev, void *data)
+{
+ struct cyttsp5_device_access_data *dad = data;
+
+ if (dad->sysfs_nodes_created) {
+ device_remove_file(dev, &dev_attr_command);
+ device_remove_file(dev, &dev_attr_status);
+ device_remove_file(dev, &dev_attr_response);
+ debugfs_remove(dad->cmcp_results_debugfs);
+ debugfs_remove_recursive(dad->base_dentry);
+#ifdef TTHE_TUNER_SUPPORT
+ debugfs_remove(dad->tthe_get_panel_data_debugfs);
+#endif
+ device_remove_file(dev, &dev_attr_cmcp_test);
+ device_remove_file(dev, &dev_attr_cmcp_threshold_loading);
+ device_remove_bin_file(dev, &bin_attr_cmcp_threshold_data);
+ kfree(dad->cmcp_threshold_data);
+ } else {
+ cmd->unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_DEVICE_ACCESS_NAME,
+ cyttsp5_setup_sysfs_attention, 0);
+ }
+
+ kfree(dad->test_search_array);
+ kfree(dad->test_field_array);
+ kfree(dad->configs);
+ cyttsp5_free_cmcp_buf(dad->cmcp_info);
+ kfree(dad->cmcp_info);
+ kfree(dad->result);
+ kfree(dad);
+}
+
+static struct cyttsp5_module device_access_module = {
+ .name = CYTTSP5_DEVICE_ACCESS_NAME,
+ .probe = cyttsp5_device_access_probe,
+ .release = cyttsp5_device_access_release,
+};
+
+static int __init cyttsp5_device_access_init(void)
+{
+ int rc;
+
+ cmd = cyttsp5_get_commands();
+ if (!cmd)
+ return -EINVAL;
+
+ rc = cyttsp5_register_module(&device_access_module);
+ if (rc < 0) {
+ pr_err("%s: Error, failed registering module\n",
+ __func__);
+ return rc;
+ }
+
+ pr_info("%s: Parade TTSP Device Access Driver (Built %s) rc=%d\n",
+ __func__, CY_DRIVER_VERSION, rc);
+ return 0;
+}
+module_init(cyttsp5_device_access_init);
+
+static void __exit cyttsp5_device_access_exit(void)
+{
+ cyttsp5_unregister_module(&device_access_module);
+}
+module_exit(cyttsp5_device_access_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
+
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_devtree.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_devtree.c
new file mode 100644
index 0000000..1f7db9b
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_devtree.c
@@ -0,0 +1,747 @@
+/*
+ * cyttsp5_devtree.c
+ * Parade TrueTouch(TM) Standard Product V5 Device Tree Support Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2013-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+/* cyttsp */
+#include "cyttsp5_regs.h"
+#include <linux/input/cyttsp5_platform.h>
+
+#define ENABLE_VIRTUAL_KEYS
+
+#define MAX_NAME_LENGTH 64
+
+enum cyttsp5_device_type {
+ DEVICE_MT,
+ DEVICE_BTN,
+ DEVICE_PROXIMITY,
+ DEVICE_TYPE_MAX,
+};
+
+struct cyttsp5_device_pdata_func {
+ void * (*create_and_get_pdata)(struct device_node *);
+ void (*free_pdata)(void *);
+};
+
+struct cyttsp5_pdata_ptr {
+ void **pdata;
+};
+
+#ifdef ENABLE_VIRTUAL_KEYS
+static struct kobject *board_properties_kobj;
+
+struct cyttsp5_virtual_keys {
+ struct kobj_attribute kobj_attr;
+ u16 *data;
+ int size;
+};
+#endif
+
+struct cyttsp5_extended_mt_platform_data {
+ struct cyttsp5_mt_platform_data pdata;
+#ifdef ENABLE_VIRTUAL_KEYS
+ struct cyttsp5_virtual_keys vkeys;
+#endif
+};
+
+static inline int get_inp_dev_name(struct device_node *dev_node,
+ const char **inp_dev_name)
+{
+ return of_property_read_string(dev_node, "cy,inp_dev_name",
+ inp_dev_name);
+}
+
+static s16 *create_and_get_u16_array(struct device_node *dev_node,
+ const char *name, int *size)
+{
+ const __be32 *values;
+ s16 *val_array;
+ int len;
+ int sz;
+ int rc;
+ int i;
+
+ values = of_get_property(dev_node, name, &len);
+ if (values == NULL)
+ return NULL;
+
+ sz = len / sizeof(u32);
+ pr_debug("%s: %s size:%d\n", __func__, name, sz);
+
+ val_array = kcalloc(sz, sizeof(s16), GFP_KERNEL);
+ if (!val_array) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < sz; i++)
+ val_array[i] = (s16)be32_to_cpup(values++);
+
+ *size = sz;
+
+ return val_array;
+
+fail:
+ return ERR_PTR(rc);
+}
+
+static struct touch_framework *create_and_get_touch_framework(
+ struct device_node *dev_node)
+{
+ struct touch_framework *frmwrk;
+ s16 *abs;
+ int size;
+ int rc;
+
+ abs = create_and_get_u16_array(dev_node, "cy,abs", &size);
+ if (IS_ERR_OR_NULL(abs))
+ return (void *)abs;
+
+ /* Check for valid abs size */
+ if (size % CY_NUM_ABS_SET) {
+ rc = -EINVAL;
+ goto fail_free_abs;
+ }
+
+ frmwrk = kzalloc(sizeof(*frmwrk), GFP_KERNEL);
+ if (!frmwrk) {
+ rc = -ENOMEM;
+ goto fail_free_abs;
+ }
+
+ frmwrk->abs = abs;
+ frmwrk->size = size;
+
+ return frmwrk;
+
+fail_free_abs:
+ kfree(abs);
+
+ return ERR_PTR(rc);
+}
+
+static void free_touch_framework(struct touch_framework *frmwrk)
+{
+ kfree(frmwrk->abs);
+ kfree(frmwrk);
+}
+
+#ifdef ENABLE_VIRTUAL_KEYS
+#define VIRTUAL_KEY_ELEMENT_SIZE 5
+static ssize_t virtual_keys_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct cyttsp5_virtual_keys *vkeys = container_of(attr,
+ struct cyttsp5_virtual_keys, kobj_attr);
+ u16 *data = vkeys->data;
+ int size = vkeys->size;
+ int index;
+ int i;
+
+ index = 0;
+ for (i = 0; i < size; i += VIRTUAL_KEY_ELEMENT_SIZE)
+ index += scnprintf(buf + index, CY_MAX_PRBUF_SIZE - index,
+ "0x01:%d:%d:%d:%d:%d\n",
+ data[i], data[i+1], data[i+2], data[i+3], data[i+4]);
+
+ return index;
+}
+
+static int setup_virtual_keys(struct device_node *dev_node,
+ const char *inp_dev_name, struct cyttsp5_virtual_keys *vkeys)
+{
+ char *name;
+ u16 *data;
+ int size;
+ int rc;
+
+ data = create_and_get_u16_array(dev_node, "cy,virtual_keys", &size);
+ if (data == NULL)
+ return 0;
+ else if (IS_ERR(data)) {
+ rc = PTR_ERR(data);
+ goto fail;
+ }
+
+ /* Check for valid virtual keys size */
+ if (size % VIRTUAL_KEY_ELEMENT_SIZE) {
+ rc = -EINVAL;
+ goto fail_free_data;
+ }
+
+ name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
+ if (!name) {
+ rc = -ENOMEM;
+ goto fail_free_data;
+ }
+
+ snprintf(name, MAX_NAME_LENGTH, "virtualkeys.%s", inp_dev_name);
+
+ vkeys->data = data;
+ vkeys->size = size;
+
+ /* TODO: Instantiate in board file and export it */
+ if (board_properties_kobj == NULL)
+ board_properties_kobj =
+ kobject_create_and_add("board_properties", NULL);
+ if (board_properties_kobj == NULL) {
+ pr_err("%s: Cannot get board_properties kobject!\n", __func__);
+ rc = -EINVAL;
+ goto fail_free_name;
+ }
+
+ /* Initialize dynamic SysFs attribute */
+ sysfs_attr_init(&vkeys->kobj_attr.attr);
+ vkeys->kobj_attr.attr.name = name;
+ vkeys->kobj_attr.attr.mode = S_IRUGO;
+ vkeys->kobj_attr.show = virtual_keys_show;
+
+ rc = sysfs_create_file(board_properties_kobj, &vkeys->kobj_attr.attr);
+ if (rc)
+ goto fail_del_kobj;
+
+ return 0;
+
+fail_del_kobj:
+ kobject_del(board_properties_kobj);
+fail_free_name:
+ kfree(name);
+ vkeys->kobj_attr.attr.name = NULL;
+fail_free_data:
+ kfree(data);
+ vkeys->data = NULL;
+fail:
+ return rc;
+}
+
+static void free_virtual_keys(struct cyttsp5_virtual_keys *vkeys)
+{
+ if (board_properties_kobj)
+ sysfs_remove_file(board_properties_kobj,
+ &vkeys->kobj_attr.attr);
+
+
+ kobject_del(board_properties_kobj);
+ board_properties_kobj = NULL;
+
+ kfree(vkeys->data);
+ kfree(vkeys->kobj_attr.attr.name);
+}
+#endif
+
+static void *create_and_get_mt_pdata(struct device_node *dev_node)
+{
+ struct cyttsp5_extended_mt_platform_data *ext_pdata;
+ struct cyttsp5_mt_platform_data *pdata;
+ u32 value;
+ int rc;
+
+ ext_pdata = kzalloc(sizeof(*ext_pdata), GFP_KERNEL);
+ if (!ext_pdata) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ pdata = &ext_pdata->pdata;
+
+ rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+ if (rc)
+ goto fail_free_pdata;
+
+ /* Optional fields */
+ rc = of_property_read_u32(dev_node, "cy,flags", &value);
+ if (!rc)
+ pdata->flags = value;
+
+ rc = of_property_read_u32(dev_node, "cy,vkeys_x", &value);
+ if (!rc)
+ pdata->vkeys_x = value;
+
+ rc = of_property_read_u32(dev_node, "cy,vkeys_y", &value);
+ if (!rc)
+ pdata->vkeys_y = value;
+
+ /* Required fields */
+ pdata->frmwrk = create_and_get_touch_framework(dev_node);
+ if (pdata->frmwrk == NULL) {
+ rc = -EINVAL;
+ goto fail_free_pdata;
+ } else if (IS_ERR(pdata->frmwrk)) {
+ rc = PTR_ERR(pdata->frmwrk);
+ goto fail_free_pdata;
+ }
+#ifdef ENABLE_VIRTUAL_KEYS
+ rc = setup_virtual_keys(dev_node, pdata->inp_dev_name,
+ &ext_pdata->vkeys);
+ if (rc) {
+ pr_err("%s: Cannot setup virtual keys!\n", __func__);
+ goto fail_free_pdata;
+ }
+#endif
+ return pdata;
+
+fail_free_pdata:
+ kfree(ext_pdata);
+fail:
+ return ERR_PTR(rc);
+}
+
+static void free_mt_pdata(void *pdata)
+{
+ struct cyttsp5_mt_platform_data *mt_pdata =
+ (struct cyttsp5_mt_platform_data *)pdata;
+ struct cyttsp5_extended_mt_platform_data *ext_mt_pdata =
+ container_of(mt_pdata,
+ struct cyttsp5_extended_mt_platform_data, pdata);
+
+ free_touch_framework(mt_pdata->frmwrk);
+#ifdef ENABLE_VIRTUAL_KEYS
+ free_virtual_keys(&ext_mt_pdata->vkeys);
+#endif
+ kfree(ext_mt_pdata);
+}
+
+static void *create_and_get_btn_pdata(struct device_node *dev_node)
+{
+ struct cyttsp5_btn_platform_data *pdata;
+ int rc;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+ if (rc)
+ goto fail_free_pdata;
+
+ return pdata;
+
+fail_free_pdata:
+ kfree(pdata);
+fail:
+ return ERR_PTR(rc);
+}
+
+static void free_btn_pdata(void *pdata)
+{
+ struct cyttsp5_btn_platform_data *btn_pdata =
+ (struct cyttsp5_btn_platform_data *)pdata;
+
+ kfree(btn_pdata);
+}
+
+static void *create_and_get_proximity_pdata(struct device_node *dev_node)
+{
+ struct cyttsp5_proximity_platform_data *pdata;
+ int rc;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+ if (rc)
+ goto fail_free_pdata;
+
+ pdata->frmwrk = create_and_get_touch_framework(dev_node);
+ if (pdata->frmwrk == NULL) {
+ rc = -EINVAL;
+ goto fail_free_pdata;
+ } else if (IS_ERR(pdata->frmwrk)) {
+ rc = PTR_ERR(pdata->frmwrk);
+ goto fail_free_pdata;
+ }
+
+ return pdata;
+
+fail_free_pdata:
+ kfree(pdata);
+fail:
+ return ERR_PTR(rc);
+}
+
+static void free_proximity_pdata(void *pdata)
+{
+ struct cyttsp5_proximity_platform_data *proximity_pdata =
+ (struct cyttsp5_proximity_platform_data *)pdata;
+
+ free_touch_framework(proximity_pdata->frmwrk);
+
+ kfree(proximity_pdata);
+}
+
+static struct cyttsp5_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = {
+ [DEVICE_MT] = {
+ .create_and_get_pdata = create_and_get_mt_pdata,
+ .free_pdata = free_mt_pdata,
+ },
+ [DEVICE_BTN] = {
+ .create_and_get_pdata = create_and_get_btn_pdata,
+ .free_pdata = free_btn_pdata,
+ },
+ [DEVICE_PROXIMITY] = {
+ .create_and_get_pdata = create_and_get_proximity_pdata,
+ .free_pdata = free_proximity_pdata,
+ },
+};
+
+static struct cyttsp5_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX];
+
+static const char *device_names[DEVICE_TYPE_MAX] = {
+ [DEVICE_MT] = "cy,mt",
+ [DEVICE_BTN] = "cy,btn",
+ [DEVICE_PROXIMITY] = "cy,proximity",
+};
+
+static void set_pdata_ptr(struct cyttsp5_platform_data *pdata)
+{
+ pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata;
+ pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata;
+ pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata;
+}
+
+static int get_device_type(struct device_node *dev_node,
+ enum cyttsp5_device_type *type)
+{
+ const char *name;
+ enum cyttsp5_device_type t;
+ int rc;
+
+ rc = of_property_read_string(dev_node, "name", &name);
+ if (rc)
+ return rc;
+
+ for (t = 0; t < DEVICE_TYPE_MAX; t++)
+ if (!strncmp(name, device_names[t], MAX_NAME_LENGTH)) {
+ *type = t;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static inline void *create_and_get_device_pdata(struct device_node *dev_node,
+ enum cyttsp5_device_type type)
+{
+ return device_pdata_funcs[type].create_and_get_pdata(dev_node);
+}
+
+static inline void free_device_pdata(enum cyttsp5_device_type type)
+{
+ device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata);
+}
+
+static struct touch_settings *create_and_get_touch_setting(
+ struct device_node *core_node, const char *name)
+{
+ struct touch_settings *setting;
+ char *tag_name;
+ u32 tag_value;
+ u16 *data;
+ int size;
+ int rc;
+
+ data = create_and_get_u16_array(core_node, name, &size);
+ if (IS_ERR_OR_NULL(data))
+ return (void *)data;
+
+ pr_debug("%s: Touch setting:'%s' size:%d\n", __func__, name, size);
+
+ setting = kzalloc(sizeof(*setting), GFP_KERNEL);
+ if (!setting) {
+ rc = -ENOMEM;
+ goto fail_free_data;
+ }
+
+ setting->data = (u8 *)data;
+ setting->size = size;
+
+ tag_name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
+ if (!tag_name) {
+ rc = -ENOMEM;
+ goto fail_free_setting;
+ }
+
+ snprintf(tag_name, MAX_NAME_LENGTH, "%s-tag", name);
+
+ rc = of_property_read_u32(core_node, tag_name, &tag_value);
+ if (!rc)
+ setting->tag = tag_value;
+
+ kfree(tag_name);
+
+ return setting;
+
+fail_free_setting:
+ kfree(setting);
+fail_free_data:
+ kfree(data);
+
+ return ERR_PTR(rc);
+}
+
+static void free_touch_setting(struct touch_settings *setting)
+{
+ if (setting) {
+ kfree(setting->data);
+ kfree(setting);
+ }
+}
+
+static char *touch_setting_names[CY_IC_GRPNUM_NUM] = {
+ NULL, /* CY_IC_GRPNUM_RESERVED */
+ "cy,cmd_regs", /* CY_IC_GRPNUM_CMD_REGS */
+ "cy,tch_rep", /* CY_IC_GRPNUM_TCH_REP */
+ "cy,data_rec", /* CY_IC_GRPNUM_DATA_REC */
+ "cy,test_rec", /* CY_IC_GRPNUM_TEST_REC */
+ "cy,pcfg_rec", /* CY_IC_GRPNUM_PCFG_REC */
+ "cy,tch_parm_val", /* CY_IC_GRPNUM_TCH_PARM_VAL */
+ "cy,tch_parm_size", /* CY_IC_GRPNUM_TCH_PARM_SIZE */
+ NULL, /* CY_IC_GRPNUM_RESERVED1 */
+ NULL, /* CY_IC_GRPNUM_RESERVED2 */
+ "cy,opcfg_rec", /* CY_IC_GRPNUM_OPCFG_REC */
+ "cy,ddata_rec", /* CY_IC_GRPNUM_DDATA_REC */
+ "cy,mdata_rec", /* CY_IC_GRPNUM_MDATA_REC */
+ "cy,test_regs", /* CY_IC_GRPNUM_TEST_REGS */
+ "cy,btn_keys", /* CY_IC_GRPNUM_BTN_KEYS */
+ NULL, /* CY_IC_GRPNUM_TTHE_REGS */
+};
+
+static struct cyttsp5_core_platform_data *create_and_get_core_pdata(
+ struct device_node *core_node)
+{
+ struct cyttsp5_core_platform_data *pdata;
+ u32 value;
+ int rc;
+ int i;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ /* Required fields */
+ rc = of_property_read_u32(core_node, "cy,irq_gpio", &value);
+ if (rc)
+ goto fail_free;
+ pdata->irq_gpio = value;
+
+ rc = of_property_read_u32(core_node, "cy,hid_desc_register", &value);
+ if (rc)
+ goto fail_free;
+ pdata->hid_desc_register = value;
+
+ /* Optional fields */
+ /* rst_gpio is optional since a platform may use
+ * power cycling instead of using the XRES pin
+ */
+ rc = of_property_read_u32(core_node, "cy,rst_gpio", &value);
+ if (!rc)
+ pdata->rst_gpio = value;
+
+ rc = of_property_read_u32(core_node, "cy,level_irq_udelay", &value);
+ if (!rc)
+ pdata->level_irq_udelay = value;
+
+ rc = of_property_read_u32(core_node, "cy,vendor_id", &value);
+ if (!rc)
+ pdata->vendor_id = value;
+
+ rc = of_property_read_u32(core_node, "cy,product_id", &value);
+ if (!rc)
+ pdata->product_id = value;
+
+ rc = of_property_read_u32(core_node, "cy,flags", &value);
+ if (!rc)
+ pdata->flags = value;
+
+ rc = of_property_read_u32(core_node, "cy,easy_wakeup_gesture", &value);
+ if (!rc)
+ pdata->easy_wakeup_gesture = (u8)value;
+
+ for (i = 0; (unsigned int)i < ARRAY_SIZE(touch_setting_names); i++) {
+ if (touch_setting_names[i] == NULL)
+ continue;
+
+ pdata->sett[i] = create_and_get_touch_setting(core_node,
+ touch_setting_names[i]);
+ if (IS_ERR(pdata->sett[i])) {
+ rc = PTR_ERR(pdata->sett[i]);
+ goto fail_free_sett;
+ } else if (pdata->sett[i] == NULL)
+ pr_debug("%s: No data for setting '%s'\n", __func__,
+ touch_setting_names[i]);
+ }
+
+ pr_debug("%s: irq_gpio:%d rst_gpio:%d\n"
+ "hid_desc_register:%d level_irq_udelay:%d vendor_id:%d product_id:%d\n"
+ "flags:%d easy_wakeup_gesture:%d\n", __func__,
+ pdata->irq_gpio, pdata->rst_gpio,
+ pdata->hid_desc_register,
+ pdata->level_irq_udelay, pdata->vendor_id, pdata->product_id,
+ pdata->flags, pdata->easy_wakeup_gesture);
+
+ pdata->xres = cyttsp5_xres;
+ pdata->init = cyttsp5_init;
+ pdata->power = cyttsp5_power;
+ pdata->detect = cyttsp5_detect;
+ pdata->irq_stat = cyttsp5_irq_stat;
+
+ return pdata;
+
+fail_free_sett:
+ for (i--; i >= 0; i--)
+ free_touch_setting(pdata->sett[i]);
+fail_free:
+ kfree(pdata);
+fail:
+ return ERR_PTR(rc);
+}
+
+static void free_core_pdata(void *pdata)
+{
+ struct cyttsp5_core_platform_data *core_pdata = pdata;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(touch_setting_names); i++)
+ free_touch_setting(core_pdata->sett[i]);
+ kfree(core_pdata);
+}
+
+int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev)
+{
+ struct cyttsp5_platform_data *pdata;
+ struct device_node *core_node, *dev_node, *dev_node_fail;
+ enum cyttsp5_device_type type;
+ int count = 0;
+ int rc = 0;
+
+ if (!adap_dev->of_node)
+ return 0;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ adap_dev->platform_data = pdata;
+ set_pdata_ptr(pdata);
+
+ /* There should be only one core node */
+ for_each_child_of_node(adap_dev->of_node, core_node) {
+ const char *name;
+
+ rc = of_property_read_string(core_node, "name", &name);
+ if (!rc)
+ pr_debug("%s: name:%s\n", __func__, name);
+
+ pdata->core_pdata = create_and_get_core_pdata(core_node);
+ if (IS_ERR(pdata->core_pdata)) {
+ rc = PTR_ERR(pdata->core_pdata);
+ break;
+ }
+
+ /* Increment reference count */
+ of_node_get(core_node);
+
+ for_each_child_of_node(core_node, dev_node) {
+ count++;
+ rc = get_device_type(dev_node, &type);
+ if (rc)
+ break;
+ *pdata_ptr[type].pdata
+ = create_and_get_device_pdata(dev_node, type);
+ if (IS_ERR(*pdata_ptr[type].pdata))
+ rc = PTR_ERR(*pdata_ptr[type].pdata);
+ if (rc)
+ break;
+ /* Increment reference count */
+ of_node_get(dev_node);
+ }
+
+ if (rc) {
+ free_core_pdata(pdata->core_pdata);
+ of_node_put(core_node);
+ for_each_child_of_node(core_node, dev_node_fail) {
+ if (dev_node == dev_node_fail)
+ break;
+ rc = get_device_type(dev_node, &type);
+ if (rc)
+ break;
+ free_device_pdata(type);
+ of_node_put(dev_node);
+ }
+ break;
+ }
+ pdata->loader_pdata = &_cyttsp5_loader_platform_data;
+ }
+
+ pr_debug("%s: %d child node(s) found\n", __func__, count);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_devtree_create_and_get_pdata);
+
+int cyttsp5_devtree_clean_pdata(struct device *adap_dev)
+{
+ struct cyttsp5_platform_data *pdata;
+ struct device_node *core_node, *dev_node;
+ enum cyttsp5_device_type type;
+ int rc = 0;
+
+ if (!adap_dev->of_node)
+ return 0;
+
+ pdata = dev_get_platdata(adap_dev);
+ set_pdata_ptr(pdata);
+ for_each_child_of_node(adap_dev->of_node, core_node) {
+ free_core_pdata(pdata->core_pdata);
+ of_node_put(core_node);
+ for_each_child_of_node(core_node, dev_node) {
+ rc = get_device_type(dev_node, &type);
+ if (rc)
+ break;
+ free_device_pdata(type);
+ of_node_put(dev_node);
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp5_devtree_clean_pdata);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_i2c.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_i2c.c
new file mode 100644
index 0000000..7f12671
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_i2c.c
@@ -0,0 +1,223 @@
+/*
+ * cyttsp5_i2c.c
+ * Parade TrueTouch(TM) Standard Product V5 I2C Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+#include <linux/i2c.h>
+#include <linux/version.h>
+
+#define CY_I2C_DATA_SIZE (2 * 256)
+
+static int cyttsp5_i2c_read_default(struct device *dev, void *buf, int size)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int rc;
+
+ if (!buf || !size || size > CY_I2C_DATA_SIZE)
+ return -EINVAL;
+
+ rc = i2c_master_recv(client, buf, size);
+
+ return (rc < 0) ? rc : rc != size ? -EIO : 0;
+}
+
+static int cyttsp5_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msgs[2];
+ u8 msg_count = 1;
+ int rc;
+ u32 size;
+
+ if (!buf)
+ return -EINVAL;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+ msgs[0].len = 2;
+ msgs[0].buf = buf;
+ rc = i2c_transfer(client->adapter, msgs, msg_count);
+ if (rc < 0 || rc != msg_count)
+ return (rc < 0) ? rc : -EIO;
+
+ size = get_unaligned_le16(&buf[0]);
+ if (!size || size == 2 || size >= CY_PIP_1P7_EMPTY_BUF)
+ /*
+ * Before PIP 1.7, empty buffer is 0x0002;
+ * From PIP 1.7, empty buffer is 0xFFXX
+ */
+ return 0;
+
+ if (size > max)
+ return -EINVAL;
+
+ rc = i2c_master_recv(client, buf, size);
+
+ return (rc < 0) ? rc : rc != (int)size ? -EIO : 0;
+}
+
+static int cyttsp5_i2c_write_read_specific(struct device *dev, u8 write_len,
+ u8 *write_buf, u8 *read_buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msgs[2];
+ u8 msg_count = 1;
+ int rc;
+
+ if (!write_buf || !write_len)
+ return -EINVAL;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = write_len;
+ msgs[0].buf = write_buf;
+ rc = i2c_transfer(client->adapter, msgs, msg_count);
+
+ if (rc < 0 || rc != msg_count)
+ return (rc < 0) ? rc : -EIO;
+
+ rc = 0;
+
+ if (read_buf)
+ rc = cyttsp5_i2c_read_default_nosize(dev, read_buf,
+ CY_I2C_DATA_SIZE);
+
+ return rc;
+}
+
+static struct cyttsp5_bus_ops cyttsp5_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .read_default = cyttsp5_i2c_read_default,
+ .read_default_nosize = cyttsp5_i2c_read_default_nosize,
+ .write_read_specific = cyttsp5_i2c_write_read_specific,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+static const struct of_device_id cyttsp5_i2c_of_match[] = {
+ { .compatible = "cy,cyttsp5_i2c_adapter", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cyttsp5_i2c_of_match);
+#endif
+
+static int cyttsp5_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct device *dev = &client->dev;
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ const struct of_device_id *match;
+#endif
+ int rc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(dev, "I2C functionality not Supported\n");
+ return -EIO;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ match = of_match_device(of_match_ptr(cyttsp5_i2c_of_match), dev);
+ if (match) {
+ rc = cyttsp5_devtree_create_and_get_pdata(dev);
+ if (rc < 0)
+ return rc;
+ }
+#endif
+
+ rc = cyttsp5_probe(&cyttsp5_i2c_bus_ops, &client->dev, client->irq,
+ CY_I2C_DATA_SIZE);
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ if (rc && match)
+ cyttsp5_devtree_clean_pdata(dev);
+#endif
+
+ return rc;
+}
+
+static int cyttsp5_i2c_remove(struct i2c_client *client)
+{
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ struct device *dev = &client->dev;
+ const struct of_device_id *match;
+#endif
+ struct cyttsp5_core_data *cd = i2c_get_clientdata(client);
+
+ cyttsp5_release(cd);
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ match = of_match_device(of_match_ptr(cyttsp5_i2c_of_match), dev);
+ if (match)
+ cyttsp5_devtree_clean_pdata(dev);
+#endif
+
+ return 0;
+}
+
+static const struct i2c_device_id cyttsp5_i2c_id[] = {
+ { CYTTSP5_I2C_NAME, 0, },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id);
+
+static struct i2c_driver cyttsp5_i2c_driver = {
+ .driver = {
+ .name = CYTTSP5_I2C_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp5_pm_ops,
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ .of_match_table = cyttsp5_i2c_of_match,
+#endif
+ },
+ .probe = cyttsp5_i2c_probe,
+ .remove = cyttsp5_i2c_remove,
+ .id_table = cyttsp5_i2c_id,
+};
+
+#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
+module_i2c_driver(cyttsp5_i2c_driver);
+#else
+static int __init cyttsp5_i2c_init(void)
+{
+ int rc = i2c_add_driver(&cyttsp5_i2c_driver);
+
+ pr_info("%s: Parade TTSP I2C Driver (Built %s) rc=%d\n",
+ __func__, CY_DRIVER_VERSION, rc);
+ return rc;
+}
+module_init(cyttsp5_i2c_init);
+
+static void __exit cyttsp5_i2c_exit(void)
+{
+ i2c_del_driver(&cyttsp5_i2c_driver);
+}
+module_exit(cyttsp5_i2c_exit);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product I2C driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_loader.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_loader.c
new file mode 100644
index 0000000..5b59550
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_loader.c
@@ -0,0 +1,1581 @@
+ /*
+ * cyttsp5_loader.c
+ * Parade TrueTouch(TM) Standard Product V5 FW Loader Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+#include <linux/firmware.h>
+
+#define CYTTSP5_LOADER_NAME "cyttsp5_loader"
+#define CY_FW_MANUAL_UPGRADE_FILE_NAME "cyttsp5_fw_manual_upgrade"
+
+/* Enable UPGRADE_FW_AND_CONFIG_IN_PROBE definition
+ * to perform FW and config upgrade during probe
+ * instead of scheduling a work for it
+ */
+/* #define UPGRADE_FW_AND_CONFIG_IN_PROBE */
+
+#define CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW 1
+#define CYTTSP5_LOADER_FW_UPGRADE_RETRY_COUNT 3
+
+#define CYTTSP5_FW_UPGRADE \
+ (defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE) \
+ || defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE))
+
+#define CYTTSP5_TTCONFIG_UPGRADE \
+ (defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE) \
+ || defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE))
+
+static const u8 cyttsp5_security_key[] = {
+ 0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A
+};
+
+/* Timeout values in ms. */
+#define CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT 500
+#define CY_LDR_SWITCH_TO_APP_MODE_TIMEOUT 300
+
+#define CY_MAX_STATUS_SIZE 32
+
+#define CY_DATA_MAX_ROW_SIZE 256
+#define CY_DATA_ROW_SIZE 128
+
+#define CY_ARRAY_ID_OFFSET 0
+#define CY_ROW_NUM_OFFSET 1
+#define CY_ROW_SIZE_OFFSET 3
+#define CY_ROW_DATA_OFFSET 5
+
+#define CY_POST_TT_CFG_CRC_MASK 0x2
+
+struct cyttsp5_loader_data {
+ struct device *dev;
+ struct cyttsp5_sysinfo *si;
+ u8 status_buf[CY_MAX_STATUS_SIZE];
+ struct completion int_running;
+ struct completion calibration_complete;
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ struct completion builtin_bin_fw_complete;
+ int builtin_bin_fw_status;
+ bool is_manual_upgrade_enabled;
+#endif
+ struct work_struct fw_and_config_upgrade;
+ struct work_struct calibration_work;
+ struct cyttsp5_loader_platform_data *loader_pdata;
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
+ struct mutex config_lock;
+ u8 *config_data;
+ int config_size;
+ bool config_loading;
+#endif
+};
+
+struct cyttsp5_dev_id {
+ u32 silicon_id;
+ u8 rev_id;
+ u32 bl_ver;
+};
+
+struct cyttsp5_hex_image {
+ u8 array_id;
+ u16 row_num;
+ u16 row_size;
+ u8 row_data[CY_DATA_ROW_SIZE];
+} __packed;
+
+static struct cyttsp5_core_commands *cmd;
+
+static struct cyttsp5_module loader_module;
+
+static inline struct cyttsp5_loader_data *cyttsp5_get_loader_data(
+ struct device *dev)
+{
+ return cyttsp5_get_module_data(dev, &loader_module);
+}
+
+#if CYTTSP5_FW_UPGRADE \
+ || defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE)
+static u8 cyttsp5_get_panel_id(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return cd->panel_id;
+}
+#endif
+
+#if CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE
+/*
+ * return code:
+ * -1: Do not upgrade firmware
+ * 0: Version info same, let caller decide
+ * 1: Do a firmware upgrade
+ */
+static int cyttsp5_check_firmware_version(struct device *dev,
+ u32 fw_ver_new, u32 fw_revctrl_new)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ u32 fw_ver_img;
+ u32 fw_revctrl_img;
+
+ fw_ver_img = ld->si->cydata.fw_ver_major << 8;
+ fw_ver_img += ld->si->cydata.fw_ver_minor;
+
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: img vers:0x%04X new vers:0x%04X\n", __func__,
+ fw_ver_img, fw_ver_new);
+
+ if (fw_ver_new > fw_ver_img) {
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: Image is newer, will upgrade\n", __func__);
+ return 1;
+ }
+
+ if (fw_ver_new < fw_ver_img) {
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: Image is older, will NOT upgrade\n", __func__);
+ return -1;
+ }
+
+ fw_revctrl_img = ld->si->cydata.revctrl;
+
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: img revctrl:0x%04X new revctrl:0x%04X\n",
+ __func__, fw_revctrl_img, fw_revctrl_new);
+
+ if (fw_revctrl_new > fw_revctrl_img) {
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: Image is newer, will upgrade\n", __func__);
+ return 1;
+ }
+
+ if (fw_revctrl_new < fw_revctrl_img) {
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: Image is older, will NOT upgrade\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void cyttsp5_calibrate_idacs(struct work_struct *calibration_work)
+{
+ struct cyttsp5_loader_data *ld = container_of(calibration_work,
+ struct cyttsp5_loader_data, calibration_work);
+ struct device *dev = ld->dev;
+ u8 mode;
+ u8 status;
+ int rc;
+
+ rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0)
+ goto exit;
+
+ rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+ if (rc < 0)
+ goto release;
+
+ for (mode = 0; mode < 3; mode++) {
+ rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, mode, &status);
+ if (rc < 0)
+ goto release;
+ }
+
+ rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+ if (rc < 0)
+ goto release;
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Calibration Done\n", __func__);
+
+release:
+ cmd->release_exclusive(dev);
+exit:
+ complete(&ld->calibration_complete);
+}
+
+static int cyttsp5_calibration_attention(struct device *dev)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ int rc = 0;
+
+ schedule_work(&ld->calibration_work);
+
+ cmd->unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_LOADER_NAME,
+ cyttsp5_calibration_attention, 0);
+
+ return rc;
+}
+
+
+#endif /* CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE */
+
+#if CYTTSP5_FW_UPGRADE
+static u8 *cyttsp5_get_row_(struct device *dev, u8 *row_buf,
+ u8 *image_buf, int size)
+{
+ memcpy(row_buf, image_buf, size);
+ return image_buf + size;
+}
+
+static int cyttsp5_ldr_enter_(struct device *dev, struct cyttsp5_dev_id *dev_id)
+{
+ int rc;
+ u8 return_data[8];
+ u8 mode;
+
+ dev_id->silicon_id = 0;
+ dev_id->rev_id = 0;
+ dev_id->bl_ver = 0;
+
+ cmd->request_reset(dev);
+
+ rc = cmd->request_get_mode(dev, 0, &mode);
+ if (rc < 0)
+ return rc;
+
+ if (mode == CY_MODE_UNKNOWN)
+ return -EINVAL;
+
+ if (mode == CY_MODE_OPERATIONAL) {
+ rc = cmd->nonhid_cmd->start_bl(dev, 0);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = cmd->nonhid_cmd->get_bl_info(dev, 0, return_data);
+ if (rc < 0)
+ return rc;
+
+ dev_id->silicon_id = get_unaligned_le32(&return_data[0]);
+ dev_id->rev_id = return_data[4];
+ dev_id->bl_ver = return_data[5] + (return_data[6] << 8)
+ + (return_data[7] << 16);
+
+ return 0;
+}
+
+static int cyttsp5_ldr_init_(struct device *dev,
+ struct cyttsp5_hex_image *row_image)
+{
+ return cmd->nonhid_cmd->initiate_bl(dev, 0, 8,
+ (u8 *)cyttsp5_security_key, row_image->row_size,
+ row_image->row_data);
+}
+
+static int cyttsp5_ldr_parse_row_(struct device *dev, u8 *row_buf,
+ struct cyttsp5_hex_image *row_image)
+{
+ int rc = 0;
+
+ row_image->array_id = row_buf[CY_ARRAY_ID_OFFSET];
+ row_image->row_num = get_unaligned_be16(&row_buf[CY_ROW_NUM_OFFSET]);
+ row_image->row_size = get_unaligned_be16(&row_buf[CY_ROW_SIZE_OFFSET]);
+
+ if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) {
+ dev_err(dev, "%s: row data buffer overflow\n", __func__);
+ rc = -EOVERFLOW;
+ goto cyttsp5_ldr_parse_row_exit;
+ }
+
+ memcpy(row_image->row_data, &row_buf[CY_ROW_DATA_OFFSET],
+ row_image->row_size);
+cyttsp5_ldr_parse_row_exit:
+ return rc;
+}
+
+static int cyttsp5_ldr_prog_row_(struct device *dev,
+ struct cyttsp5_hex_image *row_image)
+{
+ u16 length = row_image->row_size + 3;
+ u8 data[3 + row_image->row_size];
+ u8 offset = 0;
+
+ data[offset++] = row_image->array_id;
+ data[offset++] = LOW_BYTE(row_image->row_num);
+ data[offset++] = HI_BYTE(row_image->row_num);
+ memcpy(data + 3, row_image->row_data, row_image->row_size);
+ return cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data);
+}
+
+static int cyttsp5_ldr_verify_chksum_(struct device *dev)
+{
+ u8 result;
+ int rc;
+
+ rc = cmd->nonhid_cmd->verify_app_integrity(dev, 0, &result);
+ if (rc)
+ return rc;
+
+ /* fail */
+ if (result == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int cyttsp5_ldr_exit_(struct device *dev)
+{
+ return cmd->nonhid_cmd->launch_app(dev, 0);
+}
+
+static int cyttsp5_load_app_(struct device *dev, const u8 *fw, int fw_size)
+{
+ struct cyttsp5_dev_id *dev_id;
+ struct cyttsp5_hex_image *row_image;
+ u8 *row_buf;
+ size_t image_rec_size;
+ size_t row_buf_size = CY_DATA_MAX_ROW_SIZE;
+ int row_count = 0;
+ u8 *p;
+ u8 *last_row;
+ int rc;
+ int rc_tmp;
+
+ image_rec_size = sizeof(struct cyttsp5_hex_image);
+ if (fw_size % image_rec_size != 0) {
+ dev_err(dev, "%s: Firmware image is misaligned\n", __func__);
+ rc = -EINVAL;
+ goto _cyttsp5_load_app_error;
+ }
+
+ dev_info(dev, "%s: start load app\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+ cmd->request_tthe_print(dev, NULL, 0, "start load app");
+#endif
+
+ row_buf = kzalloc(row_buf_size, GFP_KERNEL);
+ row_image = kzalloc(sizeof(struct cyttsp5_hex_image), GFP_KERNEL);
+ dev_id = kzalloc(sizeof(struct cyttsp5_dev_id), GFP_KERNEL);
+ if (!row_buf || !row_image || !dev_id) {
+ rc = -ENOMEM;
+ goto _cyttsp5_load_app_exit;
+ }
+
+ cmd->request_stop_wd(dev);
+
+ dev_info(dev, "%s: Send BL Loader Enter\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+ cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Enter");
+#endif
+ rc = cyttsp5_ldr_enter_(dev, dev_id);
+ if (rc) {
+ dev_err(dev, "%s: Error cannot start Loader (ret=%d)\n",
+ __func__, rc);
+ goto _cyttsp5_load_app_exit;
+ }
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: dev: silicon id=%08X rev=%02X bl=%08X\n",
+ __func__, dev_id->silicon_id,
+ dev_id->rev_id, dev_id->bl_ver);
+
+ /* get last row */
+ last_row = (u8 *)fw + fw_size - image_rec_size;
+ cyttsp5_get_row_(dev, row_buf, last_row, image_rec_size);
+ cyttsp5_ldr_parse_row_(dev, row_buf, row_image);
+
+ /* initialise bootloader */
+ rc = cyttsp5_ldr_init_(dev, row_image);
+ if (rc) {
+ dev_err(dev, "%s: Error cannot init Loader (ret=%d)\n",
+ __func__, rc);
+ goto _cyttsp5_load_app_exit;
+ }
+
+ dev_info(dev, "%s: Send BL Loader Blocks\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+ cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Blocks");
+#endif
+ p = (u8 *)fw;
+ while (p < last_row) {
+ /* Get row */
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: read row=%d\n",
+ __func__, ++row_count);
+ memset(row_buf, 0, row_buf_size);
+ p = cyttsp5_get_row_(dev, row_buf, p, image_rec_size);
+
+ /* Parse row */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: p=%p buf=%p buf[0]=%02X\n",
+ __func__, p, row_buf, row_buf[0]);
+ rc = cyttsp5_ldr_parse_row_(dev, row_buf, row_image);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: array_id=%02X row_num=%04X(%d) row_size=%04X(%d)\n",
+ __func__, row_image->array_id,
+ row_image->row_num, row_image->row_num,
+ row_image->row_size, row_image->row_size);
+ if (rc) {
+ dev_err(dev, "%s: Parse Row Error (a=%d r=%d ret=%d\n",
+ __func__, row_image->array_id,
+ row_image->row_num, rc);
+ goto _cyttsp5_load_app_exit;
+ } else {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Parse Row (a=%d r=%d ret=%d\n",
+ __func__, row_image->array_id,
+ row_image->row_num, rc);
+ }
+
+ /* program row */
+ rc = cyttsp5_ldr_prog_row_(dev, row_image);
+ if (rc) {
+ dev_err(dev, "%s: Program Row Error (array=%d row=%d ret=%d)\n",
+ __func__, row_image->array_id,
+ row_image->row_num, rc);
+ goto _cyttsp5_load_app_exit;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: array=%d row_cnt=%d row_num=%04X\n",
+ __func__, row_image->array_id, row_count,
+ row_image->row_num);
+ }
+
+ /* exit loader */
+ dev_info(dev, "%s: Send BL Loader Terminate\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+ cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Terminate");
+#endif
+ rc = cyttsp5_ldr_exit_(dev);
+ if (rc) {
+ dev_err(dev, "%s: Error on exit Loader (ret=%d)\n",
+ __func__, rc);
+
+ /* verify app checksum */
+ rc_tmp = cyttsp5_ldr_verify_chksum_(dev);
+ if (rc_tmp)
+ dev_err(dev, "%s: ldr_verify_chksum fail r=%d\n",
+ __func__, rc_tmp);
+ else
+ dev_info(dev, "%s: APP Checksum Verified\n", __func__);
+ }
+
+_cyttsp5_load_app_exit:
+ kfree(row_buf);
+ kfree(row_image);
+ kfree(dev_id);
+_cyttsp5_load_app_error:
+ return rc;
+}
+
+static int cyttsp5_upgrade_firmware(struct device *dev, const u8 *fw_img,
+ int fw_size)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ int retry = CYTTSP5_LOADER_FW_UPGRADE_RETRY_COUNT;
+ bool wait_for_calibration_complete = false;
+ int rc;
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0)
+ goto exit;
+
+ while (retry--) {
+ rc = cyttsp5_load_app_(dev, fw_img, fw_size);
+ if (rc < 0)
+ dev_err(dev, "%s: Firmware update failed rc=%d, retry:%d\n",
+ __func__, rc, retry);
+ else
+ break;
+ msleep(20);
+ }
+ if (rc < 0) {
+ dev_err(dev, "%s: Firmware update failed with error code %d\n",
+ __func__, rc);
+ } else if (ld->loader_pdata &&
+ (ld->loader_pdata->flags
+ & CY_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+ reinit_completion(&ld->calibration_complete);
+#else
+ INIT_COMPLETION(ld->calibration_complete);
+#endif
+ /* set up call back for startup */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Adding callback for calibration\n",
+ __func__);
+ rc = cmd->subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_LOADER_NAME, cyttsp5_calibration_attention, 0);
+ if (rc) {
+ dev_err(dev, "%s: Failed adding callback for calibration\n",
+ __func__);
+ dev_err(dev, "%s: No calibration will be performed\n",
+ __func__);
+ rc = 0;
+ } else
+ wait_for_calibration_complete = true;
+ }
+
+ cmd->release_exclusive(dev);
+
+exit:
+ if (!rc)
+ cmd->request_restart(dev, true);
+
+ pm_runtime_put_sync(dev);
+
+ if (wait_for_calibration_complete)
+ wait_for_completion(&ld->calibration_complete);
+
+ return rc;
+}
+
+static int cyttsp5_loader_attention(struct device *dev)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+
+ complete(&ld->int_running);
+ return 0;
+}
+#endif /* CYTTSP5_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+static int cyttsp5_check_firmware_version_platform(struct device *dev,
+ struct cyttsp5_touch_firmware *fw)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ u32 fw_ver_new;
+ u32 fw_revctrl_new;
+ int upgrade;
+
+ if (!ld->si) {
+ dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
+ __func__);
+ return CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW;
+ }
+
+ fw_ver_new = get_unaligned_be16(fw->ver + 2);
+ /* 4 middle bytes are not used */
+ fw_revctrl_new = get_unaligned_be32(fw->ver + 8);
+
+ upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new,
+ fw_revctrl_new);
+
+ if (upgrade > 0)
+ return 1;
+
+ return 0;
+}
+
+static struct cyttsp5_touch_firmware *cyttsp5_get_platform_firmware(
+ struct device *dev)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ struct cyttsp5_touch_firmware **fws;
+ struct cyttsp5_touch_firmware *fw;
+ u8 panel_id;
+
+ panel_id = cyttsp5_get_panel_id(dev);
+ if (panel_id == PANEL_ID_NOT_ENABLED) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Panel ID not enabled, using legacy firmware\n",
+ __func__);
+ return ld->loader_pdata->fw;
+ }
+
+ fws = ld->loader_pdata->fws;
+ if (!fws) {
+ dev_err(dev, "%s: No firmwares provided\n", __func__);
+ return NULL;
+ }
+
+ /* Find FW according to the Panel ID */
+ while ((fw = *fws++)) {
+ if (fw->panel_id == panel_id) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Found matching fw:%p with Panel ID: 0x%02X\n",
+ __func__, fw, fw->panel_id);
+ return fw;
+ }
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Found mismatching fw:%p with Panel ID: 0x%02X\n",
+ __func__, fw, fw->panel_id);
+ }
+
+ return NULL;
+}
+
+static int upgrade_firmware_from_platform(struct device *dev,
+ bool forced)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ struct cyttsp5_touch_firmware *fw;
+ int rc = -ENODEV;
+ int upgrade;
+
+ if (!ld->loader_pdata) {
+ dev_err(dev, "%s: No loader platform data\n", __func__);
+ return rc;
+ }
+
+ fw = cyttsp5_get_platform_firmware(dev);
+ if (!fw || !fw->img || !fw->size) {
+ dev_err(dev, "%s: No platform firmware\n", __func__);
+ return rc;
+ }
+
+ if (!fw->ver || !fw->vsize) {
+ dev_err(dev, "%s: No platform firmware version\n",
+ __func__);
+ return rc;
+ }
+
+ if (forced)
+ upgrade = forced;
+ else
+ upgrade = cyttsp5_check_firmware_version_platform(dev, fw);
+
+ if (upgrade)
+ return cyttsp5_upgrade_firmware(dev, fw->img, fw->size);
+
+ return rc;
+}
+#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+static void _cyttsp5_firmware_cont(const struct firmware *fw, void *context)
+{
+ struct device *dev = context;
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ u8 header_size = 0;
+
+ if (!fw)
+ goto cyttsp5_firmware_cont_exit;
+
+ if (!fw->data || !fw->size) {
+ dev_err(dev, "%s: No firmware received\n", __func__);
+ goto cyttsp5_firmware_cont_release_exit;
+ }
+
+ header_size = fw->data[0];
+ if (header_size >= (fw->size + 1)) {
+ dev_err(dev, "%s: Firmware format is invalid\n", __func__);
+ goto cyttsp5_firmware_cont_release_exit;
+ }
+
+ cyttsp5_upgrade_firmware(dev, &(fw->data[header_size + 1]),
+ fw->size - (header_size + 1));
+
+cyttsp5_firmware_cont_release_exit:
+ release_firmware(fw);
+
+cyttsp5_firmware_cont_exit:
+ ld->is_manual_upgrade_enabled = 0;
+}
+
+static int cyttsp5_check_firmware_version_builtin(struct device *dev,
+ const struct firmware *fw)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ u32 fw_ver_new;
+ u32 fw_revctrl_new;
+ int upgrade;
+
+ if (!ld->si) {
+ dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
+ __func__);
+ return CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW;
+ }
+
+ fw_ver_new = get_unaligned_be16(fw->data + 3);
+ /* 4 middle bytes are not used */
+ fw_revctrl_new = get_unaligned_be32(fw->data + 9);
+
+ upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new,
+ fw_revctrl_new);
+
+ if (upgrade > 0)
+ return 1;
+
+ return 0;
+}
+
+static void _cyttsp5_firmware_cont_builtin(const struct firmware *fw,
+ void *context)
+{
+ struct device *dev = context;
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ int upgrade;
+
+ if (!fw) {
+ dev_info(dev, "%s: No builtin firmware\n", __func__);
+ goto _cyttsp5_firmware_cont_builtin_exit;
+ }
+
+ if (!fw->data || !fw->size) {
+ dev_err(dev, "%s: Invalid builtin firmware\n", __func__);
+ goto _cyttsp5_firmware_cont_builtin_exit;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Found firmware\n", __func__);
+
+ upgrade = cyttsp5_check_firmware_version_builtin(dev, fw);
+ if (upgrade) {
+ _cyttsp5_firmware_cont(fw, dev);
+ ld->builtin_bin_fw_status = 0;
+ complete(&ld->builtin_bin_fw_complete);
+ return;
+ }
+
+_cyttsp5_firmware_cont_builtin_exit:
+ release_firmware(fw);
+
+ ld->builtin_bin_fw_status = -EINVAL;
+ complete(&ld->builtin_bin_fw_complete);
+}
+
+static int upgrade_firmware_from_class(struct device *dev)
+{
+ int retval;
+
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Enabling firmware class loader\n", __func__);
+
+ retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+ CY_FW_MANUAL_UPGRADE_FILE_NAME, dev, GFP_KERNEL, dev,
+ _cyttsp5_firmware_cont);
+ if (retval < 0) {
+ dev_err(dev, "%s: Fail request firmware class file load\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+/*
+ * Generates binary FW filename as following:
+ * - Panel ID not enabled: cyttsp5_fw.bin
+ * - Panel ID enabled: cyttsp5_fw_pidXX.bin
+ */
+static char *generate_firmware_filename(struct device *dev)
+{
+ char *filename;
+ u8 panel_id;
+
+#define FILENAME_LEN_MAX 64
+ filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL);
+ if (!filename)
+ return NULL;
+
+ panel_id = cyttsp5_get_panel_id(dev);
+ if (panel_id == PANEL_ID_NOT_ENABLED)
+ snprintf(filename, FILENAME_LEN_MAX, "%s", CY_FW_FILE_NAME);
+ else
+ snprintf(filename, FILENAME_LEN_MAX, "%s_pid%02X%s",
+ CY_FW_FILE_PREFIX, panel_id, CY_FW_FILE_SUFFIX);
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Filename: %s\n",
+ __func__, filename);
+
+ return filename;
+}
+
+static int upgrade_firmware_from_builtin(struct device *dev)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ char *filename;
+ int retval;
+
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Enabling firmware class loader built-in\n",
+ __func__);
+
+ filename = generate_firmware_filename(dev);
+ if (!filename)
+ return -ENOMEM;
+
+ retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ filename, dev, GFP_KERNEL, dev,
+ _cyttsp5_firmware_cont_builtin);
+ if (retval < 0) {
+ dev_err(dev, "%s: Fail request firmware class file load\n",
+ __func__);
+ goto exit;
+ }
+
+ /* wait until FW binary upgrade finishes */
+ wait_for_completion(&ld->builtin_bin_fw_complete);
+
+ retval = ld->builtin_bin_fw_status;
+
+exit:
+ kfree(filename);
+
+ return retval;
+}
+#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE */
+
+#if CYTTSP5_TTCONFIG_UPGRADE
+static int cyttsp5_write_config_row_(struct device *dev, u8 ebid,
+ u16 row_number, u16 row_size, u8 *data)
+{
+ int rc;
+ u16 actual_write_len;
+
+ rc = cmd->nonhid_cmd->write_conf_block(dev, 0, row_number,
+ row_size, ebid, data, (u8 *)cyttsp5_security_key,
+ &actual_write_len);
+ if (rc) {
+ dev_err(dev, "%s: Fail Put EBID=%d row=%d cmd fail r=%d\n",
+ __func__, ebid, row_number, rc);
+ return rc;
+ }
+
+ if (actual_write_len != row_size) {
+ dev_err(dev, "%s: Fail Put EBID=%d row=%d wrong write size=%d\n",
+ __func__, ebid, row_number, actual_write_len);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int cyttsp5_upgrade_ttconfig(struct device *dev,
+ const u8 *ttconfig_data, int ttconfig_size)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ bool wait_for_calibration_complete = false;
+ u8 ebid = CY_TCH_PARM_EBID;
+ u16 row_size = CY_DATA_ROW_SIZE;
+ u16 table_size;
+ u16 row_count;
+ u16 residue;
+ u8 *row_buf;
+ u8 verify_crc_status;
+ u16 calculated_crc;
+ u16 stored_crc;
+ int rc = 0;
+ int i;
+
+ table_size = ttconfig_size;
+ row_count = table_size / row_size;
+ row_buf = (u8 *)ttconfig_data;
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: size:%d row_size=%d row_count=%d\n",
+ __func__, table_size, row_size, row_count);
+
+ pm_runtime_get_sync(dev);
+
+ rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0)
+ goto exit;
+
+ rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+ if (rc < 0)
+ goto release;
+
+ for (i = 0; i < row_count; i++) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: row=%d size=%d\n",
+ __func__, i, row_size);
+ rc = cyttsp5_write_config_row_(dev, ebid, i, row_size,
+ row_buf);
+ if (rc) {
+ dev_err(dev, "%s: Fail put row=%d r=%d\n",
+ __func__, i, rc);
+ break;
+ }
+ row_buf += row_size;
+ }
+ if (!rc) {
+ residue = table_size % row_size;
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: row=%d size=%d\n",
+ __func__, i, residue);
+ rc = cyttsp5_write_config_row_(dev, ebid, i, residue,
+ row_buf);
+ row_count++;
+ if (rc)
+ dev_err(dev, "%s: Fail put row=%d r=%d\n",
+ __func__, i, rc);
+ }
+
+ if (!rc)
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: TT_CFG updated: rows:%d bytes:%d\n",
+ __func__, row_count, table_size);
+
+ rc = cmd->nonhid_cmd->verify_config_block_crc(dev, 0, ebid,
+ &verify_crc_status, &calculated_crc, &stored_crc);
+ if (rc || verify_crc_status)
+ dev_err(dev, "%s: CRC Failed, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
+ __func__, ebid, verify_crc_status,
+ calculated_crc, stored_crc);
+ else
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: CRC PASS, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
+ __func__, ebid, verify_crc_status,
+ calculated_crc, stored_crc);
+
+ rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+ if (rc < 0)
+ goto release;
+
+ if (ld->loader_pdata &&
+ (ld->loader_pdata->flags
+ & CY_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+ reinit_completion(&ld->calibration_complete);
+#else
+ INIT_COMPLETION(ld->calibration_complete);
+#endif
+ /* set up call back for startup */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Adding callback for calibration\n",
+ __func__);
+ rc = cmd->subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_LOADER_NAME, cyttsp5_calibration_attention, 0);
+ if (rc) {
+ dev_err(dev, "%s: Failed adding callback for calibration\n",
+ __func__);
+ dev_err(dev, "%s: No calibration will be performed\n",
+ __func__);
+ rc = 0;
+ } else
+ wait_for_calibration_complete = true;
+ }
+
+release:
+ cmd->release_exclusive(dev);
+
+exit:
+ if (!rc)
+ cmd->request_restart(dev, true);
+
+ pm_runtime_put_sync(dev);
+
+ if (wait_for_calibration_complete)
+ wait_for_completion(&ld->calibration_complete);
+
+ return rc;
+}
+#endif /* CYTTSP5_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
+static int cyttsp5_get_ttconfig_crc(struct device *dev,
+ const u8 *ttconfig_data, int ttconfig_size, u16 *crc)
+{
+ u16 crc_loc;
+
+ crc_loc = get_unaligned_le16(&ttconfig_data[2]);
+ if (ttconfig_size < crc_loc + 2)
+ return -EINVAL;
+
+ *crc = get_unaligned_le16(&ttconfig_data[crc_loc]);
+
+ return 0;
+}
+
+static int cyttsp5_get_ttconfig_version(struct device *dev,
+ const u8 *ttconfig_data, int ttconfig_size, u16 *version)
+{
+ if (ttconfig_size < CY_TTCONFIG_VERSION_OFFSET
+ + CY_TTCONFIG_VERSION_SIZE)
+ return -EINVAL;
+
+ *version = get_unaligned_le16(
+ &ttconfig_data[CY_TTCONFIG_VERSION_OFFSET]);
+
+ return 0;
+}
+
+static int cyttsp5_check_ttconfig_version(struct device *dev,
+ const u8 *ttconfig_data, int ttconfig_size)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ u16 cfg_crc_new;
+ int rc;
+
+ if (!ld->si)
+ return 0;
+
+ /* Check for config version */
+ if (ld->loader_pdata->flags &
+ CY_LOADER_FLAG_CHECK_TTCONFIG_VERSION) {
+ u16 cfg_ver_new;
+
+ rc = cyttsp5_get_ttconfig_version(dev, ttconfig_data,
+ ttconfig_size, &cfg_ver_new);
+ if (rc)
+ return 0;
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: img_ver:0x%04X new_ver:0x%04X\n",
+ __func__, ld->si->cydata.fw_ver_conf, cfg_ver_new);
+
+ /* Check if config version is newer */
+ if (cfg_ver_new > ld->si->cydata.fw_ver_conf) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Config version newer, will upgrade\n",
+ __func__);
+ return 1;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Config version is identical or older, will NOT upgrade\n",
+ __func__);
+ /* Check for config CRC */
+ } else {
+ rc = cyttsp5_get_ttconfig_crc(dev, ttconfig_data,
+ ttconfig_size, &cfg_crc_new);
+ if (rc)
+ return 0;
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: img_crc:0x%04X new_crc:0x%04X\n",
+ __func__, ld->si->ttconfig.crc, cfg_crc_new);
+
+ if (cfg_crc_new != ld->si->ttconfig.crc) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Config CRC different, will upgrade\n",
+ __func__);
+ return 1;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Config CRC equal, will NOT upgrade\n",
+ __func__);
+ }
+
+ return 0;
+}
+
+static int cyttsp5_check_ttconfig_version_platform(struct device *dev,
+ struct cyttsp5_touch_config *ttconfig)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ u32 fw_ver_config;
+ u32 fw_revctrl_config;
+
+ if (!ld->si) {
+ dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
+ __func__);
+ return 0;
+ }
+
+ fw_ver_config = get_unaligned_be16(ttconfig->fw_ver + 2);
+ /* 4 middle bytes are not used */
+ fw_revctrl_config = get_unaligned_be32(ttconfig->fw_ver + 8);
+
+ /* FW versions should match */
+ if (cyttsp5_check_firmware_version(dev, fw_ver_config,
+ fw_revctrl_config)) {
+ dev_err(dev, "%s: FW versions mismatch\n", __func__);
+ return 0;
+ }
+
+ /* Check PowerOn Self Test, TT_CFG CRC bit */
+ if ((ld->si->cydata.post_code & CY_POST_TT_CFG_CRC_MASK) == 0) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: POST, TT_CFG failed (%X), will upgrade\n",
+ __func__, ld->si->cydata.post_code);
+ return 1;
+ }
+
+ return cyttsp5_check_ttconfig_version(dev, ttconfig->param_regs->data,
+ ttconfig->param_regs->size);
+}
+
+static struct cyttsp5_touch_config *cyttsp5_get_platform_ttconfig(
+ struct device *dev)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ struct cyttsp5_touch_config **ttconfigs;
+ struct cyttsp5_touch_config *ttconfig;
+ u8 panel_id;
+
+ panel_id = cyttsp5_get_panel_id(dev);
+ if (panel_id == PANEL_ID_NOT_ENABLED) {
+ /* TODO: Make debug message */
+ dev_info(dev, "%s: Panel ID not enabled, using legacy ttconfig\n",
+ __func__);
+ return ld->loader_pdata->ttconfig;
+ }
+
+ ttconfigs = ld->loader_pdata->ttconfigs;
+ if (!ttconfigs)
+ return NULL;
+
+ /* Find TT config according to the Panel ID */
+ while ((ttconfig = *ttconfigs++)) {
+ if (ttconfig->panel_id == panel_id) {
+ /* TODO: Make debug message */
+ dev_info(dev, "%s: Found matching ttconfig:%p with Panel ID: 0x%02X\n",
+ __func__, ttconfig, ttconfig->panel_id);
+ return ttconfig;
+ }
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Found mismatching ttconfig:%p with Panel ID: 0x%02X\n",
+ __func__, ttconfig, ttconfig->panel_id);
+ }
+
+ return NULL;
+}
+
+static int upgrade_ttconfig_from_platform(struct device *dev)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ struct cyttsp5_touch_config *ttconfig;
+ struct touch_settings *param_regs;
+ struct cyttsp5_touch_fw;
+ int rc = -ENODEV;
+ int upgrade;
+
+ if (!ld->loader_pdata) {
+ dev_info(dev, "%s: No loader platform data\n", __func__);
+ return rc;
+ }
+
+ ttconfig = cyttsp5_get_platform_ttconfig(dev);
+ if (!ttconfig) {
+ dev_info(dev, "%s: No ttconfig data\n", __func__);
+ return rc;
+ }
+
+ param_regs = ttconfig->param_regs;
+ if (!param_regs) {
+ dev_info(dev, "%s: No touch parameters\n", __func__);
+ return rc;
+ }
+
+ if (!param_regs->data || !param_regs->size) {
+ dev_info(dev, "%s: Invalid touch parameters\n", __func__);
+ return rc;
+ }
+
+ if (!ttconfig->fw_ver || !ttconfig->fw_vsize) {
+ dev_info(dev, "%s: Invalid FW version for touch parameters\n",
+ __func__);
+ return rc;
+ }
+
+ upgrade = cyttsp5_check_ttconfig_version_platform(dev, ttconfig);
+ if (upgrade)
+ return cyttsp5_upgrade_ttconfig(dev, param_regs->data,
+ param_regs->size);
+
+ return rc;
+}
+#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
+static ssize_t cyttsp5_config_data_write(struct file *filp,
+ struct kobject *kobj, struct bin_attribute *bin_attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cyttsp5_loader_data *data = cyttsp5_get_loader_data(dev);
+ u8 *p;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: offset:%lld count:%zu\n",
+ __func__, offset, count);
+
+ mutex_lock(&data->config_lock);
+
+ if (!data->config_loading) {
+ mutex_unlock(&data->config_lock);
+ return -ENODEV;
+ }
+
+ p = krealloc(data->config_data, offset + count, GFP_KERNEL);
+ if (!p) {
+ kfree(data->config_data);
+ data->config_data = NULL;
+ mutex_unlock(&data->config_lock);
+ return -ENOMEM;
+ }
+ data->config_data = p;
+
+ memcpy(&data->config_data[offset], buf, count);
+ data->config_size += count;
+
+ mutex_unlock(&data->config_lock);
+
+ return count;
+}
+
+static struct bin_attribute bin_attr_config_data = {
+ .attr = {
+ .name = "config_data",
+ .mode = S_IWUSR,
+ },
+ .size = 0,
+ .write = cyttsp5_config_data_write,
+};
+
+static ssize_t cyttsp5_config_loading_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ bool config_loading;
+
+ mutex_lock(&ld->config_lock);
+ config_loading = ld->config_loading;
+ mutex_unlock(&ld->config_lock);
+
+ return sprintf(buf, "%d\n", config_loading);
+}
+
+static int cyttsp5_verify_ttconfig_binary(struct device *dev,
+ u8 *bin_config_data, int bin_config_size, u8 **start, int *len)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ int header_size;
+ u16 config_size;
+ u32 fw_ver_config;
+ u32 fw_revctrl_config;
+
+ if (!ld->si) {
+ dev_err(dev, "%s: No firmware infomation found, device FW may be corrupted\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * We need 11 bytes for FW version control info and at
+ * least 6 bytes in config (Length + Max Length + CRC)
+ */
+ header_size = bin_config_data[0] + 1;
+ if (header_size < 11 || header_size >= bin_config_size - 6) {
+ dev_err(dev, "%s: Invalid header size %d\n", __func__,
+ header_size);
+ return -EINVAL;
+ }
+
+ fw_ver_config = get_unaligned_be16(&bin_config_data[1]);
+ /* 4 middle bytes are not used */
+ fw_revctrl_config = get_unaligned_be32(&bin_config_data[7]);
+
+ /* FW versions should match */
+ if (cyttsp5_check_firmware_version(dev, fw_ver_config,
+ fw_revctrl_config)) {
+ dev_err(dev, "%s: FW versions mismatch\n", __func__);
+ return -EINVAL;
+ }
+
+ config_size = get_unaligned_le16(&bin_config_data[header_size]);
+ /* Perform a simple size check (2 bytes for CRC) */
+ if (config_size != bin_config_size - header_size - 2) {
+ dev_err(dev, "%s: Config size invalid\n", __func__);
+ return -EINVAL;
+ }
+
+ *start = &bin_config_data[header_size];
+ *len = bin_config_size - header_size;
+
+ return 0;
+}
+
+/*
+ * 1: Start loading TT Config
+ * 0: End loading TT Config and perform upgrade
+ *-1: Exit loading
+ */
+static ssize_t cyttsp5_config_loading_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ long value;
+ u8 *start;
+ int length;
+ int rc;
+
+ rc = kstrtol(buf, 10, &value);
+ if (rc < 0 || value < -1 || value > 1) {
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ return size;
+ }
+
+ mutex_lock(&ld->config_lock);
+
+ if (value == 1)
+ ld->config_loading = true;
+ else if (value == -1)
+ ld->config_loading = false;
+ else if (value == 0 && ld->config_loading) {
+ ld->config_loading = false;
+ if (ld->config_size == 0) {
+ dev_err(dev, "%s: No config data\n", __func__);
+ goto exit_free;
+ }
+
+ rc = cyttsp5_verify_ttconfig_binary(dev,
+ ld->config_data, ld->config_size,
+ &start, &length);
+ if (rc)
+ goto exit_free;
+
+ rc = cyttsp5_upgrade_ttconfig(dev, start, length);
+ }
+
+exit_free:
+ kfree(ld->config_data);
+ ld->config_data = NULL;
+ ld->config_size = 0;
+
+ mutex_unlock(&ld->config_lock);
+
+ if (rc)
+ return rc;
+
+ return size;
+}
+
+static DEVICE_ATTR(config_loading, S_IRUSR | S_IWUSR,
+ cyttsp5_config_loading_show, cyttsp5_config_loading_store);
+#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE */
+
+static void cyttsp5_fw_and_config_upgrade(
+ struct work_struct *fw_and_config_upgrade)
+{
+ struct cyttsp5_loader_data *ld = container_of(fw_and_config_upgrade,
+ struct cyttsp5_loader_data, fw_and_config_upgrade);
+ struct device *dev = ld->dev;
+
+ ld->si = cmd->request_sysinfo(dev);
+ if (!ld->si)
+ dev_err(dev, "%s: Fail get sysinfo pointer from core\n",
+ __func__);
+#if !CYTTSP5_FW_UPGRADE
+ dev_info(dev, "%s: No FW upgrade method selected!\n", __func__);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+ if (!upgrade_firmware_from_platform(dev, false))
+ return;
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ if (!upgrade_firmware_from_builtin(dev))
+ return;
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
+ if (!upgrade_ttconfig_from_platform(dev))
+ return;
+#endif
+}
+
+#if CYTTSP5_FW_UPGRADE
+static int cyttsp5_fw_upgrade_cb(struct device *dev)
+{
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+ if (!upgrade_firmware_from_platform(dev, false))
+ return 1;
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ if (!upgrade_firmware_from_builtin(dev))
+ return 1;
+#endif
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+static ssize_t cyttsp5_forced_upgrade_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int rc = upgrade_firmware_from_platform(dev, true);
+
+ if (rc)
+ return rc;
+ return size;
+}
+
+static DEVICE_ATTR(forced_upgrade, S_IWUSR,
+ NULL, cyttsp5_forced_upgrade_store);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+static ssize_t cyttsp5_manual_upgrade_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev);
+ int rc;
+
+ if (ld->is_manual_upgrade_enabled)
+ return -EBUSY;
+
+ ld->is_manual_upgrade_enabled = 1;
+
+ rc = upgrade_firmware_from_class(ld->dev);
+
+ if (rc < 0)
+ ld->is_manual_upgrade_enabled = 0;
+
+ return size;
+}
+
+static DEVICE_ATTR(manual_upgrade, S_IWUSR,
+ NULL, cyttsp5_manual_upgrade_store);
+#endif
+
+static int cyttsp5_loader_probe(struct device *dev, void **data)
+{
+ struct cyttsp5_loader_data *ld;
+ struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
+ int rc;
+
+ if (!pdata || !pdata->loader_pdata) {
+ dev_err(dev, "%s: Missing platform data\n", __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+
+ ld = kzalloc(sizeof(*ld), GFP_KERNEL);
+ if (!ld) {
+ rc = -ENOMEM;
+ goto error_alloc_data_failed;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+ rc = device_create_file(dev, &dev_attr_forced_upgrade);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create forced_upgrade\n",
+ __func__);
+ goto error_create_forced_upgrade;
+ }
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ rc = device_create_file(dev, &dev_attr_manual_upgrade);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create manual_upgrade\n",
+ __func__);
+ goto error_create_manual_upgrade;
+ }
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
+ rc = device_create_file(dev, &dev_attr_config_loading);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create config_loading\n",
+ __func__);
+ goto error_create_config_loading;
+ }
+
+ rc = device_create_bin_file(dev, &bin_attr_config_data);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create config_data\n",
+ __func__);
+ goto error_create_config_data;
+ }
+#endif
+
+ ld->loader_pdata = pdata->loader_pdata;
+ ld->dev = dev;
+ *data = ld;
+
+#if CYTTSP5_FW_UPGRADE
+ init_completion(&ld->int_running);
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ init_completion(&ld->builtin_bin_fw_complete);
+#endif
+ cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_LOADER_NAME,
+ cyttsp5_loader_attention, CY_MODE_BOOTLOADER);
+
+ cmd->subscribe_attention(dev, CY_ATTEN_LOADER, CYTTSP5_LOADER_NAME,
+ cyttsp5_fw_upgrade_cb, CY_MODE_UNKNOWN);
+#endif
+#if CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE
+ init_completion(&ld->calibration_complete);
+ INIT_WORK(&ld->calibration_work, cyttsp5_calibrate_idacs);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
+ mutex_init(&ld->config_lock);
+#endif
+
+#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE
+ /* Call FW and config upgrade directly in probe */
+ cyttsp5_fw_and_config_upgrade(&ld->fw_and_config_upgrade);
+#else
+ INIT_WORK(&ld->fw_and_config_upgrade, cyttsp5_fw_and_config_upgrade);
+ schedule_work(&ld->fw_and_config_upgrade);
+#endif
+
+ dev_info(dev, "%s: Successful probe %s\n", __func__, dev_name(dev));
+ return 0;
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
+error_create_config_data:
+ device_remove_file(dev, &dev_attr_config_loading);
+error_create_config_loading:
+#endif
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ device_remove_file(dev, &dev_attr_manual_upgrade);
+error_create_manual_upgrade:
+#endif
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+ device_remove_file(dev, &dev_attr_forced_upgrade);
+error_create_forced_upgrade:
+#endif
+ kfree(ld);
+error_alloc_data_failed:
+error_no_pdata:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+static void cyttsp5_loader_release(struct device *dev, void *data)
+{
+ struct cyttsp5_loader_data *ld = (struct cyttsp5_loader_data *)data;
+
+#if CYTTSP5_FW_UPGRADE
+ cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_LOADER_NAME,
+ cyttsp5_loader_attention, CY_MODE_BOOTLOADER);
+
+ cmd->unsubscribe_attention(dev, CY_ATTEN_LOADER, CYTTSP5_LOADER_NAME,
+ cyttsp5_fw_upgrade_cb, CY_MODE_UNKNOWN);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE
+ device_remove_bin_file(dev, &bin_attr_config_data);
+ device_remove_file(dev, &dev_attr_config_loading);
+ kfree(ld->config_data);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE
+ device_remove_file(dev, &dev_attr_manual_upgrade);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+ device_remove_file(dev, &dev_attr_forced_upgrade);
+#endif
+ kfree(ld);
+}
+
+static struct cyttsp5_module loader_module = {
+ .name = CYTTSP5_LOADER_NAME,
+ .probe = cyttsp5_loader_probe,
+ .release = cyttsp5_loader_release,
+};
+
+static int __init cyttsp5_loader_init(void)
+{
+ int rc;
+
+ cmd = cyttsp5_get_commands();
+ if (!cmd)
+ return -EINVAL;
+
+ rc = cyttsp5_register_module(&loader_module);
+ if (rc < 0) {
+ pr_err("%s: Error, failed registering module\n",
+ __func__);
+ return rc;
+ }
+
+ pr_info("%s: Parade TTSP FW Loader Driver (Built %s) rc=%d\n",
+ __func__, CY_DRIVER_VERSION, rc);
+ return 0;
+}
+module_init(cyttsp5_loader_init);
+
+static void __exit cyttsp5_loader_exit(void)
+{
+ cyttsp5_unregister_module(&loader_module);
+}
+module_exit(cyttsp5_loader_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product FW Loader Driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_mt_common.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_mt_common.c
new file mode 100644
index 0000000..41b28f1
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_mt_common.c
@@ -0,0 +1,733 @@
+/*
+ * cyttsp5_mt_common.c
+ * Parade TrueTouch(TM) Standard Product V5 Multi-Touch Reports Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+#define CYTTSP5_MT_NAME "cyttsp5_mt"
+
+#define MT_PARAM_SIGNAL(md, sig_ost) PARAM_SIGNAL(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_MIN(md, sig_ost) PARAM_MIN(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_MAX(md, sig_ost) PARAM_MAX(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_FUZZ(md, sig_ost) PARAM_FUZZ(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_FLAT(md, sig_ost) PARAM_FLAT(md->pdata->frmwrk, sig_ost)
+
+static void cyttsp5_mt_lift_all(struct cyttsp5_mt_data *md)
+{
+ int max = md->si->tch_abs[CY_TCH_T].max;
+
+ if (md->num_prv_rec != 0) {
+ if (md->mt_function.report_slot_liftoff)
+ md->mt_function.report_slot_liftoff(md, max);
+ input_sync(md->input);
+ md->num_prv_rec = 0;
+ }
+}
+
+static void cyttsp5_get_touch_axis(struct cyttsp5_mt_data *md,
+ int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+ int nbyte;
+ int next;
+
+ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+ parade_debug(md->dev, DEBUG_LEVEL_2,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next], bofs);
+ *axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
+ next++;
+ }
+
+ *axis &= max - 1;
+
+ parade_debug(md->dev, DEBUG_LEVEL_2,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next]);
+}
+
+static void cyttsp5_get_touch_hdr(struct cyttsp5_mt_data *md,
+ struct cyttsp5_touch *touch, u8 *xy_mode)
+{
+ struct device *dev = md->dev;
+ struct cyttsp5_sysinfo *si = md->si;
+ enum cyttsp5_tch_hdr hdr;
+
+ for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) {
+ if (!si->tch_hdr[hdr].report)
+ continue;
+ cyttsp5_get_touch_axis(md, &touch->hdr[hdr],
+ si->tch_hdr[hdr].size,
+ si->tch_hdr[hdr].max,
+ xy_mode + si->tch_hdr[hdr].ofs,
+ si->tch_hdr[hdr].bofs);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n",
+ __func__, cyttsp5_tch_hdr_string[hdr],
+ touch->hdr[hdr], touch->hdr[hdr]);
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: time=%X tch_num=%d lo=%d noise=%d counter=%d\n",
+ __func__,
+ touch->hdr[CY_TCH_TIME],
+ touch->hdr[CY_TCH_NUM],
+ touch->hdr[CY_TCH_LO],
+ touch->hdr[CY_TCH_NOISE],
+ touch->hdr[CY_TCH_COUNTER]);
+}
+
+static void cyttsp5_get_touch_record(struct cyttsp5_mt_data *md,
+ struct cyttsp5_touch *touch, u8 *xy_data)
+{
+ struct device *dev = md->dev;
+ struct cyttsp5_sysinfo *si = md->si;
+ enum cyttsp5_tch_abs abs;
+
+ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+ if (!si->tch_abs[abs].report)
+ continue;
+ cyttsp5_get_touch_axis(md, &touch->abs[abs],
+ si->tch_abs[abs].size,
+ si->tch_abs[abs].max,
+ xy_data + si->tch_abs[abs].ofs,
+ si->tch_abs[abs].bofs);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n",
+ __func__, cyttsp5_tch_abs_string[abs],
+ touch->abs[abs], touch->abs[abs]);
+ }
+}
+
+static void cyttsp5_mt_process_touch(struct cyttsp5_mt_data *md,
+ struct cyttsp5_touch *touch)
+{
+ struct device *dev = md->dev;
+ struct cyttsp5_sysinfo *si = md->si;
+ int tmp;
+ bool flipped;
+
+
+ /* Orientation is signed */
+ touch->abs[CY_TCH_OR] = (int8_t)touch->abs[CY_TCH_OR];
+
+ if (md->pdata->flags & CY_MT_FLAG_FLIP) {
+ tmp = touch->abs[CY_TCH_X];
+ touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y];
+ touch->abs[CY_TCH_Y] = tmp;
+ if (touch->abs[CY_TCH_OR] > 0)
+ touch->abs[CY_TCH_OR] =
+ md->or_max - touch->abs[CY_TCH_OR];
+ else
+ touch->abs[CY_TCH_OR] =
+ md->or_min - touch->abs[CY_TCH_OR];
+ flipped = true;
+ } else
+ flipped = false;
+
+ if (md->pdata->flags & CY_MT_FLAG_INV_X) {
+ if (flipped)
+ touch->abs[CY_TCH_X] = si->sensing_conf_data.res_y -
+ touch->abs[CY_TCH_X];
+ else
+ touch->abs[CY_TCH_X] = si->sensing_conf_data.res_x -
+ touch->abs[CY_TCH_X];
+ touch->abs[CY_TCH_OR] *= -1;
+ }
+ if (md->pdata->flags & CY_MT_FLAG_INV_Y) {
+ if (flipped)
+ touch->abs[CY_TCH_Y] = si->sensing_conf_data.res_x -
+ touch->abs[CY_TCH_Y];
+ else
+ touch->abs[CY_TCH_Y] = si->sensing_conf_data.res_y -
+ touch->abs[CY_TCH_Y];
+ touch->abs[CY_TCH_OR] *= -1;
+ }
+
+ /* Convert MAJOR/MINOR from mm to resolution */
+ tmp = touch->abs[CY_TCH_MAJ] * 100 * si->sensing_conf_data.res_x;
+ touch->abs[CY_TCH_MAJ] = tmp / si->sensing_conf_data.len_x;
+ tmp = touch->abs[CY_TCH_MIN] * 100 * si->sensing_conf_data.res_x;
+ touch->abs[CY_TCH_MIN] = tmp / si->sensing_conf_data.len_x;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+ __func__, flipped ? "true" : "false",
+ md->pdata->flags & CY_MT_FLAG_INV_X ? "true" : "false",
+ md->pdata->flags & CY_MT_FLAG_INV_Y ? "true" : "false",
+ touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
+ touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
+}
+
+static void cyttsp5_report_event(struct cyttsp5_mt_data *md, int event,
+ int value)
+{
+ int sig = MT_PARAM_SIGNAL(md, event);
+
+ if (sig != CY_IGNORE_VALUE)
+ input_report_abs(md->input, sig, value);
+}
+
+static void cyttsp5_get_mt_touches(struct cyttsp5_mt_data *md,
+ struct cyttsp5_touch *tch, int num_cur_tch)
+{
+ struct device *dev = md->dev;
+ struct cyttsp5_sysinfo *si = md->si;
+ int sig;
+ int i, j, t = 0;
+ DECLARE_BITMAP(ids, si->tch_abs[CY_TCH_T].max);
+ int mt_sync_count = 0;
+ u8 *tch_addr;
+
+ bitmap_zero(ids, si->tch_abs[CY_TCH_T].max);
+ memset(tch->abs, 0, sizeof(tch->abs));
+
+ for (i = 0; i < num_cur_tch; i++) {
+ tch_addr = si->xy_data + (i * si->desc.tch_record_size);
+ cyttsp5_get_touch_record(md, tch, tch_addr);
+
+ /* Discard proximity event */
+ if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Discarding proximity event\n",
+ __func__);
+ continue;
+ }
+
+ /* Validate track_id */
+ t = tch->abs[CY_TCH_T];
+ if (t < md->t_min || t > md->t_max) {
+ dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+ __func__, i, t, md->t_max);
+ if (md->mt_function.input_sync)
+ md->mt_function.input_sync(md->input);
+ mt_sync_count++;
+ continue;
+ }
+
+ /* Lift-off */
+ if (tch->abs[CY_TCH_E] == CY_EV_LIFTOFF) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: t=%d e=%d lift-off\n",
+ __func__, t, tch->abs[CY_TCH_E]);
+ goto cyttsp5_get_mt_touches_pr_tch;
+ }
+
+ /* Process touch */
+ cyttsp5_mt_process_touch(md, tch);
+
+ /* use 0 based track id's */
+ t -= md->t_min;
+
+ sig = MT_PARAM_SIGNAL(md, CY_ABS_ID_OST);
+ if (sig != CY_IGNORE_VALUE) {
+ if (md->mt_function.input_report)
+ md->mt_function.input_report(md->input, sig,
+ t, tch->abs[CY_TCH_O]);
+ __set_bit(t, ids);
+ }
+
+ /* If touch type is hover, send P as distance, reset P */
+ if (tch->abs[CY_TCH_O] == CY_OBJ_HOVER) {
+ /* CY_ABS_D_OST signal must be in touch framework */
+ cyttsp5_report_event(md, CY_ABS_D_OST,
+ tch->abs[CY_TCH_P]);
+ tch->abs[CY_TCH_P] = 0;
+ } else
+ cyttsp5_report_event(md, CY_ABS_D_OST, 0);
+
+
+ /* all devices: position and pressure fields */
+ for (j = 0; j <= CY_ABS_W_OST; j++) {
+ if (!si->tch_abs[j].report)
+ continue;
+ cyttsp5_report_event(md, CY_ABS_X_OST + j,
+ tch->abs[CY_TCH_X + j]);
+ }
+
+ /* Get the extended touch fields */
+ for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) {
+ if (!si->tch_abs[CY_ABS_MAJ_OST + j].report)
+ continue;
+ cyttsp5_report_event(md, CY_ABS_MAJ_OST + j,
+ tch->abs[CY_TCH_MAJ + j]);
+ }
+ if (md->mt_function.input_sync)
+ md->mt_function.input_sync(md->input);
+ mt_sync_count++;
+
+cyttsp5_get_mt_touches_pr_tch:
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d obj=%d tip=%d\n",
+ __func__, t,
+ tch->abs[CY_TCH_X],
+ tch->abs[CY_TCH_Y],
+ tch->abs[CY_TCH_P],
+ tch->abs[CY_TCH_MAJ],
+ tch->abs[CY_TCH_MIN],
+ tch->abs[CY_TCH_OR],
+ tch->abs[CY_TCH_E],
+ tch->abs[CY_TCH_O],
+ tch->abs[CY_TCH_TIP]);
+ }
+
+ if (md->mt_function.final_sync)
+ md->mt_function.final_sync(md->input,
+ si->tch_abs[CY_TCH_T].max, mt_sync_count, ids);
+
+ md->num_prv_rec = num_cur_tch;
+}
+
+/* read xy_data for all current touches */
+static int cyttsp5_xy_worker(struct cyttsp5_mt_data *md)
+{
+ struct device *dev = md->dev;
+ struct cyttsp5_sysinfo *si = md->si;
+ int max_tch = si->sensing_conf_data.max_tch;
+ struct cyttsp5_touch tch;
+ u8 num_cur_tch;
+ int rc = 0;
+
+ cyttsp5_get_touch_hdr(md, &tch, si->xy_mode + 3);
+
+ num_cur_tch = tch.hdr[CY_TCH_NUM];
+ if (num_cur_tch > max_tch) {
+ dev_err(dev, "%s: Num touch err detected (n=%d)\n",
+ __func__, num_cur_tch);
+ num_cur_tch = max_tch;
+ }
+
+ if (tch.hdr[CY_TCH_LO]) {
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Large area detected\n",
+ __func__);
+ if (md->pdata->flags & CY_MT_FLAG_NO_TOUCH_ON_LO)
+ num_cur_tch = 0;
+ }
+
+ if (num_cur_tch == 0 && md->num_prv_rec == 0)
+ goto cyttsp5_xy_worker_exit;
+
+ /* extract xy_data for all currently reported touches */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: extract data num_cur_tch=%d\n",
+ __func__, num_cur_tch);
+ if (num_cur_tch)
+ cyttsp5_get_mt_touches(md, &tch, num_cur_tch);
+ else
+ cyttsp5_mt_lift_all(md);
+
+ rc = 0;
+
+cyttsp5_xy_worker_exit:
+ return rc;
+}
+
+static void cyttsp5_mt_send_dummy_event(struct cyttsp5_core_data *cd,
+ struct cyttsp5_mt_data *md)
+{
+#ifndef EASYWAKE_TSG6
+ /* TSG5 EasyWake */
+ unsigned long ids = 0;
+
+ /* for easy wakeup */
+ if (md->mt_function.input_report)
+ md->mt_function.input_report(md->input, ABS_MT_TRACKING_ID,
+ 0, CY_OBJ_STANDARD_FINGER);
+ if (md->mt_function.input_sync)
+ md->mt_function.input_sync(md->input);
+ if (md->mt_function.final_sync)
+ md->mt_function.final_sync(md->input, 0, 1, &ids);
+ if (md->mt_function.report_slot_liftoff)
+ md->mt_function.report_slot_liftoff(md, 1);
+ if (md->mt_function.final_sync)
+ md->mt_function.final_sync(md->input, 1, 1, &ids);
+#else
+ /* TSG6 FW1.3 and above only. TSG6 FW1.0 - 1.2 does not */
+ /* support EasyWake, and this function will not be called */
+ u8 key_value;
+
+ switch (cd->gesture_id) {
+ case GESTURE_DOUBLE_TAP:
+ key_value = KEY_F1;
+ break;
+ case GESTURE_TWO_FINGERS_SLIDE:
+ key_value = KEY_F2;
+ break;
+ case GESTURE_TOUCH_DETECTED:
+ key_value = KEY_F3;
+ break;
+ case GESTURE_PUSH_BUTTON:
+ key_value = KEY_F4;
+ break;
+ case GESTURE_SINGLE_SLIDE_DE_TX:
+ key_value = KEY_F5;
+ break;
+ case GESTURE_SINGLE_SLIDE_IN_TX:
+ key_value = KEY_F6;
+ break;
+ case GESTURE_SINGLE_SLIDE_DE_RX:
+ key_value = KEY_F7;
+ break;
+ case GESTURE_SINGLE_SLIDE_IN_RX:
+ key_value = KEY_F8;
+ break;
+ default:
+ break;
+ }
+
+ input_report_key(md->input, key_value, 1);
+ mdelay(10);
+ input_report_key(md->input, key_value, 0);
+ input_sync(md->input);
+#endif
+}
+
+static int cyttsp5_mt_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+ int rc;
+
+ if (md->si->xy_mode[2] != md->si->desc.tch_report_id)
+ return 0;
+
+ /* core handles handshake */
+ mutex_lock(&md->mt_lock);
+ rc = cyttsp5_xy_worker(md);
+ mutex_unlock(&md->mt_lock);
+ if (rc < 0)
+ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static int cyttsp5_mt_wake_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ mutex_lock(&md->mt_lock);
+ cyttsp5_mt_send_dummy_event(cd, md);
+ mutex_unlock(&md->mt_lock);
+ return 0;
+}
+
+static int cyttsp5_startup_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ mutex_lock(&md->mt_lock);
+ cyttsp5_mt_lift_all(md);
+ mutex_unlock(&md->mt_lock);
+
+ return 0;
+}
+
+static int cyttsp5_mt_suspend_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ mutex_lock(&md->mt_lock);
+ cyttsp5_mt_lift_all(md);
+ md->is_suspended = true;
+ mutex_unlock(&md->mt_lock);
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int cyttsp5_mt_resume_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ pm_runtime_get(dev);
+
+ mutex_lock(&md->mt_lock);
+ md->is_suspended = false;
+ mutex_unlock(&md->mt_lock);
+
+ return 0;
+}
+
+static int cyttsp5_mt_open(struct input_dev *input)
+{
+ struct device *dev = input->dev.parent;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&md->mt_lock);
+ md->is_suspended = false;
+ mutex_unlock(&md->mt_lock);
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__);
+
+ /* set up touch call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_MT_NAME,
+ cyttsp5_mt_attention, CY_MODE_OPERATIONAL);
+
+ /* set up startup call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME,
+ cyttsp5_startup_attention, 0);
+
+ /* set up wakeup call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_WAKE, CYTTSP5_MT_NAME,
+ cyttsp5_mt_wake_attention, 0);
+
+ /* set up suspend call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_MT_NAME,
+ cyttsp5_mt_suspend_attention, 0);
+
+ /* set up resume call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_MT_NAME,
+ cyttsp5_mt_resume_attention, 0);
+
+ return 0;
+}
+
+static void cyttsp5_mt_close(struct input_dev *input)
+{
+ struct device *dev = input->dev.parent;
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_MT_NAME,
+ cyttsp5_mt_attention, CY_MODE_OPERATIONAL);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME,
+ cyttsp5_startup_attention, 0);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_WAKE, CYTTSP5_MT_NAME,
+ cyttsp5_mt_wake_attention, 0);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_MT_NAME,
+ cyttsp5_mt_suspend_attention, 0);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_MT_NAME,
+ cyttsp5_mt_resume_attention, 0);
+
+ mutex_lock(&md->mt_lock);
+ if (!md->is_suspended) {
+ pm_runtime_put(dev);
+ md->is_suspended = true;
+ }
+ mutex_unlock(&md->mt_lock);
+}
+
+static int cyttsp5_setup_input_device(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+ int signal = CY_IGNORE_VALUE;
+ int max_x, max_y, max_p, min, max;
+ int max_x_tmp, max_y_tmp;
+ int i;
+ int rc;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Initialize event signals\n",
+ __func__);
+ __set_bit(EV_ABS, md->input->evbit);
+ __set_bit(EV_REL, md->input->evbit);
+ __set_bit(EV_KEY, md->input->evbit);
+#ifdef INPUT_PROP_DIRECT
+ __set_bit(INPUT_PROP_DIRECT, md->input->propbit);
+#endif
+
+ /* If virtualkeys enabled, don't use all screen */
+ if (md->pdata->flags & CY_MT_FLAG_VKEYS) {
+ max_x_tmp = md->pdata->vkeys_x;
+ max_y_tmp = md->pdata->vkeys_y;
+ } else {
+ max_x_tmp = md->si->sensing_conf_data.res_x;
+ max_y_tmp = md->si->sensing_conf_data.res_y;
+ }
+
+ /* get maximum values from the sysinfo data */
+ if (md->pdata->flags & CY_MT_FLAG_FLIP) {
+ max_x = max_y_tmp - 1;
+ max_y = max_x_tmp - 1;
+ } else {
+ max_x = max_x_tmp - 1;
+ max_y = max_y_tmp - 1;
+ }
+ max_p = md->si->sensing_conf_data.max_z;
+
+ /* set event signal capabilities */
+ for (i = 0; i < NUM_SIGNALS(md->pdata->frmwrk); i++) {
+ signal = MT_PARAM_SIGNAL(md, i);
+ if (signal != CY_IGNORE_VALUE) {
+ __set_bit(signal, md->input->absbit);
+
+ min = MT_PARAM_MIN(md, i);
+ max = MT_PARAM_MAX(md, i);
+ if (i == CY_ABS_ID_OST) {
+ /* shift track ids down to start at 0 */
+ max = max - min;
+ min = min - min;
+ } else if (i == CY_ABS_X_OST)
+ max = max_x;
+ else if (i == CY_ABS_Y_OST)
+ max = max_y;
+ else if (i == CY_ABS_P_OST)
+ max = max_p;
+
+ input_set_abs_params(md->input, signal, min, max,
+ MT_PARAM_FUZZ(md, i), MT_PARAM_FLAT(md, i));
+ parade_debug(dev, DEBUG_LEVEL_1,
+ "%s: register signal=%02X min=%d max=%d\n",
+ __func__, signal, min, max);
+ }
+ }
+
+ md->or_min = MT_PARAM_MIN(md, CY_ABS_OR_OST);
+ md->or_max = MT_PARAM_MAX(md, CY_ABS_OR_OST);
+
+ md->t_min = MT_PARAM_MIN(md, CY_ABS_ID_OST);
+ md->t_max = MT_PARAM_MAX(md, CY_ABS_ID_OST);
+
+ rc = md->mt_function.input_register_device(md->input,
+ md->si->tch_abs[CY_TCH_T].max);
+ if (rc < 0)
+ dev_err(dev, "%s: Error, failed register input device r=%d\n",
+ __func__, rc);
+ else
+ md->input_device_registered = true;
+
+#ifdef EASYWAKE_TSG6
+ input_set_capability(md->input, EV_KEY, KEY_F1);
+ input_set_capability(md->input, EV_KEY, KEY_F2);
+ input_set_capability(md->input, EV_KEY, KEY_F3);
+ input_set_capability(md->input, EV_KEY, KEY_F4);
+ input_set_capability(md->input, EV_KEY, KEY_F5);
+ input_set_capability(md->input, EV_KEY, KEY_F6);
+ input_set_capability(md->input, EV_KEY, KEY_F7);
+ input_set_capability(md->input, EV_KEY, KEY_F8);
+#endif
+ return rc;
+}
+
+static int cyttsp5_setup_input_attention(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+ int rc;
+
+ md->si = _cyttsp5_request_sysinfo(dev);
+ if (!md->si)
+ return -EINVAL;
+
+ rc = cyttsp5_setup_input_device(dev);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME,
+ cyttsp5_setup_input_attention, 0);
+
+ return rc;
+}
+
+int cyttsp5_mt_probe(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+ struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
+ struct cyttsp5_mt_platform_data *mt_pdata;
+ int rc = 0;
+
+ if (!pdata || !pdata->mt_pdata) {
+ dev_err(dev, "%s: Missing platform data\n", __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+ mt_pdata = pdata->mt_pdata;
+
+ cyttsp5_init_function_ptrs(md);
+
+ mutex_init(&md->mt_lock);
+ md->dev = dev;
+ md->pdata = mt_pdata;
+
+ /* Create the input device and register it. */
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Create the input device and register it\n", __func__);
+ md->input = input_allocate_device();
+ if (!md->input) {
+ dev_err(dev, "%s: Error, failed to allocate input device\n",
+ __func__);
+ rc = -ENODEV;
+ goto error_alloc_failed;
+ }
+
+ if (md->pdata->inp_dev_name)
+ md->input->name = md->pdata->inp_dev_name;
+ else
+ md->input->name = CYTTSP5_MT_NAME;
+ scnprintf(md->phys, sizeof(md->phys), "%s/input%d", dev_name(dev),
+ cd->phys_num++);
+ md->input->phys = md->phys;
+ md->input->dev.parent = md->dev;
+ md->input->open = cyttsp5_mt_open;
+ md->input->close = cyttsp5_mt_close;
+ input_set_drvdata(md->input, md);
+
+ /* get sysinfo */
+ md->si = _cyttsp5_request_sysinfo(dev);
+
+ if (md->si) {
+ rc = cyttsp5_setup_input_device(dev);
+ if (rc)
+ goto error_init_input;
+ } else {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+ __func__, md->si);
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_MT_NAME, cyttsp5_setup_input_attention, 0);
+ }
+
+ return 0;
+
+error_init_input:
+ input_free_device(md->input);
+error_alloc_failed:
+error_no_pdata:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+int cyttsp5_mt_release(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_mt_data *md = &cd->md;
+
+ if (md->input_device_registered) {
+ input_unregister_device(md->input);
+ } else {
+ input_free_device(md->input);
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_MT_NAME, cyttsp5_setup_input_attention, 0);
+ }
+
+ return 0;
+}
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_mta.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_mta.c
new file mode 100644
index 0000000..d457bb6
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_mta.c
@@ -0,0 +1,85 @@
+/*
+ * cyttsp5_mta.c
+ * Parade TrueTouch(TM) Standard Product V5 Multi-Touch Protocol A Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+static void cyttsp5_final_sync(struct input_dev *input, int max_slots,
+ int mt_sync_count, unsigned long *ids)
+{
+ if (mt_sync_count)
+ input_sync(input);
+}
+
+static void cyttsp5_input_sync(struct input_dev *input)
+{
+ input_mt_sync(input);
+}
+
+static void cyttsp5_input_report(struct input_dev *input, int sig,
+ int t, int type)
+{
+ if (type == CY_OBJ_STANDARD_FINGER || type == CY_OBJ_GLOVE
+ || type == CY_OBJ_HOVER) {
+ input_report_key(input, BTN_TOOL_FINGER, CY_BTN_PRESSED);
+ input_report_key(input, BTN_TOOL_PEN, CY_BTN_RELEASED);
+ } else if (type == CY_OBJ_STYLUS) {
+ input_report_key(input, BTN_TOOL_PEN, CY_BTN_PRESSED);
+ input_report_key(input, BTN_TOOL_FINGER, CY_BTN_RELEASED);
+ }
+
+ if (type != CY_OBJ_HOVER)
+ input_report_key(input, BTN_TOUCH, CY_BTN_PRESSED);
+
+ input_report_abs(input, sig, t);
+}
+
+static void cyttsp5_report_slot_liftoff(struct cyttsp5_mt_data *md,
+ int max_slots)
+{
+ input_report_key(md->input, BTN_TOUCH, CY_BTN_RELEASED);
+ input_report_key(md->input, BTN_TOOL_FINGER, CY_BTN_RELEASED);
+ input_report_key(md->input, BTN_TOOL_PEN, CY_BTN_RELEASED);
+
+}
+
+static int cyttsp5_input_register_device(struct input_dev *input, int max_slots)
+{
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+ __set_bit(BTN_TOOL_PEN, input->keybit);
+ return input_register_device(input);
+}
+
+void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md)
+{
+ md->mt_function.report_slot_liftoff = cyttsp5_report_slot_liftoff;
+ md->mt_function.final_sync = cyttsp5_final_sync;
+ md->mt_function.input_sync = cyttsp5_input_sync;
+ md->mt_function.input_report = cyttsp5_input_report;
+ md->mt_function.input_register_device = cyttsp5_input_register_device;
+}
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_mtb.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_mtb.c
new file mode 100644
index 0000000..d5f51e9
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_mtb.c
@@ -0,0 +1,93 @@
+/*
+ * cyttsp5_mtb.c
+ * Parade TrueTouch(TM) Standard Product V5 Multi-Touch Protocol B Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+#include <linux/input/mt.h>
+#include <linux/version.h>
+
+static void cyttsp5_final_sync(struct input_dev *input, int max_slots,
+ int mt_sync_count, unsigned long *ids)
+{
+ int t;
+
+ for (t = 0; t < max_slots; t++) {
+ if (test_bit(t, ids))
+ continue;
+ input_mt_slot(input, t);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ }
+
+ input_sync(input);
+}
+
+static void cyttsp5_input_report(struct input_dev *input, int sig,
+ int t, int type)
+{
+ input_mt_slot(input, t);
+
+ if (type == CY_OBJ_STANDARD_FINGER || type == CY_OBJ_GLOVE
+ || type == CY_OBJ_HOVER)
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ else if (type == CY_OBJ_STYLUS)
+ input_mt_report_slot_state(input, MT_TOOL_PEN, true);
+}
+
+static void cyttsp5_report_slot_liftoff(struct cyttsp5_mt_data *md,
+ int max_slots)
+{
+ int t;
+
+ if (md->num_prv_rec == 0)
+ return;
+
+ for (t = 0; t < max_slots; t++) {
+ input_mt_slot(md->input, t);
+ input_mt_report_slot_state(md->input,
+ MT_TOOL_FINGER, false);
+ }
+}
+
+static int cyttsp5_input_register_device(struct input_dev *input, int max_slots)
+{
+#if (KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE)
+ input_mt_init_slots(input, max_slots, 0);
+#else
+ input_mt_init_slots(input, max_slots);
+#endif
+ return input_register_device(input);
+}
+
+void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md)
+{
+ md->mt_function.report_slot_liftoff = cyttsp5_report_slot_liftoff;
+ md->mt_function.final_sync = cyttsp5_final_sync;
+ md->mt_function.input_sync = NULL;
+ md->mt_function.input_report = cyttsp5_input_report;
+ md->mt_function.input_register_device = cyttsp5_input_register_device;
+}
+
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_platform.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_platform.c
new file mode 100644
index 0000000..a87bdfa
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_platform.c
@@ -0,0 +1,289 @@
+/*
+ * cyttsp5_platform.c
+ * Parade TrueTouch(TM) Standard Product V5 Platform Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2013-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+#include <linux/input/cyttsp5_platform.h>
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+/* FW for Panel ID = 0x00 */
+#include "cyttsp5_fw_pid00.h"
+static struct cyttsp5_touch_firmware cyttsp5_firmware_pid00 = {
+ .img = cyttsp4_img_pid00,
+ .size = ARRAY_SIZE(cyttsp4_img_pid00),
+ .ver = cyttsp4_ver_pid00,
+ .vsize = ARRAY_SIZE(cyttsp4_ver_pid00),
+ .panel_id = 0x00,
+};
+
+/* FW for Panel ID = 0x01 */
+#include "cyttsp5_fw_pid01.h"
+static struct cyttsp5_touch_firmware cyttsp5_firmware_pid01 = {
+ .img = cyttsp4_img_pid01,
+ .size = ARRAY_SIZE(cyttsp4_img_pid01),
+ .ver = cyttsp4_ver_pid01,
+ .vsize = ARRAY_SIZE(cyttsp4_ver_pid01),
+ .panel_id = 0x01,
+};
+
+/* FW for Panel ID not enabled (legacy) */
+#include "cyttsp5_fw.h"
+static struct cyttsp5_touch_firmware cyttsp5_firmware = {
+ .img = cyttsp4_img,
+ .size = ARRAY_SIZE(cyttsp4_img),
+ .ver = cyttsp4_ver,
+ .vsize = ARRAY_SIZE(cyttsp4_ver),
+};
+#else
+/* FW for Panel ID not enabled (legacy) */
+static struct cyttsp5_touch_firmware cyttsp5_firmware = {
+ .img = NULL,
+ .size = 0,
+ .ver = NULL,
+ .vsize = 0,
+};
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
+/* TT Config for Panel ID = 0x00 */
+#include "cyttsp5_params_pid00.h"
+static struct touch_settings cyttsp5_sett_param_regs_pid00 = {
+ .data = (uint8_t *)&cyttsp4_param_regs_pid00[0],
+ .size = ARRAY_SIZE(cyttsp4_param_regs_pid00),
+ .tag = 0,
+};
+
+static struct touch_settings cyttsp5_sett_param_size_pid00 = {
+ .data = (uint8_t *)&cyttsp4_param_size_pid00[0],
+ .size = ARRAY_SIZE(cyttsp4_param_size_pid00),
+ .tag = 0,
+};
+
+static struct cyttsp5_touch_config cyttsp5_ttconfig_pid00 = {
+ .param_regs = &cyttsp5_sett_param_regs_pid00,
+ .param_size = &cyttsp5_sett_param_size_pid00,
+ .fw_ver = ttconfig_fw_ver_pid00,
+ .fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid00),
+ .panel_id = 0x00,
+};
+
+/* TT Config for Panel ID = 0x01 */
+#include "cyttsp5_params_pid01.h"
+static struct touch_settings cyttsp5_sett_param_regs_pid01 = {
+ .data = (uint8_t *)&cyttsp4_param_regs_pid01[0],
+ .size = ARRAY_SIZE(cyttsp4_param_regs_pid01),
+ .tag = 0,
+};
+
+static struct touch_settings cyttsp5_sett_param_size_pid01 = {
+ .data = (uint8_t *)&cyttsp4_param_size_pid01[0],
+ .size = ARRAY_SIZE(cyttsp4_param_size_pid01),
+ .tag = 0,
+};
+
+static struct cyttsp5_touch_config cyttsp5_ttconfig_pid01 = {
+ .param_regs = &cyttsp5_sett_param_regs_pid01,
+ .param_size = &cyttsp5_sett_param_size_pid01,
+ .fw_ver = ttconfig_fw_ver_pid01,
+ .fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid01),
+ .panel_id = 0x01,
+};
+
+/* TT Config for Panel ID not enabled (legacy)*/
+#include "cyttsp5_params.h"
+static struct touch_settings cyttsp5_sett_param_regs = {
+ .data = (uint8_t *)&cyttsp4_param_regs[0],
+ .size = ARRAY_SIZE(cyttsp4_param_regs),
+ .tag = 0,
+};
+
+static struct touch_settings cyttsp5_sett_param_size = {
+ .data = (uint8_t *)&cyttsp4_param_size[0],
+ .size = ARRAY_SIZE(cyttsp4_param_size),
+ .tag = 0,
+};
+
+static struct cyttsp5_touch_config cyttsp5_ttconfig = {
+ .param_regs = &cyttsp5_sett_param_regs,
+ .param_size = &cyttsp5_sett_param_size,
+ .fw_ver = ttconfig_fw_ver,
+ .fw_vsize = ARRAY_SIZE(ttconfig_fw_ver),
+};
+#else
+/* TT Config for Panel ID not enabled (legacy)*/
+static struct cyttsp5_touch_config cyttsp5_ttconfig = {
+ .param_regs = NULL,
+ .param_size = NULL,
+ .fw_ver = NULL,
+ .fw_vsize = 0,
+};
+#endif
+
+static struct cyttsp5_touch_firmware *cyttsp5_firmwares[] = {
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE
+ &cyttsp5_firmware_pid00,
+ &cyttsp5_firmware_pid01,
+#endif
+ NULL, /* Last item should always be NULL */
+};
+
+static struct cyttsp5_touch_config *cyttsp5_ttconfigs[] = {
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE
+ &cyttsp5_ttconfig_pid00,
+ &cyttsp5_ttconfig_pid01,
+#endif
+ NULL, /* Last item should always be NULL */
+};
+
+struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data = {
+ .fw = &cyttsp5_firmware,
+ .ttconfig = &cyttsp5_ttconfig,
+ .fws = cyttsp5_firmwares,
+ .ttconfigs = cyttsp5_ttconfigs,
+ .flags = CY_LOADER_FLAG_NONE,
+};
+
+int cyttsp5_xres(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev)
+{
+ int rst_gpio = pdata->rst_gpio;
+ int rc = 0;
+
+ gpio_set_value(rst_gpio, 1);
+ msleep(20);
+ gpio_set_value(rst_gpio, 0);
+ msleep(40);
+ gpio_set_value(rst_gpio, 1);
+ msleep(20);
+ dev_info(dev,
+ "%s: RESET CYTTSP gpio=%d r=%d\n", __func__,
+ pdata->rst_gpio, rc);
+ return rc;
+}
+
+int cyttsp5_init(struct cyttsp5_core_platform_data *pdata,
+ int on, struct device *dev)
+{
+ int rst_gpio = pdata->rst_gpio;
+ int irq_gpio = pdata->irq_gpio;
+ int rc = 0;
+
+ if (on) {
+ rc = gpio_request(rst_gpio, NULL);
+ if (rc < 0) {
+ gpio_free(rst_gpio);
+ rc = gpio_request(rst_gpio, NULL);
+ }
+ if (rc < 0) {
+ dev_err(dev,
+ "%s: Fail request gpio=%d\n", __func__,
+ rst_gpio);
+ } else {
+ rc = gpio_direction_output(rst_gpio, 1);
+ if (rc < 0) {
+ pr_err("%s: Fail set output gpio=%d\n",
+ __func__, rst_gpio);
+ gpio_free(rst_gpio);
+ } else {
+ rc = gpio_request(irq_gpio, NULL);
+ if (rc < 0) {
+ gpio_free(irq_gpio);
+ rc = gpio_request(irq_gpio,
+ NULL);
+ }
+ if (rc < 0) {
+ dev_err(dev,
+ "%s: Fail request gpio=%d\n",
+ __func__, irq_gpio);
+ gpio_free(rst_gpio);
+ } else {
+ gpio_direction_input(irq_gpio);
+ }
+ }
+ }
+ } else {
+ gpio_free(rst_gpio);
+ gpio_free(irq_gpio);
+ }
+
+ dev_info(dev, "%s: INIT CYTTSP RST gpio=%d and IRQ gpio=%d r=%d\n",
+ __func__, rst_gpio, irq_gpio, rc);
+ return rc;
+}
+
+static int cyttsp5_wakeup(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev, atomic_t *ignore_irq)
+{
+ return 0;
+}
+
+static int cyttsp5_sleep(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev, atomic_t *ignore_irq)
+{
+ return 0;
+}
+
+int cyttsp5_power(struct cyttsp5_core_platform_data *pdata,
+ int on, struct device *dev, atomic_t *ignore_irq)
+{
+ if (on)
+ return cyttsp5_wakeup(pdata, dev, ignore_irq);
+
+ return cyttsp5_sleep(pdata, dev, ignore_irq);
+}
+
+int cyttsp5_irq_stat(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev)
+{
+ return gpio_get_value(pdata->irq_gpio);
+}
+
+#ifdef CYTTSP5_DETECT_HW
+int cyttsp5_detect(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev, cyttsp5_platform_read read)
+{
+ int retry = 3;
+ int rc;
+ char buf[1];
+
+ while (retry--) {
+ /* Perform reset, wait for 100 ms and perform read */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Performing a reset\n",
+ __func__);
+ pdata->xres(pdata, dev);
+ msleep(100);
+ rc = read(dev, buf, 1);
+ if (!rc)
+ return 0;
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Read unsuccessful, try=%d\n",
+ __func__, 3 - retry);
+ }
+
+ return rc;
+}
+#endif
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_proximity.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_proximity.c
new file mode 100644
index 0000000..0dba2c8
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_proximity.c
@@ -0,0 +1,555 @@
+/*
+ * cyttsp5_proximity.c
+ * Parade TrueTouch(TM) Standard Product V5 Proximity Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2013-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+#define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity"
+
+/* Timeout value in ms. */
+#define CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT 1000
+
+#define CYTTSP5_PROXIMITY_ON 0
+#define CYTTSP5_PROXIMITY_OFF 1
+
+static inline struct cyttsp5_proximity_data *get_prox_data(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return &cd->pd;
+}
+
+static void cyttsp5_report_proximity(struct cyttsp5_proximity_data *pd,
+ bool on)
+{
+ int val = on ? CYTTSP5_PROXIMITY_ON : CYTTSP5_PROXIMITY_OFF;
+
+ input_report_abs(pd->input, ABS_DISTANCE, val);
+ input_sync(pd->input);
+}
+
+static void cyttsp5_get_touch_axis(struct cyttsp5_proximity_data *pd,
+ int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+ int nbyte;
+ int next;
+
+ for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+ parade_debug(pd->dev, DEBUG_LEVEL_2,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next], bofs);
+ *axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
+ next++;
+ }
+
+ *axis &= max - 1;
+
+ parade_debug(pd->dev, DEBUG_LEVEL_2,
+ "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
+ __func__, *axis, *axis, size, max, xy_data, next,
+ xy_data[next], xy_data[next]);
+}
+
+static void cyttsp5_get_touch_hdr(struct cyttsp5_proximity_data *pd,
+ struct cyttsp5_touch *touch, u8 *xy_mode)
+{
+ struct device *dev = pd->dev;
+ struct cyttsp5_sysinfo *si = pd->si;
+ enum cyttsp5_tch_hdr hdr;
+
+ for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) {
+ if (!si->tch_hdr[hdr].report)
+ continue;
+ cyttsp5_get_touch_axis(pd, &touch->hdr[hdr],
+ si->tch_hdr[hdr].size,
+ si->tch_hdr[hdr].max,
+ xy_mode + si->tch_hdr[hdr].ofs,
+ si->tch_hdr[hdr].bofs);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n",
+ __func__, cyttsp5_tch_hdr_string[hdr],
+ touch->hdr[hdr], touch->hdr[hdr]);
+ }
+}
+
+static void cyttsp5_get_touch(struct cyttsp5_proximity_data *pd,
+ struct cyttsp5_touch *touch, u8 *xy_data)
+{
+ struct device *dev = pd->dev;
+ struct cyttsp5_sysinfo *si = pd->si;
+ enum cyttsp5_tch_abs abs;
+
+ for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+ if (!si->tch_abs[abs].report)
+ continue;
+ cyttsp5_get_touch_axis(pd, &touch->abs[abs],
+ si->tch_abs[abs].size,
+ si->tch_abs[abs].max,
+ xy_data + si->tch_abs[abs].ofs,
+ si->tch_abs[abs].bofs);
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n",
+ __func__, cyttsp5_tch_abs_string[abs],
+ touch->abs[abs], touch->abs[abs]);
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: x=%04X(%d) y=%04X(%d)\n",
+ __func__, touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
+ touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
+}
+
+static void cyttsp5_get_proximity_touch(struct cyttsp5_proximity_data *pd,
+ struct cyttsp5_touch *tch, int num_cur_tch)
+{
+ struct cyttsp5_sysinfo *si = pd->si;
+ int i;
+
+ for (i = 0; i < num_cur_tch; i++) {
+ cyttsp5_get_touch(pd, tch, si->xy_data +
+ (i * si->desc.tch_record_size));
+
+ /* Check for proximity event */
+ if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) {
+ if (tch->abs[CY_TCH_E] == CY_EV_TOUCHDOWN)
+ cyttsp5_report_proximity(pd, true);
+ else if (tch->abs[CY_TCH_E] == CY_EV_LIFTOFF)
+ cyttsp5_report_proximity(pd, false);
+ break;
+ }
+ }
+}
+
+/* read xy_data for all current touches */
+static int cyttsp5_xy_worker(struct cyttsp5_proximity_data *pd)
+{
+ struct device *dev = pd->dev;
+ struct cyttsp5_sysinfo *si = pd->si;
+ struct cyttsp5_touch tch;
+ u8 num_cur_tch;
+
+ cyttsp5_get_touch_hdr(pd, &tch, si->xy_mode + 3);
+
+ num_cur_tch = tch.hdr[CY_TCH_NUM];
+ if (num_cur_tch > si->sensing_conf_data.max_tch) {
+ dev_err(dev, "%s: Num touch err detected (n=%d)\n",
+ __func__, num_cur_tch);
+ num_cur_tch = si->sensing_conf_data.max_tch;
+ }
+
+ if (tch.hdr[CY_TCH_LO])
+ parade_debug(dev, DEBUG_LEVEL_1, "%s: Large area detected\n",
+ __func__);
+
+ /* extract xy_data for all currently reported touches */
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: extract data num_cur_rec=%d\n",
+ __func__, num_cur_tch);
+ if (num_cur_tch)
+ cyttsp5_get_proximity_touch(pd, &tch, num_cur_tch);
+ else
+ cyttsp5_report_proximity(pd, false);
+
+ return 0;
+}
+
+static int cyttsp5_proximity_attention(struct device *dev)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+ int rc = 0;
+
+ if (pd->si->xy_mode[2] != pd->si->desc.tch_report_id)
+ return 0;
+
+ mutex_lock(&pd->prox_lock);
+ rc = cyttsp5_xy_worker(pd);
+ mutex_unlock(&pd->prox_lock);
+ if (rc < 0)
+ dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+ return rc;
+}
+
+static int cyttsp5_startup_attention(struct device *dev)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+
+ mutex_lock(&pd->prox_lock);
+ cyttsp5_report_proximity(pd, false);
+ mutex_unlock(&pd->prox_lock);
+
+ return 0;
+}
+
+static int _cyttsp5_set_proximity_via_touchmode_enabled(
+ struct cyttsp5_proximity_data *pd, bool enable)
+{
+ struct device *dev = pd->dev;
+ u32 touchmode_enabled;
+ int rc;
+
+ rc = cyttsp5_request_nonhid_get_param(dev, 0,
+ CY_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled);
+ if (rc)
+ return rc;
+
+ if (enable)
+ touchmode_enabled |= 0x80;
+ else
+ touchmode_enabled &= 0x7F;
+
+ rc = cyttsp5_request_nonhid_set_param(dev, 0,
+ CY_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled,
+ CY_RAM_ID_TOUCHMODE_ENABLED_SIZE);
+
+ return rc;
+}
+
+static int _cyttsp5_set_proximity_via_proximity_enable(
+ struct cyttsp5_proximity_data *pd, bool enable)
+{
+ struct device *dev = pd->dev;
+ u32 proximity_enable;
+ int rc;
+
+ rc = cyttsp5_request_nonhid_get_param(dev, 0,
+ CY_RAM_ID_PROXIMITY_ENABLE, &proximity_enable);
+ if (rc)
+ return rc;
+
+ if (enable)
+ proximity_enable |= 0x01;
+ else
+ proximity_enable &= 0xFE;
+
+ rc = cyttsp5_request_nonhid_set_param(dev, 0,
+ CY_RAM_ID_PROXIMITY_ENABLE, proximity_enable,
+ CY_RAM_ID_PROXIMITY_ENABLE_SIZE);
+
+ return rc;
+}
+
+static int _cyttsp5_set_proximity(struct cyttsp5_proximity_data *pd,
+ bool enable)
+{
+ if (!IS_PIP_VER_GE(pd->si, 1, 4))
+ return _cyttsp5_set_proximity_via_touchmode_enabled(pd,
+ enable);
+
+ return _cyttsp5_set_proximity_via_proximity_enable(pd, enable);
+}
+
+static int _cyttsp5_proximity_enable(struct cyttsp5_proximity_data *pd)
+{
+ struct device *dev = pd->dev;
+ int rc = 0;
+
+ pm_runtime_get_sync(dev);
+
+ rc = cyttsp5_request_exclusive(dev,
+ CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto exit;
+ }
+
+ rc = _cyttsp5_set_proximity(pd, true);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request enable proximity scantype r=%d\n",
+ __func__, rc);
+ goto exit_release;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__);
+
+ /* set up touch call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_PROXIMITY_NAME,
+ cyttsp5_proximity_attention, CY_MODE_OPERATIONAL);
+
+ /* set up startup call back */
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0);
+
+exit_release:
+ cyttsp5_release_exclusive(dev);
+exit:
+ return rc;
+}
+
+static int _cyttsp5_proximity_disable(struct cyttsp5_proximity_data *pd,
+ bool force)
+{
+ struct device *dev = pd->dev;
+ int rc = 0;
+
+ rc = cyttsp5_request_exclusive(dev,
+ CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request exclusive r=%d\n",
+ __func__, rc);
+ goto exit;
+ }
+
+ rc = _cyttsp5_set_proximity(pd, false);
+ if (rc < 0) {
+ dev_err(dev, "%s: Error on request disable proximity scan r=%d\n",
+ __func__, rc);
+ goto exit_release;
+ }
+
+exit_release:
+ cyttsp5_release_exclusive(dev);
+
+exit:
+ if (!rc || force) {
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ,
+ CYTTSP5_PROXIMITY_NAME, cyttsp5_proximity_attention,
+ CY_MODE_OPERATIONAL);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0);
+ }
+
+ pm_runtime_put(dev);
+
+ return rc;
+}
+
+static ssize_t cyttsp5_proximity_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+ int val = 0;
+
+ mutex_lock(&pd->sysfs_lock);
+ val = pd->enable_count;
+ mutex_unlock(&pd->sysfs_lock);
+
+ return scnprintf(buf, CY_MAX_PRBUF_SIZE, "%d\n", val);
+}
+
+static ssize_t cyttsp5_proximity_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+ unsigned long value;
+ int rc;
+
+ rc = kstrtoul(buf, 10, &value);
+ if (rc < 0 || (value != 0 && value != 1)) {
+ dev_err(dev, "%s: Invalid value\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&pd->sysfs_lock);
+ if (value) {
+ if (pd->enable_count++) {
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: '%s' already enabled\n",
+ __func__, pd->input->name);
+ } else {
+ rc = _cyttsp5_proximity_enable(pd);
+ if (rc)
+ pd->enable_count--;
+ }
+ } else {
+ if (--pd->enable_count) {
+ if (pd->enable_count < 0) {
+ dev_err(dev, "%s: '%s' unbalanced disable\n",
+ __func__, pd->input->name);
+ pd->enable_count = 0;
+ }
+ } else {
+ rc = _cyttsp5_proximity_disable(pd, false);
+ if (rc)
+ pd->enable_count++;
+ }
+ }
+ mutex_unlock(&pd->sysfs_lock);
+
+ if (rc)
+ return rc;
+
+ return size;
+}
+
+static DEVICE_ATTR(prox_enable, S_IRUSR | S_IWUSR,
+ cyttsp5_proximity_enable_show,
+ cyttsp5_proximity_enable_store);
+
+static int cyttsp5_setup_input_device_and_sysfs(struct device *dev)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+ int signal = CY_IGNORE_VALUE;
+ int i;
+ int rc;
+
+ rc = device_create_file(dev, &dev_attr_prox_enable);
+ if (rc) {
+ dev_err(dev, "%s: Error, could not create enable\n",
+ __func__);
+ goto exit;
+ }
+
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: Initialize event signals\n",
+ __func__);
+
+ __set_bit(EV_ABS, pd->input->evbit);
+
+ /* set event signal capabilities */
+ for (i = 0; i < NUM_SIGNALS(pd->pdata->frmwrk); i++) {
+ signal = PARAM_SIGNAL(pd->pdata->frmwrk, i);
+ if (signal != CY_IGNORE_VALUE) {
+ input_set_abs_params(pd->input, signal,
+ PARAM_MIN(pd->pdata->frmwrk, i),
+ PARAM_MAX(pd->pdata->frmwrk, i),
+ PARAM_FUZZ(pd->pdata->frmwrk, i),
+ PARAM_FLAT(pd->pdata->frmwrk, i));
+ }
+ }
+
+ rc = input_register_device(pd->input);
+ if (rc) {
+ dev_err(dev, "%s: Error, failed register input device r=%d\n",
+ __func__, rc);
+ goto unregister_enable;
+ }
+
+ pd->input_device_registered = true;
+ return rc;
+
+unregister_enable:
+ device_remove_file(dev, &dev_attr_prox_enable);
+exit:
+ return rc;
+}
+
+static int cyttsp5_setup_input_attention(struct device *dev)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+ int rc;
+
+ pd->si = _cyttsp5_request_sysinfo(dev);
+ if (!pd->si)
+ return -EINVAL;
+
+ rc = cyttsp5_setup_input_device_and_sysfs(dev);
+ if (!rc)
+ rc = _cyttsp5_set_proximity(pd, false);
+
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention, 0);
+
+ return rc;
+}
+
+int cyttsp5_proximity_probe(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+ struct cyttsp5_proximity_data *pd = &cd->pd;
+ struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
+ struct cyttsp5_proximity_platform_data *prox_pdata;
+ int rc = 0;
+
+ if (!pdata || !pdata->prox_pdata) {
+ dev_err(dev, "%s: Missing platform data\n", __func__);
+ rc = -ENODEV;
+ goto error_no_pdata;
+ }
+ prox_pdata = pdata->prox_pdata;
+
+ mutex_init(&pd->prox_lock);
+ mutex_init(&pd->sysfs_lock);
+ pd->dev = dev;
+ pd->pdata = prox_pdata;
+
+ /* Create the input device and register it. */
+ parade_debug(dev, DEBUG_LEVEL_2,
+ "%s: Create the input device and register it\n", __func__);
+ pd->input = input_allocate_device();
+ if (!pd->input) {
+ dev_err(dev, "%s: Error, failed to allocate input device\n",
+ __func__);
+ rc = -ENODEV;
+ goto error_alloc_failed;
+ }
+
+ if (pd->pdata->inp_dev_name)
+ pd->input->name = pd->pdata->inp_dev_name;
+ else
+ pd->input->name = CYTTSP5_PROXIMITY_NAME;
+ scnprintf(pd->phys, sizeof(pd->phys), "%s/input%d", dev_name(dev),
+ cd->phys_num++);
+ pd->input->phys = pd->phys;
+ pd->input->dev.parent = pd->dev;
+ input_set_drvdata(pd->input, pd);
+
+ /* get sysinfo */
+ pd->si = _cyttsp5_request_sysinfo(dev);
+
+ if (pd->si) {
+ rc = cyttsp5_setup_input_device_and_sysfs(dev);
+ if (rc)
+ goto error_init_input;
+
+ rc = _cyttsp5_set_proximity(pd, false);
+ } else {
+ dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+ __func__, pd->si);
+ _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention,
+ 0);
+ }
+
+ return 0;
+
+error_init_input:
+ input_free_device(pd->input);
+error_alloc_failed:
+error_no_pdata:
+ dev_err(dev, "%s failed.\n", __func__);
+ return rc;
+}
+
+int cyttsp5_proximity_release(struct device *dev)
+{
+ struct cyttsp5_proximity_data *pd = get_prox_data(dev);
+
+ if (pd->input_device_registered) {
+ /* Disable proximity sensing */
+ mutex_lock(&pd->sysfs_lock);
+ if (pd->enable_count)
+ _cyttsp5_proximity_disable(pd, true);
+ mutex_unlock(&pd->sysfs_lock);
+ device_remove_file(dev, &dev_attr_prox_enable);
+ input_unregister_device(pd->input);
+ } else {
+ input_free_device(pd->input);
+ _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
+ CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention,
+ 0);
+ }
+
+ return 0;
+}
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_regs.h b/drivers/input/touchscreen/cyttsp5/cyttsp5_regs.h
new file mode 100644
index 0000000..0a6066c
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_regs.h
@@ -0,0 +1,1186 @@
+/*
+ * cyttsp5_regs.h
+ * Parade TrueTouch(TM) Standard Product V5 Registers.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#ifndef _CYTTSP5_REGS_H
+#define _CYTTSP5_REGS_H
+
+#include <linux/device.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#elif defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+#include <asm/unaligned.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/version.h>
+#include <linux/input/cyttsp5_core.h>
+
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+
+/* #define EASYWAKE_TSG6 */
+
+#define CY_FW_FILE_PREFIX "cyttsp5_fw"
+#define CY_FW_FILE_SUFFIX ".bin"
+#define CY_FW_FILE_NAME "cyttsp5_fw.bin"
+
+#ifdef TTHE_TUNER_SUPPORT
+#define CYTTSP5_TTHE_TUNER_FILE_NAME "tthe_tuner"
+#endif
+
+#define CY_MAX_PRBUF_SIZE PIPE_BUF
+#define CY_PR_TRUNCATED " truncated..."
+
+#define CY_DEFAULT_CORE_ID "cyttsp5_core0"
+#define CY_MAX_NUM_CORE_DEVS 5
+#define CY_IRQ_ASSERTED_VALUE 0
+
+
+#ifdef CY_ENABLE_MAX_ELEN
+#define CY_MAX_ELEN 100
+#endif
+
+enum PARADE_DEBUG_LEVEL {
+ DEBUG_LEVEL_0,/*no debug info*/
+ DEBUG_LEVEL_1,/*only print dev_dbg info*/
+ DEBUG_LEVEL_2,/*print dev_dbg and dev_vdbg info*/
+ DEBUG_LEVEL_NUM
+};
+
+#define CY_INITIAL_DEBUG_LEVEL 0
+#define CY_INITIAL_SHOW_TIME_STAMP 0
+
+/*
+* those current debug level smaller than setted debug level
+* will be printed out
+*/
+
+#define parade_debug(dev, dlevel, format, arg...) \
+ do { \
+ struct cyttsp5_core_data *cd_tmp = dev_get_drvdata(dev);\
+ if (cd_tmp->debug_level >= dlevel) {\
+ dev_dbg(dev, format, ##arg);\
+ } \
+ } while (0)
+
+
+/* HID */
+#define HID_CYVENDOR 0xff010000
+#define CY_HID_VENDOR_ID 0x04B4
+#define CY_HID_BL_PRODUCT_ID 0xC100
+#define CY_HID_APP_PRODUCT_ID 0xC101
+#define CY_HID_VERSION 0x0100
+#define CY_HID_APP_REPORT_ID 0xF7
+#define CY_HID_BL_REPORT_ID 0xFF
+
+#define HID_INVALID_REPORT_ID 0x0
+#define HID_TOUCH_REPORT_ID 0x1
+#define HID_BTN_REPORT_ID 0x3
+#define HID_WAKEUP_REPORT_ID 0x4
+#define HID_NOISE_METRIC_REPORT_ID 0x5
+#define HID_TRACKING_HEATMAP_REPOR_ID 0xE
+#define HID_SENSOR_DATA_REPORT_ID 0xF
+#define HID_APP_RESPONSE_REPORT_ID 0x1F
+#define HID_APP_OUTPUT_REPORT_ID 0x2F
+#define HID_BL_RESPONSE_REPORT_ID 0x30
+#define HID_BL_OUTPUT_REPORT_ID 0x40
+#define HID_RESPONSE_REPORT_ID 0xF0
+
+#define HID_OUTPUT_RESPONSE_REPORT_OFFSET 2
+#define HID_OUTPUT_RESPONSE_CMD_OFFSET 4
+#define HID_OUTPUT_RESPONSE_CMD_MASK 0x7F
+#define HID_OUTPUT_CMD_OFFSET 6
+#define HID_OUTPUT_CMD_MASK 0x7F
+
+#define HID_SYSINFO_CYDATA_OFFSET 5
+#define HID_SYSINFO_SENSING_OFFSET 33
+#define HID_SYSINFO_BTN_OFFSET 48
+#define HID_SYSINFO_BTN_MASK 0xFF
+#define HID_SYSINFO_MAX_BTN 8
+
+#define HID_POWER_ON 0x0
+#define HID_POWER_SLEEP 0x1
+#define HID_LENGTH_BYTES 2
+#define HID_LENGTH_AND_REPORT_ID_BYTES 3
+
+/* Timeout in ms */
+#define CY_REQUEST_EXCLUSIVE_TIMEOUT 8000
+#define CY_WATCHDOG_TIMEOUT 1000
+#define CY_HID_RESET_TIMEOUT 5000
+#define CY_HID_AUTO_CALI_CPLT_TIMEOUT 2500
+/* HID_DESCRIPTOR_TIMEOUT value based on FW spec (CAL_OS) */
+#define CY_HID_GET_HID_DESCRIPTOR_TIMEOUT 4000
+#define CY_HID_GET_REPORT_DESCRIPTOR_TIMEOUT 500
+#define CY_HID_SET_POWER_TIMEOUT 500
+#ifdef VERBOSE_DEBUG
+#define CY_HID_OUTPUT_TIMEOUT 2000
+#else
+#define CY_HID_OUTPUT_TIMEOUT 200
+#endif
+#define CY_HID_OUTPUT_START_BOOTLOADER_TIMEOUT 2000
+#define CY_HID_OUTPUT_USER_TIMEOUT 8000
+#define CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT 3000
+#define CY_HID_OUTPUT_CALIBRATE_IDAC_TIMEOUT 5000
+#define CY_HID_OUTPUT_WRITE_CONF_BLOCK_TIMEOUT 400
+#define CY_HID_OUTPUT_RUN_SELF_TEST_TIMEOUT 10000
+#define CY_HID_OUTPUT_BL_INITIATE_BL_TIMEOUT 20000
+#define CY_HID_OUTPUT_BL_PROGRAM_AND_VERIFY_TIMEOUT 400
+
+#define CY_WATCHDOG_RETRY_COUNT 60
+
+/* maximum number of concurrent tracks */
+#define TOUCH_REPORT_SIZE 10
+#define TOUCH_INPUT_HEADER_SIZE 7
+#define TOUCH_COUNT_BYTE_OFFSET 5
+#define BTN_REPORT_SIZE 9
+#define BTN_INPUT_HEADER_SIZE 5
+#define SENSOR_REPORT_SIZE 150
+#define SENSOR_HEADER_SIZE 4
+
+/* helpers */
+#define GET_NUM_TOUCHES(x) ((x) & 0x1F)
+#define IS_LARGE_AREA(x) ((x) & 0x20)
+#define IS_BAD_PKT(x) ((x) & 0x20)
+#define IS_TMO(t) ((t) == 0)
+#define HI_BYTE(x) (u8)(((x) >> 8) & 0xFF)
+#define LOW_BYTE(x) (u8)((x) & 0xFF)
+#define SET_CMD_LOW(byte, bits) \
+ ((byte) = (((byte) & 0xF0) | ((bits) & 0x0F)))
+#define SET_CMD_HIGH(byte, bits)\
+ ((byte) = (((byte) & 0x0F) | ((bits) & 0xF0)))
+
+#define GET_MASK(length) \
+ ((1 << length) - 1)
+#define GET_FIELD(name, length, shift) \
+ ((name >> shift) & GET_MASK(length))
+
+#define HID_ITEM_SIZE_MASK 0x03
+#define HID_ITEM_TYPE_MASK 0x0C
+#define HID_ITEM_TAG_MASK 0xF0
+
+#define HID_ITEM_SIZE_SHIFT 0
+#define HID_ITEM_TYPE_SHIFT 2
+#define HID_ITEM_TAG_SHIFT 4
+
+#define HID_GET_ITEM_SIZE(x) \
+ ((x & HID_ITEM_SIZE_MASK) >> HID_ITEM_SIZE_SHIFT)
+#define HID_GET_ITEM_TYPE(x) \
+ ((x & HID_ITEM_TYPE_MASK) >> HID_ITEM_TYPE_SHIFT)
+#define HID_GET_ITEM_TAG(x) \
+ ((x & HID_ITEM_TAG_MASK) >> HID_ITEM_TAG_SHIFT)
+
+#define IS_DEEP_SLEEP_CONFIGURED(x) \
+ ((x) == 0 || (x) == 0xFF)
+
+#define IS_PIP_VER_GE(p, maj, min) \
+ ((p)->cydata.pip_ver_major < (maj) ? \
+ 0 : \
+ ((p)->cydata.pip_ver_minor < (min) ? \
+ 0 : \
+ 1))
+
+/* drv_debug commands */
+#define CY_DBG_SUSPEND 4
+#define CY_DBG_RESUME 5
+#define CY_DBG_SOFT_RESET 97
+#define CY_DBG_RESET 98
+#define CY_DBG_HID_RESET 50
+#define CY_DBG_HID_GET_REPORT 51
+#define CY_DBG_HID_SET_REPORT 52
+#define CY_DBG_HID_SET_POWER_ON 53
+#define CY_DBG_HID_SET_POWER_SLEEP 54
+#define CY_DBG_HID_NULL 100
+#define CY_DBG_HID_ENTER_BL 101
+#define CY_DBG_HID_SYSINFO 102
+#define CY_DBG_HID_SUSPEND_SCAN 103
+#define CY_DBG_HID_RESUME_SCAN 104
+#define CY_DBG_HID_STOP_WD 105
+#define CY_DBG_HID_START_WD 106
+
+#define CY_TTHE_TUNER_EXIT 107
+#define CY_TTHE_BUF_CLEAN 108
+
+/*
+ * Commands that require additional parameters
+ * will be in the 200 range. Commands that do not
+ * require additional parameters remain below 200.
+ */
+#define CY_DBG_REPORT_LEVEL 200
+#define CY_DBG_WATCHDOG_INTERVAL 201
+#define CY_DBG_SHOW_TIMESTAMP 202
+
+/* Recognized usages */
+/* undef them first for possible redefinition in Linux */
+#undef HID_DI_PRESSURE
+#undef HID_DI_TIP
+#undef HID_DI_CONTACTID
+#undef HID_DI_CONTACTCOUNT
+#undef HID_DI_SCANTIME
+#define HID_DI_PRESSURE 0x000d0030
+#define HID_DI_TIP 0x000d0042
+#define HID_DI_CONTACTID 0x000d0051
+#define HID_DI_CONTACTCOUNT 0x000d0054
+#define HID_DI_SCANTIME 0x000d0056
+
+/* Parade vendor specific usages */
+#define HID_CY_UNDEFINED 0xff010000
+#define HID_CY_BOOTLOADER 0xff010001
+#define HID_CY_TOUCHAPPLICATION 0xff010002
+#define HID_CY_BUTTONS 0xff010020
+#define HID_CY_GENERICITEM 0xff010030
+#define HID_CY_LARGEOBJECT 0xff010040
+#define HID_CY_NOISEEFFECTS 0xff010041
+#define HID_CY_REPORTCOUNTER 0xff010042
+#define HID_CY_TOUCHTYPE 0xff010060
+#define HID_CY_EVENTID 0xff010061
+#define HID_CY_MAJORAXISLENGTH 0xff010062
+#define HID_CY_MINORAXISLENGTH 0xff010063
+#define HID_CY_ORIENTATION 0xff010064
+#define HID_CY_BUTTONSIGNAL 0xff010065
+#define HID_CY_MAJOR_CONTACT_AXIS_LENGTH 0xff010066
+#define HID_CY_MINOR_CONTACT_AXIS_LENGTH 0xff010067
+#define HID_CY_TCH_COL_USAGE_PG 0x000D0022
+#define HID_CY_BTN_COL_USAGE_PG 0xFF010020
+
+#define PANEL_ID_NOT_ENABLED 0xFF
+
+#ifdef EASYWAKE_TSG6
+#define GESTURE_DOUBLE_TAP (1)
+#define GESTURE_TWO_FINGERS_SLIDE (2)
+#define GESTURE_TOUCH_DETECTED (3)
+#define GESTURE_PUSH_BUTTON (4)
+#define GESTURE_SINGLE_SLIDE_DE_TX (5)
+#define GESTURE_SINGLE_SLIDE_IN_TX (6)
+#define GESTURE_SINGLE_SLIDE_DE_RX (7)
+#define GESTURE_SINGLE_SLIDE_IN_RX (8)
+#endif
+
+/* FW RAM parameters */
+#define CY_RAM_ID_TOUCHMODE_ENABLED 0x02
+#define CY_RAM_ID_PROXIMITY_ENABLE 0x20
+#define CY_RAM_ID_TOUCHMODE_ENABLED_SIZE 1
+#define CY_RAM_ID_PROXIMITY_ENABLE_SIZE 1
+
+/* abs signal capabilities offsets in the frameworks array */
+enum cyttsp5_sig_caps {
+ CY_SIGNAL_OST,
+ CY_MIN_OST,
+ CY_MAX_OST,
+ CY_FUZZ_OST,
+ CY_FLAT_OST,
+ CY_NUM_ABS_SET /* number of signal capability fields */
+};
+
+/* helpers */
+#define NUM_SIGNALS(frmwrk) ((frmwrk)->size / CY_NUM_ABS_SET)
+#define PARAM(frmwrk, sig_ost, cap_ost) \
+ ((frmwrk)->abs[((sig_ost) * CY_NUM_ABS_SET) + (cap_ost)])
+
+#define PARAM_SIGNAL(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_SIGNAL_OST)
+#define PARAM_MIN(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_MIN_OST)
+#define PARAM_MAX(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_MAX_OST)
+#define PARAM_FUZZ(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_FUZZ_OST)
+#define PARAM_FLAT(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_FLAT_OST)
+
+/* abs axis signal offsets in the framworks array */
+enum cyttsp5_sig_ost {
+ CY_ABS_X_OST,
+ CY_ABS_Y_OST,
+ CY_ABS_P_OST,
+ CY_ABS_W_OST,
+ CY_ABS_ID_OST,
+ CY_ABS_MAJ_OST,
+ CY_ABS_MIN_OST,
+ CY_ABS_OR_OST,
+ CY_ABS_TOOL_OST,
+ CY_ABS_D_OST,
+ CY_NUM_ABS_OST /* number of abs signals */
+};
+
+enum hid_command {
+ HID_CMD_RESERVED,
+ HID_CMD_RESET,
+ HID_CMD_GET_REPORT,
+ HID_CMD_SET_REPORT,
+ HID_CMD_GET_IDLE,
+ HID_CMD_SET_IDLE,
+ HID_CMD_GET_PROTOCOL,
+ HID_CMD_SET_PROTOCOL,
+ HID_CMD_SET_POWER,
+ HID_CMD_VENDOR = 0xE,
+};
+
+enum hid_output_cmd_type {
+ HID_OUTPUT_CMD_APP,
+ HID_OUTPUT_CMD_BL,
+};
+
+enum hid_output {
+ HID_OUTPUT_NULL,
+ HID_OUTPUT_START_BOOTLOADER,
+ HID_OUTPUT_GET_SYSINFO,
+ HID_OUTPUT_SUSPEND_SCANNING,
+ HID_OUTPUT_RESUME_SCANNING,
+ HID_OUTPUT_GET_PARAM,
+ HID_OUTPUT_SET_PARAM,
+ HID_OUTPUT_GET_NOISE_METRICS,
+ HID_OUTPUT_RESERVED,
+ HID_OUTPUT_ENTER_EASYWAKE_STATE,
+ HID_OUTPUT_VERIFY_CONFIG_BLOCK_CRC = 0x20,
+ HID_OUTPUT_GET_CONFIG_ROW_SIZE,
+ HID_OUTPUT_READ_CONF_BLOCK,
+ HID_OUTPUT_WRITE_CONF_BLOCK,
+ HID_OUTPUT_GET_DATA_STRUCTURE,
+ HID_OUTPUT_LOAD_SELF_TEST_PARAM,
+ HID_OUTPUT_RUN_SELF_TEST,
+ HID_OUTPUT_GET_SELF_TEST_RESULT,
+ HID_OUTPUT_CALIBRATE_IDACS,
+ HID_OUTPUT_INITIALIZE_BASELINES,
+ HID_OUTPUT_EXEC_PANEL_SCAN,
+ HID_OUTPUT_RETRIEVE_PANEL_SCAN,
+ HID_OUTPUT_START_SENSOR_DATA_MODE,
+ HID_OUTPUT_STOP_SENSOR_DATA_MODE,
+ HID_OUTPUT_START_TRACKING_HEATMAP_MODE,
+ HID_OUTPUT_INT_PIN_OVERRIDE = 0x40,
+ HID_OUTPUT_STORE_PANEL_SCAN = 0x60,
+ HID_OUTPUT_PROCESS_PANEL_SCAN,
+ HID_OUTPUT_DISCARD_INPUT_REPORT,
+ HID_OUTPUT_LAST,
+ HID_OUTPUT_USER_CMD,
+};
+
+enum hid_output_bl {
+ HID_OUTPUT_BL_VERIFY_APP_INTEGRITY = 0x31,
+ HID_OUTPUT_BL_GET_INFO = 0x38,
+ HID_OUTPUT_BL_PROGRAM_AND_VERIFY,
+ HID_OUTPUT_BL_LAUNCH_APP = 0x3B,
+ HID_OUTPUT_BL_GET_PANEL_ID = 0x3E,
+ HID_OUTPUT_BL_INITIATE_BL = 0x48,
+ HID_OUTPUT_BL_LAST,
+};
+
+#define HID_OUTPUT_BL_SOP 0x1
+#define HID_OUTPUT_BL_EOP 0x17
+
+enum hid_output_bl_status {
+ ERROR_SUCCESS,
+ ERROR_KEY,
+ ERROR_VERIFICATION,
+ ERROR_LENGTH,
+ ERROR_DATA,
+ ERROR_COMMAND,
+ ERROR_CRC = 8,
+ ERROR_FLASH_ARRAY,
+ ERROR_FLASH_ROW,
+ ERROR_FLASH_PROTECTION,
+ ERROR_UKNOWN = 15,
+ ERROR_INVALID,
+};
+
+enum cyttsp5_mode {
+ CY_MODE_UNKNOWN,
+ CY_MODE_BOOTLOADER,
+ CY_MODE_OPERATIONAL,
+};
+
+enum cyttsp5_cmd_status {
+ CY_CMD_STATUS_SUCCESS,
+ CY_CMD_STATUS_FAILURE,
+};
+
+enum {
+ CY_IC_GRPNUM_RESERVED,
+ CY_IC_GRPNUM_CMD_REGS,
+ CY_IC_GRPNUM_TCH_REP,
+ CY_IC_GRPNUM_DATA_REC,
+ CY_IC_GRPNUM_TEST_REC,
+ CY_IC_GRPNUM_PCFG_REC,
+ CY_IC_GRPNUM_TCH_PARM_VAL,
+ CY_IC_GRPNUM_TCH_PARM_SIZE,
+ CY_IC_GRPNUM_RESERVED1,
+ CY_IC_GRPNUM_RESERVED2,
+ CY_IC_GRPNUM_OPCFG_REC,
+ CY_IC_GRPNUM_DDATA_REC,
+ CY_IC_GRPNUM_MDATA_REC,
+ CY_IC_GRPNUM_TEST_REGS,
+ CY_IC_GRPNUM_BTN_KEYS,
+ CY_IC_GRPNUM_TTHE_REGS,
+ CY_IC_GRPNUM_SENSING_CONF,
+ CY_IC_GRPNUM_NUM,
+};
+
+enum cyttsp5_event_id {
+ CY_EV_NO_EVENT,
+ CY_EV_TOUCHDOWN,
+ CY_EV_MOVE, /* significant displacement (> act dist) */
+ CY_EV_LIFTOFF, /* record reports last position */
+};
+
+enum cyttsp5_object_id {
+ CY_OBJ_STANDARD_FINGER,
+ CY_OBJ_PROXIMITY,
+ CY_OBJ_STYLUS,
+ CY_OBJ_HOVER,
+ CY_OBJ_GLOVE,
+};
+
+enum cyttsp5_self_test_result {
+ CY_ST_RESULT_PASS,
+ CY_ST_RESULT_FAIL,
+ CY_ST_RESULT_HOST_MUST_INTERPRET = 0xFF,
+};
+
+enum cyttsp5_self_test_id {
+ CY_ST_ID_NULL,
+ CY_ST_ID_BIST,
+ CY_ST_ID_SHORTS,
+ CY_ST_ID_OPENS,
+ CY_ST_ID_AUTOSHORTS,
+ CY_ST_ID_CM_PANEL,
+ CY_ST_ID_CP_PANEL,
+ CY_ST_ID_CM_BUTTON,
+ CY_ST_ID_CP_BUTTON,
+};
+
+#define CY_NUM_MFGID 8
+
+/* System Information interface definitions */
+struct cyttsp5_cydata_dev {
+ u8 pip_ver_major;
+ u8 pip_ver_minor;
+ __le16 fw_pid;
+ u8 fw_ver_major;
+ u8 fw_ver_minor;
+ __le32 revctrl;
+ __le16 fw_ver_conf;
+ u8 bl_ver_major;
+ u8 bl_ver_minor;
+ __le16 jtag_si_id_l;
+ __le16 jtag_si_id_h;
+ u8 mfg_id[CY_NUM_MFGID];
+ __le16 post_code;
+} __packed;
+
+struct cyttsp5_sensing_conf_data_dev {
+ u8 electrodes_x;
+ u8 electrodes_y;
+ __le16 len_x;
+ __le16 len_y;
+ __le16 res_x;
+ __le16 res_y;
+ __le16 max_z;
+ u8 origin_x;
+ u8 origin_y;
+ u8 panel_id;
+ u8 btn;
+ u8 scan_mode;
+ u8 max_num_of_tch_per_refresh_cycle;
+} __packed;
+
+struct cyttsp5_cydata {
+ u8 pip_ver_major;
+ u8 pip_ver_minor;
+ u8 bl_ver_major;
+ u8 bl_ver_minor;
+ u8 fw_ver_major;
+ u8 fw_ver_minor;
+ u16 fw_pid;
+ u16 fw_ver_conf;
+ u16 post_code;
+ u32 revctrl;
+ u16 jtag_id_l;
+ u16 jtag_id_h;
+ u8 mfg_id[CY_NUM_MFGID];
+};
+
+struct cyttsp5_sensing_conf_data {
+ u16 res_x;
+ u16 res_y;
+ u16 max_z;
+ u16 len_x;
+ u16 len_y;
+ u8 electrodes_x;
+ u8 electrodes_y;
+ u8 origin_x;
+ u8 origin_y;
+ u8 panel_id;
+ u8 btn;
+ u8 scan_mode;
+ u8 max_tch;
+ u8 rx_num;
+ u8 tx_num;
+};
+
+enum cyttsp5_tch_abs { /* for ordering within the extracted touch data array */
+ CY_TCH_X, /* X */
+ CY_TCH_Y, /* Y */
+ CY_TCH_P, /* P (Z) */
+ CY_TCH_T, /* TOUCH ID */
+ CY_TCH_E, /* EVENT ID */
+ CY_TCH_O, /* OBJECT ID */
+ CY_TCH_TIP, /* OBJECT ID */
+ CY_TCH_MAJ, /* TOUCH_MAJOR */
+ CY_TCH_MIN, /* TOUCH_MINOR */
+ CY_TCH_OR, /* ORIENTATION */
+ CY_TCH_NUM_ABS,
+};
+
+enum cyttsp5_tch_hdr {
+ CY_TCH_TIME, /* SCAN TIME */
+ CY_TCH_NUM, /* NUMBER OF RECORDS */
+ CY_TCH_LO, /* LARGE OBJECT */
+ CY_TCH_NOISE, /* NOISE EFFECT */
+ CY_TCH_COUNTER, /* REPORT_COUNTER */
+ CY_TCH_NUM_HDR,
+};
+
+static const char * const cyttsp5_tch_abs_string[] = {
+ [CY_TCH_X] = "X",
+ [CY_TCH_Y] = "Y",
+ [CY_TCH_P] = "P",
+ [CY_TCH_T] = "T",
+ [CY_TCH_E] = "E",
+ [CY_TCH_O] = "O",
+ [CY_TCH_TIP] = "TIP",
+ [CY_TCH_MAJ] = "MAJ",
+ [CY_TCH_MIN] = "MIN",
+ [CY_TCH_OR] = "OR",
+ [CY_TCH_NUM_ABS] = "INVALID",
+};
+
+static const char * const cyttsp5_tch_hdr_string[] = {
+ [CY_TCH_TIME] = "SCAN TIME",
+ [CY_TCH_NUM] = "NUMBER OF RECORDS",
+ [CY_TCH_LO] = "LARGE OBJECT",
+ [CY_TCH_NOISE] = "NOISE EFFECT",
+ [CY_TCH_COUNTER] = "REPORT_COUNTER",
+ [CY_TCH_NUM_HDR] = "INVALID",
+};
+
+static const int cyttsp5_tch_abs_field_map[] = {
+ [CY_TCH_X] = 0x00010030 /* HID_GD_X */,
+ [CY_TCH_Y] = 0x00010031 /* HID_GD_Y */,
+ [CY_TCH_P] = HID_DI_PRESSURE,
+ [CY_TCH_T] = HID_DI_CONTACTID,
+ [CY_TCH_E] = HID_CY_EVENTID,
+ [CY_TCH_O] = HID_CY_TOUCHTYPE,
+ [CY_TCH_TIP] = HID_DI_TIP,
+ [CY_TCH_MAJ] = HID_CY_MAJORAXISLENGTH,
+ [CY_TCH_MIN] = HID_CY_MINORAXISLENGTH,
+ [CY_TCH_OR] = HID_CY_ORIENTATION,
+ [CY_TCH_NUM_ABS] = 0,
+};
+
+static const int cyttsp5_tch_hdr_field_map[] = {
+ [CY_TCH_TIME] = HID_DI_SCANTIME,
+ [CY_TCH_NUM] = HID_DI_CONTACTCOUNT,
+ [CY_TCH_LO] = HID_CY_LARGEOBJECT,
+ [CY_TCH_NOISE] = HID_CY_NOISEEFFECTS,
+ [CY_TCH_COUNTER] = HID_CY_REPORTCOUNTER,
+ [CY_TCH_NUM_HDR] = 0,
+};
+
+#define CY_NUM_EXT_TCH_FIELDS 3
+
+struct cyttsp5_tch_abs_params {
+ size_t ofs; /* abs byte offset */
+ size_t size; /* size in bits */
+ size_t min; /* min value */
+ size_t max; /* max value */
+ size_t bofs; /* bit offset */
+ u8 report;
+};
+
+struct cyttsp5_touch {
+ int hdr[CY_TCH_NUM_HDR];
+ int abs[CY_TCH_NUM_ABS];
+};
+
+/* button to keycode support */
+#define CY_BITS_PER_BTN 1
+#define CY_NUM_BTN_EVENT_ID ((1 << CY_BITS_PER_BTN) - 1)
+
+enum cyttsp5_btn_state {
+ CY_BTN_RELEASED = 0,
+ CY_BTN_PRESSED = 1,
+ CY_BTN_NUM_STATE
+};
+
+struct cyttsp5_btn {
+ bool enabled;
+ int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */
+ int key_code;
+};
+
+enum cyttsp5_ic_ebid {
+ CY_TCH_PARM_EBID,
+ CY_MDATA_EBID,
+ CY_DDATA_EBID,
+};
+
+/* ttconfig block */
+#define CY_TTCONFIG_VERSION_OFFSET 8
+#define CY_TTCONFIG_VERSION_SIZE 2
+#define CY_TTCONFIG_VERSION_ROW 0
+
+struct cyttsp5_ttconfig {
+ u16 version;
+ u16 crc;
+};
+
+struct cyttsp5_report_desc_data {
+ u16 tch_report_id;
+ u16 tch_record_size;
+ u16 tch_header_size;
+ u16 btn_report_id;
+};
+
+struct cyttsp5_sysinfo {
+ bool ready;
+ struct cyttsp5_cydata cydata;
+ struct cyttsp5_sensing_conf_data sensing_conf_data;
+ struct cyttsp5_report_desc_data desc;
+ int num_btns;
+ struct cyttsp5_btn *btn;
+ struct cyttsp5_ttconfig ttconfig;
+ struct cyttsp5_tch_abs_params tch_hdr[CY_TCH_NUM_HDR];
+ struct cyttsp5_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
+ u8 *xy_mode;
+ u8 *xy_data;
+};
+
+enum cyttsp5_atten_type {
+ CY_ATTEN_IRQ,
+ CY_ATTEN_STARTUP,
+ CY_ATTEN_EXCLUSIVE,
+ CY_ATTEN_WAKE,
+ CY_ATTEN_LOADER,
+ CY_ATTEN_SUSPEND,
+ CY_ATTEN_RESUME,
+ CY_ATTEN_NUM_ATTEN,
+};
+
+enum cyttsp5_sleep_state {
+ SS_SLEEP_OFF,
+ SS_SLEEP_ON,
+ SS_SLEEPING,
+ SS_WAKING,
+};
+
+enum cyttsp5_fb_state {
+ FB_ON,
+ FB_OFF,
+};
+
+enum cyttsp5_startup_state {
+ STARTUP_NONE,
+ STARTUP_QUEUED,
+ STARTUP_RUNNING,
+ STARTUP_ILLEGAL,
+};
+
+struct cyttsp5_hid_desc {
+ __le16 hid_desc_len;
+ u8 packet_id;
+ u8 reserved_byte;
+ __le16 bcd_version;
+ __le16 report_desc_len;
+ __le16 report_desc_register;
+ __le16 input_register;
+ __le16 max_input_len;
+ __le16 output_register;
+ __le16 max_output_len;
+ __le16 command_register;
+ __le16 data_register;
+ __le16 vendor_id;
+ __le16 product_id;
+ __le16 version_id;
+ u8 reserved[4];
+} __packed;
+
+struct cyttsp5_hid_core {
+ u16 hid_vendor_id;
+ u16 hid_product_id;
+ __le16 hid_desc_register;
+ u16 hid_report_desc_len;
+ u16 hid_max_input_len;
+ u16 hid_max_output_len;
+};
+
+#define CY_HID_MAX_REPORTS 8
+#define CY_HID_MAX_FIELDS 128
+#define CY_HID_MAX_COLLECTIONS 3
+#define CY_HID_MAX_NESTED_COLLECTIONS CY_HID_MAX_COLLECTIONS
+
+#define CY_MAX_INPUT 512
+#define CY_PIP_1P7_EMPTY_BUF 0xFF00
+
+enum cyttsp5_module_id {
+ CY_MODULE_MT,
+ CY_MODULE_BTN,
+ CY_MODULE_PROX,
+ CY_MODULE_LAST,
+};
+
+struct cyttsp5_mt_data;
+struct cyttsp5_mt_function {
+ int (*mt_release)(struct device *dev);
+ int (*mt_probe)(struct device *dev, struct cyttsp5_mt_data *md);
+ void (*report_slot_liftoff)(struct cyttsp5_mt_data *md, int max_slots);
+ void (*input_sync)(struct input_dev *input);
+ void (*input_report)(struct input_dev *input, int sig, int t, int type);
+ void (*final_sync)(struct input_dev *input, int max_slots,
+ int mt_sync_count, unsigned long *ids);
+ int (*input_register_device)(struct input_dev *input, int max_slots);
+};
+
+struct cyttsp5_mt_data {
+ struct device *dev;
+ struct cyttsp5_mt_platform_data *pdata;
+ struct cyttsp5_sysinfo *si;
+ struct input_dev *input;
+ struct cyttsp5_mt_function mt_function;
+ struct mutex mt_lock;
+ bool is_suspended;
+ bool input_device_registered;
+ char phys[NAME_MAX];
+ int num_prv_rec;
+ int or_min;
+ int or_max;
+ int t_min;
+ int t_max;
+};
+
+struct cyttsp5_btn_data {
+ struct device *dev;
+ struct cyttsp5_btn_platform_data *pdata;
+ struct cyttsp5_sysinfo *si;
+ struct input_dev *input;
+ struct mutex btn_lock;
+ bool is_suspended;
+ bool input_device_registered;
+ char phys[NAME_MAX];
+};
+
+struct cyttsp5_proximity_data {
+ struct device *dev;
+ struct cyttsp5_proximity_platform_data *pdata;
+ struct cyttsp5_sysinfo *si;
+ struct input_dev *input;
+ struct mutex prox_lock;
+ struct mutex sysfs_lock;
+ int enable_count;
+ bool input_device_registered;
+ char phys[NAME_MAX];
+};
+
+enum cyttsp5_calibrate_idacs_sensing_mode {
+ CY_CI_SM_MUTCAP_FINE,
+ CY_CI_SM_MUTCAP_BUTTON,
+ CY_CI_SM_SELFCAP,
+};
+
+enum cyttsp5_initialize_baselines_sensing_mode {
+ CY_IB_SM_MUTCAP = 1,
+ CY_IB_SM_BUTTON = 2,
+ CY_IB_SM_SELFCAP = 4,
+ CY_IB_SM_BALANCED = 8,
+};
+
+struct cyttsp5_core_nonhid_cmd {
+ int (*start_bl)(struct device *dev, int protect);
+ int (*suspend_scanning)(struct device *dev, int protect);
+ int (*resume_scanning)(struct device *dev, int protect);
+ int (*get_param)(struct device *dev, int protect, u8 param_id,
+ u32 *value);
+ int (*set_param)(struct device *dev, int protect, u8 param_id,
+ u32 value, u8 size);
+ int (*verify_config_block_crc)(struct device *dev, int protect,
+ u8 ebid, u8 *status, u16 *calculated_crc,
+ u16 *stored_crc);
+ int (*get_config_row_size)(struct device *dev, int protect,
+ u16 *row_size);
+ int (*get_data_structure)(struct device *dev, int protect,
+ u16 read_offset, u16 read_length, u8 data_id,
+ u8 *status, u8 *data_format, u16 *actual_read_len,
+ u8 *data);
+ int (*run_selftest)(struct device *dev, int protect, u8 test_id,
+ u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+ u8 *results_available);
+ int (*get_selftest_result)(struct device *dev, int protect,
+ u16 read_offset, u16 read_length, u8 test_id, u8 *status,
+ u16 *actual_read_len, u8 *data);
+ int (*calibrate_idacs)(struct device *dev, int protect, u8 mode,
+ u8 *status);
+ int (*initialize_baselines)(struct device *dev, int protect,
+ u8 test_id, u8 *status);
+ int (*exec_panel_scan)(struct device *dev, int protect);
+ int (*retrieve_panel_scan)(struct device *dev, int protect,
+ u16 read_offset, u16 read_count, u8 data_id,
+ u8 *response, u8 *config, u16 *actual_read_len,
+ u8 *read_buf);
+ int (*write_conf_block)(struct device *dev, int protect,
+ u16 row_number, u16 write_length, u8 ebid,
+ u8 *write_buf, u8 *security_key, u16 *actual_write_len);
+ int (*user_cmd)(struct device *dev, int protect, u16 read_len,
+ u8 *read_buf, u16 write_len, u8 *write_buf,
+ u16 *actual_read_len);
+ int (*get_bl_info)(struct device *dev, int protect, u8 *return_data);
+ int (*initiate_bl)(struct device *dev, int protect, u16 key_size,
+ u8 *key_buf, u16 row_size, u8 *metadata_row_buf);
+ int (*launch_app)(struct device *dev, int protect);
+ int (*prog_and_verify)(struct device *dev, int protect, u16 data_len,
+ u8 *data_buf);
+ int (*verify_app_integrity)(struct device *dev, int protect,
+ u8 *result);
+ int (*get_panel_id)(struct device *dev, int protect, u8 *panel_id);
+};
+
+typedef int (*cyttsp5_atten_func) (struct device *);
+
+struct cyttsp5_core_commands {
+ int (*subscribe_attention)(struct device *dev,
+ enum cyttsp5_atten_type type, char *id,
+ cyttsp5_atten_func func, int flags);
+ int (*unsubscribe_attention)(struct device *dev,
+ enum cyttsp5_atten_type type, char *id,
+ cyttsp5_atten_func func, int flags);
+ int (*request_exclusive)(struct device *dev, int timeout_ms);
+ int (*release_exclusive)(struct device *dev);
+ int (*request_reset)(struct device *dev);
+ int (*request_restart)(struct device *dev, bool wait);
+ struct cyttsp5_sysinfo * (*request_sysinfo)(struct device *dev);
+ struct cyttsp5_loader_platform_data
+ *(*request_loader_pdata)(struct device *dev);
+ int (*request_stop_wd)(struct device *dev);
+ int (*request_start_wd)(struct device *dev);
+ int (*request_get_hid_desc)(struct device *dev, int protect);
+ int (*request_get_mode)(struct device *dev, int protect, u8 *mode);
+ int (*request_enable_scan_type)(struct device *dev, u8 scan_type);
+ int (*request_disable_scan_type)(struct device *dev, u8 scan_type);
+ struct cyttsp5_core_nonhid_cmd *nonhid_cmd;
+#ifdef TTHE_TUNER_SUPPORT
+ int (*request_tthe_print)(struct device *dev, u8 *buf, int buf_len,
+ const u8 *data_name);
+#endif
+};
+
+struct cyttsp5_features {
+ uint8_t easywake;
+ uint8_t noise_metric;
+ uint8_t tracking_heatmap;
+ uint8_t sensor_data;
+};
+
+#define NEED_SUSPEND_NOTIFIER \
+ ((LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)) \
+ && defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME))
+
+struct cyttsp5_module {
+ struct list_head node;
+ char *name;
+ int (*probe)(struct device *dev, void **data);
+ void (*release)(struct device *dev, void *data);
+};
+
+struct cyttsp5_core_data {
+ struct list_head node;
+ struct list_head module_list; /* List of probed modules */
+ char core_id[20];
+ struct device *dev;
+ struct list_head atten_list[CY_ATTEN_NUM_ATTEN];
+ struct list_head param_list;
+ struct mutex module_list_lock;
+ struct mutex system_lock;
+ struct mutex adap_lock;
+ struct mutex hid_report_lock;
+ enum cyttsp5_mode mode;
+ spinlock_t spinlock;
+ struct cyttsp5_mt_data md;
+ struct cyttsp5_btn_data bd;
+ struct cyttsp5_proximity_data pd;
+ int phys_num;
+ int number_of_open_input_device;
+ int pm_runtime_usage_count;
+ void *cyttsp5_dynamic_data[CY_MODULE_LAST];
+ struct cyttsp5_platform_data *pdata;
+ struct cyttsp5_core_platform_data *cpdata;
+ const struct cyttsp5_bus_ops *bus_ops;
+ wait_queue_head_t wait_q;
+ enum cyttsp5_sleep_state sleep_state;
+ enum cyttsp5_startup_state startup_state;
+ int irq;
+ bool irq_enabled;
+ bool irq_wake;
+ bool irq_disabled;
+ u8 easy_wakeup_gesture;
+#ifdef EASYWAKE_TSG6
+ u8 gesture_id;
+ u8 gesture_data_length;
+ u8 gesture_data[80];
+#endif
+ bool wake_initiated_by_device;
+ bool wait_until_wake;
+ u8 panel_id;
+#if NEED_SUSPEND_NOTIFIER
+ /*
+ * This notifier is used to receive suspend prepare events
+ * When device is PM runtime suspended, pm_generic_suspend()
+ * does not call our PM suspend callback for kernels with
+ * version less than 3.3.0.
+ */
+ struct notifier_block pm_notifier;
+#endif
+ struct work_struct startup_work;
+ struct cyttsp5_sysinfo sysinfo;
+ void *exclusive_dev;
+ int exclusive_waits;
+ struct work_struct watchdog_work;
+ struct timer_list watchdog_timer;
+ u16 startup_retry_count;
+ struct cyttsp5_hid_core hid_core;
+ int hid_cmd_state;
+ int hid_reset_cmd_state; /* reset can happen any time */
+ struct cyttsp5_hid_desc hid_desc;
+ struct cyttsp5_hid_report *hid_reports[CY_HID_MAX_REPORTS];
+ int num_hid_reports;
+ struct cyttsp5_features features;
+#define CYTTSP5_PREALLOCATED_CMD_BUFFER 32
+ u8 cmd_buf[CYTTSP5_PREALLOCATED_CMD_BUFFER];
+ u8 input_buf[CY_MAX_INPUT];
+ u8 response_buf[CY_MAX_INPUT];
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend es;
+#elif defined(CONFIG_FB)
+ struct notifier_block fb_notifier;
+ enum cyttsp5_fb_state fb_state;
+#endif
+#ifdef TTHE_TUNER_SUPPORT
+ struct dentry *tthe_debugfs;
+ u8 *tthe_buf;
+ u32 tthe_buf_len;
+ struct mutex tthe_lock;
+ u8 tthe_exit;
+#endif
+#ifdef VERBOSE_DEBUG
+ u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+ u8 debug_level;
+ u32 watchdog_interval;
+ u8 show_timestamp;
+
+};
+struct gd_sensor {
+ int32_t cm_min;
+ int32_t cm_max;
+ int32_t cm_ave;
+ int32_t cm_min_exclude_edge;
+ int32_t cm_max_exclude_edge;
+ int32_t cm_ave_exclude_edge;
+ int32_t gradient_val;
+};
+
+#ifdef TTHE_TUNER_SUPPORT
+#define CY_CMD_RET_PANEL_IN_DATA_OFFSET 0
+#define CY_CMD_RET_PANEL_ELMNT_SZ_MASK 0x07
+#define CY_CMD_RET_PANEL_HDR 0x0A
+#define CY_CMD_RET_PANEL_ELMNT_SZ_MAX 0x2
+
+enum scan_data_type_list {
+ CY_MUT_RAW,
+ CY_MUT_BASE,
+ CY_MUT_DIFF,
+ CY_SELF_RAW,
+ CY_SELF_BASE,
+ CY_SELF_DIFF,
+ CY_BAL_RAW,
+ CY_BAL_BASE,
+ CY_BAL_DIFF,
+};
+#endif
+
+struct cyttsp5_bus_ops {
+ u16 bustype;
+
+ int (*read_default)(struct device *dev, void *buf, int size);
+ int (*read_default_nosize)(struct device *dev, u8 *buf, u32 max);
+ int (*write_read_specific)(struct device *dev, u8 write_len,
+ u8 *write_buf, u8 *read_buf);
+};
+
+static inline int cyttsp5_adap_read_default(struct cyttsp5_core_data *cd,
+ void *buf, int size)
+{
+ return cd->bus_ops->read_default(cd->dev, buf, size);
+}
+
+static inline int cyttsp5_adap_read_default_nosize(struct cyttsp5_core_data *cd,
+ void *buf, int max)
+{
+ return cd->bus_ops->read_default_nosize(cd->dev, buf, max);
+}
+
+static inline int cyttsp5_adap_write_read_specific(struct cyttsp5_core_data *cd,
+ u8 write_len, u8 *write_buf, u8 *read_buf)
+{
+ return cd->bus_ops->write_read_specific(cd->dev, write_len, write_buf,
+ read_buf);
+}
+
+static inline void *cyttsp5_get_dynamic_data(struct device *dev, int id)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return cd->cyttsp5_dynamic_data[id];
+}
+
+int request_exclusive(struct cyttsp5_core_data *cd, void *ownptr,
+ int timeout_ms);
+int release_exclusive(struct cyttsp5_core_data *cd, void *ownptr);
+int _cyttsp5_request_hid_output_get_param(struct device *dev,
+ int protect, u8 param_id, u32 *value);
+int _cyttsp5_request_hid_output_set_param(struct device *dev,
+ int protect, u8 param_id, u32 value, u8 size);
+
+static inline int cyttsp5_request_exclusive(struct device *dev, int timeout_ms)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return request_exclusive(cd, dev, timeout_ms);
+}
+
+static inline int cyttsp5_release_exclusive(struct device *dev)
+{
+ struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
+
+ return release_exclusive(cd, dev);
+}
+
+static inline int cyttsp5_request_nonhid_get_param(struct device *dev,
+ int protect, u8 param_id, u32 *value)
+{
+ return _cyttsp5_request_hid_output_get_param(dev, protect, param_id,
+ value);
+}
+
+static inline int cyttsp5_request_nonhid_set_param(struct device *dev,
+ int protect, u8 param_id, u32 value, u8 size)
+{
+ return _cyttsp5_request_hid_output_set_param(dev, protect, param_id,
+ value, size);
+}
+
+#ifdef VERBOSE_DEBUG
+void cyttsp5_pr_buf(struct device *dev, u8 *dptr, int size,
+ const char *data_name);
+#else
+#define cyttsp5_pr_buf(a, b, c, d) do { } while (0)
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev);
+int cyttsp5_devtree_clean_pdata(struct device *adap_dev);
+#else
+static inline int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev)
+{
+ return 0;
+}
+
+static inline int cyttsp5_devtree_clean_pdata(struct device *adap_dev)
+{
+ return 0;
+}
+#endif
+
+int cyttsp5_probe(const struct cyttsp5_bus_ops *ops, struct device *dev,
+ u16 irq, size_t xfer_buf_size);
+int cyttsp5_release(struct cyttsp5_core_data *cd);
+
+struct cyttsp5_core_commands *cyttsp5_get_commands(void);
+struct cyttsp5_core_data *cyttsp5_get_core_data(char *id);
+
+int cyttsp5_mt_release(struct device *dev);
+int cyttsp5_mt_probe(struct device *dev);
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON
+int cyttsp5_btn_probe(struct device *dev);
+int cyttsp5_btn_release(struct device *dev);
+#else
+static inline int cyttsp5_btn_probe(struct device *dev) { return 0; }
+static inline int cyttsp5_btn_release(struct device *dev) { return 0; }
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY
+int cyttsp5_proximity_probe(struct device *dev);
+int cyttsp5_proximity_release(struct device *dev);
+#else
+static inline int cyttsp5_proximity_probe(struct device *dev) { return 0; }
+static inline int cyttsp5_proximity_release(struct device *dev) { return 0; }
+#endif
+
+void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md);
+int _cyttsp5_subscribe_attention(struct device *dev,
+ enum cyttsp5_atten_type type, char *id, int (*func)(struct device *),
+ int mode);
+int _cyttsp5_unsubscribe_attention(struct device *dev,
+ enum cyttsp5_atten_type type, char *id, int (*func)(struct device *),
+ int mode);
+struct cyttsp5_sysinfo *_cyttsp5_request_sysinfo(struct device *dev);
+
+extern const struct dev_pm_ops cyttsp5_pm_ops;
+
+int cyttsp5_register_module(struct cyttsp5_module *module);
+void cyttsp5_unregister_module(struct cyttsp5_module *module);
+
+void *cyttsp5_get_module_data(struct device *dev,
+ struct cyttsp5_module *module);
+
+#endif /* _CYTTSP5_REGS_H */
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_spi.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_spi.c
new file mode 100644
index 0000000..a01b88b
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_spi.c
@@ -0,0 +1,255 @@
+/*
+ * cyttsp5_spi.c
+ * Parade TrueTouch(TM) Standard Product V5 SPI Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include "cyttsp5_regs.h"
+
+#include <linux/spi/spi.h>
+#include <linux/version.h>
+
+#define CY_SPI_WR_OP 0x00 /* r/~w */
+#define CY_SPI_RD_OP 0x01
+#define CY_SPI_BITS_PER_WORD 8
+#define CY_SPI_SYNC_ACK 0x62
+
+#define CY_SPI_CMD_BYTES 0
+#define CY_SPI_DATA_SIZE (2 * 256)
+#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+
+static void cyttsp5_spi_add_rw_msg(struct spi_message *msg,
+ struct spi_transfer *xfer, u8 *w_header, u8 *r_header, u8 op)
+{
+ xfer->tx_buf = w_header;
+ xfer->rx_buf = r_header;
+ w_header[0] = op;
+ xfer->len = 1;
+ spi_message_add_tail(xfer, msg);
+}
+
+static int cyttsp5_spi_xfer(struct device *dev, u8 op, u8 *buf, int length)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ u8 w_header[2];
+ u8 r_header[2];
+ int rc;
+
+ memset(xfer, 0, sizeof(xfer));
+
+ spi_message_init(&msg);
+ cyttsp5_spi_add_rw_msg(&msg, &xfer[0], w_header, r_header, op);
+
+ switch (op) {
+ case CY_SPI_RD_OP:
+ xfer[1].rx_buf = buf;
+ xfer[1].len = length;
+ spi_message_add_tail(&xfer[1], &msg);
+ break;
+ case CY_SPI_WR_OP:
+ xfer[1].tx_buf = buf;
+ xfer[1].len = length;
+ spi_message_add_tail(&xfer[1], &msg);
+ break;
+ default:
+ rc = -EIO;
+ goto exit;
+ }
+
+ rc = spi_sync(spi, &msg);
+exit:
+ if (rc < 0)
+ parade_debug(dev, DEBUG_LEVEL_2, "%s: spi_sync() error %d\n",
+ __func__, rc);
+
+ if (r_header[0] != CY_SPI_SYNC_ACK)
+ return -EIO;
+
+ return rc;
+}
+
+static int cyttsp5_spi_read_default(struct device *dev, void *buf, int size)
+{
+ if (!buf || !size)
+ return 0;
+
+ return cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, size);
+}
+
+static int cyttsp5_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max)
+{
+ u32 size;
+ int rc;
+
+ if (!buf)
+ return 0;
+
+ rc = cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, 2);
+ if (rc < 0)
+ return rc;
+
+ size = get_unaligned_le16(&buf[0]);
+ if (!size)
+ return rc;
+
+ if (size > max)
+ return -EINVAL;
+
+ return cyttsp5_spi_read_default(dev, buf, size);
+}
+
+static int cyttsp5_spi_write_read_specific(struct device *dev, u8 write_len,
+ u8 *write_buf, u8 *read_buf)
+{
+ int rc;
+
+ rc = cyttsp5_spi_xfer(dev, CY_SPI_WR_OP, write_buf, write_len);
+ if (rc < 0)
+ return rc;
+
+ if (read_buf)
+ rc = cyttsp5_spi_read_default_nosize(dev, read_buf,
+ CY_SPI_DATA_SIZE);
+
+ return rc;
+}
+
+static struct cyttsp5_bus_ops cyttsp5_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .read_default = cyttsp5_spi_read_default,
+ .read_default_nosize = cyttsp5_spi_read_default_nosize,
+ .write_read_specific = cyttsp5_spi_write_read_specific,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+static const struct of_device_id cyttsp5_spi_of_match[] = {
+ { .compatible = "cy,cyttsp5_spi_adapter", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cyttsp5_spi_of_match);
+#endif
+
+static int cyttsp5_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ const struct of_device_id *match;
+#endif
+ int rc;
+
+ /* Set up SPI*/
+ spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+ spi->mode = SPI_MODE_0;
+ rc = spi_setup(spi);
+ if (rc < 0) {
+ dev_err(dev, "%s: SPI setup error %d\n", __func__, rc);
+ return rc;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev);
+ if (match) {
+ rc = cyttsp5_devtree_create_and_get_pdata(dev);
+ if (rc < 0)
+ return rc;
+ }
+#endif
+
+ rc = cyttsp5_probe(&cyttsp5_spi_bus_ops, &spi->dev, spi->irq,
+ CY_SPI_DATA_BUF_SIZE);
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ if (rc && match)
+ cyttsp5_devtree_clean_pdata(dev);
+#endif
+
+ return rc;
+}
+
+static int cyttsp5_spi_remove(struct spi_device *spi)
+{
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ struct device *dev = &spi->dev;
+ const struct of_device_id *match;
+#endif
+ struct cyttsp5_core_data *cd = dev_get_drvdata(&spi->dev);
+
+ cyttsp5_release(cd);
+
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev);
+ if (match)
+ cyttsp5_devtree_clean_pdata(dev);
+#endif
+
+ return 0;
+}
+
+static const struct spi_device_id cyttsp5_spi_id[] = {
+ { CYTTSP5_SPI_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cyttsp5_spi_id);
+
+static struct spi_driver cyttsp5_spi_driver = {
+ .driver = {
+ .name = CYTTSP5_SPI_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp5_pm_ops,
+#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT
+ .of_match_table = cyttsp5_spi_of_match,
+#endif
+ },
+ .probe = cyttsp5_spi_probe,
+ .remove = (cyttsp5_spi_remove),
+ .id_table = cyttsp5_spi_id,
+};
+
+#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
+module_spi_driver(cyttsp5_spi_driver);
+#else
+static int __init cyttsp5_spi_init(void)
+{
+ int err = spi_register_driver(&cyttsp5_spi_driver);
+
+ pr_info("%s: Parade TTSP SPI Driver (Built %s) rc=%d\n",
+ __func__, CY_DRIVER_VERSION, err);
+ return err;
+}
+module_init(cyttsp5_spi_init);
+
+static void __exit cyttsp5_spi_exit(void)
+{
+ spi_unregister_driver(&cyttsp5_spi_driver);
+}
+module_exit(cyttsp5_spi_exit);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/drivers/input/touchscreen/cyttsp5/cyttsp5_test_device_access_api.c b/drivers/input/touchscreen/cyttsp5/cyttsp5_test_device_access_api.c
new file mode 100644
index 0000000..e8a2cbb
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp5/cyttsp5_test_device_access_api.c
@@ -0,0 +1,442 @@
+/*
+ * cyttsp5_test_device_access_api.c
+ * Parade TrueTouch(TM) Standard Product V5 Device Access API test module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Technologies
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input/cyttsp5_device_access-api.h>
+#include <asm/unaligned.h>
+
+#define BUFFER_SIZE 256
+
+#define COMMAND_GET_SYSTEM_INFO 2
+#define COMMAND_SUSPEND_SCANNING 3
+#define COMMAND_RESUME_SCANNING 4
+#define COMMAND_GET_PARAMETER 5
+#define COMMAND_SET_PARAMETER 6
+
+#define PARAMETER_ACTIVE_DISTANCE_2 0x0B
+
+struct tt_output_report {
+ __le16 reg_address;
+ __le16 length;
+ u8 report_id;
+ u8 reserved;
+ u8 command;
+ u8 parameters[0];
+} __packed;
+
+struct tt_input_report {
+ __le16 length;
+ u8 report_id;
+ u8 reserved;
+ u8 command;
+ u8 return_data[0];
+} __packed;
+
+static int prepare_tt_output_report(struct tt_output_report *out,
+ u16 length, u8 command)
+{
+ put_unaligned_le16(0x04, &out->reg_address);
+ put_unaligned_le16(5 + length, &out->length);
+
+ out->report_id = 0x2f;
+ out->reserved = 0x00;
+ out->command = command;
+
+ return 7 + length;
+}
+
+static int check_and_parse_tt_input_report(struct tt_input_report *in,
+ u16 *length, u8 *command)
+{
+ if (in->report_id != 0x1f)
+ return -EINVAL;
+
+ *length = get_unaligned_le16(&in->length);
+ *command = in->command & 0x7f;
+
+ return 0;
+}
+
+static int prepare_get_system_info_report(u8 *buf)
+{
+ struct tt_output_report *out = (struct tt_output_report *)buf;
+
+ return prepare_tt_output_report(out, 0, COMMAND_GET_SYSTEM_INFO);
+}
+
+static int check_get_system_info_response(u8 *buf, u16 read_length)
+{
+ struct tt_input_report *in = (struct tt_input_report *)buf;
+ u16 length = 0;
+ u8 command = 0;
+
+ if (read_length != 51)
+ return -EINVAL;
+
+ if (check_and_parse_tt_input_report(in, &length, &command)
+ || command != COMMAND_GET_SYSTEM_INFO
+ || length != 51)
+ return -EINVAL;
+
+ pr_info("PIP Major Version: %d\n", in->return_data[0]);
+ pr_info("PIP Minor Version: %d\n", in->return_data[1]);
+ pr_info("Touch Firmware Product Id: %d\n",
+ get_unaligned_le16(&in->return_data[2]));
+ pr_info("Touch Firmware Major Version: %d\n", in->return_data[4]);
+ pr_info("Touch Firmware Minor Version: %d\n", in->return_data[5]);
+ pr_info("Touch Firmware Internal Revision Control Number: %d\n",
+ get_unaligned_le32(&in->return_data[6]));
+ pr_info("Customer Specified Firmware/Configuration Version: %d\n",
+ get_unaligned_le16(&in->return_data[10]));
+ pr_info("Bootloader Major Version: %d\n", in->return_data[12]);
+ pr_info("Bootloader Minor Version: %d\n", in->return_data[13]);
+ pr_info("Family ID: 0x%02x\n", in->return_data[14]);
+ pr_info("Revision ID: 0x%02x\n", in->return_data[15]);
+ pr_info("Silicon ID: 0x%02x\n",
+ get_unaligned_le16(&in->return_data[16]));
+ pr_info("Parade Manufacturing ID[0]: 0x%02x\n", in->return_data[18]);
+ pr_info("Parade Manufacturing ID[1]: 0x%02x\n", in->return_data[19]);
+ pr_info("Parade Manufacturing ID[2]: 0x%02x\n", in->return_data[20]);
+ pr_info("Parade Manufacturing ID[3]: 0x%02x\n", in->return_data[21]);
+ pr_info("Parade Manufacturing ID[4]: 0x%02x\n", in->return_data[22]);
+ pr_info("Parade Manufacturing ID[5]: 0x%02x\n", in->return_data[23]);
+ pr_info("Parade Manufacturing ID[6]: 0x%02x\n", in->return_data[24]);
+ pr_info("Parade Manufacturing ID[7]: 0x%02x\n", in->return_data[25]);
+ pr_info("POST Result Code: 0x%02x\n",
+ get_unaligned_le16(&in->return_data[26]));
+
+ pr_info("Number of X Electrodes: %d\n", in->return_data[28]);
+ pr_info("Number of Y Electrodes: %d\n", in->return_data[29]);
+ pr_info("Panel X Axis Length: %d\n",
+ get_unaligned_le16(&in->return_data[30]));
+ pr_info("Panel Y Axis Length: %d\n",
+ get_unaligned_le16(&in->return_data[32]));
+ pr_info("Panel X Axis Resolution: %d\n",
+ get_unaligned_le16(&in->return_data[34]));
+ pr_info("Panel Y Axis Resolution: %d\n",
+ get_unaligned_le16(&in->return_data[36]));
+ pr_info("Panel Pressure Resolution: %d\n",
+ get_unaligned_le16(&in->return_data[38]));
+ pr_info("X_ORG: %d\n", in->return_data[40]);
+ pr_info("Y_ORG: %d\n", in->return_data[41]);
+ pr_info("Panel ID: %d\n", in->return_data[42]);
+ pr_info("Buttons: 0x%02x\n", in->return_data[43]);
+ pr_info("BAL SELF MC: 0x%02x\n", in->return_data[44]);
+ pr_info("Max Number of Touch Records per Refresh Cycle: %d\n",
+ in->return_data[45]);
+
+ return 0;
+}
+
+static int prepare_get_parameter_report(u8 *buf, u8 parameter_id)
+{
+ struct tt_output_report *out = (struct tt_output_report *)buf;
+
+ out->parameters[0] = parameter_id;
+
+ return prepare_tt_output_report(out, 1, COMMAND_GET_PARAMETER);
+}
+
+static int check_get_parameter_response(u8 *buf, u16 read_length,
+ u32 *parameter_value)
+{
+ struct tt_input_report *in = (struct tt_input_report *)buf;
+ u16 length = 0;
+ u8 command = 0;
+ u32 param_value = 0;
+ u8 param_id;
+ u8 param_size = 0;
+
+ if (read_length != 8 && read_length != 9 && read_length != 11)
+ return -EINVAL;
+
+ if (check_and_parse_tt_input_report(in, &length, &command)
+ || command != COMMAND_GET_PARAMETER
+ || (length != 8 && length != 9 && length != 11))
+ return -EINVAL;
+
+ param_id = in->return_data[0];
+
+ param_size = in->return_data[1];
+
+ if (param_size == 1)
+ param_value = in->return_data[2];
+ else if (param_size == 2)
+ param_value = get_unaligned_le16(&in->return_data[2]);
+ else if (param_size == 4)
+ param_value = get_unaligned_le32(&in->return_data[2]);
+ else
+ return -EINVAL;
+
+ pr_info("%s: Parameter ID: 0x%02x Value: 0x%02x\n",
+ __func__, param_id, param_value);
+
+ if (parameter_value)
+ *parameter_value = param_value;
+
+ return 0;
+}
+
+static int prepare_set_parameter_report(u8 *buf, u8 parameter_id,
+ u8 parameter_size, u32 parameter_value)
+{
+ struct tt_output_report *out = (struct tt_output_report *)buf;
+
+ out->parameters[0] = parameter_id;
+ out->parameters[1] = parameter_size;
+
+ if (parameter_size == 1)
+ out->parameters[2] = (u8)parameter_value;
+ else if (parameter_size == 2)
+ put_unaligned_le16(parameter_value, &out->parameters[2]);
+ else if (parameter_size == 4)
+ put_unaligned_le32(parameter_value, &out->parameters[2]);
+ else
+ return -EINVAL;
+
+ return prepare_tt_output_report(out, 2 + parameter_size,
+ COMMAND_SET_PARAMETER);
+}
+
+static int check_set_parameter_response(u8 *buf, u16 read_length)
+{
+ struct tt_input_report *in = (struct tt_input_report *)buf;
+ u16 length = 0;
+ u8 command = 0;
+ u8 param_id;
+ u8 param_size = 0;
+
+ if (read_length != 7)
+ return -EINVAL;
+
+ if (check_and_parse_tt_input_report(in, &length, &command)
+ || command != COMMAND_SET_PARAMETER
+ || length != 7)
+ return -EINVAL;
+
+ param_id = in->return_data[0];
+ param_size = in->return_data[1];
+
+ pr_info("%s: Parameter ID: 0x%02x Size: 0x%02x\n",
+ __func__, param_id, param_size);
+
+ return 0;
+}
+
+static int prepare_suspend_scanning_report(u8 *buf)
+{
+ struct tt_output_report *out = (struct tt_output_report *)buf;
+
+ return prepare_tt_output_report(out, 0, COMMAND_SUSPEND_SCANNING);
+}
+
+static int check_suspend_scanning_response(u8 *buf, u16 read_length)
+{
+ struct tt_input_report *in = (struct tt_input_report *)buf;
+ u16 length = 0;
+ u8 command = 0;
+
+ if (read_length != 5)
+ return -EINVAL;
+
+ if (check_and_parse_tt_input_report(in, &length, &command)
+ || command != COMMAND_SUSPEND_SCANNING
+ || length != 5)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int prepare_resume_scanning_report(u8 *buf)
+{
+ struct tt_output_report *out = (struct tt_output_report *)buf;
+
+ return prepare_tt_output_report(out, 0, COMMAND_RESUME_SCANNING);
+}
+
+static int check_resume_scanning_response(u8 *buf, u16 read_length)
+{
+ struct tt_input_report *in = (struct tt_input_report *)buf;
+ u16 length = 0;
+ u8 command = 0;
+
+ if (read_length != 5)
+ return -EINVAL;
+
+ if (check_and_parse_tt_input_report(in, &length, &command)
+ || command != COMMAND_RESUME_SCANNING
+ || length != 5)
+ return -EINVAL;
+
+ return 0;
+}
+
+void cyttsp5_user_command_async_cont(const char *core_name,
+ u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+ u16 actual_read_length, int rc)
+{
+ if (rc) {
+ pr_err("%s: suspend scan fails\n", __func__);
+ goto exit;
+ }
+
+ rc = check_suspend_scanning_response(read_buf, actual_read_length);
+ if (rc) {
+ pr_err("%s: check suspend scanning response fails\n", __func__);
+ goto exit;
+ }
+
+ pr_info("%s: suspend scanning succeeds\n", __func__);
+exit:
+ return;
+}
+
+/* Read and write buffers */
+static u8 write_buf[BUFFER_SIZE];
+static u8 read_buf[BUFFER_SIZE];
+
+static uint active_distance;
+module_param(active_distance, uint, 0);
+
+static int __init cyttsp5_test_device_access_api_init(void)
+{
+ u32 initial_active_distance;
+ u16 actual_read_len;
+ int write_len;
+ int rc;
+
+ pr_info("%s: Enter\n", __func__);
+
+ /* CASE 1: Run get system information */
+ write_len = prepare_get_system_info_report(write_buf);
+
+ rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
+ read_buf, write_len, write_buf,
+ &actual_read_len);
+ if (rc)
+ goto exit;
+
+ rc = check_get_system_info_response(read_buf, actual_read_len);
+ if (rc)
+ goto exit;
+
+ /* CASE 2: Run get parameter (Active distance) */
+ write_len = prepare_get_parameter_report(write_buf,
+ PARAMETER_ACTIVE_DISTANCE_2);
+
+ rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
+ read_buf, write_len, write_buf,
+ &actual_read_len);
+ if (rc)
+ goto exit;
+
+ rc = check_get_parameter_response(read_buf, actual_read_len,
+ &initial_active_distance);
+ if (rc)
+ goto exit;
+
+ pr_info("%s: Initial Active Distance: %d\n",
+ __func__, initial_active_distance);
+
+ /* CASE 3: Run set parameter (Active distance) */
+ write_len = prepare_set_parameter_report(write_buf,
+ PARAMETER_ACTIVE_DISTANCE_2, 1,
+ active_distance);
+
+ rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
+ read_buf, write_len, write_buf,
+ &actual_read_len);
+ if (rc)
+ goto exit;
+
+ rc = check_set_parameter_response(read_buf, actual_read_len);
+ if (rc)
+ goto exit;
+
+ pr_info("%s: Active Distance set to %d\n", __func__, active_distance);
+
+ /* CASE 4: Run get parameter (Active distance) */
+ write_len = prepare_get_parameter_report(write_buf,
+ PARAMETER_ACTIVE_DISTANCE_2);
+
+ rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
+ read_buf, write_len, write_buf,
+ &actual_read_len);
+ if (rc)
+ goto exit;
+
+ rc = check_get_parameter_response(read_buf, actual_read_len,
+ &active_distance);
+ if (rc)
+ goto exit;
+
+ pr_info("%s: New Active Distance: %d\n", __func__, active_distance);
+
+ /* CASE 5: Run suspend scanning asynchronously */
+ write_len = prepare_suspend_scanning_report(write_buf);
+
+ preempt_disable();
+ rc = cyttsp5_device_access_user_command_async(NULL,
+ sizeof(read_buf), read_buf, write_len, write_buf,
+ cyttsp5_user_command_async_cont);
+ preempt_enable();
+ if (rc)
+ goto exit;
+exit:
+ return rc;
+}
+module_init(cyttsp5_test_device_access_api_init);
+
+static void __exit cyttsp5_test_device_access_api_exit(void)
+{
+ u16 actual_read_len;
+ int write_len;
+ int rc;
+
+ /* CASE 6: Run resume scanning */
+ write_len = prepare_resume_scanning_report(write_buf);
+
+ rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
+ read_buf, write_len, write_buf,
+ &actual_read_len);
+ if (rc)
+ goto exit;
+
+ rc = check_resume_scanning_response(read_buf, actual_read_len);
+ if (rc)
+ goto exit;
+
+ pr_info("%s: resume scanning succeeds\n", __func__);
+exit:
+ return;
+}
+module_exit(cyttsp5_test_device_access_api_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver API Tester");
+MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
diff --git a/include/linux/input/cyttsp5_core.h b/include/linux/input/cyttsp5_core.h
new file mode 100644
index 0000000..04e5199
--- /dev/null
+++ b/include/linux/input/cyttsp5_core.h
@@ -0,0 +1,179 @@
+/*
+ * cyttsp5_core.h
+ * Parade TrueTouch(TM) Standard Product V5 Core Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Semiconductor
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Semiconductor at www.parade.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP5_CORE_H
+#define _LINUX_CYTTSP5_CORE_H
+
+#include <linux/stringify.h>
+
+#define CYTTSP5_I2C_NAME "cyttsp5_i2c_adapter"
+#define CYTTSP5_SPI_NAME "cyttsp5_spi_adapter"
+
+#define CYTTSP5_CORE_NAME "cyttsp5_core"
+#define CYTTSP5_MT_NAME "cyttsp5_mt"
+#define CYTTSP5_BTN_NAME "cyttsp5_btn"
+#define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity"
+
+#define CY_DRIVER_NAME TTDA
+#define CY_DRIVER_MAJOR 03
+#define CY_DRIVER_MINOR 08
+
+#define CY_DRIVER_REVCTRL 874312
+
+#define CY_DRIVER_VERSION \
+__stringify(CY_DRIVER_NAME) \
+"." __stringify(CY_DRIVER_MAJOR) \
+"." __stringify(CY_DRIVER_MINOR) \
+"." __stringify(CY_DRIVER_REVCTRL)
+
+#define CY_DRIVER_DATE "20170125"
+
+/* abs settings */
+#define CY_IGNORE_VALUE -1
+
+enum cyttsp5_core_platform_flags {
+ CY_CORE_FLAG_NONE,
+ CY_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02,
+ CY_CORE_FLAG_RESTORE_PARAMETERS = 0x04,
+};
+
+enum cyttsp5_core_platform_easy_wakeup_gesture {
+ CY_CORE_EWG_NONE,
+ CY_CORE_EWG_TAP_TAP,
+ CY_CORE_EWG_TWO_FINGER_SLIDE,
+ CY_CORE_EWG_RESERVED,
+ CY_CORE_EWG_WAKE_ON_INT_FROM_HOST = 0xFF,
+};
+
+enum cyttsp5_loader_platform_flags {
+ CY_LOADER_FLAG_NONE,
+ CY_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE,
+ /* Use CONFIG_VER field in TT_CFG to decide TT_CFG update */
+ CY_LOADER_FLAG_CHECK_TTCONFIG_VERSION,
+ CY_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE,
+};
+
+struct touch_settings {
+ const uint8_t *data;
+ uint32_t size;
+ uint8_t tag;
+};
+
+struct cyttsp5_touch_firmware {
+ const uint8_t *img;
+ uint32_t size;
+ const uint8_t *ver;
+ uint8_t vsize;
+ uint8_t panel_id;
+};
+
+struct cyttsp5_touch_config {
+ struct touch_settings *param_regs;
+ struct touch_settings *param_size;
+ const uint8_t *fw_ver;
+ uint8_t fw_vsize;
+ uint8_t panel_id;
+};
+
+struct cyttsp5_loader_platform_data {
+ struct cyttsp5_touch_firmware *fw;
+ struct cyttsp5_touch_config *ttconfig;
+ struct cyttsp5_touch_firmware **fws;
+ struct cyttsp5_touch_config **ttconfigs;
+ u32 flags;
+};
+
+typedef int (*cyttsp5_platform_read) (struct device *dev, void *buf, int size);
+
+#define CY_TOUCH_SETTINGS_MAX 32
+
+struct cyttsp5_core_platform_data {
+ int irq_gpio;
+ int rst_gpio;
+ int level_irq_udelay;
+ u16 hid_desc_register;
+ u16 vendor_id;
+ u16 product_id;
+
+ int (*xres)(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev);
+ int (*init)(struct cyttsp5_core_platform_data *pdata,
+ int on, struct device *dev);
+ int (*power)(struct cyttsp5_core_platform_data *pdata,
+ int on, struct device *dev, atomic_t *ignore_irq);
+ int (*detect)(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev, cyttsp5_platform_read read);
+ int (*irq_stat)(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev);
+ struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX];
+ u32 flags;
+ u8 easy_wakeup_gesture;
+};
+
+struct touch_framework {
+ const int16_t *abs;
+ uint8_t size;
+ uint8_t enable_vkeys;
+} __packed;
+
+enum cyttsp5_mt_platform_flags {
+ CY_MT_FLAG_NONE,
+ CY_MT_FLAG_HOVER = 0x04,
+ CY_MT_FLAG_FLIP = 0x08,
+ CY_MT_FLAG_INV_X = 0x10,
+ CY_MT_FLAG_INV_Y = 0x20,
+ CY_MT_FLAG_VKEYS = 0x40,
+ CY_MT_FLAG_NO_TOUCH_ON_LO = 0x80,
+};
+
+struct cyttsp5_mt_platform_data {
+ struct touch_framework *frmwrk;
+ unsigned short flags;
+ char const *inp_dev_name;
+ int vkeys_x;
+ int vkeys_y;
+};
+
+struct cyttsp5_btn_platform_data {
+ char const *inp_dev_name;
+};
+
+struct cyttsp5_proximity_platform_data {
+ struct touch_framework *frmwrk;
+ char const *inp_dev_name;
+};
+
+struct cyttsp5_platform_data {
+ struct cyttsp5_core_platform_data *core_pdata;
+ struct cyttsp5_mt_platform_data *mt_pdata;
+ struct cyttsp5_btn_platform_data *btn_pdata;
+ struct cyttsp5_proximity_platform_data *prox_pdata;
+ struct cyttsp5_loader_platform_data *loader_pdata;
+};
+
+#endif /* _LINUX_CYTTSP5_CORE_H */
diff --git a/include/linux/input/cyttsp5_device_access-api.h b/include/linux/input/cyttsp5_device_access-api.h
new file mode 100644
index 0000000..c071e52
--- /dev/null
+++ b/include/linux/input/cyttsp5_device_access-api.h
@@ -0,0 +1,44 @@
+/*
+ * cyttsp5_device_access-api.h
+ * Parade TrueTouch(TM) Standard Product V5 Device Access API module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Semiconductor
+ * Copyright (C) 2012-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Semiconductor at www.parade.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP5_DEVICE_ACCESS_API_H
+#define _LINUX_CYTTSP5_DEVICE_ACCESS_API_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+int cyttsp5_device_access_user_command(const char *core_name, u16 read_len,
+ u8 *read_buf, u16 write_len, u8 *write_buf,
+ u16 *actual_read_len);
+
+int cyttsp5_device_access_user_command_async(const char *core_name,
+ u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+ void (*cont)(const char *core_name, u16 read_len, u8 *read_buf,
+ u16 write_len, u8 *write_buf, u16 actual_read_length,
+ int rc));
+#endif /* _LINUX_CYTTSP5_DEVICE_ACCESS_API_H */
diff --git a/include/linux/input/cyttsp5_platform.h b/include/linux/input/cyttsp5_platform.h
new file mode 100644
index 0000000..9469c4a
--- /dev/null
+++ b/include/linux/input/cyttsp5_platform.h
@@ -0,0 +1,60 @@
+/*
+ * cyttsp5_platform.h
+ * Parade TrueTouch(TM) Standard Product V5 Platform Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * CYTMA5XX
+ * CYTMA448
+ * CYTMA445A
+ * CYTT21XXX
+ * CYTT31XXX
+ *
+ * Copyright (C) 2015 Parade Semiconductor
+ * Copyright (C) 2013-2015 Cypress Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Semiconductor at www.parade.com <ttdrivers@paradetech.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP5_PLATFORM_H
+#define _LINUX_CYTTSP5_PLATFORM_H
+
+#include <linux/input/cyttsp5_core.h>
+
+#if defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) \
+ || defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MODULE)
+extern struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data;
+
+int cyttsp5_xres(struct cyttsp5_core_platform_data *pdata, struct device *dev);
+int cyttsp5_init(struct cyttsp5_core_platform_data *pdata, int on,
+ struct device *dev);
+int cyttsp5_power(struct cyttsp5_core_platform_data *pdata, int on,
+ struct device *dev, atomic_t *ignore_irq);
+#ifdef CYTTSP5_DETECT_HW
+int cyttsp5_detect(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev, cyttsp5_platform_read read);
+#else
+#define cyttsp5_detect NULL
+#endif
+int cyttsp5_irq_stat(struct cyttsp5_core_platform_data *pdata,
+ struct device *dev);
+#else /* !CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 */
+static struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data;
+#define cyttsp5_xres NULL
+#define cyttsp5_init NULL
+#define cyttsp5_power NULL
+#define cyttsp5_irq_stat NULL
+#define cyttsp5_detect NULL
+#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 */
+
+#endif /* _LINUX_CYTTSP5_PLATFORM_H */