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(&param_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 = &param_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 */