All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Input: add support for HiDeep touchscreen
@ 2017-07-05  6:19 Anthony Kim
       [not found] ` <1499235588-32219-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
  2017-07-12  5:24 ` Anthony Kim
  0 siblings, 2 replies; 29+ messages in thread
From: Anthony Kim @ 2017-07-05  6:19 UTC (permalink / raw)
  To: linux-input, devicetree; +Cc: Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |   40 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/input/touchscreen/Kconfig                  |   32 +
 drivers/input/touchscreen/Makefile                 |    2 +
 drivers/input/touchscreen/hideep.h                 |  338 +++++++
 drivers/input/touchscreen/hideep_core.c            | 1029 ++++++++++++++++++++
 drivers/input/touchscreen/hideep_dbg.c             |  405 ++++++++
 drivers/input/touchscreen/hideep_dbg.h             |   24 +
 drivers/input/touchscreen/hideep_isp.c             |  584 +++++++++++
 drivers/input/touchscreen/hideep_isp.h             |   96 ++
 drivers/input/touchscreen/hideep_sysfs.c           |  249 +++++
 11 files changed, 2800 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.h
 create mode 100644 drivers/input/touchscreen/hideep_core.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.h
 create mode 100644 drivers/input/touchscreen/hideep_isp.c
 create mode 100644 drivers/input/touchscreen/hideep_isp.h
 create mode 100644 drivers/input/touchscreen/hideep_sysfs.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..f5ab5e6
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,40 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep_ts".
+- reg			: I2C slave address, (e.g. 0x6C).
+- hideep,max_coords	: Max value for axis X, Y, W, Z.
+
+Optional properties:
+- pinctrl-names	: "reset_down", "reset-up", "intb-ctrl".
+				They are gpio pinctrl names for should be search in driver code.
+- pinctrl-0	: Gpio control for "reset-down".
+- pinctrl-1	: Gpio control for "reset-up".
+- pinctrl-2	: Gpio control for "intb-ctrl".
+- hideep,regulator_vdd	: Main voltage(3.3V) name.
+- hideep,regulator_vid	: IO voltage(1.8V) name.
+- hideep,irq_gpio		: Define for interrupt gpio pin.
+						It is to use for set interrupt type.
+- hideep,reset_gpio		: Define for reset gpio pin.
+						It is to use for reset IC.
+
+Example:
+
+i2c@00000000 {
+
+	/* ... */
+
+	hideep@6c {
+		compatible = "hideep,hideep_ts";
+		reg = <0x6c>;
+		pinctrl-names = "reset-down", "reset-up", "intb-ctrl";
+		pinctrl-0 = <&reset_gpio0>;
+		pinctrl-1 = <&reset_gpio1>;
+		pinctrl-2 = <&touch_int>;
+		hideep,regulator_vdd = "vdd_ldo33";	// need modify
+		hideep,regulator_vid = "vdd_ldo18";	// need modify
+		hideep,irq_gpio = <&gpx0 5 0x0>;	// <gpio-name gpio-num gpio-val> need modify
+		hideep,reset_gpio = <&gpa0 5 0x1>;	// <gpio-name gpio-num gpio-val> need modify
+		hideep,max_coords = <1080 1920 65535 65535>;	// x y w z, need modify
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..558f655 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1246,4 +1246,36 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
+choice
+	prompt "Select IC"
+	depends on TOUCHSCREEN_HIDEEP
+	default TOUCHSCREEN_HIDEEP_CRIMSON
+	help
+	  This driver support two type IC.
+	  So it need to select IC.
+
+config TOUCHSCREEN_HIDEEP_CRIMSON
+	bool "Crimson Touch IC"
+	depends on TOUCHSCREEN_HIDEEP
+	help
+	  say Y to enable driver for Touchpanel using HiDeep Crimson Touch IC.
+
+config TOUCHSCREEN_HIDEEP_LIME
+	bool "Lime Touch IC"
+	depends on TOUCHSCREEN_HIDEEP
+	help
+	  say Y to enable driver for Touchpanel using HiDeep Lime Touch IC.
+endchoice
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..3aab466 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
+hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
new file mode 100644
index 0000000..3fff76d
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.h
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_H
+#define _LINUX_HIDEEP_H
+
+/*************************************************************************
+ * this is include special HEAD file.
+ *************************************************************************/
+#ifdef CONFIG_FB
+#include <linux/fb.h>
+#include <linux/notifier.h>
+#endif
+
+/*************************************************************************
+ * this is include normal HEAD file.
+ *************************************************************************/
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/kthread.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/task_work.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/input/mt.h>
+#include <linux/fs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
+#include <linux/acpi.h>
+
+/*************************************************************************
+ * definition part.
+ * define is (open, set, enable) if not, is (close, clear, disable)
+ * some special switch of functions.
+ *************************************************************************/
+
+/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
+#define HIDEEP_PROTOCOL_2_0
+
+/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
+ * otherwise TYPE_A.
+ */
+#define HIDEEP_TYPE_B_PROTOCOL
+
+/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
+#define HIDEEP_DWZ_VERSION_CHECK
+
+/* HIDEEP_SUPPORT_KE if define, it will use key button. */
+#define HIDEEP_SUPPORT_KEY
+
+/* HIDEEP_SUPPORT_STYLUS if define, it will use key button. */
+#define HIDEEP_SUPPORT_STYLUS
+
+/*************************************************************************
+ * Firmware name & size.
+ *************************************************************************/
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
+#define FIRMWARE_SIZE					(48 * 1024)
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
+#define FIRMWARE_SIZE					(64 * 1024)
+#endif
+
+/*************************************************************************
+ * Buffer size
+ *************************************************************************/
+#define FRAME_HEADER_SIZE				8
+/* if size of system i2c buffer is smaller than this value, need to modify */
+#define MAX_I2C_BUFFER_SIZE				512
+
+/*************************************************************************
+ * board porting config
+ *************************************************************************/
+#define HIDEEP_DEBUG_DEVICE_NAME		"hideep_debug"
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+/*************************************************************************
+ * register addr
+ *************************************************************************/
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/*************************************************************************
+ * multi-touch & key definitions.
+ *************************************************************************/
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/*************************************************************************
+ * HIDEEP PROTOCOL
+ *************************************************************************/
+#ifdef HIDEEP_PROTOCOL_2_0
+struct hideep_mt_t {
+	unsigned short x;
+	unsigned short y;
+	unsigned short z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+};
+#else
+struct hideep_mt_t {
+	unsigned char flag;
+	unsigned char index;
+	unsigned short x;
+	unsigned short y;
+	unsigned char z;
+	unsigned char w;
+};
+#endif
+
+struct hideep_key_t {
+	unsigned char flag;
+	unsigned int key;
+};
+
+/*************************************************************************
+ * IC State
+ *************************************************************************/
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+	state_debugging,
+};
+
+/*************************************************************************
+ * Firmware info
+ *************************************************************************/
+struct dwz_info_t {
+	unsigned int code_start;
+	unsigned char code_crc[12];
+
+	unsigned int c_code_start;
+	unsigned short c_code_len;
+	unsigned short gen_ver;
+
+	unsigned int vr_start;
+	unsigned short vr_len;
+	unsigned short rsv0;
+
+	unsigned int ft_start;
+	unsigned short ft_len;
+	unsigned short vr_version;
+
+	unsigned short boot_ver;
+	unsigned short core_ver;
+	unsigned short custom_ver;
+	unsigned short release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	unsigned short product_code;
+	unsigned short extra_option;
+
+	unsigned short product_id;
+	unsigned short vendor_id;
+};
+
+/*************************************************************************
+ * driver information for hideep_t by device tree
+ *************************************************************************/
+struct hideep_platform_data_t {
+	unsigned int max_x;
+	unsigned int max_y;
+	unsigned int max_z;
+	unsigned int max_w;
+
+#ifdef CONFIG_OF
+	int irq_gpio;
+	int reset_gpio;
+
+	const char *regulator_vdd;
+	const char *regulator_vid;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *reset_up;
+	struct pinctrl_state *reset_down;
+	struct pinctrl_state *intb_ctrl;
+#endif
+};
+
+struct hideep_debug_dev_t {
+	struct miscdevice misc;
+
+	wait_queue_head_t i_packet;
+
+	unsigned int ready;
+	unsigned char *vr_buff;
+	unsigned char *img_buff;
+	unsigned short vr_size;
+	unsigned short img_size;
+
+	bool debug_enable;
+	bool release_flag;
+
+	struct hideep_t *ts;
+};
+
+struct hideep_function_list_t {
+	//core
+	int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	void (*reset_ic)(struct hideep_t *);
+	void (*power)(struct hideep_t *, int);
+
+	//isp
+	int (*update_all)(struct hideep_t *, const char *);
+	int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
+	int (*get_dwz_info)(struct hideep_t *);
+	void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
+};
+
+struct hideep_t {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct hideep_platform_data_t *p_data;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	struct hideep_debug_dev_t debug_dev;
+	struct hideep_function_list_t *hideep_api;
+
+	struct task_struct *hthread_event;
+
+	bool suspended;
+	enum e_dev_state dev_state;
+
+#ifdef CONFIG_FB
+	struct notifier_block fb_notif;
+#endif
+
+	long tch_bit;
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
+
+#ifdef HIDEEP_SUPPORT_KEY
+	struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
+	int key_codes[HIDEEP_KEY_MAX];
+#endif
+
+
+	unsigned char i2c_buf[256];
+	struct dwz_info_t *dwz_info;
+
+	int interrupt_state;
+};
+
+/*************************************************************************
+ * function define
+ *************************************************************************/
+int hideep_debug_init(struct hideep_t *ts);
+void hideep_debug_uninit(void);
+
+int hideep_sysfs_init(struct hideep_t *ts);
+int hideep_sysfs_exit(struct hideep_t *ts);
+
+void hideep_isp_init(struct hideep_t *ts);
+
+#endif
diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
new file mode 100644
index 0000000..7803c68
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.c
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
+	((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
+
+static int hideep_pwr_on(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if ((!IS_ERR(pdata->vcc_vdd)) && (pdata->regulator_vdd != NULL)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+	}
+	usleep_range(999, 1000);
+
+	if ((!IS_ERR(pdata->vcc_vid)) && (pdata->regulator_vid != NULL)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+	usleep_range(2999, 3000);
+
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->reset_up);
+		if (ret < 0)
+			dev_err(&ts->client->dev, "can not reset pin control!!!");
+	}
+#endif
+
+	return ret;
+}
+
+static int hideep_pwr_off(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
+
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->reset_down);
+		if (ret < 0)
+			dev_err(&ts->client->dev, "can not reset pin control!!!");
+	}
+
+	if (!IS_ERR(pdata->vcc_vid) && (pdata->regulator_vid != NULL)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
+		ret = regulator_disable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+
+	if (!IS_ERR(pdata->vcc_vdd) && (pdata->regulator_vdd != NULL)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
+		ret = regulator_disable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd disable failed ret=%d", ret);
+	}
+#endif
+	return ret;
+}
+
+static void hideep_power(struct hideep_t *ts, int on)
+{
+	int ret = 0;
+
+	if (on) {
+		dev_info(&ts->client->dev, "power on");
+		ret = hideep_pwr_on(ts);
+	} else {
+		dev_info(&ts->client->dev, "power off");
+		ret = hideep_pwr_off(ts);
+	}
+}
+
+static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned short r_len, raddr;
+	int length;
+
+	length = len;
+	raddr = addr;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	do {
+		if (length > MAX_I2C_BUFFER_SIZE)
+			r_len = MAX_I2C_BUFFER_SIZE;
+		else
+			r_len = length;
+
+		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
+			raddr, r_len);
+
+		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
+
+		if (ret < 0)
+			goto i2c_err;
+
+		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
+		length -= MAX_I2C_BUFFER_SIZE;
+		buf += MAX_I2C_BUFFER_SIZE;
+		raddr += MAX_I2C_BUFFER_SIZE;
+
+		if (ret < 0)
+			goto i2c_err;
+	} while (length > 0);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	return  0;
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	// data mangling..
+	ts->i2c_buf[0] = (addr >> 0) & 0xFF;
+	ts->i2c_buf[1] = (addr >> 8) & 0xFF;
+	memcpy(&ts->i2c_buf[2], buf, len);
+
+	ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
+
+	if (ret < 0)
+		goto i2c_err;
+
+	mutex_unlock(&ts->i2c_mutex);
+	return  0;
+
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void pops_mt(struct hideep_t *ts)
+{
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	int id;
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+#else
+	input_report_key(ts->input_dev, BTN_TOUCH, false);
+	input_mt_sync(ts->input_dev);
+#endif
+
+	input_sync(ts->input_dev);
+}
+
+static void push_mt(struct hideep_t *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	bool btn_dn = 0;
+	bool btn_mv = 0;
+	int evt = 0;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
+		btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
+			& 0x01;
+		btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
+
+		if (btn_up)
+			clear_bit(id, &ts->tch_bit);
+		else
+			__set_bit(id, &ts->tch_bit);
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			ts->touch_evt[i].type, ts->touch_evt[i].index,
+			i, ts->touch_evt[i].x, ts->touch_evt[i].y,
+			ts->touch_evt[i].z);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			evt++;
+		}
+#else
+		if (btn_up) {
+			input_mt_sync(ts->input_dev);
+		} else {
+			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			input_mt_sync(ts->input_dev);
+			evt++;
+		}
+#endif
+	}
+
+	if (ts->tch_bit == 0)
+		evt = 0;
+
+	input_report_key(ts->input_dev, BTN_TOUCH, evt);
+	input_mt_sync_frame(ts->input_dev);
+	input_sync(ts->input_dev);
+}
+
+#ifdef HIDEEP_SUPPORT_KEY
+static void pops_ky(struct hideep_t *ts)
+{
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		input_report_key(ts->input_dev, ts->key_codes[i], false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+
+	input_sync(ts->input_dev);
+}
+
+static void push_ky(struct hideep_t *ts)
+{
+	int i;
+	int pressed;
+	int key_code;
+	int key_status;
+
+	for (i = 0; i < ts->key_count; i++) {
+		key_code = ts->key_evt[i].flag & 0x0F;
+		key_status = ts->key_evt[i].flag & 0xF0;
+		pressed = false;
+
+		if (key_status & HIDEEP_KEY_PRESSED_MASK)
+			pressed = true;
+		else
+			pressed = false;
+
+		input_report_key(ts->input_dev, ts->key_codes[key_code],
+			pressed);
+		input_report_key(ts->input_dev, BTN_TOUCH, pressed);
+	}
+
+	input_sync(ts->input_dev);
+}
+#endif
+
+static void hideep_put_event(struct hideep_t *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0)
+		push_ky(ts);
+#endif
+}
+
+static int hideep_get_event(struct hideep_t *ts)
+{
+	int ret;
+	int touch_count;
+	int info_size;
+
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		info_size = ts->tch_count * sizeof(struct hideep_mt_t);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			info_size, (unsigned char *)ts->touch_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0) {
+		info_size = ts->key_count * sizeof(struct hideep_key_t);
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			info_size, (unsigned char *)ts->key_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+#endif
+
+	return touch_count;
+}
+
+static int hideep_event_thread(void *arg)
+{
+	int t_evt;
+
+	struct hideep_t *ts = arg;
+
+	dev_info(&ts->client->dev, "start event thread");
+	// sched_setscheduler(current, SCHED_FIFO, ts->param);
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		schedule();
+
+		t_evt = hideep_get_event(ts);
+
+		if (t_evt >= 0)
+			hideep_put_event(ts);
+
+		if (kthread_should_stop())
+			break;
+	}
+
+	dev_info(&ts->client->dev, "end thread");
+	return 0;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char i2c_buff[2];
+	int ret;
+
+	struct hideep_t *ts = (struct hideep_t *) handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
+		ts->dev_state, ts->debug_dev.debug_enable);
+
+	if (ts->debug_dev.debug_enable == false &&
+		ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, (u8 *)&i2c_buff);
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			ts->interrupt_state = 0;
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = i2c_buff[0];
+		ts->key_count = i2c_buff[1] & 0x0f;
+		ts->lpm_count = i2c_buff[1] & 0xf0;
+
+		wake_up_process(ts->hthread_event);
+
+	} else if (ts->debug_dev.debug_enable == true
+		&& ts->dev_state == state_debugging) {
+		ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
+			FRAME_HEADER_SIZE + ts->debug_dev.img_size,
+			ts->debug_dev.img_buff);
+		ts->debug_dev.img_size =
+			GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
+		ts->debug_dev.ready = 1;
+		wake_up_interruptible(&ts->debug_dev.i_packet);
+	}
+	dev_dbg(&ts->client->dev, "end.");
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_t *ts)
+{
+#ifdef HIDEEP_SUPPORT_KEY
+	int i;
+
+	ts->key_codes[0] = KEY_MENU;
+	ts->key_codes[1] = KEY_HOME;
+	ts->key_codes[2] = KEY_BACK;
+
+	for (i = 0; i < HIDEEP_KEY_MAX; i++)
+		set_bit(ts->key_codes[i], ts->input_dev->keybit);
+#endif
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	set_bit(EV_ABS, ts->input_dev->evbit);
+	set_bit(EV_KEY, ts->input_dev->evbit);
+	set_bit(EV_SYN, ts->input_dev->evbit);
+	set_bit(BTN_TOUCH, ts->input_dev->keybit);
+	set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
+	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
+		ts->p_data->max_z, 0, 0);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+#else
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TRACKING_ID, 0, 10, 0, 0);
+#endif
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
+
+	return 0;
+}
+
+static void hideep_reset_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret;
+	unsigned char cmd = 0x01;
+
+	pdata = ts->p_data;
+
+	dev_info(&ts->client->dev, "start!!");
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
+		// up, down, up
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->reset_up);
+
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "can not reset pin control!!!");
+		} else {
+			mdelay(1);    //modified
+			ret = pinctrl_select_state(pdata->pinctrl,
+				pdata->reset_down);
+
+			if (ret < 0) {
+				dev_err(&ts->client->dev, "can not reset pin control!!!");
+			} else {
+				mdelay(20);    //modified
+				ret = pinctrl_select_state(pdata->pinctrl,
+					pdata->reset_up);
+				if (ret < 0)
+					dev_err(&ts->client->dev, "can not reset pin control!!!");
+			}
+		}
+	} else
+#endif
+	{
+		ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+	dev_info(&ts->client->dev, "end!!");
+}
+
+static void hideep_get_info(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	unsigned char val[4];
+
+	pdata = ts->p_data;
+	ts->hideep_api->i2c_read(ts, 0x28, 4, val);
+
+	pdata->max_x = val[3] << 8 | val[2];
+	pdata->max_y = val[1] << 8 | val[0];
+	pdata->max_w = 255;
+	pdata->max_z = 255;
+
+	dev_info(&ts->client->dev, "X : %d, Y : %d",
+		pdata->max_x, pdata->max_y);
+}
+
+static void hideep_init_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	struct device dev;
+
+	pdata = ts->p_data;
+	dev = ts->client->dev;
+
+#ifdef CONFIG_OF
+	/* regulator get */
+	if (pdata->regulator_vdd != NULL) {
+		pdata->vcc_vdd = regulator_get(&dev,
+			pdata->regulator_vdd);   // main valtage
+		if (IS_ERR(pdata->vcc_vdd)) {
+			ret = PTR_ERR(pdata->vcc_vdd);
+			dev_err(&dev, "Regulator get vcc_vdd ret=%d", ret);
+		}
+	}
+
+	if (pdata->regulator_vid != NULL) {
+		pdata->vcc_vid = regulator_get(&dev,
+			pdata->regulator_vid);   // I/O voltage
+		if (IS_ERR(pdata->vcc_vid)) {
+			ret = PTR_ERR(pdata->vcc_vid);
+			dev_info(&dev, "Regulator get vcc_vid ret=%d", ret);
+		}
+	}
+
+	if (pdata->reset_gpio > 0) {
+		if (gpio_is_valid(pdata->reset_gpio)) {
+			ret = gpio_request(pdata->reset_gpio, "reset_gpio");
+			if (ret)
+				dev_err(&dev, "unable to request gpio [%d]",
+					pdata->reset_gpio);
+		} else {
+			dev_err(&dev, "reset[%d] is used by other device",
+				pdata->reset_gpio);
+		}
+	}
+#endif
+
+	/* power on */
+	ts->hideep_api->power(ts, true);
+	ts->dev_state = state_init;
+	mdelay(30);
+
+#ifdef CONFIG_OF
+	/* interrupt pin set */
+	ret = pinctrl_select_state(pdata->pinctrl, pdata->intb_ctrl);
+	if (ret < 0)
+		dev_err(&dev, "can not intb pin setting!!!!");
+#endif
+	/* ic reset */
+	ts->hideep_api->reset_ic(ts);
+}
+
+static int hideep_resume(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state == state_normal)
+		goto hideep_resume_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+hideep_resume_exit:
+	mdelay(10);
+	ts->hideep_api->reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+static int hideep_suspend(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+	if (ts->dev_state == state_sleep)
+		goto hideep_suspend_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_sleep;
+
+	/* send sleep command.. */
+	pops_mt(ts);
+#ifdef HIDEEP_SUPPORT_KEY
+	pops_ky(ts);
+#endif
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+hideep_suspend_exit:
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+#ifdef CONFIG_FB
+static int fb_notifier_callback(struct notifier_block *self,
+	unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+
+	struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
+
+	if ((evdata) && (evdata->data) &&
+		(event == FB_EVENT_BLANK) &&
+		(ts) && (ts->client)) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			dev_info(&ts->client->dev, "resume");
+			if (ts->suspended == 1) {
+				hideep_resume(&ts->client->dev);
+				ts->suspended = 0;
+			}
+		} else if (*blank == FB_BLANK_POWERDOWN) {
+			dev_info(&ts->client->dev, "suspend");
+			if (ts->suspended == 0) {
+				ts->suspended = 1;
+				hideep_suspend(&ts->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_OF
+static int hideep_parse_dts(struct device *dev,
+	struct hideep_platform_data_t *pdata)
+{
+	int ret = 0;
+	unsigned int coords[4];
+	struct device_node *np;
+
+	dev_info(dev, "enter");
+	np = dev->of_node;
+
+	ret = of_property_read_u32_array(np, "hideep,max_coords", coords, 4);
+	if (ret) {
+		dev_err(dev, "Failed to get max_coords property\n");
+		return ret;
+	}
+
+	pdata->max_x = coords[0];
+	pdata->max_y = coords[1];
+	pdata->max_w = coords[2];
+	pdata->max_z = coords[3];
+
+	dev_info(dev, "max coord data x : %d\n", pdata->max_x);
+	dev_info(dev, "max coord data y : %d\n", pdata->max_y);
+	dev_info(dev, "max coord data w : %d\n", pdata->max_w);
+	dev_info(dev, "max coord data z : %d\n", pdata->max_z);
+
+	/* device tree information get */
+	pdata->irq_gpio = of_get_named_gpio(np,
+		"hideep,irq_gpio", 0);
+	dev_info(dev, "irq_gpio = %d, is %s specified",
+		pdata->irq_gpio, pdata->irq_gpio < 0 ? "not" : "");
+
+	pdata->reset_gpio = of_get_named_gpio(np,
+		"hideep,reset_gpio", 0);
+	dev_info(dev, "reset_gpio = %d, is %s specified",
+		pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
+
+	ret = of_property_read_string(np, "hideep,regulator_vid",
+		&pdata->regulator_vid);
+	if (ret)
+		dev_err(dev, "Failed to get regulator vid");
+	else
+		dev_info(dev, "p_data->regulator_vid = %s",
+			pdata->regulator_vid);
+
+	ret = of_property_read_string(np, "hideep,regulator_vdd",
+		&pdata->regulator_vdd);
+	if (ret)
+		dev_err(dev, "Failed to get regulator vdd");
+	else
+		dev_info(dev, "p_data->regulator_vdd = %s",
+			pdata->regulator_vdd);
+
+	pdata->pinctrl = devm_pinctrl_get(dev);
+
+	if (IS_ERR(pdata->pinctrl)) {
+		dev_err(dev, "can not get pinctrl!!!");
+		return PTR_ERR(pdata->pinctrl);
+	}
+
+	pdata->reset_down = pinctrl_lookup_state(pdata->pinctrl,
+		"reset-down");
+	if (IS_ERR(pdata->reset_down))
+		dev_err(dev, "can not get reset_down state");
+
+	pdata->reset_up = pinctrl_lookup_state(pdata->pinctrl,
+		"reset-up");
+	if (IS_ERR(pdata->reset_up))
+		dev_err(dev, "can not get reset_up state");
+
+	pdata->intb_ctrl = pinctrl_lookup_state(pdata->pinctrl,
+		"intb-ctrl");
+	if (IS_ERR(pdata->intb_ctrl))
+		dev_err(dev, "can not get intb-ctrl state");
+
+	return ret;
+}
+#endif
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_platform_data_t *p_data;
+	struct dwz_info_t *dwz;
+	struct hideep_function_list_t *function;
+	struct hideep_t *ts;
+
+	dev_info(&client->dev, "enter");
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		ret = -ENODEV;
+		return ret;
+	}
+
+	/* init platform data */
+	p_data = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_platform_data_t), GFP_KERNEL);
+
+#ifdef CONFIG_OF
+	if (client->dev.of_node) {
+		ret = hideep_parse_dts(&client->dev, p_data);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	/* init hideep_t */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_t), GFP_KERNEL);
+	dwz = devm_kzalloc(&client->dev,
+		sizeof(struct dwz_info_t), GFP_KERNEL);
+	function = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_function_list_t), GFP_KERNEL);
+
+	ts->client = client;
+	ts->p_data = p_data;
+	ts->dwz_info = dwz;
+	ts->hideep_api = function;
+
+	ts->hideep_api->i2c_read = hideep_i2c_read;
+	ts->hideep_api->i2c_write = hideep_i2c_write;
+	ts->hideep_api->reset_ic = hideep_reset_ic;
+	ts->hideep_api->power = hideep_power;
+
+	/* init for isp function */
+	hideep_isp_init(ts);
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	if (!client->dev.of_node)
+		hideep_get_info(ts);
+
+	/* hardware init */
+	hideep_init_ic(ts);
+
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+	/* read info */
+	ret = ts->hideep_api->get_dwz_info(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		goto hideep_probe_read_dwz_err;
+	}
+#endif
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_input_dev_memory_err;
+	}
+
+	hideep_capability(ts);
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_register_input_dev_err;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	/* init event thread */
+	ts->hthread_event = kthread_run(hideep_event_thread,
+		ts, "hideep_event_thread");
+	if (IS_ERR(ts->hthread_event)) {
+		dev_err(&client->dev, "can't create event thread !!!");
+		ret = PTR_ERR(ts->hthread_event);
+		goto hideep_probe_create_thread_err;
+	}
+
+#ifdef CONFIG_OF
+	/* interrupt set */
+	ts->client->irq = gpio_to_irq(p_data->irq_gpio);
+#endif
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (ts->client->irq <= 0) {
+		dev_err(&client->dev, "can't be assigned irq");
+		goto hideep_probe_assigned_irq_err;
+	}
+
+	if (ts->client->irq) {
+		ret = request_threaded_irq(ts->client->irq, NULL,
+			hideep_irq_task,
+			(IRQF_TRIGGER_LOW | IRQF_ONESHOT),
+			ts->client->name, ts);
+		disable_irq(ts->client->irq);
+		ts->interrupt_state = 0;
+		if (ret < 0) {
+			dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+				ret);
+			goto hideep_probe_request_irq_err;
+		}
+	}
+
+	ret = hideep_debug_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_debug_init_err;
+	}
+
+	ret = hideep_sysfs_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_sysfs_init_err;
+	}
+
+#ifdef CONFIG_FB
+	ts->suspended = 0;
+	ts->fb_notif.notifier_call = fb_notifier_callback;
+	ret = fb_register_client(&ts->fb_notif);
+	if (ret) {
+		dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
+			ret);
+		ret = -1;
+		goto hideep_probe_register_fb_err;
+	}
+#endif
+
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+	dev_info(&client->dev, "probe is ok!");
+	return 0;
+
+hideep_probe_register_fb_err:
+	hideep_sysfs_exit(ts);
+
+hideep_probe_sysfs_init_err:
+	hideep_debug_uninit();
+
+hideep_probe_debug_init_err:
+hideep_probe_request_irq_err:
+	free_irq(ts->client->irq, ts);
+
+hideep_probe_assigned_irq_err:
+hideep_probe_create_thread_err:
+	input_unregister_device(ts->input_dev);
+
+hideep_probe_register_input_dev_err:
+	if (ts->input_dev)
+		input_free_device(ts->input_dev);
+
+hideep_probe_input_dev_memory_err:
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+hideep_probe_read_dwz_err:
+#endif
+
+#ifdef CONFIG_OF
+	if (ts->p_data->regulator_vid != NULL ||
+		ts->p_data->regulator_vdd != NULL)
+		ts->hideep_api->power(ts, false);
+#endif
+	dev_err(&client->dev, "probe err!");
+	return ret;
+}
+
+static int hideep_remove(struct i2c_client *client)
+{
+	struct hideep_t *ts = i2c_get_clientdata(client);
+
+	kthread_stop(ts->hthread_event);
+
+#ifdef CONFIG_FB
+	if (fb_unregister_client(&ts->fb_notif))
+		dev_info(&client->dev,
+			"Error occurred while unregistering fb_notifier");
+#endif
+
+#ifdef CONFIG_OF
+	if (ts->p_data->regulator_vid != NULL ||
+		ts->p_data->regulator_vdd != NULL)
+		ts->hideep_api->power(ts, false);
+#endif
+	free_irq(client->irq, ts);
+
+	input_unregister_device(ts->input_dev);
+	hideep_sysfs_exit(ts);
+
+	hideep_debug_uninit();
+	devm_kfree(&client->dev, ts);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops hideep_pm_ops = {
+	.suspend = hideep_suspend,
+	.resume = hideep_resume,
+};
+#endif
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep_ts" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.remove = hideep_remove,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
new file mode 100644
index 0000000..1dc3d83
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+#include "hideep_dbg.h"
+
+static struct hideep_debug_dev_t *hdd;
+
+static int hideep_i2c_recv(unsigned int len)
+{
+	int ret = 0;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_i2c_send(unsigned int len)
+{
+	int ret = 0;
+	unsigned char *buff = hdd->vr_buff;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_send(hdd->ts->client, buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_get_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+
+	ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
+		hdd->vr_buff);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_set_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+	int wr_remain = len;
+	int vr_addr = addr;
+	int wr_len = len;
+	unsigned char *buff = hdd->vr_buff;
+
+	do {
+		if (wr_remain >=  MAX_VR_BUFF)
+			wr_len = MAX_VR_BUFF;
+		else
+			wr_len = wr_remain;
+
+		ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
+			wr_len, buff);
+		if (ret < 0)
+			goto i2c_err;
+
+		wr_remain -= MAX_VR_BUFF;
+		vr_addr += MAX_VR_BUFF;
+		buff += MAX_VR_BUFF;
+	} while (wr_remain > 0);
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_download_uc(const char __user *uc, size_t count, int offset)
+{
+	int ret;
+	unsigned char *ucode;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (count > FIRMWARE_SIZE) {
+		dev_err(&hdd->ts->client->dev, "over size data!!!");
+		return -1;
+	}
+
+	ucode = kmalloc(count, GFP_KERNEL);
+
+	ret = copy_from_user(ucode + offset, uc, count);
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
+		kfree(ucode);
+		return 0;
+	}
+
+	disable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 0;
+	hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
+	enable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 1;
+	kfree(ucode);
+
+	dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
+
+	return count;
+}
+
+static int hideep_debug_open(struct inode *inode, struct file *file)
+{
+	hdd->release_flag = false;
+
+	file->private_data = hdd;
+	dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
+
+	return 0;
+}
+
+static int hideep_debug_release(struct inode *inode, struct file *file)
+{
+	if (!hdd->release_flag)
+		return -1;
+	hdd->release_flag = false;
+	file->private_data = NULL;
+	return 0;
+}
+
+static unsigned int hideep_debug_poll(struct file *file,
+	struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	struct hideep_debug_dev_t *drv_info;
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info = file->private_data;
+
+	poll_wait(file, &drv_info->i_packet, wait);
+
+	if (drv_info->ready) {
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mask |= POLLIN | POLLRDNORM;
+		drv_info->ready = 0;
+	}
+
+	return mask;
+}
+
+static ssize_t hideep_debug_read(struct file *file, char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret = -1;
+	ssize_t rd_len = 0;
+	unsigned char *rd_buffer = NULL;
+	unsigned char *data = NULL;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info->vr_size = count;
+	rd_len = count;
+
+	data = kmalloc(rd_len, GFP_KERNEL);
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		if ((*offset & HIDEEP_MAX_RAW_LEN) &&
+			(*offset < HIDEEP_MAX_RAW_LEN) &&
+			(drv_info->debug_enable == true)) {
+			mutex_lock(&drv_info->ts->dev_mutex);
+			rd_buffer = drv_info->img_buff;
+			ret = 0;
+			mutex_unlock(&drv_info->ts->dev_mutex);
+		} else {
+			ret = hideep_get_vreg(*offset, rd_len);
+			rd_buffer = drv_info->vr_buff;
+		}
+		if (ret < 0)
+			rd_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		ret = hideep_i2c_recv(rd_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "ret = %d", ret);
+			rd_len = 0;
+		} else {
+			rd_buffer = drv_info->vr_buff;
+		}
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		rd_len = count;
+		memset(data, 0x0, rd_len);
+
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mutex_lock(&drv_info->ts->dev_mutex);
+		mutex_lock(&drv_info->ts->i2c_mutex);
+
+		drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
+			*offset & 0xfffff);
+
+		mutex_unlock(&drv_info->ts->dev_mutex);
+		mutex_unlock(&drv_info->ts->i2c_mutex);
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+
+		rd_buffer = data;
+	} else {
+		dev_err(&hdd->ts->client->dev, "undefined address");
+		kfree(data);
+		return 0;
+	}
+
+	ret = copy_to_user(buf, rd_buffer, rd_len);
+
+	if (drv_info->debug_enable == true && drv_info->ready == 0) {
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+	}
+
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+		kfree(data);
+		return -EFAULT;
+	}
+
+	kfree(data);
+	return rd_len;
+}
+
+static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+	int wr_len = 0;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+	if (file->private_data == NULL)
+		return 0;
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		wr_len = count;
+
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+			return -EFAULT;
+		}
+
+		ret = hideep_set_vreg(*offset, wr_len);
+		if (ret < 0)
+			wr_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		wr_len = count;
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		ret = hideep_i2c_send(wr_len);
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
+	} else {
+		dev_err(&hdd->ts->client->dev,
+			"hideep_write : undefined address, 0x%08x",
+			(int)*offset);
+		return 0;
+	}
+
+	return wr_len;
+}
+
+static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
+{
+	loff_t newpos;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
+		(unsigned int)off, whence);
+	if (file->private_data == NULL)
+		return -EFAULT;
+
+	switch (whence) {
+	/* SEEK_SET */
+	case 0:
+		newpos = off;
+		break;
+	/* SEEK_CUR */
+	case 1:
+		dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
+			(unsigned int)off);
+		if (off & HIDEEP_RELEASE_FLAG) {
+			dev_dbg(&hdd->ts->client->dev, "set release flag");
+			drv_info->release_flag = true;
+		}
+		newpos = file->f_pos;
+		break;
+	/* SEEK_END */
+	case 2:
+		newpos = file->f_pos;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (newpos < 0)
+		return -EINVAL;
+
+	file->f_pos = newpos;
+
+	return newpos;
+}
+
+static const struct file_operations hideep_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = hideep_debug_open,
+	.poll = hideep_debug_poll,
+	.release = hideep_debug_release,
+	.read = hideep_debug_read,
+	.write = hideep_debug_write,
+	.llseek = hideep_debug_llseek,
+};
+
+static struct miscdevice hideep_debug_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = HIDEEP_DEBUG_DEVICE_NAME,
+	.fops = &hideep_debug_fops
+};
+
+void hideep_debug_uninit(void)
+{
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+
+	misc_deregister(&hideep_debug_dev);
+}
+
+int hideep_debug_init(struct hideep_t *ts)
+{
+	int ret = 0;
+
+	hdd = &ts->debug_dev;
+
+	ret = misc_register(&hideep_debug_dev);
+	if (ret) {
+		dev_err(&ts->client->dev,
+			"hideep debug device register fail!!!");
+		goto fail;
+	}
+
+	init_waitqueue_head(&hdd->i_packet);
+
+	hdd->ts = ts;
+	hdd->debug_enable = false;
+
+	hdd->img_size = 0;
+	hdd->vr_size = 0;
+
+	hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
+	hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
+
+	memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
+	memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
+
+	if (!hdd->vr_buff || !hdd->img_buff)
+		goto fail;
+
+	dev_info(&ts->client->dev, "debug init....");
+	return 0;
+
+fail:
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+	return ret;
+}
diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
new file mode 100644
index 0000000..f18a09f
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_DBG_H
+#define _LINUX_HIDEEP_DBG_H
+
+/* Device Driver <==> Application */
+/* lseek(), write(), read() command */
+#define HIDEEP_RELEASE_FLAG				0x8000
+#define HIDEEP_VR_ADDR_LEN				0xFFFF
+#define HIDEEP_MAX_RAW_LEN				0x3000
+#define HIDEEP_READ_WRITE_VR			0xF000
+#define HIDEEP_I2C_BYPASS				0x20000000
+
+#define MAX_VR_BUFF						2048
+
+/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
+#define MAX_RAW_SIZE					7369
+#endif /* _LINUX_HIDEEP_DBG_H */
diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
new file mode 100644
index 0000000..7cdd422
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+
+static struct pgm_packet packet_w;
+static struct pgm_packet packet_r;
+
+static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x80 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		(len+5));
+
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x00 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		5);
+
+	if (ret < 0)
+		goto err;
+
+	ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
+		len);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x00);
+	packet.header.w[1] = htonl(addr);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x80);
+	packet.header.w[1] = htonl(addr);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void get_dwz_from_binary(unsigned char *pres,
+	struct dwz_info_t *dwz_info)
+{
+	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
+		sizeof(struct dwz_info_t));
+}
+
+static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
+{
+	SW_RESET_IN_PGM(food);
+}
+
+static int hideep_enter_pgm(struct hideep_t *ts)
+{
+	int ret = 0;
+	int retry_count = 10;
+	int retry = 0;
+	unsigned int status;
+	unsigned int pattern = 0xDF9DAF39;
+
+	while (retry < retry_count) {
+		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
+		mdelay(1);
+
+		/* flush invalid Tx load register */
+		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+		if (status != htonl(pattern)) {
+			retry++;
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				status);
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry < retry_count) {
+		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
+
+		mdelay(1);
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_t *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	mdelay(50);
+
+	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
+		sizeof(struct dwz_info_t));
+
+	memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info_t));
+	hideep_sw_reset(ts, 10);
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mdelay(50);
+
+	return ret;
+}
+
+static int hideep_nvm_unlock(struct hideep_t *ts)
+{
+	int ret = 0;
+	unsigned int unmask_code = 0;
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != NVM_MASK)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
+	SET_FLASH_HWCONTROL();
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	return ret;
+}
+
+static int hideep_program_page(struct hideep_t *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret = 0;
+	unsigned int pio_cmd = WRONLY;
+	unsigned int pio_cmd_page_erase = PERASE;
+	unsigned int status;
+	int time_out = 0;
+	int inf_en = 0;
+	unsigned int end_flag = 124;
+#ifndef PGM_BURST_WR
+	unsigned int i;
+#endif
+
+	hideep_pgm_r_reg(ts, FLASH_STA, &status);
+	ret = (status == 0) ? -1:0;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	if (addr > INF_SECTION) {
+		/* added INF flag set in pio_cmd */
+		addr -= INF_SECTION;
+		pio_cmd |= INF;
+		pio_cmd_page_erase |= INF;
+		inf_en = 1;
+	}
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* first erase */
+	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+	time_out = 0;
+
+	while (1) {
+		mdelay(1);
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+		if (time_out++ > 100)
+			break;
+	}
+	SET_FLASH_PIO(1);
+	/* first erase end*/
+
+	SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
+
+#ifdef PGM_BURST_WR
+	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
+		packet_w, NVM_PAGE_SIZE);
+#else
+	for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
+		SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
+#endif
+	if (inf_en == 0)
+		SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
+	else
+		SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	while (1) {
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+	}
+	/* write routine end */
+
+	SET_FLASH_HWCONTROL();
+
+	return ret;
+}
+
+static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset, unsigned char *old_fw)
+{
+	int i;
+	int ret = 0;
+	int len_r;
+	int len_w;
+	int addr = 0;
+	unsigned int pages;
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	addr = offset;
+	len_r = len;
+	len_w = len_r;
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_w = NVM_PAGE_SIZE;
+
+		/* compare */
+		if (old_fw != NULL)
+			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
+
+		if (ret != 0 || old_fw == NULL) {
+			/* write page */
+			memcpy(packet_w.payload, &(ucode[addr]), len_w);
+
+			ret = hideep_program_page(ts, addr, &packet_w);
+			mdelay(1);
+			if (ret < 0)
+				dev_err(&ts->client->dev,
+					"hideep_program_nvm : error(%08x):",
+					addr);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_w = len_r;
+	}
+
+	return ret;
+}
+
+static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset)
+{
+	int i, j;
+	int ret = 0;
+	unsigned char page_chk = 0;
+	unsigned int addr = offset;
+	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	int len_r = len;
+	int len_v = len_r;
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
+
+		if (page_chk != 0) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++)
+				dev_err(&ts->client->dev, "%02x : %02x",
+						ucode[addr+j], read[j]);
+
+			dev_err(&ts->client->dev, "verify : error(addr : %d)",
+				addr);
+
+			ret = -1;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	return ret;
+}
+
+static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
+	int offset)
+{
+	int ret = 0;
+	int pages, i;
+	int len_r, len_v;
+	int addr = offset;
+#ifndef PGM_BURST_WR
+	int j;
+#endif
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	len_r = len;
+	len_v = len_r;
+
+	ts->hideep_api->reset_ic(ts);
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	hideep_sw_reset(ts, 1000);
+}
+
+static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
+	size_t len, int offset)
+{
+	int ret = 0;
+	int retry = 3;
+
+	while (retry--) {
+		ret = hideep_verify_nvm(ts, fw, len, offset);
+		if (ret == 0) {
+			dev_dbg(&ts->client->dev, "update success");
+			break;
+		}
+		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
+	}
+
+	ret = (retry == 0) ? -1:0;
+
+	return ret;
+}
+
+static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
+	int len, int offset, bool mode)
+{
+	int ret = 0;
+	int firm_len;
+	unsigned char *ic_fw;
+	unsigned char *dwz_info;
+
+	ic_fw = kmalloc(FIRMWARE_SIZE, GFP_KERNEL);
+	dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
+
+	memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
+
+	firm_len = len;
+	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
+		firm_len, FIRMWARE_SIZE);
+
+	if (firm_len > FIRMWARE_SIZE)
+		firm_len = FIRMWARE_SIZE;
+
+	dev_dbg(&ts->client->dev, "enter");
+	/* memory dump of target IC */
+	hideep_read_nvm(ts, ic_fw, firm_len, offset);
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
+
+#ifdef PGM_VERIFY
+	hideep_fw_verify_run(ts, code, firm_len, offset);
+	if (ret < 0) {
+		if (mode == true) {
+			/* clear dwz version, it will be store once again
+			 * after update success
+			 */
+			ts->dwz_info->release_ver = 0;
+			memcpy(&dwz_info[64], ts->dwz_info,
+				sizeof(struct dwz_info_t));
+			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
+				0x280, NULL);
+		}
+	}
+#endif
+	get_dwz_from_binary(code, ts->dwz_info);
+
+	hideep_sw_reset(ts, 1000);
+
+	kfree(ic_fw);
+	kfree(dwz_info);
+
+	return ret;
+}
+
+static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
+{
+	int ret = 0;
+	const struct firmware *fw_entry;
+	unsigned char *fw_buf;
+	unsigned int fw_length;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	fw_buf = (unsigned char *)fw_entry->data;
+	fw_length = (unsigned int)fw_entry->size;
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_update_part_firmware(struct hideep_t *ts,
+	unsigned char *code, int len, int offset, bool mode)
+{
+	int ret = 0;
+
+	mutex_lock(&ts->dev_mutex);
+
+	ret = hideep_wr_firmware(ts, code, len, offset, false);
+
+	mutex_unlock(&ts->dev_mutex);
+
+	return ret;
+}
+
+void hideep_isp_init(struct hideep_t *ts)
+{
+	ts->hideep_api->update_all = hideep_update_all_firmware;
+	ts->hideep_api->update_part = hideep_update_part_firmware;
+	ts->hideep_api->get_dwz_info = hideep_load_dwz;
+	ts->hideep_api->sp_func = hideep_read_nvm;
+}
diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
new file mode 100644
index 0000000..648f271
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_ISP_H
+#define _LINUX_HIDEEP_ISP_H
+
+#define PGM_BURST_WR
+#define PGM_VERIFY
+
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_NVM_DOWNLOAD		0x10000000
+
+/*************************************************************************
+ * register map
+ *************************************************************************/
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+/*************************************************************************
+ * flash commands
+ *************************************************************************/
+#define MERASE					0x00010000
+#define SERASE					0x00020000
+#define PERASE					0x00040000
+#define PROG					0x00080000
+#define WRONLY					0x00100000
+#define INF						0x00200000
+
+/*************************************************************************
+ * NVM Mask
+ *************************************************************************/
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x00310000
+#define INF_SECTION				0x0000C000
+#endif
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x0030027B
+#define INF_SECTION				0x00010000
+#endif
+
+/*************************************************************************
+ * DWZ info
+ *************************************************************************/
+#define HIDEEP_BOOT_SECTION		0x00000400
+#define HIDEEP_BOOT_LEN			0x00000400
+#define HIDEEP_DWZ_SECTION		0x00000280
+#define HIDEEP_DWZ_INFO_OFS		0x000002C0
+#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
+							- HIDEEP_DWZ_SECTION)
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		unsigned int w[2];
+	} header;
+
+	unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+#endif /* _LINUX_HIDEEP_ISP_H */
diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
new file mode 100644
index 0000000..4aedec6
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_sysfs.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	if (count == 1)
+		mode = buf[0] & 0xff;
+	else
+		return count;
+
+	if (mode == 1) {
+		disable_irq(ts->client->irq);
+
+		ts->dev_state = state_updating;
+		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+			ts->dwz_info->product_id);
+		ret = ts->hideep_api->update_all(ts, fw_name);
+
+		kfree(fw_name);
+
+		enable_irq(ts->client->irq);
+
+		ts->dev_state = state_normal;
+		if (ret != 0)
+			dev_err(dev, "The firmware update failed(%d)", ret);
+	}
+
+	return count;
+}
+
+static ssize_t fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->release_ver);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		ts->dwz_info->product_id);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->product_id);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t power_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
+		(ts->dev_state == state_init) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t power_control(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on;
+
+	if (count == 1)
+		on = buf[0] & 0xff;
+	else
+		return count;
+
+	if (on) {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_normal;
+		ts->hideep_api->reset_ic(ts);
+	} else {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_init;
+	}
+
+	return count;
+}
+
+#ifdef HIDEEP_SUPPORT_STYLUS
+static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+	unsigned char status[1];
+
+	ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
+
+	len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
+		(status[0] == 0x00) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on;
+	unsigned char data[2];
+
+	if (count == 1)
+		on = buf[0] & 0xff;
+	else
+		return count;
+
+	data[0] = 0x04;
+
+	if (on) {
+		data[1] = 0x01;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	} else {
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	}
+
+	return count;
+}
+#endif
+
+static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
+		(ts->debug_dev.debug_enable == 0) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on;
+	unsigned char data[2];
+
+	if (count == 1)
+		on = buf[0] & 0xff;
+	else
+		return count;
+
+	if (on) {
+		ts->debug_dev.debug_enable = 1;
+		ts->dev_state = state_debugging;
+	} else {
+		ts->debug_dev.debug_enable = 0;
+		ts->dev_state = state_normal;
+		/* set touch mode */
+		data[0] = 0x00;
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
+		if (ts->interrupt_state == 0) {
+			data[0] = 0x5A;
+			ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
+				data);
+			enable_irq(ts->client->irq);
+			ts->interrupt_state = 1;
+		}
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
+static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
+static DEVICE_ATTR(power_en, 0664, power_status, power_control);
+static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);
+#ifdef HIDEEP_SUPPORT_STYLUS
+static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);
+#endif
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_power_en.attr,
+	&dev_attr_debug_en.attr,
+#ifdef HIDEEP_SUPPORT_STYLUS
+	&dev_attr_stylus_en.attr,
+#endif
+	NULL
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+int hideep_sysfs_init(struct hideep_t *ts)
+{
+	int ret;
+	struct i2c_client *client = ts->client;
+
+	/* Create the files associated with this kobject */
+	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
+
+	if (ret)
+		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
+			 __func__, ret);
+
+	return ret;
+}
+
+int hideep_sysfs_exit(struct hideep_t *ts)
+{
+	struct i2c_client *client = ts->client;
+
+	sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	return 0;
+}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
       [not found] ` <1499235588-32219-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-07-10  1:17   ` Rob Herring
  0 siblings, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-07-10  1:17 UTC (permalink / raw)
  To: Anthony Kim
  Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

On Wed, Jul 05, 2017 at 03:19:48PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |   40 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>  drivers/input/touchscreen/Kconfig                  |   32 +
>  drivers/input/touchscreen/Makefile                 |    2 +
>  drivers/input/touchscreen/hideep.h                 |  338 +++++++
>  drivers/input/touchscreen/hideep_core.c            | 1029 ++++++++++++++++++++
>  drivers/input/touchscreen/hideep_dbg.c             |  405 ++++++++
>  drivers/input/touchscreen/hideep_dbg.h             |   24 +
>  drivers/input/touchscreen/hideep_isp.c             |  584 +++++++++++
>  drivers/input/touchscreen/hideep_isp.h             |   96 ++
>  drivers/input/touchscreen/hideep_sysfs.c           |  249 +++++
>  11 files changed, 2800 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.h
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..f5ab5e6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,40 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible		: must be "hideep,hideep_ts".

s/_/-/

Is there only 1 version or another way to determine the exact chip and 
firmware? If not, then you need a more specific compatible.

> +- reg			: I2C slave address, (e.g. 0x6C).
> +- hideep,max_coords	: Max value for axis X, Y, W, Z.
> +
> +Optional properties:
> +- pinctrl-names	: "reset_down", "reset-up", "intb-ctrl".
> +				They are gpio pinctrl names for should be search in driver code.
> +- pinctrl-0	: Gpio control for "reset-down".
> +- pinctrl-1	: Gpio control for "reset-up".
> +- pinctrl-2	: Gpio control for "intb-ctrl".
> +- hideep,regulator_vdd	: Main voltage(3.3V) name.
> +- hideep,regulator_vid	: IO voltage(1.8V) name.

Use standard regulator binding.

> +- hideep,irq_gpio		: Define for interrupt gpio pin.
> +						It is to use for set interrupt type.

Use interrupt binding.

> +- hideep,reset_gpio		: Define for reset gpio pin.
> +						It is to use for reset IC.

reset-gpios is the standard name.

> +
> +Example:
> +
> +i2c@00000000 {
> +
> +	/* ... */
> +
> +	hideep@6c {

touchscreen@6c

> +		compatible = "hideep,hideep_ts";
> +		reg = <0x6c>;
> +		pinctrl-names = "reset-down", "reset-up", "intb-ctrl";
> +		pinctrl-0 = <&reset_gpio0>;
> +		pinctrl-1 = <&reset_gpio1>;
> +		pinctrl-2 = <&touch_int>;
> +		hideep,regulator_vdd = "vdd_ldo33";	// need modify
> +		hideep,regulator_vid = "vdd_ldo18";	// need modify
> +		hideep,irq_gpio = <&gpx0 5 0x0>;	// <gpio-name gpio-num gpio-val> need modify
> +		hideep,reset_gpio = <&gpa0 5 0x1>;	// <gpio-name gpio-num gpio-val> need modify
> +		hideep,max_coords = <1080 1920 65535 65535>;	// x y w z, need modify

Not documented. Probably should be standard touchscreen properties.

> +	};
> +};
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
  2017-07-05  6:19 [PATCH] Input: add support for HiDeep touchscreen Anthony Kim
       [not found] ` <1499235588-32219-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-07-12  5:24 ` Anthony Kim
       [not found]   ` <1499837054-4659-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
  1 sibling, 1 reply; 29+ messages in thread
From: Anthony Kim @ 2017-07-12  5:24 UTC (permalink / raw)
  To: robh+dt, dmitry.torokhov, mark.rutland, rydberg
  Cc: linux-input, devicetree, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |  37 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/input/touchscreen/Kconfig                  |  32 +
 drivers/input/touchscreen/Makefile                 |   2 +
 drivers/input/touchscreen/hideep.h                 | 329 ++++++++
 drivers/input/touchscreen/hideep_core.c            | 924 +++++++++++++++++++++
 drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
 drivers/input/touchscreen/hideep_dbg.h             |  24 +
 drivers/input/touchscreen/hideep_isp.c             | 584 +++++++++++++
 drivers/input/touchscreen/hideep_isp.h             |  96 +++
 drivers/input/touchscreen/hideep_sysfs.c           | 249 ++++++
 11 files changed, 2683 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.h
 create mode 100644 drivers/input/touchscreen/hideep_core.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.h
 create mode 100644 drivers/input/touchscreen/hideep_isp.c
 create mode 100644 drivers/input/touchscreen/hideep_isp.h
 create mode 100644 drivers/input/touchscreen/hideep_sysfs.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..5eb8c1d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,37 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep_ts".
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- irq-gpios		: Define for interrupt gpio pin.
+						It is to use for set interrupt type.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- hideep,max_coords	: Max value for axis X, Y, W, Z.
+
+Example:
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep_ts";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		irq-gpios = <&gpx1 2 0>;
+		reset-gpios = <&gpx1 5 0>;
+		hideep,max_coords = <1080 1920 65535 65535>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..558f655 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1246,4 +1246,36 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
+choice
+	prompt "Select IC"
+	depends on TOUCHSCREEN_HIDEEP
+	default TOUCHSCREEN_HIDEEP_CRIMSON
+	help
+	  This driver support two type IC.
+	  So it need to select IC.
+
+config TOUCHSCREEN_HIDEEP_CRIMSON
+	bool "Crimson Touch IC"
+	depends on TOUCHSCREEN_HIDEEP
+	help
+	  say Y to enable driver for Touchpanel using HiDeep Crimson Touch IC.
+
+config TOUCHSCREEN_HIDEEP_LIME
+	bool "Lime Touch IC"
+	depends on TOUCHSCREEN_HIDEEP
+	help
+	  say Y to enable driver for Touchpanel using HiDeep Lime Touch IC.
+endchoice
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..3aab466 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
+hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
new file mode 100644
index 0000000..a7fca81
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.h
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_H
+#define _LINUX_HIDEEP_H
+
+/*************************************************************************
+ * this is include special HEAD file.
+ *************************************************************************/
+#ifdef CONFIG_FB
+#include <linux/fb.h>
+#include <linux/notifier.h>
+#endif
+
+/*************************************************************************
+ * this is include normal HEAD file.
+ *************************************************************************/
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/kthread.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/task_work.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/input/mt.h>
+#include <linux/fs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
+#include <linux/acpi.h>
+
+/*************************************************************************
+ * definition part.
+ * define is (open, set, enable) if not, is (close, clear, disable)
+ * some special switch of functions.
+ *************************************************************************/
+
+/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
+#define HIDEEP_PROTOCOL_2_0
+
+/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
+ * otherwise TYPE_A.
+ */
+#define HIDEEP_TYPE_B_PROTOCOL
+
+/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
+#define HIDEEP_DWZ_VERSION_CHECK
+
+/* HIDEEP_SUPPORT_KE if define, it will use key button. */
+#define HIDEEP_SUPPORT_KEY
+
+/* HIDEEP_SUPPORT_STYLUS if define, it will use key button. */
+#define HIDEEP_SUPPORT_STYLUS
+
+/*************************************************************************
+ * Firmware name & size.
+ *************************************************************************/
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
+#define FIRMWARE_SIZE					(48 * 1024)
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
+#define FIRMWARE_SIZE					(64 * 1024)
+#endif
+
+/*************************************************************************
+ * Buffer size
+ *************************************************************************/
+#define FRAME_HEADER_SIZE				8
+/* if size of system i2c buffer is smaller than this value, need to modify */
+#define MAX_I2C_BUFFER_SIZE				512
+
+/*************************************************************************
+ * board porting config
+ *************************************************************************/
+#define HIDEEP_DEBUG_DEVICE_NAME		"hideep_debug"
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+/*************************************************************************
+ * register addr
+ *************************************************************************/
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/*************************************************************************
+ * multi-touch & key definitions.
+ *************************************************************************/
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/*************************************************************************
+ * HIDEEP PROTOCOL
+ *************************************************************************/
+#ifdef HIDEEP_PROTOCOL_2_0
+struct hideep_mt_t {
+	unsigned short x;
+	unsigned short y;
+	unsigned short z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+};
+#else
+struct hideep_mt_t {
+	unsigned char flag;
+	unsigned char index;
+	unsigned short x;
+	unsigned short y;
+	unsigned char z;
+	unsigned char w;
+};
+#endif
+
+struct hideep_key_t {
+	unsigned char flag;
+	unsigned int key;
+};
+
+/*************************************************************************
+ * IC State
+ *************************************************************************/
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+	state_debugging,
+};
+
+/*************************************************************************
+ * Firmware info
+ *************************************************************************/
+struct dwz_info_t {
+	unsigned int code_start;
+	unsigned char code_crc[12];
+
+	unsigned int c_code_start;
+	unsigned short c_code_len;
+	unsigned short gen_ver;
+
+	unsigned int vr_start;
+	unsigned short vr_len;
+	unsigned short rsv0;
+
+	unsigned int ft_start;
+	unsigned short ft_len;
+	unsigned short vr_version;
+
+	unsigned short boot_ver;
+	unsigned short core_ver;
+	unsigned short custom_ver;
+	unsigned short release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	unsigned short product_code;
+	unsigned short extra_option;
+
+	unsigned short product_id;
+	unsigned short vendor_id;
+};
+
+/*************************************************************************
+ * driver information for hideep_t by device tree
+ *************************************************************************/
+struct hideep_platform_data_t {
+	unsigned int max_x;
+	unsigned int max_y;
+	unsigned int max_z;
+	unsigned int max_w;
+
+#ifdef CONFIG_OF
+	int irq_gpio;
+	int reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+#endif
+};
+
+struct hideep_debug_dev_t {
+	struct miscdevice misc;
+
+	wait_queue_head_t i_packet;
+
+	unsigned int ready;
+	unsigned char *vr_buff;
+	unsigned char *img_buff;
+	unsigned short vr_size;
+	unsigned short img_size;
+
+	bool debug_enable;
+	bool release_flag;
+
+	struct hideep_t *ts;
+};
+
+struct hideep_function_list_t {
+	//core
+	int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	void (*reset_ic)(struct hideep_t *);
+	void (*power)(struct hideep_t *, int);
+
+	//isp
+	int (*update_all)(struct hideep_t *, const char *);
+	int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
+	int (*get_dwz_info)(struct hideep_t *);
+	void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
+};
+
+struct hideep_t {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct hideep_platform_data_t *p_data;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	struct hideep_debug_dev_t debug_dev;
+	struct hideep_function_list_t *hideep_api;
+
+	struct task_struct *hthread_event;
+
+	bool suspended;
+	enum e_dev_state dev_state;
+
+#ifdef CONFIG_FB
+	struct notifier_block fb_notif;
+#endif
+
+	long tch_bit;
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
+
+#ifdef HIDEEP_SUPPORT_KEY
+	struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
+	int key_codes[HIDEEP_KEY_MAX];
+#endif
+
+	unsigned char i2c_buf[256];
+	struct dwz_info_t *dwz_info;
+
+	int interrupt_state;
+};
+
+/*************************************************************************
+ * function define
+ *************************************************************************/
+int hideep_debug_init(struct hideep_t *ts);
+void hideep_debug_uninit(void);
+
+int hideep_sysfs_init(struct hideep_t *ts);
+int hideep_sysfs_exit(struct hideep_t *ts);
+
+void hideep_isp_init(struct hideep_t *ts);
+
+#endif
diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
new file mode 100644
index 0000000..e9249c7
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.c
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
+	((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
+
+static int hideep_pwr_on(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (!IS_ERR(pdata->vcc_vdd)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+	}
+	usleep_range(999, 1000);
+
+	if (!IS_ERR(pdata->vcc_vid)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+	usleep_range(2999, 3000);
+#endif
+
+	return ret;
+}
+
+static int hideep_pwr_off(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
+		gpio_set_value(pdata->reset_gpio, 0);
+	}
+
+	if (!IS_ERR(pdata->vcc_vid)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
+		ret = regulator_disable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+
+	if (!IS_ERR(pdata->vcc_vdd)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
+		ret = regulator_disable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd disable failed ret=%d", ret);
+	}
+#endif
+	return ret;
+}
+
+static void hideep_power(struct hideep_t *ts, int on)
+{
+	int ret = 0;
+
+	if (on) {
+		dev_info(&ts->client->dev, "power on");
+		ret = hideep_pwr_on(ts);
+	} else {
+		dev_info(&ts->client->dev, "power off");
+		ret = hideep_pwr_off(ts);
+	}
+}
+
+static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned short r_len, raddr;
+	int length;
+
+	length = len;
+	raddr = addr;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	do {
+		if (length > MAX_I2C_BUFFER_SIZE)
+			r_len = MAX_I2C_BUFFER_SIZE;
+		else
+			r_len = length;
+
+		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
+			raddr, r_len);
+
+		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
+
+		if (ret < 0)
+			goto i2c_err;
+
+		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
+		length -= MAX_I2C_BUFFER_SIZE;
+		buf += MAX_I2C_BUFFER_SIZE;
+		raddr += MAX_I2C_BUFFER_SIZE;
+
+		if (ret < 0)
+			goto i2c_err;
+	} while (length > 0);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	return  0;
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	// data mangling..
+	ts->i2c_buf[0] = (addr >> 0) & 0xFF;
+	ts->i2c_buf[1] = (addr >> 8) & 0xFF;
+	memcpy(&ts->i2c_buf[2], buf, len);
+
+	ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
+
+	if (ret < 0)
+		goto i2c_err;
+
+	mutex_unlock(&ts->i2c_mutex);
+	return  0;
+
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void pops_mt(struct hideep_t *ts)
+{
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	int id;
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+#else
+	input_report_key(ts->input_dev, BTN_TOUCH, false);
+	input_mt_sync(ts->input_dev);
+#endif
+
+	input_sync(ts->input_dev);
+}
+
+static void push_mt(struct hideep_t *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	bool btn_dn = 0;
+	bool btn_mv = 0;
+	int evt = 0;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
+		btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
+			& 0x01;
+		btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
+
+		if (btn_up)
+			clear_bit(id, &ts->tch_bit);
+		else
+			__set_bit(id, &ts->tch_bit);
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			ts->touch_evt[i].type, ts->touch_evt[i].index,
+			i, ts->touch_evt[i].x, ts->touch_evt[i].y,
+			ts->touch_evt[i].z);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			evt++;
+		}
+#else
+		if (btn_up) {
+			input_mt_sync(ts->input_dev);
+		} else {
+			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			input_mt_sync(ts->input_dev);
+			evt++;
+		}
+#endif
+	}
+
+	if (ts->tch_bit == 0)
+		evt = 0;
+
+	input_report_key(ts->input_dev, BTN_TOUCH, evt);
+	input_mt_sync_frame(ts->input_dev);
+	input_sync(ts->input_dev);
+}
+
+#ifdef HIDEEP_SUPPORT_KEY
+static void pops_ky(struct hideep_t *ts)
+{
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		input_report_key(ts->input_dev, ts->key_codes[i], false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+
+	input_sync(ts->input_dev);
+}
+
+static void push_ky(struct hideep_t *ts)
+{
+	int i;
+	int pressed;
+	int key_code;
+	int key_status;
+
+	for (i = 0; i < ts->key_count; i++) {
+		key_code = ts->key_evt[i].flag & 0x0F;
+		key_status = ts->key_evt[i].flag & 0xF0;
+		pressed = false;
+
+		if (key_status & HIDEEP_KEY_PRESSED_MASK)
+			pressed = true;
+		else
+			pressed = false;
+
+		input_report_key(ts->input_dev, ts->key_codes[key_code],
+			pressed);
+		input_report_key(ts->input_dev, BTN_TOUCH, pressed);
+	}
+
+	input_sync(ts->input_dev);
+}
+#endif
+
+static void hideep_put_event(struct hideep_t *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0)
+		push_ky(ts);
+#endif
+}
+
+static int hideep_get_event(struct hideep_t *ts)
+{
+	int ret;
+	int touch_count;
+	int info_size;
+
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		info_size = ts->tch_count * sizeof(struct hideep_mt_t);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			info_size, (unsigned char *)ts->touch_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0) {
+		info_size = ts->key_count * sizeof(struct hideep_key_t);
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			info_size, (unsigned char *)ts->key_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+#endif
+
+	return touch_count;
+}
+
+static int hideep_event_thread(void *arg)
+{
+	int t_evt;
+
+	struct hideep_t *ts = arg;
+
+	dev_info(&ts->client->dev, "start event thread");
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		schedule();
+
+		t_evt = hideep_get_event(ts);
+
+		if (t_evt >= 0)
+			hideep_put_event(ts);
+
+		if (kthread_should_stop())
+			break;
+	}
+
+	dev_info(&ts->client->dev, "end thread");
+	return 0;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char i2c_buff[2];
+	int ret;
+
+	struct hideep_t *ts = (struct hideep_t *) handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
+		ts->dev_state, ts->debug_dev.debug_enable);
+
+	if (ts->debug_dev.debug_enable == false &&
+		ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, (u8 *)&i2c_buff);
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			ts->interrupt_state = 0;
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = i2c_buff[0];
+		ts->key_count = i2c_buff[1] & 0x0f;
+		ts->lpm_count = i2c_buff[1] & 0xf0;
+
+		wake_up_process(ts->hthread_event);
+
+	} else if (ts->debug_dev.debug_enable == true
+		&& ts->dev_state == state_debugging) {
+		ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
+			FRAME_HEADER_SIZE + ts->debug_dev.img_size,
+			ts->debug_dev.img_buff);
+		ts->debug_dev.img_size =
+			GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
+		ts->debug_dev.ready = 1;
+		wake_up_interruptible(&ts->debug_dev.i_packet);
+	}
+	dev_dbg(&ts->client->dev, "end.");
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_t *ts)
+{
+#ifdef HIDEEP_SUPPORT_KEY
+	int i;
+
+	ts->key_codes[0] = KEY_MENU;
+	ts->key_codes[1] = KEY_HOME;
+	ts->key_codes[2] = KEY_BACK;
+
+	for (i = 0; i < HIDEEP_KEY_MAX; i++)
+		set_bit(ts->key_codes[i], ts->input_dev->keybit);
+#endif
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	set_bit(EV_ABS, ts->input_dev->evbit);
+	set_bit(EV_KEY, ts->input_dev->evbit);
+	set_bit(EV_SYN, ts->input_dev->evbit);
+	set_bit(BTN_TOUCH, ts->input_dev->keybit);
+	set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
+	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
+		ts->p_data->max_z, 0, 0);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+#else
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TRACKING_ID, 0, 10, 0, 0);
+#endif
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
+
+	return 0;
+}
+
+static void hideep_reset_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret;
+	unsigned char cmd = 0x01;
+
+	pdata = ts->p_data;
+
+	dev_info(&ts->client->dev, "start!!");
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpio_set_value(pdata->reset_gpio, 0);
+		mdelay(20);
+		gpio_set_value(pdata->reset_gpio, 1);
+	} else
+#endif
+	{
+		ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+	dev_info(&ts->client->dev, "end!!");
+}
+
+static void hideep_get_info(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	unsigned char val[4];
+
+	pdata = ts->p_data;
+	ts->hideep_api->i2c_read(ts, 0x28, 4, val);
+
+	pdata->max_x = val[3] << 8 | val[2];
+	pdata->max_y = val[1] << 8 | val[0];
+	pdata->max_w = 255;
+	pdata->max_z = 255;
+
+	dev_info(&ts->client->dev, "X : %d, Y : %d",
+		pdata->max_x, pdata->max_y);
+}
+
+static void hideep_init_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	struct device dev;
+
+	pdata = ts->p_data;
+	dev = ts->client->dev;
+
+	/* power on */
+	ts->hideep_api->power(ts, true);
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	ts->hideep_api->reset_ic(ts);
+}
+
+static int hideep_resume(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state == state_normal)
+		goto hideep_resume_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+hideep_resume_exit:
+	mdelay(10);
+	ts->hideep_api->reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+static int hideep_suspend(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+	if (ts->dev_state == state_sleep)
+		goto hideep_suspend_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_sleep;
+
+	/* send sleep command.. */
+	pops_mt(ts);
+#ifdef HIDEEP_SUPPORT_KEY
+	pops_ky(ts);
+#endif
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+hideep_suspend_exit:
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+#ifdef CONFIG_FB
+static int fb_notifier_callback(struct notifier_block *self,
+	unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+
+	struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
+
+	if ((evdata) && (evdata->data) &&
+		(event == FB_EVENT_BLANK) &&
+		(ts) && (ts->client)) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			dev_info(&ts->client->dev, "resume");
+			if (ts->suspended == 1) {
+				hideep_resume(&ts->client->dev);
+				ts->suspended = 0;
+			}
+		} else if (*blank == FB_BLANK_POWERDOWN) {
+			dev_info(&ts->client->dev, "suspend");
+			if (ts->suspended == 0) {
+				ts->suspended = 1;
+				hideep_suspend(&ts->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_OF
+static int hideep_parse_dts(struct device *dev,
+	struct hideep_platform_data_t *pdata)
+{
+	int ret = 0;
+	unsigned int coords[4];
+	struct device_node *np;
+
+	dev_info(dev, "enter");
+	np = dev->of_node;
+
+	ret = of_property_read_u32_array(np, "hideep,max_coords", coords, 4);
+	if (ret) {
+		dev_err(dev, "Failed to get max_coords property\n");
+		return ret;
+	}
+
+	pdata->max_x = coords[0];
+	pdata->max_y = coords[1];
+	pdata->max_w = coords[2];
+	pdata->max_z = coords[3];
+
+	dev_info(dev, "max coord data x : %d\n", pdata->max_x);
+	dev_info(dev, "max coord data y : %d\n", pdata->max_y);
+	dev_info(dev, "max coord data w : %d\n", pdata->max_w);
+	dev_info(dev, "max coord data z : %d\n", pdata->max_z);
+
+	/* device tree information get */
+	pdata->irq_gpio = of_get_named_gpio(np, "irq-gpios", 0);
+	dev_info(dev, "irq_gpio = %d, is %s specified",
+		pdata->irq_gpio, pdata->irq_gpio < 0 ? "not" : "");
+
+	pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+	dev_info(dev, "reset_gpio = %d, is %s specified",
+		pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
+	if (pdata->reset_gpio)
+		ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
+			"reset-gpios");
+
+	pdata->vcc_vdd = regulator_get(dev, "vdd");
+	pdata->vcc_vid = regulator_get(dev, "vid");
+
+	return ret;
+}
+#endif
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_platform_data_t *p_data;
+	struct dwz_info_t *dwz;
+	struct hideep_function_list_t *function;
+	struct hideep_t *ts;
+
+	dev_info(&client->dev, "enter");
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		ret = -ENODEV;
+		return ret;
+	}
+
+	/* init platform data */
+	p_data = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_platform_data_t), GFP_KERNEL);
+
+#ifdef CONFIG_OF
+	if (client->dev.of_node) {
+		ret = hideep_parse_dts(&client->dev, p_data);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	/* init hideep_t */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_t), GFP_KERNEL);
+	dwz = devm_kzalloc(&client->dev,
+		sizeof(struct dwz_info_t), GFP_KERNEL);
+	function = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_function_list_t), GFP_KERNEL);
+
+	ts->client = client;
+	ts->p_data = p_data;
+	ts->dwz_info = dwz;
+	ts->hideep_api = function;
+
+	ts->hideep_api->i2c_read = hideep_i2c_read;
+	ts->hideep_api->i2c_write = hideep_i2c_write;
+	ts->hideep_api->reset_ic = hideep_reset_ic;
+	ts->hideep_api->power = hideep_power;
+
+	/* init for isp function */
+	hideep_isp_init(ts);
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	if (!client->dev.of_node)
+		hideep_get_info(ts);
+
+	/* hardware init */
+	hideep_init_ic(ts);
+
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+	/* read info */
+	ret = ts->hideep_api->get_dwz_info(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		goto hideep_probe_read_dwz_err;
+	}
+#endif
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_input_dev_memory_err;
+	}
+
+	hideep_capability(ts);
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_register_input_dev_err;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	/* init event thread */
+	ts->hthread_event = kthread_run(hideep_event_thread,
+		ts, "hideep_event_thread");
+	if (IS_ERR(ts->hthread_event)) {
+		dev_err(&client->dev, "can't create event thread !!!");
+		ret = PTR_ERR(ts->hthread_event);
+		goto hideep_probe_create_thread_err;
+	}
+
+#ifdef CONFIG_OF
+	/* interrupt set */
+	ts->client->irq = gpio_to_irq(p_data->irq_gpio);
+#endif
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (ts->client->irq <= 0) {
+		dev_err(&client->dev, "can't be assigned irq");
+		goto hideep_probe_assigned_irq_err;
+	}
+
+	if (ts->client->irq) {
+		ret = request_threaded_irq(ts->client->irq, NULL,
+			hideep_irq_task,
+			(IRQF_TRIGGER_LOW | IRQF_ONESHOT),
+			ts->client->name, ts);
+		disable_irq(ts->client->irq);
+		ts->interrupt_state = 0;
+		if (ret < 0) {
+			dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+				ret);
+			goto hideep_probe_request_irq_err;
+		}
+	}
+
+	ret = hideep_debug_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_debug_init_err;
+	}
+
+	ret = hideep_sysfs_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_sysfs_init_err;
+	}
+
+#ifdef CONFIG_FB
+	ts->suspended = 0;
+	ts->fb_notif.notifier_call = fb_notifier_callback;
+	ret = fb_register_client(&ts->fb_notif);
+	if (ret) {
+		dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
+			ret);
+		ret = -1;
+		goto hideep_probe_register_fb_err;
+	}
+#endif
+
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+	dev_info(&client->dev, "probe is ok!");
+	return 0;
+
+hideep_probe_register_fb_err:
+	hideep_sysfs_exit(ts);
+
+hideep_probe_sysfs_init_err:
+	hideep_debug_uninit();
+
+hideep_probe_debug_init_err:
+hideep_probe_request_irq_err:
+	free_irq(ts->client->irq, ts);
+
+hideep_probe_assigned_irq_err:
+hideep_probe_create_thread_err:
+	input_unregister_device(ts->input_dev);
+
+hideep_probe_register_input_dev_err:
+	if (ts->input_dev)
+		input_free_device(ts->input_dev);
+
+hideep_probe_input_dev_memory_err:
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+hideep_probe_read_dwz_err:
+#endif
+
+#ifdef CONFIG_OF
+	ts->hideep_api->power(ts, false);
+#endif
+	dev_err(&client->dev, "probe err!");
+	return ret;
+}
+
+static int hideep_remove(struct i2c_client *client)
+{
+	struct hideep_t *ts = i2c_get_clientdata(client);
+
+	kthread_stop(ts->hthread_event);
+
+#ifdef CONFIG_FB
+	if (fb_unregister_client(&ts->fb_notif))
+		dev_info(&client->dev,
+			"Error occurred while unregistering fb_notifier");
+#endif
+
+#ifdef CONFIG_OF
+	ts->hideep_api->power(ts, false);
+#endif
+	free_irq(client->irq, ts);
+
+	input_unregister_device(ts->input_dev);
+	hideep_sysfs_exit(ts);
+
+	hideep_debug_uninit();
+	devm_kfree(&client->dev, ts);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops hideep_pm_ops = {
+	.suspend = hideep_suspend,
+	.resume = hideep_resume,
+};
+#endif
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep_ts" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.remove = hideep_remove,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
new file mode 100644
index 0000000..1dc3d83
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+#include "hideep_dbg.h"
+
+static struct hideep_debug_dev_t *hdd;
+
+static int hideep_i2c_recv(unsigned int len)
+{
+	int ret = 0;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_i2c_send(unsigned int len)
+{
+	int ret = 0;
+	unsigned char *buff = hdd->vr_buff;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_send(hdd->ts->client, buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_get_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+
+	ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
+		hdd->vr_buff);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_set_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+	int wr_remain = len;
+	int vr_addr = addr;
+	int wr_len = len;
+	unsigned char *buff = hdd->vr_buff;
+
+	do {
+		if (wr_remain >=  MAX_VR_BUFF)
+			wr_len = MAX_VR_BUFF;
+		else
+			wr_len = wr_remain;
+
+		ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
+			wr_len, buff);
+		if (ret < 0)
+			goto i2c_err;
+
+		wr_remain -= MAX_VR_BUFF;
+		vr_addr += MAX_VR_BUFF;
+		buff += MAX_VR_BUFF;
+	} while (wr_remain > 0);
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_download_uc(const char __user *uc, size_t count, int offset)
+{
+	int ret;
+	unsigned char *ucode;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (count > FIRMWARE_SIZE) {
+		dev_err(&hdd->ts->client->dev, "over size data!!!");
+		return -1;
+	}
+
+	ucode = kmalloc(count, GFP_KERNEL);
+
+	ret = copy_from_user(ucode + offset, uc, count);
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
+		kfree(ucode);
+		return 0;
+	}
+
+	disable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 0;
+	hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
+	enable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 1;
+	kfree(ucode);
+
+	dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
+
+	return count;
+}
+
+static int hideep_debug_open(struct inode *inode, struct file *file)
+{
+	hdd->release_flag = false;
+
+	file->private_data = hdd;
+	dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
+
+	return 0;
+}
+
+static int hideep_debug_release(struct inode *inode, struct file *file)
+{
+	if (!hdd->release_flag)
+		return -1;
+	hdd->release_flag = false;
+	file->private_data = NULL;
+	return 0;
+}
+
+static unsigned int hideep_debug_poll(struct file *file,
+	struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	struct hideep_debug_dev_t *drv_info;
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info = file->private_data;
+
+	poll_wait(file, &drv_info->i_packet, wait);
+
+	if (drv_info->ready) {
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mask |= POLLIN | POLLRDNORM;
+		drv_info->ready = 0;
+	}
+
+	return mask;
+}
+
+static ssize_t hideep_debug_read(struct file *file, char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret = -1;
+	ssize_t rd_len = 0;
+	unsigned char *rd_buffer = NULL;
+	unsigned char *data = NULL;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info->vr_size = count;
+	rd_len = count;
+
+	data = kmalloc(rd_len, GFP_KERNEL);
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		if ((*offset & HIDEEP_MAX_RAW_LEN) &&
+			(*offset < HIDEEP_MAX_RAW_LEN) &&
+			(drv_info->debug_enable == true)) {
+			mutex_lock(&drv_info->ts->dev_mutex);
+			rd_buffer = drv_info->img_buff;
+			ret = 0;
+			mutex_unlock(&drv_info->ts->dev_mutex);
+		} else {
+			ret = hideep_get_vreg(*offset, rd_len);
+			rd_buffer = drv_info->vr_buff;
+		}
+		if (ret < 0)
+			rd_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		ret = hideep_i2c_recv(rd_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "ret = %d", ret);
+			rd_len = 0;
+		} else {
+			rd_buffer = drv_info->vr_buff;
+		}
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		rd_len = count;
+		memset(data, 0x0, rd_len);
+
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mutex_lock(&drv_info->ts->dev_mutex);
+		mutex_lock(&drv_info->ts->i2c_mutex);
+
+		drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
+			*offset & 0xfffff);
+
+		mutex_unlock(&drv_info->ts->dev_mutex);
+		mutex_unlock(&drv_info->ts->i2c_mutex);
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+
+		rd_buffer = data;
+	} else {
+		dev_err(&hdd->ts->client->dev, "undefined address");
+		kfree(data);
+		return 0;
+	}
+
+	ret = copy_to_user(buf, rd_buffer, rd_len);
+
+	if (drv_info->debug_enable == true && drv_info->ready == 0) {
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+	}
+
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+		kfree(data);
+		return -EFAULT;
+	}
+
+	kfree(data);
+	return rd_len;
+}
+
+static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+	int wr_len = 0;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+	if (file->private_data == NULL)
+		return 0;
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		wr_len = count;
+
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+			return -EFAULT;
+		}
+
+		ret = hideep_set_vreg(*offset, wr_len);
+		if (ret < 0)
+			wr_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		wr_len = count;
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		ret = hideep_i2c_send(wr_len);
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
+	} else {
+		dev_err(&hdd->ts->client->dev,
+			"hideep_write : undefined address, 0x%08x",
+			(int)*offset);
+		return 0;
+	}
+
+	return wr_len;
+}
+
+static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
+{
+	loff_t newpos;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
+		(unsigned int)off, whence);
+	if (file->private_data == NULL)
+		return -EFAULT;
+
+	switch (whence) {
+	/* SEEK_SET */
+	case 0:
+		newpos = off;
+		break;
+	/* SEEK_CUR */
+	case 1:
+		dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
+			(unsigned int)off);
+		if (off & HIDEEP_RELEASE_FLAG) {
+			dev_dbg(&hdd->ts->client->dev, "set release flag");
+			drv_info->release_flag = true;
+		}
+		newpos = file->f_pos;
+		break;
+	/* SEEK_END */
+	case 2:
+		newpos = file->f_pos;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (newpos < 0)
+		return -EINVAL;
+
+	file->f_pos = newpos;
+
+	return newpos;
+}
+
+static const struct file_operations hideep_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = hideep_debug_open,
+	.poll = hideep_debug_poll,
+	.release = hideep_debug_release,
+	.read = hideep_debug_read,
+	.write = hideep_debug_write,
+	.llseek = hideep_debug_llseek,
+};
+
+static struct miscdevice hideep_debug_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = HIDEEP_DEBUG_DEVICE_NAME,
+	.fops = &hideep_debug_fops
+};
+
+void hideep_debug_uninit(void)
+{
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+
+	misc_deregister(&hideep_debug_dev);
+}
+
+int hideep_debug_init(struct hideep_t *ts)
+{
+	int ret = 0;
+
+	hdd = &ts->debug_dev;
+
+	ret = misc_register(&hideep_debug_dev);
+	if (ret) {
+		dev_err(&ts->client->dev,
+			"hideep debug device register fail!!!");
+		goto fail;
+	}
+
+	init_waitqueue_head(&hdd->i_packet);
+
+	hdd->ts = ts;
+	hdd->debug_enable = false;
+
+	hdd->img_size = 0;
+	hdd->vr_size = 0;
+
+	hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
+	hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
+
+	memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
+	memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
+
+	if (!hdd->vr_buff || !hdd->img_buff)
+		goto fail;
+
+	dev_info(&ts->client->dev, "debug init....");
+	return 0;
+
+fail:
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+	return ret;
+}
diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
new file mode 100644
index 0000000..f18a09f
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_DBG_H
+#define _LINUX_HIDEEP_DBG_H
+
+/* Device Driver <==> Application */
+/* lseek(), write(), read() command */
+#define HIDEEP_RELEASE_FLAG				0x8000
+#define HIDEEP_VR_ADDR_LEN				0xFFFF
+#define HIDEEP_MAX_RAW_LEN				0x3000
+#define HIDEEP_READ_WRITE_VR			0xF000
+#define HIDEEP_I2C_BYPASS				0x20000000
+
+#define MAX_VR_BUFF						2048
+
+/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
+#define MAX_RAW_SIZE					7369
+#endif /* _LINUX_HIDEEP_DBG_H */
diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
new file mode 100644
index 0000000..7cdd422
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+
+static struct pgm_packet packet_w;
+static struct pgm_packet packet_r;
+
+static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x80 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		(len+5));
+
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x00 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		5);
+
+	if (ret < 0)
+		goto err;
+
+	ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
+		len);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x00);
+	packet.header.w[1] = htonl(addr);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x80);
+	packet.header.w[1] = htonl(addr);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void get_dwz_from_binary(unsigned char *pres,
+	struct dwz_info_t *dwz_info)
+{
+	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
+		sizeof(struct dwz_info_t));
+}
+
+static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
+{
+	SW_RESET_IN_PGM(food);
+}
+
+static int hideep_enter_pgm(struct hideep_t *ts)
+{
+	int ret = 0;
+	int retry_count = 10;
+	int retry = 0;
+	unsigned int status;
+	unsigned int pattern = 0xDF9DAF39;
+
+	while (retry < retry_count) {
+		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
+		mdelay(1);
+
+		/* flush invalid Tx load register */
+		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+		if (status != htonl(pattern)) {
+			retry++;
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				status);
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry < retry_count) {
+		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
+
+		mdelay(1);
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_t *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	mdelay(50);
+
+	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
+		sizeof(struct dwz_info_t));
+
+	memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info_t));
+	hideep_sw_reset(ts, 10);
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mdelay(50);
+
+	return ret;
+}
+
+static int hideep_nvm_unlock(struct hideep_t *ts)
+{
+	int ret = 0;
+	unsigned int unmask_code = 0;
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != NVM_MASK)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
+	SET_FLASH_HWCONTROL();
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	return ret;
+}
+
+static int hideep_program_page(struct hideep_t *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret = 0;
+	unsigned int pio_cmd = WRONLY;
+	unsigned int pio_cmd_page_erase = PERASE;
+	unsigned int status;
+	int time_out = 0;
+	int inf_en = 0;
+	unsigned int end_flag = 124;
+#ifndef PGM_BURST_WR
+	unsigned int i;
+#endif
+
+	hideep_pgm_r_reg(ts, FLASH_STA, &status);
+	ret = (status == 0) ? -1:0;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	if (addr > INF_SECTION) {
+		/* added INF flag set in pio_cmd */
+		addr -= INF_SECTION;
+		pio_cmd |= INF;
+		pio_cmd_page_erase |= INF;
+		inf_en = 1;
+	}
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* first erase */
+	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+	time_out = 0;
+
+	while (1) {
+		mdelay(1);
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+		if (time_out++ > 100)
+			break;
+	}
+	SET_FLASH_PIO(1);
+	/* first erase end*/
+
+	SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
+
+#ifdef PGM_BURST_WR
+	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
+		packet_w, NVM_PAGE_SIZE);
+#else
+	for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
+		SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
+#endif
+	if (inf_en == 0)
+		SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
+	else
+		SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	while (1) {
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+	}
+	/* write routine end */
+
+	SET_FLASH_HWCONTROL();
+
+	return ret;
+}
+
+static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset, unsigned char *old_fw)
+{
+	int i;
+	int ret = 0;
+	int len_r;
+	int len_w;
+	int addr = 0;
+	unsigned int pages;
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	addr = offset;
+	len_r = len;
+	len_w = len_r;
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_w = NVM_PAGE_SIZE;
+
+		/* compare */
+		if (old_fw != NULL)
+			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
+
+		if (ret != 0 || old_fw == NULL) {
+			/* write page */
+			memcpy(packet_w.payload, &(ucode[addr]), len_w);
+
+			ret = hideep_program_page(ts, addr, &packet_w);
+			mdelay(1);
+			if (ret < 0)
+				dev_err(&ts->client->dev,
+					"hideep_program_nvm : error(%08x):",
+					addr);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_w = len_r;
+	}
+
+	return ret;
+}
+
+static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset)
+{
+	int i, j;
+	int ret = 0;
+	unsigned char page_chk = 0;
+	unsigned int addr = offset;
+	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	int len_r = len;
+	int len_v = len_r;
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
+
+		if (page_chk != 0) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++)
+				dev_err(&ts->client->dev, "%02x : %02x",
+						ucode[addr+j], read[j]);
+
+			dev_err(&ts->client->dev, "verify : error(addr : %d)",
+				addr);
+
+			ret = -1;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	return ret;
+}
+
+static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
+	int offset)
+{
+	int ret = 0;
+	int pages, i;
+	int len_r, len_v;
+	int addr = offset;
+#ifndef PGM_BURST_WR
+	int j;
+#endif
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	len_r = len;
+	len_v = len_r;
+
+	ts->hideep_api->reset_ic(ts);
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	hideep_sw_reset(ts, 1000);
+}
+
+static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
+	size_t len, int offset)
+{
+	int ret = 0;
+	int retry = 3;
+
+	while (retry--) {
+		ret = hideep_verify_nvm(ts, fw, len, offset);
+		if (ret == 0) {
+			dev_dbg(&ts->client->dev, "update success");
+			break;
+		}
+		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
+	}
+
+	ret = (retry == 0) ? -1:0;
+
+	return ret;
+}
+
+static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
+	int len, int offset, bool mode)
+{
+	int ret = 0;
+	int firm_len;
+	unsigned char *ic_fw;
+	unsigned char *dwz_info;
+
+	ic_fw = kmalloc(FIRMWARE_SIZE, GFP_KERNEL);
+	dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
+
+	memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
+
+	firm_len = len;
+	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
+		firm_len, FIRMWARE_SIZE);
+
+	if (firm_len > FIRMWARE_SIZE)
+		firm_len = FIRMWARE_SIZE;
+
+	dev_dbg(&ts->client->dev, "enter");
+	/* memory dump of target IC */
+	hideep_read_nvm(ts, ic_fw, firm_len, offset);
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
+
+#ifdef PGM_VERIFY
+	hideep_fw_verify_run(ts, code, firm_len, offset);
+	if (ret < 0) {
+		if (mode == true) {
+			/* clear dwz version, it will be store once again
+			 * after update success
+			 */
+			ts->dwz_info->release_ver = 0;
+			memcpy(&dwz_info[64], ts->dwz_info,
+				sizeof(struct dwz_info_t));
+			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
+				0x280, NULL);
+		}
+	}
+#endif
+	get_dwz_from_binary(code, ts->dwz_info);
+
+	hideep_sw_reset(ts, 1000);
+
+	kfree(ic_fw);
+	kfree(dwz_info);
+
+	return ret;
+}
+
+static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
+{
+	int ret = 0;
+	const struct firmware *fw_entry;
+	unsigned char *fw_buf;
+	unsigned int fw_length;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	fw_buf = (unsigned char *)fw_entry->data;
+	fw_length = (unsigned int)fw_entry->size;
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_update_part_firmware(struct hideep_t *ts,
+	unsigned char *code, int len, int offset, bool mode)
+{
+	int ret = 0;
+
+	mutex_lock(&ts->dev_mutex);
+
+	ret = hideep_wr_firmware(ts, code, len, offset, false);
+
+	mutex_unlock(&ts->dev_mutex);
+
+	return ret;
+}
+
+void hideep_isp_init(struct hideep_t *ts)
+{
+	ts->hideep_api->update_all = hideep_update_all_firmware;
+	ts->hideep_api->update_part = hideep_update_part_firmware;
+	ts->hideep_api->get_dwz_info = hideep_load_dwz;
+	ts->hideep_api->sp_func = hideep_read_nvm;
+}
diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
new file mode 100644
index 0000000..648f271
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_ISP_H
+#define _LINUX_HIDEEP_ISP_H
+
+#define PGM_BURST_WR
+#define PGM_VERIFY
+
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_NVM_DOWNLOAD		0x10000000
+
+/*************************************************************************
+ * register map
+ *************************************************************************/
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+/*************************************************************************
+ * flash commands
+ *************************************************************************/
+#define MERASE					0x00010000
+#define SERASE					0x00020000
+#define PERASE					0x00040000
+#define PROG					0x00080000
+#define WRONLY					0x00100000
+#define INF						0x00200000
+
+/*************************************************************************
+ * NVM Mask
+ *************************************************************************/
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x00310000
+#define INF_SECTION				0x0000C000
+#endif
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x0030027B
+#define INF_SECTION				0x00010000
+#endif
+
+/*************************************************************************
+ * DWZ info
+ *************************************************************************/
+#define HIDEEP_BOOT_SECTION		0x00000400
+#define HIDEEP_BOOT_LEN			0x00000400
+#define HIDEEP_DWZ_SECTION		0x00000280
+#define HIDEEP_DWZ_INFO_OFS		0x000002C0
+#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
+							- HIDEEP_DWZ_SECTION)
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		unsigned int w[2];
+	} header;
+
+	unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+#endif /* _LINUX_HIDEEP_ISP_H */
diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
new file mode 100644
index 0000000..4aedec6
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_sysfs.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	if (count == 1)
+		mode = buf[0] & 0xff;
+	else
+		return count;
+
+	if (mode == 1) {
+		disable_irq(ts->client->irq);
+
+		ts->dev_state = state_updating;
+		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+			ts->dwz_info->product_id);
+		ret = ts->hideep_api->update_all(ts, fw_name);
+
+		kfree(fw_name);
+
+		enable_irq(ts->client->irq);
+
+		ts->dev_state = state_normal;
+		if (ret != 0)
+			dev_err(dev, "The firmware update failed(%d)", ret);
+	}
+
+	return count;
+}
+
+static ssize_t fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->release_ver);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		ts->dwz_info->product_id);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->product_id);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t power_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
+		(ts->dev_state == state_init) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t power_control(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on;
+
+	if (count == 1)
+		on = buf[0] & 0xff;
+	else
+		return count;
+
+	if (on) {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_normal;
+		ts->hideep_api->reset_ic(ts);
+	} else {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_init;
+	}
+
+	return count;
+}
+
+#ifdef HIDEEP_SUPPORT_STYLUS
+static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+	unsigned char status[1];
+
+	ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
+
+	len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
+		(status[0] == 0x00) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on;
+	unsigned char data[2];
+
+	if (count == 1)
+		on = buf[0] & 0xff;
+	else
+		return count;
+
+	data[0] = 0x04;
+
+	if (on) {
+		data[1] = 0x01;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	} else {
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	}
+
+	return count;
+}
+#endif
+
+static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
+		(ts->debug_dev.debug_enable == 0) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on;
+	unsigned char data[2];
+
+	if (count == 1)
+		on = buf[0] & 0xff;
+	else
+		return count;
+
+	if (on) {
+		ts->debug_dev.debug_enable = 1;
+		ts->dev_state = state_debugging;
+	} else {
+		ts->debug_dev.debug_enable = 0;
+		ts->dev_state = state_normal;
+		/* set touch mode */
+		data[0] = 0x00;
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
+		if (ts->interrupt_state == 0) {
+			data[0] = 0x5A;
+			ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
+				data);
+			enable_irq(ts->client->irq);
+			ts->interrupt_state = 1;
+		}
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
+static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
+static DEVICE_ATTR(power_en, 0664, power_status, power_control);
+static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);
+#ifdef HIDEEP_SUPPORT_STYLUS
+static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);
+#endif
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_power_en.attr,
+	&dev_attr_debug_en.attr,
+#ifdef HIDEEP_SUPPORT_STYLUS
+	&dev_attr_stylus_en.attr,
+#endif
+	NULL
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+int hideep_sysfs_init(struct hideep_t *ts)
+{
+	int ret;
+	struct i2c_client *client = ts->client;
+
+	/* Create the files associated with this kobject */
+	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
+
+	if (ret)
+		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
+			 __func__, ret);
+
+	return ret;
+}
+
+int hideep_sysfs_exit(struct hideep_t *ts)
+{
+	struct i2c_client *client = ts->client;
+
+	sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	return 0;
+}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
       [not found]   ` <1499837054-4659-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-07-17 17:31     ` Rob Herring
  2017-07-20  0:22     ` Anthony Kim
  1 sibling, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-07-17 17:31 UTC (permalink / raw)
  To: Anthony Kim
  Cc: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	rydberg-FFUHeuDi6mxAfugRpC6u6w,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

On Wed, Jul 12, 2017 at 02:24:14PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |  37 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/input/touchscreen/Kconfig                  |  32 +
>  drivers/input/touchscreen/Makefile                 |   2 +
>  drivers/input/touchscreen/hideep.h                 | 329 ++++++++
>  drivers/input/touchscreen/hideep_core.c            | 924 +++++++++++++++++++++
>  drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
>  drivers/input/touchscreen/hideep_dbg.h             |  24 +
>  drivers/input/touchscreen/hideep_isp.c             | 584 +++++++++++++
>  drivers/input/touchscreen/hideep_isp.h             |  96 +++
>  drivers/input/touchscreen/hideep_sysfs.c           | 249 ++++++
>  11 files changed, 2683 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.h
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..5eb8c1d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,37 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible		: must be "hideep,hideep_ts".

See my reply on previous version.

> +- reg			: I2C slave address, (e.g. 0x6C).
> +- interrupt-parent : Interrupt controller to which the chip is connected.
> +- interrupts : Interrupt to which the chip is connected.
> +
> +Optional properties:
> +- vdd-supply	: It is the controller supply for controlling
> +					 main voltage(3.3V) through the regulator.
> +- vid-supply	: It is the controller supply for controlling
> +					IO voltage(1.8V) through the regulator.
> +- irq-gpios		: Define for interrupt gpio pin.
> +						It is to use for set interrupt type.

As mentioned in the last version, use "interrupts".

> +- reset-gpios	: Define for reset gpio pin.
> +						It is to use for reset IC.
> +- hideep,max_coords	: Max value for axis X, Y, W, Z.

s/_/-/

> +
> +Example:
> +
> +i2c@00000000 {
> +
> +	/* ... */
> +
> +	touchscreen@6c {
> +		compatible = "hideep,hideep_ts";
> +		reg = <0x6c>;
> +		interrupt-parent = <&gpx1>;
> +		interrupts = <2>;
> +		vdd-supply = <&ldo15_reg>";
> +		vid-supply = <&ldo18_reg>;
> +		irq-gpios = <&gpx1 2 0>;
> +		reset-gpios = <&gpx1 5 0>;
> +		hideep,max_coords = <1080 1920 65535 65535>;
> +	};
> +};
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
       [not found]   ` <1499837054-4659-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
  2017-07-17 17:31     ` Rob Herring
@ 2017-07-20  0:22     ` Anthony Kim
       [not found]       ` <1500510154-6661-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
  2017-07-25  6:53       ` Anthony Kim
  1 sibling, 2 replies; 29+ messages in thread
From: Anthony Kim @ 2017-07-20  0:22 UTC (permalink / raw)
  To: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	rydberg-FFUHeuDi6mxAfugRpC6u6w
  Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
---
 .../bindings/input/touchscreen/hideep.txt          |  35 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/input/touchscreen/Kconfig                  |  11 +
 drivers/input/touchscreen/Makefile                 |   2 +
 drivers/input/touchscreen/hideep.h                 | 318 +++++++
 drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
 drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
 drivers/input/touchscreen/hideep_dbg.h             |  24 +
 drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
 drivers/input/touchscreen/hideep_isp.h             |  96 +++
 drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
 11 files changed, 2645 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.h
 create mode 100644 drivers/input/touchscreen/hideep_core.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.h
 create mode 100644 drivers/input/touchscreen/hideep_isp.c
 create mode 100644 drivers/input/touchscreen/hideep_isp.h
 create mode 100644 drivers/input/touchscreen/hideep_sysfs.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..76ea8b1
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,35 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep_crimson"
+					or "hideep,hideep_lime".
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- hideep,max_coords	: Max value for axis X, Y, W, Z.
+
+Example:
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep_ts";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 0>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		hideep,max_coords = <1080 1920 65535 65535>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..13e11c7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..3aab466 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
+hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
new file mode 100644
index 0000000..50306a1
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_H
+#define _LINUX_HIDEEP_H
+
+/*************************************************************************
+ * this is include special HEAD file.
+ *************************************************************************/
+#ifdef CONFIG_FB
+#include <linux/fb.h>
+#include <linux/notifier.h>
+#endif
+
+/*************************************************************************
+ * this is include normal HEAD file.
+ *************************************************************************/
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/kthread.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/task_work.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/input/mt.h>
+#include <linux/fs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
+#include <linux/acpi.h>
+
+/*************************************************************************
+ * definition part.
+ * define is (open, set, enable) if not, is (close, clear, disable)
+ * some special switch of functions.
+ *************************************************************************/
+
+/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
+#define HIDEEP_PROTOCOL_2_0
+
+/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
+ * otherwise TYPE_A.
+ */
+#define HIDEEP_TYPE_B_PROTOCOL
+
+/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
+#define HIDEEP_DWZ_VERSION_CHECK
+
+/* HIDEEP_SUPPORT_KE if define, it will use key button. */
+#define HIDEEP_SUPPORT_KEY
+
+/* HIDEEP_SUPPORT_STYLUS if define, it will use stylus mode. */
+#define HIDEEP_SUPPORT_STYLUS
+
+/*************************************************************************
+ * Buffer size
+ *************************************************************************/
+#define FRAME_HEADER_SIZE				8
+/* if size of system i2c buffer is smaller than this value, need to modify */
+#define MAX_I2C_BUFFER_SIZE				512
+
+/*************************************************************************
+ * board porting config
+ *************************************************************************/
+#define HIDEEP_DEBUG_DEVICE_NAME		"hideep_debug"
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+/*************************************************************************
+ * register addr
+ *************************************************************************/
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/*************************************************************************
+ * multi-touch & key definitions.
+ *************************************************************************/
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/*************************************************************************
+ * HIDEEP PROTOCOL
+ *************************************************************************/
+#ifdef HIDEEP_PROTOCOL_2_0
+struct hideep_mt_t {
+	unsigned short x;
+	unsigned short y;
+	unsigned short z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+};
+#else
+struct hideep_mt_t {
+	unsigned char flag;
+	unsigned char index;
+	unsigned short x;
+	unsigned short y;
+	unsigned char z;
+	unsigned char w;
+};
+#endif
+
+struct hideep_key_t {
+	unsigned char flag;
+	unsigned int key;
+};
+
+/*************************************************************************
+ * IC State
+ *************************************************************************/
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+	state_debugging,
+};
+
+/*************************************************************************
+ * Firmware info
+ *************************************************************************/
+struct dwz_info_t {
+	unsigned int code_start;
+	unsigned char code_crc[12];
+
+	unsigned int c_code_start;
+	unsigned short c_code_len;
+	unsigned short gen_ver;
+
+	unsigned int vr_start;
+	unsigned short vr_len;
+	unsigned short rsv0;
+
+	unsigned int ft_start;
+	unsigned short ft_len;
+	unsigned short vr_version;
+
+	unsigned short boot_ver;
+	unsigned short core_ver;
+	unsigned short custom_ver;
+	unsigned short release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	unsigned short product_code;
+	unsigned short extra_option;
+
+	unsigned short product_id;
+	unsigned short vendor_id;
+};
+
+/*************************************************************************
+ * driver information for hideep_t by device tree
+ *************************************************************************/
+struct hideep_platform_data_t {
+	unsigned int max_x;
+	unsigned int max_y;
+	unsigned int max_z;
+	unsigned int max_w;
+
+#ifdef CONFIG_OF
+	int reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+#endif
+};
+
+struct hideep_debug_dev_t {
+	struct miscdevice misc;
+
+	wait_queue_head_t i_packet;
+
+	unsigned int ready;
+	unsigned char *vr_buff;
+	unsigned char *img_buff;
+	unsigned short vr_size;
+	unsigned short img_size;
+
+	bool debug_enable;
+	bool release_flag;
+
+	struct hideep_t *ts;
+};
+
+struct hideep_function_list_t {
+	//core
+	int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	void (*reset_ic)(struct hideep_t *);
+	void (*power)(struct hideep_t *, int);
+
+	//isp
+	int (*update_all)(struct hideep_t *, const char *);
+	int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
+	int (*get_dwz_info)(struct hideep_t *);
+	void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
+};
+
+struct hideep_t {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct hideep_platform_data_t *p_data;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	struct hideep_debug_dev_t debug_dev;
+	struct hideep_function_list_t *hideep_api;
+
+	struct task_struct *hthread_event;
+
+	bool suspended;
+	enum e_dev_state dev_state;
+
+#ifdef CONFIG_FB
+	struct notifier_block fb_notif;
+#endif
+
+	long tch_bit;
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
+
+#ifdef HIDEEP_SUPPORT_KEY
+	struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
+	int key_codes[HIDEEP_KEY_MAX];
+#endif
+
+	unsigned char i2c_buf[256];
+	struct dwz_info_t *dwz_info;
+
+	int interrupt_state;
+	int fw_size;
+};
+
+/*************************************************************************
+ * function define
+ *************************************************************************/
+int hideep_debug_init(struct hideep_t *ts);
+void hideep_debug_uninit(void);
+
+int hideep_sysfs_init(struct hideep_t *ts);
+int hideep_sysfs_exit(struct hideep_t *ts);
+
+void hideep_isp_init(struct hideep_t *ts);
+
+#endif
diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
new file mode 100644
index 0000000..2b71b84d
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.c
@@ -0,0 +1,916 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
+	((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
+
+static int hideep_pwr_on(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (!IS_ERR(pdata->vcc_vdd)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+	}
+	usleep_range(999, 1000);
+
+	if (!IS_ERR(pdata->vcc_vid)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+	usleep_range(2999, 3000);
+#endif
+
+	return ret;
+}
+
+static int hideep_pwr_off(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
+		gpio_set_value(pdata->reset_gpio, 0);
+	}
+
+	if (!IS_ERR(pdata->vcc_vid)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
+		ret = regulator_disable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+
+	if (!IS_ERR(pdata->vcc_vdd)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
+		ret = regulator_disable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd disable failed ret=%d", ret);
+	}
+#endif
+	return ret;
+}
+
+static void hideep_power(struct hideep_t *ts, int on)
+{
+	int ret = 0;
+
+	if (on) {
+		dev_info(&ts->client->dev, "power on");
+		ret = hideep_pwr_on(ts);
+	} else {
+		dev_info(&ts->client->dev, "power off");
+		ret = hideep_pwr_off(ts);
+	}
+}
+
+static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned short r_len, raddr;
+	int length;
+
+	length = len;
+	raddr = addr;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	do {
+		if (length > MAX_I2C_BUFFER_SIZE)
+			r_len = MAX_I2C_BUFFER_SIZE;
+		else
+			r_len = length;
+
+		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
+			raddr, r_len);
+
+		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
+
+		if (ret < 0)
+			goto i2c_err;
+
+		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
+		length -= MAX_I2C_BUFFER_SIZE;
+		buf += MAX_I2C_BUFFER_SIZE;
+		raddr += MAX_I2C_BUFFER_SIZE;
+
+		if (ret < 0)
+			goto i2c_err;
+	} while (length > 0);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	return  0;
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	// data mangling..
+	ts->i2c_buf[0] = (addr >> 0) & 0xFF;
+	ts->i2c_buf[1] = (addr >> 8) & 0xFF;
+	memcpy(&ts->i2c_buf[2], buf, len);
+
+	ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
+
+	if (ret < 0)
+		goto i2c_err;
+
+	mutex_unlock(&ts->i2c_mutex);
+	return  0;
+
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void pops_mt(struct hideep_t *ts)
+{
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	int id;
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+#else
+	input_report_key(ts->input_dev, BTN_TOUCH, false);
+	input_mt_sync(ts->input_dev);
+#endif
+
+	input_sync(ts->input_dev);
+}
+
+static void push_mt(struct hideep_t *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	bool btn_dn = 0;
+	bool btn_mv = 0;
+	int evt = 0;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
+		btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
+			& 0x01;
+		btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
+
+		if (btn_up)
+			clear_bit(id, &ts->tch_bit);
+		else
+			__set_bit(id, &ts->tch_bit);
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			ts->touch_evt[i].type, ts->touch_evt[i].index,
+			i, ts->touch_evt[i].x, ts->touch_evt[i].y,
+			ts->touch_evt[i].z);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			evt++;
+		}
+#else
+		if (btn_up) {
+			input_mt_sync(ts->input_dev);
+		} else {
+			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			input_mt_sync(ts->input_dev);
+			evt++;
+		}
+#endif
+	}
+
+	if (ts->tch_bit == 0)
+		evt = 0;
+
+	input_report_key(ts->input_dev, BTN_TOUCH, evt);
+	input_mt_sync_frame(ts->input_dev);
+	input_sync(ts->input_dev);
+}
+
+#ifdef HIDEEP_SUPPORT_KEY
+static void pops_ky(struct hideep_t *ts)
+{
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		input_report_key(ts->input_dev, ts->key_codes[i], false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+
+	input_sync(ts->input_dev);
+}
+
+static void push_ky(struct hideep_t *ts)
+{
+	int i;
+	int pressed;
+	int key_code;
+	int key_status;
+
+	for (i = 0; i < ts->key_count; i++) {
+		key_code = ts->key_evt[i].flag & 0x0F;
+		key_status = ts->key_evt[i].flag & 0xF0;
+		pressed = false;
+
+		if (key_status & HIDEEP_KEY_PRESSED_MASK)
+			pressed = true;
+		else
+			pressed = false;
+
+		input_report_key(ts->input_dev, ts->key_codes[key_code],
+			pressed);
+		input_report_key(ts->input_dev, BTN_TOUCH, pressed);
+	}
+
+	input_sync(ts->input_dev);
+}
+#endif
+
+static void hideep_put_event(struct hideep_t *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0)
+		push_ky(ts);
+#endif
+}
+
+static int hideep_get_event(struct hideep_t *ts)
+{
+	int ret;
+	int touch_count;
+	int info_size;
+
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		info_size = ts->tch_count * sizeof(struct hideep_mt_t);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			info_size, (unsigned char *)ts->touch_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0) {
+		info_size = ts->key_count * sizeof(struct hideep_key_t);
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			info_size, (unsigned char *)ts->key_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+#endif
+
+	return touch_count;
+}
+
+static int hideep_event_thread(void *arg)
+{
+	int t_evt;
+
+	struct hideep_t *ts = arg;
+
+	dev_info(&ts->client->dev, "start event thread");
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		schedule();
+
+		t_evt = hideep_get_event(ts);
+
+		if (t_evt >= 0)
+			hideep_put_event(ts);
+
+		if (kthread_should_stop())
+			break;
+	}
+
+	dev_info(&ts->client->dev, "end thread");
+	return 0;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char i2c_buff[2];
+	int ret;
+
+	struct hideep_t *ts = (struct hideep_t *) handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
+		ts->dev_state, ts->debug_dev.debug_enable);
+
+	if (ts->debug_dev.debug_enable == false &&
+		ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, (u8 *)&i2c_buff);
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			ts->interrupt_state = 0;
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = i2c_buff[0];
+		ts->key_count = i2c_buff[1] & 0x0f;
+		ts->lpm_count = i2c_buff[1] & 0xf0;
+
+		wake_up_process(ts->hthread_event);
+
+	} else if (ts->debug_dev.debug_enable == true
+		&& ts->dev_state == state_debugging) {
+		ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
+			FRAME_HEADER_SIZE + ts->debug_dev.img_size,
+			ts->debug_dev.img_buff);
+		ts->debug_dev.img_size =
+			GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
+		ts->debug_dev.ready = 1;
+		wake_up_interruptible(&ts->debug_dev.i_packet);
+	}
+	dev_dbg(&ts->client->dev, "end.");
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_t *ts)
+{
+#ifdef HIDEEP_SUPPORT_KEY
+	int i;
+
+	ts->key_codes[0] = KEY_MENU;
+	ts->key_codes[1] = KEY_HOME;
+	ts->key_codes[2] = KEY_BACK;
+
+	for (i = 0; i < HIDEEP_KEY_MAX; i++)
+		set_bit(ts->key_codes[i], ts->input_dev->keybit);
+#endif
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	set_bit(EV_ABS, ts->input_dev->evbit);
+	set_bit(EV_KEY, ts->input_dev->evbit);
+	set_bit(EV_SYN, ts->input_dev->evbit);
+	set_bit(BTN_TOUCH, ts->input_dev->keybit);
+	set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
+	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
+		ts->p_data->max_z, 0, 0);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+#else
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TRACKING_ID, 0, 10, 0, 0);
+#endif
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
+
+	return 0;
+}
+
+static void hideep_reset_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret;
+	unsigned char cmd = 0x01;
+
+	pdata = ts->p_data;
+
+	dev_info(&ts->client->dev, "start!!");
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpio_set_value(pdata->reset_gpio, 0);
+		mdelay(20);
+		gpio_set_value(pdata->reset_gpio, 1);
+	} else
+#endif
+	{
+		ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+	dev_info(&ts->client->dev, "end!!");
+}
+
+static void hideep_get_info(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	unsigned char val[4];
+
+	pdata = ts->p_data;
+	ts->hideep_api->i2c_read(ts, 0x28, 4, val);
+
+	pdata->max_x = val[3] << 8 | val[2];
+	pdata->max_y = val[1] << 8 | val[0];
+	pdata->max_w = 255;
+	pdata->max_z = 255;
+
+	dev_info(&ts->client->dev, "X : %d, Y : %d",
+		pdata->max_x, pdata->max_y);
+}
+
+static void hideep_init_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	struct device dev;
+
+	pdata = ts->p_data;
+	dev = ts->client->dev;
+
+	/* power on */
+	ts->hideep_api->power(ts, true);
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	ts->hideep_api->reset_ic(ts);
+}
+
+static int hideep_resume(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state == state_normal)
+		goto hideep_resume_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+hideep_resume_exit:
+	mdelay(10);
+	ts->hideep_api->reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+static int hideep_suspend(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+	if (ts->dev_state == state_sleep)
+		goto hideep_suspend_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_sleep;
+
+	/* send sleep command.. */
+	pops_mt(ts);
+#ifdef HIDEEP_SUPPORT_KEY
+	pops_ky(ts);
+#endif
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+hideep_suspend_exit:
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+#ifdef CONFIG_FB
+static int fb_notifier_callback(struct notifier_block *self,
+	unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+
+	struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
+
+	if ((evdata) && (evdata->data) &&
+		(event == FB_EVENT_BLANK) &&
+		(ts) && (ts->client)) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			dev_info(&ts->client->dev, "resume");
+			if (ts->suspended == 1) {
+				hideep_resume(&ts->client->dev);
+				ts->suspended = 0;
+			}
+		} else if (*blank == FB_BLANK_POWERDOWN) {
+			dev_info(&ts->client->dev, "suspend");
+			if (ts->suspended == 0) {
+				ts->suspended = 1;
+				hideep_suspend(&ts->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_OF
+static int hideep_parse_dts(struct device *dev,
+	struct hideep_platform_data_t *pdata)
+{
+	int ret = 0;
+	unsigned int coords[4];
+	struct device_node *np;
+
+	dev_info(dev, "enter");
+	np = dev->of_node;
+
+	ret = of_property_read_u32_array(np, "hideep,max_coords", coords, 4);
+	if (ret) {
+		dev_err(dev, "Failed to get max_coords property\n");
+		return ret;
+	}
+
+	pdata->max_x = coords[0];
+	pdata->max_y = coords[1];
+	pdata->max_w = coords[2];
+	pdata->max_z = coords[3];
+
+	dev_info(dev, "max coord data x : %d\n", pdata->max_x);
+	dev_info(dev, "max coord data y : %d\n", pdata->max_y);
+	dev_info(dev, "max coord data w : %d\n", pdata->max_w);
+	dev_info(dev, "max coord data z : %d\n", pdata->max_z);
+
+	/* device tree information get */
+	pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+	dev_info(dev, "reset_gpio = %d, is %s specified",
+		pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
+	if (pdata->reset_gpio)
+		ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
+			"reset-gpios");
+
+	pdata->vcc_vdd = regulator_get(dev, "vdd");
+	pdata->vcc_vid = regulator_get(dev, "vid");
+
+	return ret;
+}
+#endif
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_platform_data_t *p_data;
+	struct dwz_info_t *dwz;
+	struct hideep_function_list_t *function;
+	struct hideep_t *ts;
+
+	dev_info(&client->dev, "enter");
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		ret = -ENODEV;
+		return ret;
+	}
+
+	/* init platform data */
+	p_data = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_platform_data_t), GFP_KERNEL);
+
+#ifdef CONFIG_OF
+	if (client->dev.of_node) {
+		ret = hideep_parse_dts(&client->dev, p_data);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	/* init hideep_t */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_t), GFP_KERNEL);
+	dwz = devm_kzalloc(&client->dev,
+		sizeof(struct dwz_info_t), GFP_KERNEL);
+	function = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_function_list_t), GFP_KERNEL);
+
+	ts->client = client;
+	ts->p_data = p_data;
+	ts->dwz_info = dwz;
+	ts->hideep_api = function;
+
+	ts->hideep_api->i2c_read = hideep_i2c_read;
+	ts->hideep_api->i2c_write = hideep_i2c_write;
+	ts->hideep_api->reset_ic = hideep_reset_ic;
+	ts->hideep_api->power = hideep_power;
+
+	/* init for isp function */
+	hideep_isp_init(ts);
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	if (!client->dev.of_node)
+		hideep_get_info(ts);
+
+	/* hardware init */
+	hideep_init_ic(ts);
+
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+	/* read info */
+	ret = ts->hideep_api->get_dwz_info(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		goto hideep_probe_read_dwz_err;
+	}
+#endif
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_input_dev_memory_err;
+	}
+
+	hideep_capability(ts);
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_register_input_dev_err;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	/* init event thread */
+	ts->hthread_event = kthread_run(hideep_event_thread,
+		ts, "hideep_event_thread");
+	if (IS_ERR(ts->hthread_event)) {
+		dev_err(&client->dev, "can't create event thread !!!");
+		ret = PTR_ERR(ts->hthread_event);
+		goto hideep_probe_create_thread_err;
+	}
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (ts->client->irq <= 0) {
+		dev_err(&client->dev, "can't be assigned irq");
+		goto hideep_probe_assigned_irq_err;
+	}
+
+	if (ts->client->irq) {
+		ret = request_threaded_irq(ts->client->irq, NULL,
+			hideep_irq_task,
+			(IRQF_TRIGGER_LOW | IRQF_ONESHOT),
+			ts->client->name, ts);
+		disable_irq(ts->client->irq);
+		ts->interrupt_state = 0;
+		if (ret < 0) {
+			dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+				ret);
+			goto hideep_probe_request_irq_err;
+		}
+	}
+
+	ret = hideep_debug_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_debug_init_err;
+	}
+
+	ret = hideep_sysfs_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_sysfs_init_err;
+	}
+
+#ifdef CONFIG_FB
+	ts->suspended = 0;
+	ts->fb_notif.notifier_call = fb_notifier_callback;
+	ret = fb_register_client(&ts->fb_notif);
+	if (ret) {
+		dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
+			ret);
+		ret = -1;
+		goto hideep_probe_register_fb_err;
+	}
+#endif
+
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+	dev_info(&client->dev, "probe is ok!");
+	return 0;
+
+hideep_probe_register_fb_err:
+	hideep_sysfs_exit(ts);
+
+hideep_probe_sysfs_init_err:
+	hideep_debug_uninit();
+
+hideep_probe_debug_init_err:
+hideep_probe_request_irq_err:
+	free_irq(ts->client->irq, ts);
+
+hideep_probe_assigned_irq_err:
+hideep_probe_create_thread_err:
+	input_unregister_device(ts->input_dev);
+
+hideep_probe_register_input_dev_err:
+	if (ts->input_dev)
+		input_free_device(ts->input_dev);
+
+hideep_probe_input_dev_memory_err:
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+hideep_probe_read_dwz_err:
+#endif
+
+#ifdef CONFIG_OF
+	ts->hideep_api->power(ts, false);
+#endif
+	dev_err(&client->dev, "probe err!");
+	return ret;
+}
+
+static int hideep_remove(struct i2c_client *client)
+{
+	struct hideep_t *ts = i2c_get_clientdata(client);
+
+	kthread_stop(ts->hthread_event);
+
+#ifdef CONFIG_FB
+	if (fb_unregister_client(&ts->fb_notif))
+		dev_info(&client->dev,
+			"Error occurred while unregistering fb_notifier");
+#endif
+
+#ifdef CONFIG_OF
+	ts->hideep_api->power(ts, false);
+#endif
+	free_irq(client->irq, ts);
+
+	input_unregister_device(ts->input_dev);
+	hideep_sysfs_exit(ts);
+
+	hideep_debug_uninit();
+	devm_kfree(&client->dev, ts);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops hideep_pm_ops = {
+	.suspend = hideep_suspend,
+	.resume = hideep_resume,
+};
+#endif
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep_lime" },
+	{ .compatible = "hideep,hideep_crimson" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.remove = hideep_remove,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
new file mode 100644
index 0000000..549f2ad2
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+#include "hideep_dbg.h"
+
+static struct hideep_debug_dev_t *hdd;
+
+static int hideep_i2c_recv(unsigned int len)
+{
+	int ret = 0;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_i2c_send(unsigned int len)
+{
+	int ret = 0;
+	unsigned char *buff = hdd->vr_buff;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_send(hdd->ts->client, buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_get_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+
+	ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
+		hdd->vr_buff);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_set_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+	int wr_remain = len;
+	int vr_addr = addr;
+	int wr_len = len;
+	unsigned char *buff = hdd->vr_buff;
+
+	do {
+		if (wr_remain >=  MAX_VR_BUFF)
+			wr_len = MAX_VR_BUFF;
+		else
+			wr_len = wr_remain;
+
+		ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
+			wr_len, buff);
+		if (ret < 0)
+			goto i2c_err;
+
+		wr_remain -= MAX_VR_BUFF;
+		vr_addr += MAX_VR_BUFF;
+		buff += MAX_VR_BUFF;
+	} while (wr_remain > 0);
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_download_uc(const char __user *uc, size_t count, int offset)
+{
+	int ret;
+	unsigned char *ucode;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (count > hdd->ts->fw_size) {
+		dev_err(&hdd->ts->client->dev, "over size data!!!");
+		return -1;
+	}
+
+	ucode = kmalloc(count, GFP_KERNEL);
+
+	ret = copy_from_user(ucode + offset, uc, count);
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
+		kfree(ucode);
+		return 0;
+	}
+
+	disable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 0;
+	hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
+	enable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 1;
+	kfree(ucode);
+
+	dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
+
+	return count;
+}
+
+static int hideep_debug_open(struct inode *inode, struct file *file)
+{
+	hdd->release_flag = false;
+
+	file->private_data = hdd;
+	dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
+
+	return 0;
+}
+
+static int hideep_debug_release(struct inode *inode, struct file *file)
+{
+	if (!hdd->release_flag)
+		return -1;
+	hdd->release_flag = false;
+	file->private_data = NULL;
+	return 0;
+}
+
+static unsigned int hideep_debug_poll(struct file *file,
+	struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	struct hideep_debug_dev_t *drv_info;
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info = file->private_data;
+
+	poll_wait(file, &drv_info->i_packet, wait);
+
+	if (drv_info->ready) {
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mask |= POLLIN | POLLRDNORM;
+		drv_info->ready = 0;
+	}
+
+	return mask;
+}
+
+static ssize_t hideep_debug_read(struct file *file, char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret = -1;
+	ssize_t rd_len = 0;
+	unsigned char *rd_buffer = NULL;
+	unsigned char *data = NULL;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info->vr_size = count;
+	rd_len = count;
+
+	data = kmalloc(rd_len, GFP_KERNEL);
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		if ((*offset & HIDEEP_MAX_RAW_LEN) &&
+			(*offset < HIDEEP_MAX_RAW_LEN) &&
+			(drv_info->debug_enable == true)) {
+			mutex_lock(&drv_info->ts->dev_mutex);
+			rd_buffer = drv_info->img_buff;
+			ret = 0;
+			mutex_unlock(&drv_info->ts->dev_mutex);
+		} else {
+			ret = hideep_get_vreg(*offset, rd_len);
+			rd_buffer = drv_info->vr_buff;
+		}
+		if (ret < 0)
+			rd_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		ret = hideep_i2c_recv(rd_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "ret = %d", ret);
+			rd_len = 0;
+		} else {
+			rd_buffer = drv_info->vr_buff;
+		}
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		rd_len = count;
+		memset(data, 0x0, rd_len);
+
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mutex_lock(&drv_info->ts->dev_mutex);
+		mutex_lock(&drv_info->ts->i2c_mutex);
+
+		drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
+			*offset & 0xfffff);
+
+		mutex_unlock(&drv_info->ts->dev_mutex);
+		mutex_unlock(&drv_info->ts->i2c_mutex);
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+
+		rd_buffer = data;
+	} else {
+		dev_err(&hdd->ts->client->dev, "undefined address");
+		kfree(data);
+		return 0;
+	}
+
+	ret = copy_to_user(buf, rd_buffer, rd_len);
+
+	if (drv_info->debug_enable == true && drv_info->ready == 0) {
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+	}
+
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+		kfree(data);
+		return -EFAULT;
+	}
+
+	kfree(data);
+	return rd_len;
+}
+
+static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+	int wr_len = 0;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+	if (file->private_data == NULL)
+		return 0;
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		wr_len = count;
+
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+			return -EFAULT;
+		}
+
+		ret = hideep_set_vreg(*offset, wr_len);
+		if (ret < 0)
+			wr_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		wr_len = count;
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		ret = hideep_i2c_send(wr_len);
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
+	} else {
+		dev_err(&hdd->ts->client->dev,
+			"hideep_write : undefined address, 0x%08x",
+			(int)*offset);
+		return 0;
+	}
+
+	return wr_len;
+}
+
+static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
+{
+	loff_t newpos;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
+		(unsigned int)off, whence);
+	if (file->private_data == NULL)
+		return -EFAULT;
+
+	switch (whence) {
+	/* SEEK_SET */
+	case 0:
+		newpos = off;
+		break;
+	/* SEEK_CUR */
+	case 1:
+		dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
+			(unsigned int)off);
+		if (off & HIDEEP_RELEASE_FLAG) {
+			dev_dbg(&hdd->ts->client->dev, "set release flag");
+			drv_info->release_flag = true;
+		}
+		newpos = file->f_pos;
+		break;
+	/* SEEK_END */
+	case 2:
+		newpos = file->f_pos;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (newpos < 0)
+		return -EINVAL;
+
+	file->f_pos = newpos;
+
+	return newpos;
+}
+
+static const struct file_operations hideep_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = hideep_debug_open,
+	.poll = hideep_debug_poll,
+	.release = hideep_debug_release,
+	.read = hideep_debug_read,
+	.write = hideep_debug_write,
+	.llseek = hideep_debug_llseek,
+};
+
+static struct miscdevice hideep_debug_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = HIDEEP_DEBUG_DEVICE_NAME,
+	.fops = &hideep_debug_fops
+};
+
+void hideep_debug_uninit(void)
+{
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+
+	misc_deregister(&hideep_debug_dev);
+}
+
+int hideep_debug_init(struct hideep_t *ts)
+{
+	int ret = 0;
+
+	hdd = &ts->debug_dev;
+
+	ret = misc_register(&hideep_debug_dev);
+	if (ret) {
+		dev_err(&ts->client->dev,
+			"hideep debug device register fail!!!");
+		goto fail;
+	}
+
+	init_waitqueue_head(&hdd->i_packet);
+
+	hdd->ts = ts;
+	hdd->debug_enable = false;
+
+	hdd->img_size = 0;
+	hdd->vr_size = 0;
+
+	hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
+	hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
+
+	memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
+	memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
+
+	if (!hdd->vr_buff || !hdd->img_buff)
+		goto fail;
+
+	dev_info(&ts->client->dev, "debug init....");
+	return 0;
+
+fail:
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+	return ret;
+}
diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
new file mode 100644
index 0000000..f18a09f
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_DBG_H
+#define _LINUX_HIDEEP_DBG_H
+
+/* Device Driver <==> Application */
+/* lseek(), write(), read() command */
+#define HIDEEP_RELEASE_FLAG				0x8000
+#define HIDEEP_VR_ADDR_LEN				0xFFFF
+#define HIDEEP_MAX_RAW_LEN				0x3000
+#define HIDEEP_READ_WRITE_VR			0xF000
+#define HIDEEP_I2C_BYPASS				0x20000000
+
+#define MAX_VR_BUFF						2048
+
+/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
+#define MAX_RAW_SIZE					7369
+#endif /* _LINUX_HIDEEP_DBG_H */
diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
new file mode 100644
index 0000000..c8d2e932
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+
+static struct pgm_packet packet_w;
+static struct pgm_packet packet_r;
+
+static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x80 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		(len+5));
+
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x00 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		5);
+
+	if (ret < 0)
+		goto err;
+
+	ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
+		len);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x00);
+	packet.header.w[1] = htonl(addr);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x80);
+	packet.header.w[1] = htonl(addr);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void get_dwz_from_binary(unsigned char *pres,
+	struct dwz_info_t *dwz_info)
+{
+	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
+		sizeof(struct dwz_info_t));
+}
+
+static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
+{
+	SW_RESET_IN_PGM(food);
+}
+
+static int hideep_enter_pgm(struct hideep_t *ts)
+{
+	int ret = 0;
+	int retry_count = 10;
+	int retry = 0;
+	unsigned int status;
+	unsigned int pattern = 0xDF9DAF39;
+
+	while (retry < retry_count) {
+		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
+		mdelay(1);
+
+		/* flush invalid Tx load register */
+		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+		if (status != htonl(pattern)) {
+			retry++;
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				status);
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry < retry_count) {
+		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
+
+		mdelay(1);
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_t *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	mdelay(50);
+
+	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
+		sizeof(struct dwz_info_t));
+
+	memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info_t));
+	hideep_sw_reset(ts, 10);
+
+	if (ts->dwz_info->product_code & 0x40) {
+		/* Crimson IC */
+		ts->fw_size = 1024 * 48;
+	} else {
+		/* default fw size */
+		ts->fw_size = 1024 * 64;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mdelay(50);
+
+	return ret;
+}
+
+static int hideep_nvm_unlock(struct hideep_t *ts)
+{
+	int ret = 0;
+	unsigned int unmask_code = 0;
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != NVM_MASK)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
+	SET_FLASH_HWCONTROL();
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	return ret;
+}
+
+static int hideep_program_page(struct hideep_t *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret = 0;
+	unsigned int pio_cmd = WRONLY;
+	unsigned int pio_cmd_page_erase = PERASE;
+	unsigned int status;
+	int time_out = 0;
+	int inf_en = 0;
+	unsigned int end_flag = 124;
+#ifndef PGM_BURST_WR
+	unsigned int i;
+#endif
+
+	hideep_pgm_r_reg(ts, FLASH_STA, &status);
+	ret = (status == 0) ? -1:0;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	if (addr > INF_SECTION) {
+		/* added INF flag set in pio_cmd */
+		addr -= INF_SECTION;
+		pio_cmd |= INF;
+		pio_cmd_page_erase |= INF;
+		inf_en = 1;
+	}
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* first erase */
+	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+	time_out = 0;
+
+	while (1) {
+		mdelay(1);
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+		if (time_out++ > 100)
+			break;
+	}
+	SET_FLASH_PIO(1);
+	/* first erase end*/
+
+	SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
+
+#ifdef PGM_BURST_WR
+	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
+		packet_w, NVM_PAGE_SIZE);
+#else
+	for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
+		SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
+#endif
+	if (inf_en == 0)
+		SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
+	else
+		SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	while (1) {
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+	}
+	/* write routine end */
+
+	SET_FLASH_HWCONTROL();
+
+	return ret;
+}
+
+static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset, unsigned char *old_fw)
+{
+	int i;
+	int ret = 0;
+	int len_r;
+	int len_w;
+	int addr = 0;
+	unsigned int pages;
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	addr = offset;
+	len_r = len;
+	len_w = len_r;
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_w = NVM_PAGE_SIZE;
+
+		/* compare */
+		if (old_fw != NULL)
+			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
+
+		if (ret != 0 || old_fw == NULL) {
+			/* write page */
+			memcpy(packet_w.payload, &(ucode[addr]), len_w);
+
+			ret = hideep_program_page(ts, addr, &packet_w);
+			mdelay(1);
+			if (ret < 0)
+				dev_err(&ts->client->dev,
+					"hideep_program_nvm : error(%08x):",
+					addr);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_w = len_r;
+	}
+
+	return ret;
+}
+
+static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset)
+{
+	int i, j;
+	int ret = 0;
+	unsigned char page_chk = 0;
+	unsigned int addr = offset;
+	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	int len_r = len;
+	int len_v = len_r;
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
+
+		if (page_chk != 0) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++)
+				dev_err(&ts->client->dev, "%02x : %02x",
+						ucode[addr+j], read[j]);
+
+			dev_err(&ts->client->dev, "verify : error(addr : %d)",
+				addr);
+
+			ret = -1;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	return ret;
+}
+
+static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
+	int offset)
+{
+	int ret = 0;
+	int pages, i;
+	int len_r, len_v;
+	int addr = offset;
+#ifndef PGM_BURST_WR
+	int j;
+#endif
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	len_r = len;
+	len_v = len_r;
+
+	ts->hideep_api->reset_ic(ts);
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	hideep_sw_reset(ts, 1000);
+}
+
+static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
+	size_t len, int offset)
+{
+	int ret = 0;
+	int retry = 3;
+
+	while (retry--) {
+		ret = hideep_verify_nvm(ts, fw, len, offset);
+		if (ret == 0) {
+			dev_dbg(&ts->client->dev, "update success");
+			break;
+		}
+		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
+	}
+
+	ret = (retry == 0) ? -1:0;
+
+	return ret;
+}
+
+static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
+	int len, int offset, bool mode)
+{
+	int ret = 0;
+	int firm_len;
+	unsigned char *ic_fw;
+	unsigned char *dwz_info;
+
+	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
+	dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
+
+	memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
+
+	firm_len = len;
+	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
+		firm_len, ts->fw_size);
+
+	if (firm_len > ts->fw_size)
+		firm_len = ts->fw_size;
+
+	dev_dbg(&ts->client->dev, "enter");
+	/* memory dump of target IC */
+	hideep_read_nvm(ts, ic_fw, firm_len, offset);
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
+
+#ifdef PGM_VERIFY
+	hideep_fw_verify_run(ts, code, firm_len, offset);
+	if (ret < 0) {
+		if (mode == true) {
+			/* clear dwz version, it will be store once again
+			 * after update success
+			 */
+			ts->dwz_info->release_ver = 0;
+			memcpy(&dwz_info[64], ts->dwz_info,
+				sizeof(struct dwz_info_t));
+			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
+				0x280, NULL);
+		}
+	}
+#endif
+	get_dwz_from_binary(code, ts->dwz_info);
+
+	hideep_sw_reset(ts, 1000);
+
+	kfree(ic_fw);
+	kfree(dwz_info);
+
+	return ret;
+}
+
+static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
+{
+	int ret = 0;
+	const struct firmware *fw_entry;
+	unsigned char *fw_buf;
+	unsigned int fw_length;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	fw_buf = (unsigned char *)fw_entry->data;
+	fw_length = (unsigned int)fw_entry->size;
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_update_part_firmware(struct hideep_t *ts,
+	unsigned char *code, int len, int offset, bool mode)
+{
+	int ret = 0;
+
+	mutex_lock(&ts->dev_mutex);
+
+	ret = hideep_wr_firmware(ts, code, len, offset, false);
+
+	mutex_unlock(&ts->dev_mutex);
+
+	return ret;
+}
+
+void hideep_isp_init(struct hideep_t *ts)
+{
+	ts->hideep_api->update_all = hideep_update_all_firmware;
+	ts->hideep_api->update_part = hideep_update_part_firmware;
+	ts->hideep_api->get_dwz_info = hideep_load_dwz;
+	ts->hideep_api->sp_func = hideep_read_nvm;
+}
diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
new file mode 100644
index 0000000..648f271
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_ISP_H
+#define _LINUX_HIDEEP_ISP_H
+
+#define PGM_BURST_WR
+#define PGM_VERIFY
+
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_NVM_DOWNLOAD		0x10000000
+
+/*************************************************************************
+ * register map
+ *************************************************************************/
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+/*************************************************************************
+ * flash commands
+ *************************************************************************/
+#define MERASE					0x00010000
+#define SERASE					0x00020000
+#define PERASE					0x00040000
+#define PROG					0x00080000
+#define WRONLY					0x00100000
+#define INF						0x00200000
+
+/*************************************************************************
+ * NVM Mask
+ *************************************************************************/
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x00310000
+#define INF_SECTION				0x0000C000
+#endif
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x0030027B
+#define INF_SECTION				0x00010000
+#endif
+
+/*************************************************************************
+ * DWZ info
+ *************************************************************************/
+#define HIDEEP_BOOT_SECTION		0x00000400
+#define HIDEEP_BOOT_LEN			0x00000400
+#define HIDEEP_DWZ_SECTION		0x00000280
+#define HIDEEP_DWZ_INFO_OFS		0x000002C0
+#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
+							- HIDEEP_DWZ_SECTION)
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		unsigned int w[2];
+	} header;
+
+	unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+#endif /* _LINUX_HIDEEP_ISP_H */
diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
new file mode 100644
index 0000000..7f28fb4
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_sysfs.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	if (mode == 1) {
+		disable_irq(ts->client->irq);
+
+		ts->dev_state = state_updating;
+		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+			ts->dwz_info->product_id);
+		ret = ts->hideep_api->update_all(ts, fw_name);
+
+		kfree(fw_name);
+
+		enable_irq(ts->client->irq);
+
+		ts->dev_state = state_normal;
+		if (ret != 0)
+			dev_err(dev, "The firmware update failed(%d)", ret);
+	}
+
+	return count;
+}
+
+static ssize_t fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->release_ver);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		ts->dwz_info->product_id);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->product_id);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t power_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
+		(ts->dev_state == state_init) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t power_control(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on, ret;
+
+	ret = kstrtoint(buf, 8, &on);
+	if (ret)
+		return ret;
+
+	if (on) {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_normal;
+		ts->hideep_api->reset_ic(ts);
+	} else {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_init;
+	}
+
+	return count;
+}
+
+#ifdef HIDEEP_SUPPORT_STYLUS
+static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+	unsigned char status[1];
+
+	ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
+
+	len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
+		(status[0] == 0x00) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on, ret;
+	unsigned char data[2];
+
+	ret = kstrtoint(buf, 8, &on);
+	if (ret)
+		return ret;
+
+	data[0] = 0x04;
+
+	if (on) {
+		data[1] = 0x01;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	} else {
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	}
+
+	return count;
+}
+#endif
+
+static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
+		(ts->debug_dev.debug_enable == 0) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on, ret;
+	unsigned char data[2];
+
+	ret = kstrtoint(buf, 8, &on);
+	if (ret)
+		return ret;
+
+	if (on) {
+		ts->debug_dev.debug_enable = 1;
+		ts->dev_state = state_debugging;
+	} else {
+		ts->debug_dev.debug_enable = 0;
+		ts->dev_state = state_normal;
+		/* set touch mode */
+		data[0] = 0x00;
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
+		if (ts->interrupt_state == 0) {
+			data[0] = 0x5A;
+			ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
+				data);
+			enable_irq(ts->client->irq);
+			ts->interrupt_state = 1;
+		}
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
+static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
+static DEVICE_ATTR(power_en, 0664, power_status, power_control);
+static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);
+#ifdef HIDEEP_SUPPORT_STYLUS
+static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);
+#endif
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_power_en.attr,
+	&dev_attr_debug_en.attr,
+#ifdef HIDEEP_SUPPORT_STYLUS
+	&dev_attr_stylus_en.attr,
+#endif
+	NULL
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+int hideep_sysfs_init(struct hideep_t *ts)
+{
+	int ret;
+	struct i2c_client *client = ts->client;
+
+	/* Create the files associated with this kobject */
+	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
+
+	if (ret)
+		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
+			 __func__, ret);
+
+	return ret;
+}
+
+int hideep_sysfs_exit(struct hideep_t *ts)
+{
+	struct i2c_client *client = ts->client;
+
+	sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	return 0;
+}
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
       [not found]       ` <1500510154-6661-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-07-24 19:52         ` Rob Herring
  0 siblings, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-07-24 19:52 UTC (permalink / raw)
  To: Anthony Kim
  Cc: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	rydberg-FFUHeuDi6mxAfugRpC6u6w,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

On Thu, Jul 20, 2017 at 09:22:34AM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |  35 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/input/touchscreen/Kconfig                  |  11 +
>  drivers/input/touchscreen/Makefile                 |   2 +
>  drivers/input/touchscreen/hideep.h                 | 318 +++++++
>  drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
>  drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
>  drivers/input/touchscreen/hideep_dbg.h             |  24 +
>  drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
>  drivers/input/touchscreen/hideep_isp.h             |  96 +++
>  drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
>  11 files changed, 2645 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.h
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..76ea8b1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,35 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible		: must be "hideep,hideep_crimson"
> +					or "hideep,hideep_lime".

s/_/-/

> +- reg			: I2C slave address, (e.g. 0x6C).
> +- interrupt-parent : Interrupt controller to which the chip is connected.
> +- interrupts : Interrupt to which the chip is connected.
> +
> +Optional properties:
> +- vdd-supply	: It is the controller supply for controlling
> +					 main voltage(3.3V) through the regulator.
> +- vid-supply	: It is the controller supply for controlling
> +					IO voltage(1.8V) through the regulator.
> +- reset-gpios	: Define for reset gpio pin.
> +						It is to use for reset IC.
> +- hideep,max_coords	: Max value for axis X, Y, W, Z.

s/_/-/

IOW, don't use underscores.

> +
> +Example:
> +
> +i2c@00000000 {
> +
> +	/* ... */
> +
> +	touchscreen@6c {
> +		compatible = "hideep,hideep_ts";
> +		reg = <0x6c>;
> +		interrupt-parent = <&gpx1>;
> +		interrupts = <2 0>;
> +		vdd-supply = <&ldo15_reg>";
> +		vid-supply = <&ldo18_reg>;
> +		reset-gpios = <&gpx1 5 0>;
> +		hideep,max_coords = <1080 1920 65535 65535>;
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index c03d201..aa2a301 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -131,6 +131,7 @@ gw	Gateworks Corporation
>  hannstar	HannStar Display Corporation
>  haoyu	Haoyu Microelectronic Co. Ltd.
>  hardkernel	Hardkernel Co., Ltd
> +hideep	HiDeep Inc.
>  himax	Himax Technologies, Inc.
>  hisilicon	Hisilicon Limited.
>  hit	Hitachi Ltd.
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 64b30fe..13e11c7 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called bu21023_ts.
>  
> +config TOUCHSCREEN_HIDEEP
> +	tristate "HiDeep Touch IC"
> +	depends on I2C
> +	help
> +	  Say Y here if you have a touchscreen using HiDeep.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a moudle, choose M here : the
> +	  module will be called hideep_ts.
> +
>  endif
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 6badce8..3aab466 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
> +obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
> +hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
> diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
> new file mode 100644
> index 0000000..50306a1
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep.h
> @@ -0,0 +1,318 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_H
> +#define _LINUX_HIDEEP_H
> +
> +/*************************************************************************
> + * this is include special HEAD file.
> + *************************************************************************/
> +#ifdef CONFIG_FB
> +#include <linux/fb.h>
> +#include <linux/notifier.h>
> +#endif
> +
> +/*************************************************************************
> + * this is include normal HEAD file.
> + *************************************************************************/
> +#include <linux/of_gpio.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/input.h>
> +#include <linux/uaccess.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/proc_fs.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/firmware.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/kthread.h>
> +#include <linux/random.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/sched/rt.h>
> +#include <linux/task_work.h>
> +#include <linux/rtc.h>
> +#include <linux/syscalls.h>
> +#include <linux/timer.h>
> +#include <linux/time.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/hrtimer.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/input/mt.h>
> +#include <linux/fs.h>
> +#include <linux/completion.h>
> +#include <linux/platform_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/init.h>
> +#include <linux/gfp.h>
> +#include <linux/kobject.h>
> +#include <linux/string.h>
> +#include <linux/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/poll.h>
> +#include <linux/fcntl.h>
> +#include <linux/unistd.h>
> +#include <linux/version.h>
> +#include <linux/acpi.h>
> +
> +/*************************************************************************
> + * definition part.
> + * define is (open, set, enable) if not, is (close, clear, disable)
> + * some special switch of functions.
> + *************************************************************************/
> +
> +/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
> +#define HIDEEP_PROTOCOL_2_0
> +
> +/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
> + * otherwise TYPE_A.
> + */
> +#define HIDEEP_TYPE_B_PROTOCOL
> +
> +/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
> +#define HIDEEP_DWZ_VERSION_CHECK
> +
> +/* HIDEEP_SUPPORT_KE if define, it will use key button. */
> +#define HIDEEP_SUPPORT_KEY
> +
> +/* HIDEEP_SUPPORT_STYLUS if define, it will use stylus mode. */
> +#define HIDEEP_SUPPORT_STYLUS
> +
> +/*************************************************************************
> + * Buffer size
> + *************************************************************************/
> +#define FRAME_HEADER_SIZE				8
> +/* if size of system i2c buffer is smaller than this value, need to modify */
> +#define MAX_I2C_BUFFER_SIZE				512
> +
> +/*************************************************************************
> + * board porting config
> + *************************************************************************/
> +#define HIDEEP_DEBUG_DEVICE_NAME		"hideep_debug"
> +#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
> +#define HIDEEP_I2C_NAME					"hideep_ts"
> +
> +/*************************************************************************
> + * register addr
> + *************************************************************************/
> +/* Touch & key event */
> +#define HIDEEP_EVENT_COUNT_ADDR			0x240
> +#define HIDEEP_TOUCH_DATA_ADDR			0x242
> +#define HIDEEP_KEY_DATA_ADDR			0x2A6
> +#define HIDEEP_RAW_DATA_ADDR			0x1000
> +
> +/* command list */
> +#define HIDEEP_RESET_CMD				0x9800
> +#define HIDEEP_INTCLR_CMD				0x9802
> +#define HIDEEP_OPMODE_CMD				0x9804
> +#define HIDEEP_SWTICH_CMD				0x9805
> +#define HIDEEP_SLEEP_CMD				0x980D
> +
> +/*************************************************************************
> + * multi-touch & key definitions.
> + *************************************************************************/
> +#define HIDEEP_MT_MAX					10
> +#define HIDEEP_KEY_MAX					3
> +
> +/* multi touch event bit */
> +#define HIDEEP_MT_ALWAYS_REPORT			0
> +#define HIDEEP_MT_TOUCHED				1
> +#define HIDEEP_MT_FIRST_CONTACT			2
> +#define HIDEEP_MT_DRAG_MOVE				3
> +#define HIDEEP_MT_RELEASED				4
> +#define HIDEEP_MT_PINCH					5
> +#define HIDEEP_MT_PRESSURE				6
> +
> +/* key event bit */
> +#define HIDEEP_KEY_RELEASED				0x20
> +#define HIDEEP_KEY_PRESSED				0x40
> +#define HIDEEP_KEY_FIRST_PRESSED		0x80
> +#define HIDEEP_KEY_PRESSED_MASK			0xC0
> +
> +/*************************************************************************
> + * HIDEEP PROTOCOL
> + *************************************************************************/
> +#ifdef HIDEEP_PROTOCOL_2_0
> +struct hideep_mt_t {
> +	unsigned short x;
> +	unsigned short y;
> +	unsigned short z;
> +	unsigned char w;
> +	unsigned char flag;
> +	unsigned char type;
> +	unsigned char index;
> +};
> +#else
> +struct hideep_mt_t {
> +	unsigned char flag;
> +	unsigned char index;
> +	unsigned short x;
> +	unsigned short y;
> +	unsigned char z;
> +	unsigned char w;
> +};
> +#endif
> +
> +struct hideep_key_t {
> +	unsigned char flag;
> +	unsigned int key;
> +};
> +
> +/*************************************************************************
> + * IC State
> + *************************************************************************/
> +enum e_dev_state {
> +	state_init = 1,
> +	state_normal,
> +	state_sleep,
> +	state_updating,
> +	state_debugging,
> +};
> +
> +/*************************************************************************
> + * Firmware info
> + *************************************************************************/
> +struct dwz_info_t {
> +	unsigned int code_start;
> +	unsigned char code_crc[12];
> +
> +	unsigned int c_code_start;
> +	unsigned short c_code_len;
> +	unsigned short gen_ver;
> +
> +	unsigned int vr_start;
> +	unsigned short vr_len;
> +	unsigned short rsv0;
> +
> +	unsigned int ft_start;
> +	unsigned short ft_len;
> +	unsigned short vr_version;
> +
> +	unsigned short boot_ver;
> +	unsigned short core_ver;
> +	unsigned short custom_ver;
> +	unsigned short release_ver;
> +
> +	unsigned char factory_id;
> +	unsigned char panel_type;
> +	unsigned char model_name[6];
> +	unsigned short product_code;
> +	unsigned short extra_option;
> +
> +	unsigned short product_id;
> +	unsigned short vendor_id;
> +};
> +
> +/*************************************************************************
> + * driver information for hideep_t by device tree
> + *************************************************************************/
> +struct hideep_platform_data_t {
> +	unsigned int max_x;
> +	unsigned int max_y;
> +	unsigned int max_z;
> +	unsigned int max_w;
> +
> +#ifdef CONFIG_OF
> +	int reset_gpio;
> +
> +	struct regulator *vcc_vdd;
> +	struct regulator *vcc_vid;
> +#endif
> +};
> +
> +struct hideep_debug_dev_t {
> +	struct miscdevice misc;
> +
> +	wait_queue_head_t i_packet;
> +
> +	unsigned int ready;
> +	unsigned char *vr_buff;
> +	unsigned char *img_buff;
> +	unsigned short vr_size;
> +	unsigned short img_size;
> +
> +	bool debug_enable;
> +	bool release_flag;
> +
> +	struct hideep_t *ts;
> +};
> +
> +struct hideep_function_list_t {
> +	//core
> +	int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
> +		unsigned char *);
> +	int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
> +		unsigned char *);
> +	void (*reset_ic)(struct hideep_t *);
> +	void (*power)(struct hideep_t *, int);
> +
> +	//isp
> +	int (*update_all)(struct hideep_t *, const char *);
> +	int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
> +	int (*get_dwz_info)(struct hideep_t *);
> +	void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
> +};
> +
> +struct hideep_t {
> +	struct i2c_client *client;
> +	struct input_dev *input_dev;
> +	struct hideep_platform_data_t *p_data;
> +
> +	struct mutex dev_mutex;
> +	struct mutex i2c_mutex;
> +
> +	struct hideep_debug_dev_t debug_dev;
> +	struct hideep_function_list_t *hideep_api;
> +
> +	struct task_struct *hthread_event;
> +
> +	bool suspended;
> +	enum e_dev_state dev_state;
> +
> +#ifdef CONFIG_FB
> +	struct notifier_block fb_notif;
> +#endif
> +
> +	long tch_bit;
> +	unsigned int tch_count;
> +	unsigned int key_count;
> +	unsigned int lpm_count;
> +
> +	struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +	struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
> +	int key_codes[HIDEEP_KEY_MAX];
> +#endif
> +
> +	unsigned char i2c_buf[256];
> +	struct dwz_info_t *dwz_info;
> +
> +	int interrupt_state;
> +	int fw_size;
> +};
> +
> +/*************************************************************************
> + * function define
> + *************************************************************************/
> +int hideep_debug_init(struct hideep_t *ts);
> +void hideep_debug_uninit(void);
> +
> +int hideep_sysfs_init(struct hideep_t *ts);
> +int hideep_sysfs_exit(struct hideep_t *ts);
> +
> +void hideep_isp_init(struct hideep_t *ts);
> +
> +#endif
> diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
> new file mode 100644
> index 0000000..2b71b84d
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_core.c
> @@ -0,0 +1,916 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +
> +#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
> +	((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
> +
> +static int hideep_pwr_on(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	int ret = 0;
> +
> +	pdata = ts->p_data;
> +
> +#ifdef CONFIG_OF
> +	if (!IS_ERR(pdata->vcc_vdd)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
> +		ret = regulator_enable(pdata->vcc_vdd);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vdd enable failed ret=%d", ret);
> +	}
> +	usleep_range(999, 1000);
> +
> +	if (!IS_ERR(pdata->vcc_vid)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
> +		ret = regulator_enable(pdata->vcc_vid);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vcc_vid enable failed ret=%d", ret);
> +	}
> +	usleep_range(2999, 3000);
> +#endif
> +
> +	return ret;
> +}
> +
> +static int hideep_pwr_off(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	int ret = 0;
> +
> +	pdata = ts->p_data;
> +
> +#ifdef CONFIG_OF
> +	if (pdata->reset_gpio > 0) {
> +		dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
> +		gpio_set_value(pdata->reset_gpio, 0);
> +	}
> +
> +	if (!IS_ERR(pdata->vcc_vid)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
> +		ret = regulator_disable(pdata->vcc_vid);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vcc_vid enable failed ret=%d", ret);
> +	}
> +
> +	if (!IS_ERR(pdata->vcc_vdd)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
> +		ret = regulator_disable(pdata->vcc_vdd);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vdd disable failed ret=%d", ret);
> +	}
> +#endif
> +	return ret;
> +}
> +
> +static void hideep_power(struct hideep_t *ts, int on)
> +{
> +	int ret = 0;
> +
> +	if (on) {
> +		dev_info(&ts->client->dev, "power on");
> +		ret = hideep_pwr_on(ts);
> +	} else {
> +		dev_info(&ts->client->dev, "power off");
> +		ret = hideep_pwr_off(ts);
> +	}
> +}
> +
> +static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret = -1;
> +	unsigned short r_len, raddr;
> +	int length;
> +
> +	length = len;
> +	raddr = addr;
> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	do {
> +		if (length > MAX_I2C_BUFFER_SIZE)
> +			r_len = MAX_I2C_BUFFER_SIZE;
> +		else
> +			r_len = length;
> +
> +		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
> +			raddr, r_len);
> +
> +		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
> +
> +		if (ret < 0)
> +			goto i2c_err;
> +
> +		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
> +		length -= MAX_I2C_BUFFER_SIZE;
> +		buf += MAX_I2C_BUFFER_SIZE;
> +		raddr += MAX_I2C_BUFFER_SIZE;
> +
> +		if (ret < 0)
> +			goto i2c_err;
> +	} while (length > 0);
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +
> +	return  0;
> +i2c_err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return -1;
> +}
> +
> +static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret = -1;
> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	// data mangling..
> +	ts->i2c_buf[0] = (addr >> 0) & 0xFF;
> +	ts->i2c_buf[1] = (addr >> 8) & 0xFF;
> +	memcpy(&ts->i2c_buf[2], buf, len);
> +
> +	ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
> +
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +	return  0;
> +
> +i2c_err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return -1;
> +}
> +
> +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
> +
> +static void pops_mt(struct hideep_t *ts)
> +{
> +#ifdef HIDEEP_TYPE_B_PROTOCOL
> +	int id;
> +	int i;
> +
> +	for (i = 0; i < ts->tch_count; i++) {
> +		id = (ts->touch_evt[i].index >> 0) & 0x0F;
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
> +		input_report_key(ts->input_dev, BTN_TOUCH, false);
> +	}
> +#else
> +	input_report_key(ts->input_dev, BTN_TOUCH, false);
> +	input_mt_sync(ts->input_dev);
> +#endif
> +
> +	input_sync(ts->input_dev);
> +}
> +
> +static void push_mt(struct hideep_t *ts)
> +{
> +	int id;
> +	int i;
> +	bool btn_up = 0;
> +	bool btn_dn = 0;
> +	bool btn_mv = 0;
> +	int evt = 0;
> +
> +	/* load multi-touch event to input system */
> +	for (i = 0; i < ts->tch_count; i++) {
> +		id = (ts->touch_evt[i].index >> 0) & 0x0F;
> +		btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
> +		btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
> +			& 0x01;
> +		btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
> +
> +		if (btn_up)
> +			clear_bit(id, &ts->tch_bit);
> +		else
> +			__set_bit(id, &ts->tch_bit);
> +
> +		dev_dbg(&ts->client->dev,
> +			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
> +			ts->touch_evt[i].type, ts->touch_evt[i].index,
> +			i, ts->touch_evt[i].x, ts->touch_evt[i].y,
> +			ts->touch_evt[i].z);
> +
> +#ifdef HIDEEP_TYPE_B_PROTOCOL
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
> +			(btn_up == 0));
> +
> +		if (btn_up == 0) {
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +				ts->touch_evt[i].x);
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +				ts->touch_evt[i].y);
> +			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +				ts->touch_evt[i].z);
> +			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +				ts->touch_evt[i].w);
> +			evt++;
> +		}
> +#else
> +		if (btn_up) {
> +			input_mt_sync(ts->input_dev);
> +		} else {
> +			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +				ts->touch_evt[i].x);
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +				ts->touch_evt[i].y);
> +			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +				ts->touch_evt[i].z);
> +			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +				ts->touch_evt[i].w);
> +			input_mt_sync(ts->input_dev);
> +			evt++;
> +		}
> +#endif
> +	}
> +
> +	if (ts->tch_bit == 0)
> +		evt = 0;
> +
> +	input_report_key(ts->input_dev, BTN_TOUCH, evt);
> +	input_mt_sync_frame(ts->input_dev);
> +	input_sync(ts->input_dev);
> +}
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +static void pops_ky(struct hideep_t *ts)
> +{
> +	int i;
> +
> +	for (i = 0; i < ts->tch_count; i++) {
> +		input_report_key(ts->input_dev, ts->key_codes[i], false);
> +		input_report_key(ts->input_dev, BTN_TOUCH, false);
> +	}
> +
> +	input_sync(ts->input_dev);
> +}
> +
> +static void push_ky(struct hideep_t *ts)
> +{
> +	int i;
> +	int pressed;
> +	int key_code;
> +	int key_status;
> +
> +	for (i = 0; i < ts->key_count; i++) {
> +		key_code = ts->key_evt[i].flag & 0x0F;
> +		key_status = ts->key_evt[i].flag & 0xF0;
> +		pressed = false;
> +
> +		if (key_status & HIDEEP_KEY_PRESSED_MASK)
> +			pressed = true;
> +		else
> +			pressed = false;
> +
> +		input_report_key(ts->input_dev, ts->key_codes[key_code],
> +			pressed);
> +		input_report_key(ts->input_dev, BTN_TOUCH, pressed);
> +	}
> +
> +	input_sync(ts->input_dev);
> +}
> +#endif
> +
> +static void hideep_put_event(struct hideep_t *ts)
> +{
> +	/* mangling touch information */
> +	if (ts->tch_count > 0)
> +		push_mt(ts);
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +	if (ts->key_count > 0)
> +		push_ky(ts);
> +#endif
> +}
> +
> +static int hideep_get_event(struct hideep_t *ts)
> +{
> +	int ret;
> +	int touch_count;
> +	int info_size;
> +
> +
> +	/* get touch event count */
> +	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
> +		ts->tch_count, ts->key_count, ts->lpm_count);
> +
> +	/* get touch event information */
> +	if (ts->tch_count > HIDEEP_MT_MAX)
> +		ts->tch_count = 0;
> +
> +	if (ts->key_count > HIDEEP_KEY_MAX)
> +		ts->key_count = 0;
> +
> +	touch_count = ts->tch_count + ts->key_count;
> +
> +	if (ts->tch_count > 0) {
> +		info_size = ts->tch_count * sizeof(struct hideep_mt_t);
> +		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
> +			info_size, (unsigned char *)ts->touch_evt);
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return -1;
> +		}
> +	}
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +	if (ts->key_count > 0) {
> +		info_size = ts->key_count * sizeof(struct hideep_key_t);
> +		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
> +			info_size, (unsigned char *)ts->key_evt);
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return -1;
> +		}
> +	}
> +#endif
> +
> +	return touch_count;
> +}
> +
> +static int hideep_event_thread(void *arg)
> +{
> +	int t_evt;
> +
> +	struct hideep_t *ts = arg;
> +
> +	dev_info(&ts->client->dev, "start event thread");
> +
> +	while (!kthread_should_stop()) {
> +		set_current_state(TASK_INTERRUPTIBLE);
> +
> +		schedule();
> +
> +		t_evt = hideep_get_event(ts);
> +
> +		if (t_evt >= 0)
> +			hideep_put_event(ts);
> +
> +		if (kthread_should_stop())
> +			break;
> +	}
> +
> +	dev_info(&ts->client->dev, "end thread");
> +	return 0;
> +}
> +
> +static irqreturn_t hideep_irq_task(int irq, void *handle)
> +{
> +	unsigned char i2c_buff[2];
> +	int ret;
> +
> +	struct hideep_t *ts = (struct hideep_t *) handle;
> +
> +	dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
> +		ts->dev_state, ts->debug_dev.debug_enable);
> +
> +	if (ts->debug_dev.debug_enable == false &&
> +		ts->dev_state == state_normal) {
> +		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
> +			2, (u8 *)&i2c_buff);
> +		if (ret < 0) {
> +			disable_irq(ts->client->irq);
> +			ts->interrupt_state = 0;
> +			return IRQ_HANDLED;
> +		}
> +
> +		ts->tch_count = i2c_buff[0];
> +		ts->key_count = i2c_buff[1] & 0x0f;
> +		ts->lpm_count = i2c_buff[1] & 0xf0;
> +
> +		wake_up_process(ts->hthread_event);
> +
> +	} else if (ts->debug_dev.debug_enable == true
> +		&& ts->dev_state == state_debugging) {
> +		ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
> +			FRAME_HEADER_SIZE + ts->debug_dev.img_size,
> +			ts->debug_dev.img_buff);
> +		ts->debug_dev.img_size =
> +			GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
> +		ts->debug_dev.ready = 1;
> +		wake_up_interruptible(&ts->debug_dev.i_packet);
> +	}
> +	dev_dbg(&ts->client->dev, "end.");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int hideep_capability(struct hideep_t *ts)
> +{
> +#ifdef HIDEEP_SUPPORT_KEY
> +	int i;
> +
> +	ts->key_codes[0] = KEY_MENU;
> +	ts->key_codes[1] = KEY_HOME;
> +	ts->key_codes[2] = KEY_BACK;
> +
> +	for (i = 0; i < HIDEEP_KEY_MAX; i++)
> +		set_bit(ts->key_codes[i], ts->input_dev->keybit);
> +#endif
> +
> +	ts->input_dev->name = HIDEEP_TS_NAME;
> +	ts->input_dev->id.bustype = BUS_I2C;
> +
> +	set_bit(EV_ABS, ts->input_dev->evbit);
> +	set_bit(EV_KEY, ts->input_dev->evbit);
> +	set_bit(EV_SYN, ts->input_dev->evbit);
> +	set_bit(BTN_TOUCH, ts->input_dev->keybit);
> +	set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
> +	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
> +
> +	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
> +	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
> +	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
> +		ts->p_data->max_z, 0, 0);
> +
> +#ifdef HIDEEP_TYPE_B_PROTOCOL
> +	input_mt_init_slots(ts->input_dev,
> +		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
> +#else
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TRACKING_ID, 0, 10, 0, 0);
> +#endif
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
> +
> +	return 0;
> +}
> +
> +static void hideep_reset_ic(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	int ret;
> +	unsigned char cmd = 0x01;
> +
> +	pdata = ts->p_data;
> +
> +	dev_info(&ts->client->dev, "start!!");
> +
> +#ifdef CONFIG_OF
> +	if (pdata->reset_gpio > 0) {
> +		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
> +		gpio_set_value(pdata->reset_gpio, 0);
> +		mdelay(20);
> +		gpio_set_value(pdata->reset_gpio, 1);
> +	} else
> +#endif
> +	{
> +		ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
> +	}
> +	mdelay(50);
> +	dev_info(&ts->client->dev, "end!!");
> +}
> +
> +static void hideep_get_info(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	unsigned char val[4];
> +
> +	pdata = ts->p_data;
> +	ts->hideep_api->i2c_read(ts, 0x28, 4, val);
> +
> +	pdata->max_x = val[3] << 8 | val[2];
> +	pdata->max_y = val[1] << 8 | val[0];
> +	pdata->max_w = 255;
> +	pdata->max_z = 255;
> +
> +	dev_info(&ts->client->dev, "X : %d, Y : %d",
> +		pdata->max_x, pdata->max_y);
> +}
> +
> +static void hideep_init_ic(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	struct device dev;
> +
> +	pdata = ts->p_data;
> +	dev = ts->client->dev;
> +
> +	/* power on */
> +	ts->hideep_api->power(ts, true);
> +	ts->dev_state = state_init;
> +	mdelay(30);
> +
> +	/* ic reset */
> +	ts->hideep_api->reset_ic(ts);
> +}
> +
> +static int hideep_resume(struct device *dev)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x00;
> +
> +	dev_info(dev, "enter");
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	if (ts->dev_state == state_normal)
> +		goto hideep_resume_exit;
> +
> +	dev_info(dev, "not waiting.");
> +	ts->dev_state = state_normal;
> +
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	enable_irq(ts->client->irq);
> +	ts->interrupt_state = 1;
> +
> +hideep_resume_exit:
> +	mdelay(10);
> +	ts->hideep_api->reset_ic(ts);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +	dev_info(dev, "exit.");
> +	return 0;
> +}
> +
> +static int hideep_suspend(struct device *dev)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x01;
> +
> +	dev_info(dev, "enter");
> +
> +	mutex_lock(&ts->dev_mutex);
> +	if (ts->dev_state == state_sleep)
> +		goto hideep_suspend_exit;
> +
> +	dev_info(dev, "not waiting.");
> +	ts->dev_state = state_sleep;
> +
> +	/* send sleep command.. */
> +	pops_mt(ts);
> +#ifdef HIDEEP_SUPPORT_KEY
> +	pops_ky(ts);
> +#endif
> +
> +	/* default deep sleep */
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	disable_irq(ts->client->irq);
> +	ts->interrupt_state = 0;
> +
> +hideep_suspend_exit:
> +	mutex_unlock(&ts->dev_mutex);
> +	dev_info(dev, "exit.");
> +	return 0;
> +}
> +
> +#ifdef CONFIG_FB
> +static int fb_notifier_callback(struct notifier_block *self,
> +	unsigned long event, void *data)
> +{
> +	struct fb_event *evdata = data;
> +	int *blank;
> +
> +	struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
> +
> +	if ((evdata) && (evdata->data) &&
> +		(event == FB_EVENT_BLANK) &&
> +		(ts) && (ts->client)) {
> +		blank = evdata->data;
> +		if (*blank == FB_BLANK_UNBLANK) {
> +			dev_info(&ts->client->dev, "resume");
> +			if (ts->suspended == 1) {
> +				hideep_resume(&ts->client->dev);
> +				ts->suspended = 0;
> +			}
> +		} else if (*blank == FB_BLANK_POWERDOWN) {
> +			dev_info(&ts->client->dev, "suspend");
> +			if (ts->suspended == 0) {
> +				ts->suspended = 1;
> +				hideep_suspend(&ts->client->dev);
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_OF
> +static int hideep_parse_dts(struct device *dev,
> +	struct hideep_platform_data_t *pdata)
> +{
> +	int ret = 0;
> +	unsigned int coords[4];
> +	struct device_node *np;
> +
> +	dev_info(dev, "enter");
> +	np = dev->of_node;
> +
> +	ret = of_property_read_u32_array(np, "hideep,max_coords", coords, 4);
> +	if (ret) {
> +		dev_err(dev, "Failed to get max_coords property\n");
> +		return ret;
> +	}
> +
> +	pdata->max_x = coords[0];
> +	pdata->max_y = coords[1];
> +	pdata->max_w = coords[2];
> +	pdata->max_z = coords[3];
> +
> +	dev_info(dev, "max coord data x : %d\n", pdata->max_x);
> +	dev_info(dev, "max coord data y : %d\n", pdata->max_y);
> +	dev_info(dev, "max coord data w : %d\n", pdata->max_w);
> +	dev_info(dev, "max coord data z : %d\n", pdata->max_z);
> +
> +	/* device tree information get */
> +	pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
> +	dev_info(dev, "reset_gpio = %d, is %s specified",
> +		pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
> +	if (pdata->reset_gpio)
> +		ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
> +			"reset-gpios");
> +
> +	pdata->vcc_vdd = regulator_get(dev, "vdd");
> +	pdata->vcc_vid = regulator_get(dev, "vid");
> +
> +	return ret;
> +}
> +#endif
> +
> +static int hideep_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
> +{
> +	int ret = 0;
> +	struct hideep_platform_data_t *p_data;
> +	struct dwz_info_t *dwz;
> +	struct hideep_function_list_t *function;
> +	struct hideep_t *ts;
> +
> +	dev_info(&client->dev, "enter");
> +
> +	/* check i2c bus */
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_I2C)) {
> +		dev_err(&client->dev, "check i2c device error");
> +		ret = -ENODEV;
> +		return ret;
> +	}
> +
> +	/* init platform data */
> +	p_data = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_platform_data_t), GFP_KERNEL);
> +
> +#ifdef CONFIG_OF
> +	if (client->dev.of_node) {
> +		ret = hideep_parse_dts(&client->dev, p_data);
> +		if (ret)
> +			return ret;
> +	}
> +#endif
> +
> +	/* init hideep_t */
> +	ts = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_t), GFP_KERNEL);
> +	dwz = devm_kzalloc(&client->dev,
> +		sizeof(struct dwz_info_t), GFP_KERNEL);
> +	function = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_function_list_t), GFP_KERNEL);
> +
> +	ts->client = client;
> +	ts->p_data = p_data;
> +	ts->dwz_info = dwz;
> +	ts->hideep_api = function;
> +
> +	ts->hideep_api->i2c_read = hideep_i2c_read;
> +	ts->hideep_api->i2c_write = hideep_i2c_write;
> +	ts->hideep_api->reset_ic = hideep_reset_ic;
> +	ts->hideep_api->power = hideep_power;
> +
> +	/* init for isp function */
> +	hideep_isp_init(ts);
> +
> +	i2c_set_clientdata(client, ts);
> +
> +	mutex_init(&ts->i2c_mutex);
> +	mutex_init(&ts->dev_mutex);
> +
> +	if (!client->dev.of_node)
> +		hideep_get_info(ts);
> +
> +	/* hardware init */
> +	hideep_init_ic(ts);
> +
> +#ifdef HIDEEP_DWZ_VERSION_CHECK
> +	/* read info */
> +	ret = ts->hideep_api->get_dwz_info(ts);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
> +		goto hideep_probe_read_dwz_err;
> +	}
> +#endif
> +
> +	/* init input device */
> +	ts->input_dev = devm_input_allocate_device(&client->dev);
> +	if (!ts->input_dev) {
> +		dev_err(&client->dev, "can't allocate memory for input_dev");
> +		ret = -ENOMEM;
> +		goto hideep_probe_input_dev_memory_err;
> +	}
> +
> +	hideep_capability(ts);
> +
> +	ret = input_register_device(ts->input_dev);
> +	if (ret) {
> +		dev_err(&client->dev, "can't register input_dev");
> +		ret = -ENOMEM;
> +		goto hideep_probe_register_input_dev_err;
> +	}
> +
> +	input_set_drvdata(ts->input_dev, ts);
> +
> +	/* init event thread */
> +	ts->hthread_event = kthread_run(hideep_event_thread,
> +		ts, "hideep_event_thread");
> +	if (IS_ERR(ts->hthread_event)) {
> +		dev_err(&client->dev, "can't create event thread !!!");
> +		ret = PTR_ERR(ts->hthread_event);
> +		goto hideep_probe_create_thread_err;
> +	}
> +
> +	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
> +	if (ts->client->irq <= 0) {
> +		dev_err(&client->dev, "can't be assigned irq");
> +		goto hideep_probe_assigned_irq_err;
> +	}
> +
> +	if (ts->client->irq) {
> +		ret = request_threaded_irq(ts->client->irq, NULL,
> +			hideep_irq_task,
> +			(IRQF_TRIGGER_LOW | IRQF_ONESHOT),
> +			ts->client->name, ts);
> +		disable_irq(ts->client->irq);
> +		ts->interrupt_state = 0;
> +		if (ret < 0) {
> +			dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
> +				ret);
> +			goto hideep_probe_request_irq_err;
> +		}
> +	}
> +
> +	ret = hideep_debug_init(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
> +		ret = -1;
> +		goto hideep_probe_debug_init_err;
> +	}
> +
> +	ret = hideep_sysfs_init(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
> +		ret = -1;
> +		goto hideep_probe_sysfs_init_err;
> +	}
> +
> +#ifdef CONFIG_FB
> +	ts->suspended = 0;
> +	ts->fb_notif.notifier_call = fb_notifier_callback;
> +	ret = fb_register_client(&ts->fb_notif);
> +	if (ret) {
> +		dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
> +			ret);
> +		ret = -1;
> +		goto hideep_probe_register_fb_err;
> +	}
> +#endif
> +
> +
> +	ts->dev_state = state_normal;
> +	enable_irq(ts->client->irq);
> +	ts->interrupt_state = 1;
> +
> +	dev_info(&client->dev, "probe is ok!");
> +	return 0;
> +
> +hideep_probe_register_fb_err:
> +	hideep_sysfs_exit(ts);
> +
> +hideep_probe_sysfs_init_err:
> +	hideep_debug_uninit();
> +
> +hideep_probe_debug_init_err:
> +hideep_probe_request_irq_err:
> +	free_irq(ts->client->irq, ts);
> +
> +hideep_probe_assigned_irq_err:
> +hideep_probe_create_thread_err:
> +	input_unregister_device(ts->input_dev);
> +
> +hideep_probe_register_input_dev_err:
> +	if (ts->input_dev)
> +		input_free_device(ts->input_dev);
> +
> +hideep_probe_input_dev_memory_err:
> +#ifdef HIDEEP_DWZ_VERSION_CHECK
> +hideep_probe_read_dwz_err:
> +#endif
> +
> +#ifdef CONFIG_OF
> +	ts->hideep_api->power(ts, false);
> +#endif
> +	dev_err(&client->dev, "probe err!");
> +	return ret;
> +}
> +
> +static int hideep_remove(struct i2c_client *client)
> +{
> +	struct hideep_t *ts = i2c_get_clientdata(client);
> +
> +	kthread_stop(ts->hthread_event);
> +
> +#ifdef CONFIG_FB
> +	if (fb_unregister_client(&ts->fb_notif))
> +		dev_info(&client->dev,
> +			"Error occurred while unregistering fb_notifier");
> +#endif
> +
> +#ifdef CONFIG_OF
> +	ts->hideep_api->power(ts, false);
> +#endif
> +	free_irq(client->irq, ts);
> +
> +	input_unregister_device(ts->input_dev);
> +	hideep_sysfs_exit(ts);
> +
> +	hideep_debug_uninit();
> +	devm_kfree(&client->dev, ts);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static const struct dev_pm_ops hideep_pm_ops = {
> +	.suspend = hideep_suspend,
> +	.resume = hideep_resume,
> +};
> +#endif
> +
> +static const struct i2c_device_id hideep_dev_idtable[] = {
> +	{ HIDEEP_I2C_NAME, 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id hideep_acpi_id[] = {
> +	{ "HIDP0001", 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id hideep_match_table[] = {
> +	{ .compatible = "hideep,hideep_lime" },
> +	{ .compatible = "hideep,hideep_crimson" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hideep_match_table);
> +#endif
> +
> +static struct i2c_driver hideep_driver = {
> +	.probe = hideep_probe,
> +	.remove = hideep_remove,
> +	.id_table = hideep_dev_idtable,
> +	.driver = {
> +		.name = HIDEEP_I2C_NAME,
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_match_ptr(hideep_match_table),
> +		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
> +		.pm = &hideep_pm_ops,
> +	},
> +};
> +
> +module_i2c_driver(hideep_driver);
> +
> +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
> +MODULE_AUTHOR("anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
> new file mode 100644
> index 0000000..549f2ad2
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_dbg.c
> @@ -0,0 +1,405 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +#include "hideep_isp.h"
> +#include "hideep_dbg.h"
> +
> +static struct hideep_debug_dev_t *hdd;
> +
> +static int hideep_i2c_recv(unsigned int len)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&hdd->ts->i2c_mutex);
> +	ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
> +	mutex_unlock(&hdd->ts->i2c_mutex);
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	dev_info(&hdd->ts->client->dev, "(%d)", len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_i2c_send(unsigned int len)
> +{
> +	int ret = 0;
> +	unsigned char *buff = hdd->vr_buff;
> +
> +	mutex_lock(&hdd->ts->i2c_mutex);
> +	ret = i2c_master_send(hdd->ts->client, buff, len);
> +	mutex_unlock(&hdd->ts->i2c_mutex);
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	dev_info(&hdd->ts->client->dev, "(%d)", len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_get_vreg(unsigned int addr, unsigned int len)
> +{
> +	int ret = 0;
> +
> +	ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
> +		hdd->vr_buff);
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_set_vreg(unsigned int addr, unsigned int len)
> +{
> +	int ret = 0;
> +	int wr_remain = len;
> +	int vr_addr = addr;
> +	int wr_len = len;
> +	unsigned char *buff = hdd->vr_buff;
> +
> +	do {
> +		if (wr_remain >=  MAX_VR_BUFF)
> +			wr_len = MAX_VR_BUFF;
> +		else
> +			wr_len = wr_remain;
> +
> +		ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
> +			wr_len, buff);
> +		if (ret < 0)
> +			goto i2c_err;
> +
> +		wr_remain -= MAX_VR_BUFF;
> +		vr_addr += MAX_VR_BUFF;
> +		buff += MAX_VR_BUFF;
> +	} while (wr_remain > 0);
> +
> +	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_download_uc(const char __user *uc, size_t count, int offset)
> +{
> +	int ret;
> +	unsigned char *ucode;
> +
> +	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
> +
> +	if (count > hdd->ts->fw_size) {
> +		dev_err(&hdd->ts->client->dev, "over size data!!!");
> +		return -1;
> +	}
> +
> +	ucode = kmalloc(count, GFP_KERNEL);
> +
> +	ret = copy_from_user(ucode + offset, uc, count);
> +	if (ret < 0) {
> +		dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
> +		kfree(ucode);
> +		return 0;
> +	}
> +
> +	disable_irq(hdd->ts->client->irq);
> +	hdd->ts->interrupt_state = 0;
> +	hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
> +	enable_irq(hdd->ts->client->irq);
> +	hdd->ts->interrupt_state = 1;
> +	kfree(ucode);
> +
> +	dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
> +
> +	return count;
> +}
> +
> +static int hideep_debug_open(struct inode *inode, struct file *file)
> +{
> +	hdd->release_flag = false;
> +
> +	file->private_data = hdd;
> +	dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
> +
> +	return 0;
> +}
> +
> +static int hideep_debug_release(struct inode *inode, struct file *file)
> +{
> +	if (!hdd->release_flag)
> +		return -1;
> +	hdd->release_flag = false;
> +	file->private_data = NULL;
> +	return 0;
> +}
> +
> +static unsigned int hideep_debug_poll(struct file *file,
> +	struct poll_table_struct *wait)
> +{
> +	unsigned int mask = 0;
> +	struct hideep_debug_dev_t *drv_info;
> +
> +	if (file->private_data == NULL)
> +		return 0;
> +
> +	drv_info = file->private_data;
> +
> +	poll_wait(file, &drv_info->i_packet, wait);
> +
> +	if (drv_info->ready) {
> +		disable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 0;
> +		mask |= POLLIN | POLLRDNORM;
> +		drv_info->ready = 0;
> +	}
> +
> +	return mask;
> +}
> +
> +static ssize_t hideep_debug_read(struct file *file, char __user *buf,
> +	size_t count, loff_t *offset)
> +{
> +	int ret = -1;
> +	ssize_t rd_len = 0;
> +	unsigned char *rd_buffer = NULL;
> +	unsigned char *data = NULL;
> +	struct hideep_debug_dev_t *drv_info = file->private_data;
> +
> +	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
> +
> +	if (file->private_data == NULL)
> +		return 0;
> +
> +	drv_info->vr_size = count;
> +	rd_len = count;
> +
> +	data = kmalloc(rd_len, GFP_KERNEL);
> +
> +	if (*offset <= HIDEEP_VR_ADDR_LEN) {
> +		// if offset is not belong to any special command
> +		if ((*offset & HIDEEP_MAX_RAW_LEN) &&
> +			(*offset < HIDEEP_MAX_RAW_LEN) &&
> +			(drv_info->debug_enable == true)) {
> +			mutex_lock(&drv_info->ts->dev_mutex);
> +			rd_buffer = drv_info->img_buff;
> +			ret = 0;
> +			mutex_unlock(&drv_info->ts->dev_mutex);
> +		} else {
> +			ret = hideep_get_vreg(*offset, rd_len);
> +			rd_buffer = drv_info->vr_buff;
> +		}
> +		if (ret < 0)
> +			rd_len = 0;
> +	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
> +		// if offset is belong to special command "i2c bypass"
> +		ret = hideep_i2c_recv(rd_len);
> +		if (ret < 0) {
> +			dev_err(&hdd->ts->client->dev, "ret = %d", ret);
> +			rd_len = 0;
> +		} else {
> +			rd_buffer = drv_info->vr_buff;
> +		}
> +	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
> +		// if offset is belong to special command "nvm download"
> +		rd_len = count;
> +		memset(data, 0x0, rd_len);
> +
> +		disable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 0;
> +		mutex_lock(&drv_info->ts->dev_mutex);
> +		mutex_lock(&drv_info->ts->i2c_mutex);
> +
> +		drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
> +			*offset & 0xfffff);
> +
> +		mutex_unlock(&drv_info->ts->dev_mutex);
> +		mutex_unlock(&drv_info->ts->i2c_mutex);
> +		enable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 1;
> +
> +		rd_buffer = data;
> +	} else {
> +		dev_err(&hdd->ts->client->dev, "undefined address");
> +		kfree(data);
> +		return 0;
> +	}
> +
> +	ret = copy_to_user(buf, rd_buffer, rd_len);
> +
> +	if (drv_info->debug_enable == true && drv_info->ready == 0) {
> +		enable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 1;
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&hdd->ts->client->dev, "error : copy_to_user");
> +		kfree(data);
> +		return -EFAULT;
> +	}
> +
> +	kfree(data);
> +	return rd_len;
> +}
> +
> +static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
> +	size_t count, loff_t *offset)
> +{
> +	int ret;
> +	struct hideep_debug_dev_t *drv_info = file->private_data;
> +	int wr_len = 0;
> +
> +	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
> +	if (file->private_data == NULL)
> +		return 0;
> +
> +	if (*offset <= HIDEEP_VR_ADDR_LEN) {
> +		// if offset is not belong to any special command
> +		wr_len = count;
> +
> +		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
> +		if (ret < 0) {
> +			dev_err(&hdd->ts->client->dev, "error : copy_to_user");
> +			return -EFAULT;
> +		}
> +
> +		ret = hideep_set_vreg(*offset, wr_len);
> +		if (ret < 0)
> +			wr_len = 0;
> +	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
> +		// if offset is belong to special command "i2c bypass"
> +		wr_len = count;
> +		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
> +		ret = hideep_i2c_send(wr_len);
> +	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
> +		// if offset is belong to special command "nvm download"
> +		wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
> +	} else {
> +		dev_err(&hdd->ts->client->dev,
> +			"hideep_write : undefined address, 0x%08x",
> +			(int)*offset);
> +		return 0;
> +	}
> +
> +	return wr_len;
> +}
> +
> +static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
> +{
> +	loff_t newpos;
> +	struct hideep_debug_dev_t *drv_info = file->private_data;
> +
> +	dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
> +		(unsigned int)off, whence);
> +	if (file->private_data == NULL)
> +		return -EFAULT;
> +
> +	switch (whence) {
> +	/* SEEK_SET */
> +	case 0:
> +		newpos = off;
> +		break;
> +	/* SEEK_CUR */
> +	case 1:
> +		dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
> +			(unsigned int)off);
> +		if (off & HIDEEP_RELEASE_FLAG) {
> +			dev_dbg(&hdd->ts->client->dev, "set release flag");
> +			drv_info->release_flag = true;
> +		}
> +		newpos = file->f_pos;
> +		break;
> +	/* SEEK_END */
> +	case 2:
> +		newpos = file->f_pos;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (newpos < 0)
> +		return -EINVAL;
> +
> +	file->f_pos = newpos;
> +
> +	return newpos;
> +}
> +
> +static const struct file_operations hideep_debug_fops = {
> +	.owner = THIS_MODULE,
> +	.open = hideep_debug_open,
> +	.poll = hideep_debug_poll,
> +	.release = hideep_debug_release,
> +	.read = hideep_debug_read,
> +	.write = hideep_debug_write,
> +	.llseek = hideep_debug_llseek,
> +};
> +
> +static struct miscdevice hideep_debug_dev = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = HIDEEP_DEBUG_DEVICE_NAME,
> +	.fops = &hideep_debug_fops
> +};
> +
> +void hideep_debug_uninit(void)
> +{
> +	kfree(hdd->vr_buff);
> +	kfree(hdd->img_buff);
> +
> +	misc_deregister(&hideep_debug_dev);
> +}
> +
> +int hideep_debug_init(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +
> +	hdd = &ts->debug_dev;
> +
> +	ret = misc_register(&hideep_debug_dev);
> +	if (ret) {
> +		dev_err(&ts->client->dev,
> +			"hideep debug device register fail!!!");
> +		goto fail;
> +	}
> +
> +	init_waitqueue_head(&hdd->i_packet);
> +
> +	hdd->ts = ts;
> +	hdd->debug_enable = false;
> +
> +	hdd->img_size = 0;
> +	hdd->vr_size = 0;
> +
> +	hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
> +	hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
> +
> +	memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
> +	memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
> +
> +	if (!hdd->vr_buff || !hdd->img_buff)
> +		goto fail;
> +
> +	dev_info(&ts->client->dev, "debug init....");
> +	return 0;
> +
> +fail:
> +	kfree(hdd->vr_buff);
> +	kfree(hdd->img_buff);
> +	return ret;
> +}
> diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
> new file mode 100644
> index 0000000..f18a09f
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_dbg.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_DBG_H
> +#define _LINUX_HIDEEP_DBG_H
> +
> +/* Device Driver <==> Application */
> +/* lseek(), write(), read() command */
> +#define HIDEEP_RELEASE_FLAG				0x8000
> +#define HIDEEP_VR_ADDR_LEN				0xFFFF
> +#define HIDEEP_MAX_RAW_LEN				0x3000
> +#define HIDEEP_READ_WRITE_VR			0xF000
> +#define HIDEEP_I2C_BYPASS				0x20000000
> +
> +#define MAX_VR_BUFF						2048
> +
> +/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
> +#define MAX_RAW_SIZE					7369
> +#endif /* _LINUX_HIDEEP_DBG_H */
> diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
> new file mode 100644
> index 0000000..c8d2e932
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_isp.c
> @@ -0,0 +1,592 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +#include "hideep_isp.h"
> +
> +static struct pgm_packet packet_w;
> +static struct pgm_packet packet_r;
> +
> +static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	if ((len % 4) != 0)
> +		return -1;
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	packet->header.w[0] = htonl((0x80 | (len / 4-1)));
> +	packet->header.w[1] = htonl(addr);
> +
> +	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
> +		packet->payload[i] = htonl(packet->payload[i]);
> +
> +	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
> +		(len+5));
> +
> +	if (ret < 0)
> +		goto err;
> +
> +err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	if ((len % 4) != 0)
> +		return -1;
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	packet->header.w[0] = htonl((0x00 | (len / 4-1)));
> +	packet->header.w[1] = htonl(addr);
> +
> +	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
> +		5);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
> +		len);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
> +		packet->payload[i] = htonl(packet->payload[i]);
> +
> +err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
> +	unsigned int *val)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet;
> +
> +	packet.header.w[0] = htonl(0x00);
> +	packet.header.w[1] = htonl(addr);
> +
> +	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	*val = packet.payload[0];
> +
> +err:
> +	return ret;
> +}
> +
> +static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
> +	unsigned int data)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet;
> +
> +	packet.header.w[0] = htonl(0x80);
> +	packet.header.w[1] = htonl(addr);
> +	packet.payload[0] = data;
> +
> +	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
> +
> +	return ret;
> +}
> +
> +#define SW_RESET_IN_PGM(CLK) \
> +{ \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
> +}
> +
> +#define SET_FLASH_PIO(CE) \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
> +#define SET_PIO_SIG(X, Y) \
> +	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
> +#define SET_FLASH_HWCONTROL() \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
> +
> +#define NVM_W_SFR(x, y) \
> +{ \
> +	SET_FLASH_PIO(1); \
> +	SET_PIO_SIG(x, y); \
> +	SET_FLASH_PIO(0); \
> +}
> +
> +static void get_dwz_from_binary(unsigned char *pres,
> +	struct dwz_info_t *dwz_info)
> +{
> +	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
> +		sizeof(struct dwz_info_t));
> +}
> +
> +static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
> +{
> +	SW_RESET_IN_PGM(food);
> +}
> +
> +static int hideep_enter_pgm(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +	int retry_count = 10;
> +	int retry = 0;
> +	unsigned int status;
> +	unsigned int pattern = 0xDF9DAF39;
> +
> +	while (retry < retry_count) {
> +		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
> +		mdelay(1);
> +
> +		/* flush invalid Tx load register */
> +		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
> +
> +		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
> +
> +		if (status != htonl(pattern)) {
> +			retry++;
> +			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
> +				status);
> +		} else {
> +			dev_dbg(&ts->client->dev, "found magic code");
> +			break;
> +		}
> +	}
> +
> +	if (retry < retry_count) {
> +		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
> +		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
> +		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
> +		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
> +		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
> +		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
> +		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
> +		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
> +
> +		mdelay(1);
> +	} else {
> +		ret = -1;
> +	}
> +
> +	return ret;
> +}
> +
> +static int hideep_load_dwz(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet_r;
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	mdelay(50);
> +
> +	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
> +		sizeof(struct dwz_info_t));
> +
> +	memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
> +		sizeof(struct dwz_info_t));
> +	hideep_sw_reset(ts, 10);
> +
> +	if (ts->dwz_info->product_code & 0x40) {
> +		/* Crimson IC */
> +		ts->fw_size = 1024 * 48;
> +	} else {
> +		/* default fw size */
> +		ts->fw_size = 1024 * 64;
> +	}
> +
> +	dev_dbg(&ts->client->dev, "firmware release version : %04x",
> +		ts->dwz_info->release_ver);
> +
> +	mdelay(50);
> +
> +	return ret;
> +}
> +
> +static int hideep_nvm_unlock(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +	unsigned int unmask_code = 0;
> +
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
> +
> +	ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
> +
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +
> +	/* make it unprotected code */
> +	unmask_code &= (~_PROT_MODE);
> +
> +	/* compare unmask code */
> +	if (unmask_code != NVM_MASK)
> +		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
> +			unmask_code);
> +
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
> +	SET_FLASH_PIO(0);
> +
> +	NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
> +	SET_FLASH_HWCONTROL();
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +
> +	return ret;
> +}
> +
> +static int hideep_program_page(struct hideep_t *ts,
> +	unsigned int addr, struct pgm_packet *packet_w)
> +{
> +	int ret = 0;
> +	unsigned int pio_cmd = WRONLY;
> +	unsigned int pio_cmd_page_erase = PERASE;
> +	unsigned int status;
> +	int time_out = 0;
> +	int inf_en = 0;
> +	unsigned int end_flag = 124;
> +#ifndef PGM_BURST_WR
> +	unsigned int i;
> +#endif
> +
> +	hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +	ret = (status == 0) ? -1:0;
> +
> +	addr = addr & ~(NVM_PAGE_SIZE - 1);
> +
> +	if (addr > INF_SECTION) {
> +		/* added INF flag set in pio_cmd */
> +		addr -= INF_SECTION;
> +		pio_cmd |= INF;
> +		pio_cmd_page_erase |= INF;
> +		inf_en = 1;
> +	}
> +
> +	SET_FLASH_PIO(0);
> +	SET_FLASH_PIO(1);
> +
> +	/* first erase */
> +	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
> +
> +	SET_FLASH_PIO(0);
> +	time_out = 0;
> +
> +	while (1) {
> +		mdelay(1);
> +		hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +		if ((status) != 0)
> +			break;
> +		if (time_out++ > 100)
> +			break;
> +	}
> +	SET_FLASH_PIO(1);
> +	/* first erase end*/
> +
> +	SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
> +
> +#ifdef PGM_BURST_WR
> +	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
> +		packet_w, NVM_PAGE_SIZE);
> +#else
> +	for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
> +		SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
> +#endif
> +	if (inf_en == 0)
> +		SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
> +	else
> +		SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
> +
> +	SET_FLASH_PIO(0);
> +
> +	mdelay(1);
> +
> +	while (1) {
> +		hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +		if ((status) != 0)
> +			break;
> +	}
> +	/* write routine end */
> +
> +	SET_FLASH_HWCONTROL();
> +
> +	return ret;
> +}
> +
> +static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
> +	int len, int offset, unsigned char *old_fw)
> +{
> +	int i;
> +	int ret = 0;
> +	int len_r;
> +	int len_w;
> +	int addr = 0;
> +	unsigned int pages;
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	hideep_nvm_unlock(ts);
> +
> +	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	addr = offset;
> +	len_r = len;
> +	len_w = len_r;
> +
> +	dev_dbg(&ts->client->dev, "pages : %d", pages);
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_w = NVM_PAGE_SIZE;
> +
> +		/* compare */
> +		if (old_fw != NULL)
> +			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
> +
> +		if (ret != 0 || old_fw == NULL) {
> +			/* write page */
> +			memcpy(packet_w.payload, &(ucode[addr]), len_w);
> +
> +			ret = hideep_program_page(ts, addr, &packet_w);
> +			mdelay(1);
> +			if (ret < 0)
> +				dev_err(&ts->client->dev,
> +					"hideep_program_nvm : error(%08x):",
> +					addr);
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_w = len_r;
> +	}
> +
> +	return ret;
> +}
> +
> +static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
> +	int len, int offset)
> +{
> +	int i, j;
> +	int ret = 0;
> +	unsigned char page_chk = 0;
> +	unsigned int addr = offset;
> +	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	int len_r = len;
> +	int len_v = len_r;
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_v = NVM_PAGE_SIZE;
> +
> +#ifdef PGM_BURST_WR
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +#else
> +		for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
> +			hideep_pgm_r_reg(ts, addr + (j << 2),
> +				&(packet_r.payload[j]));
> +#endif
> +		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
> +
> +		if (page_chk != 0) {
> +			u8 *read = (u8 *)packet_r.payload;
> +
> +			for (j = 0; j < NVM_PAGE_SIZE; j++)
> +				dev_err(&ts->client->dev, "%02x : %02x",
> +						ucode[addr+j], read[j]);
> +
> +			dev_err(&ts->client->dev, "verify : error(addr : %d)",
> +				addr);
> +
> +			ret = -1;
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_v = len_r;
> +	}
> +
> +	return ret;
> +}
> +
> +static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
> +	int offset)
> +{
> +	int ret = 0;
> +	int pages, i;
> +	int len_r, len_v;
> +	int addr = offset;
> +#ifndef PGM_BURST_WR
> +	int j;
> +#endif
> +
> +	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	len_r = len;
> +	len_v = len_r;
> +
> +	ts->hideep_api->reset_ic(ts);
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	hideep_nvm_unlock(ts);
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_v = NVM_PAGE_SIZE;
> +
> +#ifdef PGM_BURST_WR
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +#else
> +		for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
> +			hideep_pgm_r_reg(ts, addr + (j << 2),
> +				&(packet_r.payload[j]));
> +#endif
> +		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_v = len_r;
> +	}
> +
> +	hideep_sw_reset(ts, 1000);
> +}
> +
> +static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
> +	size_t len, int offset)
> +{
> +	int ret = 0;
> +	int retry = 3;
> +
> +	while (retry--) {
> +		ret = hideep_verify_nvm(ts, fw, len, offset);
> +		if (ret == 0) {
> +			dev_dbg(&ts->client->dev, "update success");
> +			break;
> +		}
> +		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
> +	}
> +
> +	ret = (retry == 0) ? -1:0;
> +
> +	return ret;
> +}
> +
> +static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
> +	int len, int offset, bool mode)
> +{
> +	int ret = 0;
> +	int firm_len;
> +	unsigned char *ic_fw;
> +	unsigned char *dwz_info;
> +
> +	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
> +	dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
> +
> +	memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
> +
> +	firm_len = len;
> +	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
> +		firm_len, ts->fw_size);
> +
> +	if (firm_len > ts->fw_size)
> +		firm_len = ts->fw_size;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	/* memory dump of target IC */
> +	hideep_read_nvm(ts, ic_fw, firm_len, offset);
> +	/* comparing & programming each page, if the memory of specified
> +	 * page is exactly same, no need to update.
> +	 */
> +	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
> +
> +#ifdef PGM_VERIFY
> +	hideep_fw_verify_run(ts, code, firm_len, offset);
> +	if (ret < 0) {
> +		if (mode == true) {
> +			/* clear dwz version, it will be store once again
> +			 * after update success
> +			 */
> +			ts->dwz_info->release_ver = 0;
> +			memcpy(&dwz_info[64], ts->dwz_info,
> +				sizeof(struct dwz_info_t));
> +			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
> +				0x280, NULL);
> +		}
> +	}
> +#endif
> +	get_dwz_from_binary(code, ts->dwz_info);
> +
> +	hideep_sw_reset(ts, 1000);
> +
> +	kfree(ic_fw);
> +	kfree(dwz_info);
> +
> +	return ret;
> +}
> +
> +static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
> +{
> +	int ret = 0;
> +	const struct firmware *fw_entry;
> +	unsigned char *fw_buf;
> +	unsigned int fw_length;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
> +
> +	if (ret != 0) {
> +		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
> +		return ret;
> +	}
> +
> +	fw_buf = (unsigned char *)fw_entry->data;
> +	fw_length = (unsigned int)fw_entry->size;
> +
> +	/* chip specific code for flash fuse */
> +	mutex_lock(&ts->dev_mutex);
> +
> +	ts->dev_state = state_updating;
> +
> +	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
> +
> +	ts->dev_state = state_normal;
> +
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	release_firmware(fw_entry);
> +
> +	return ret;
> +}
> +
> +static int hideep_update_part_firmware(struct hideep_t *ts,
> +	unsigned char *code, int len, int offset, bool mode)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	ret = hideep_wr_firmware(ts, code, len, offset, false);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return ret;
> +}
> +
> +void hideep_isp_init(struct hideep_t *ts)
> +{
> +	ts->hideep_api->update_all = hideep_update_all_firmware;
> +	ts->hideep_api->update_part = hideep_update_part_firmware;
> +	ts->hideep_api->get_dwz_info = hideep_load_dwz;
> +	ts->hideep_api->sp_func = hideep_read_nvm;
> +}
> diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
> new file mode 100644
> index 0000000..648f271
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_isp.h
> @@ -0,0 +1,96 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_ISP_H
> +#define _LINUX_HIDEEP_ISP_H
> +
> +#define PGM_BURST_WR
> +#define PGM_VERIFY
> +
> +#define NVM_DEFAULT_PAGE		0
> +#define NVM_SFR_WPAGE			1
> +#define NVM_SFR_RPAGE			2
> +
> +#define PIO_SIG					0x00400000
> +#define _PROT_MODE				0x03400000
> +
> +#define NVM_PAGE_SIZE			128
> +
> +#define HIDEEP_NVM_DOWNLOAD		0x10000000
> +
> +/*************************************************************************
> + * register map
> + *************************************************************************/
> +#define YRAM_BASE				0x40000000
> +#define PERIPHERAL_BASE			0x50000000
> +#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
> +#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
> +#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
> +
> +#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
> +#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
> +#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
> +#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
> +#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
> +#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
> +#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
> +#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
> +#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
> +
> +#define FLASH_CON				(FLASH_BASE + 0x0000)
> +#define FLASH_STA				(FLASH_BASE + 0x0004)
> +#define FLASH_CFG				(FLASH_BASE + 0x0008)
> +#define FLASH_TIM				(FLASH_BASE + 0x000C)
> +#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
> +
> +#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
> +
> +/*************************************************************************
> + * flash commands
> + *************************************************************************/
> +#define MERASE					0x00010000
> +#define SERASE					0x00020000
> +#define PERASE					0x00040000
> +#define PROG					0x00080000
> +#define WRONLY					0x00100000
> +#define INF						0x00200000
> +
> +/*************************************************************************
> + * NVM Mask
> + *************************************************************************/
> +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
> +#define NVM_MASK_OFS			0x0000000C
> +#define NVM_MASK				0x00310000
> +#define INF_SECTION				0x0000C000
> +#endif
> +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
> +#define NVM_MASK_OFS			0x0000000C
> +#define NVM_MASK				0x0030027B
> +#define INF_SECTION				0x00010000
> +#endif
> +
> +/*************************************************************************
> + * DWZ info
> + *************************************************************************/
> +#define HIDEEP_BOOT_SECTION		0x00000400
> +#define HIDEEP_BOOT_LEN			0x00000400
> +#define HIDEEP_DWZ_SECTION		0x00000280
> +#define HIDEEP_DWZ_INFO_OFS		0x000002C0
> +#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
> +							- HIDEEP_DWZ_SECTION)
> +
> +struct pgm_packet {
> +	union {
> +		unsigned char b[8];
> +		unsigned int w[2];
> +	} header;
> +
> +	unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
> +};
> +
> +#endif /* _LINUX_HIDEEP_ISP_H */
> diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
> new file mode 100644
> index 0000000..7f28fb4
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_sysfs.c
> @@ -0,0 +1,245 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +
> +static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int mode, ret;
> +	char *fw_name;
> +
> +	ret = kstrtoint(buf, 8, &mode);
> +	if (ret)
> +		return ret;
> +
> +	if (mode == 1) {
> +		disable_irq(ts->client->irq);
> +
> +		ts->dev_state = state_updating;
> +		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
> +			ts->dwz_info->product_id);
> +		ret = ts->hideep_api->update_all(ts, fw_name);
> +
> +		kfree(fw_name);
> +
> +		enable_irq(ts->client->irq);
> +
> +		ts->dev_state = state_normal;
> +		if (ret != 0)
> +			dev_err(dev, "The firmware update failed(%d)", ret);
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t fw_version_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "release version : %04x",
> +		ts->dwz_info->release_ver);
> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", ts->dwz_info->release_ver);
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static ssize_t product_id_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "product id : %04x",
> +		ts->dwz_info->product_id);
> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", ts->dwz_info->product_id);
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static ssize_t power_status(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int len;
> +
> +	len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
> +		(ts->dev_state == state_init) ? "off" : "on");
> +
> +	return len;
> +}
> +
> +static ssize_t power_control(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int on, ret;
> +
> +	ret = kstrtoint(buf, 8, &on);
> +	if (ret)
> +		return ret;
> +
> +	if (on) {
> +		ts->hideep_api->power(ts, on);
> +		ts->dev_state = state_normal;
> +		ts->hideep_api->reset_ic(ts);
> +	} else {
> +		ts->hideep_api->power(ts, on);
> +		ts->dev_state = state_init;
> +	}
> +
> +	return count;
> +}
> +
> +#ifdef HIDEEP_SUPPORT_STYLUS
> +static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int len;
> +	unsigned char status[1];
> +
> +	ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
> +
> +	len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
> +		(status[0] == 0x00) ? "off" : "on");
> +
> +	return len;
> +}
> +
> +static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int on, ret;
> +	unsigned char data[2];
> +
> +	ret = kstrtoint(buf, 8, &on);
> +	if (ret)
> +		return ret;
> +
> +	data[0] = 0x04;
> +
> +	if (on) {
> +		data[1] = 0x01;
> +		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
> +	} else {
> +		data[1] = 0x00;
> +		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
> +	}
> +
> +	return count;
> +}
> +#endif
> +
> +static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int len;
> +
> +	len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
> +		(ts->debug_dev.debug_enable == 0) ? "off" : "on");
> +
> +	return len;
> +}
> +
> +static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int on, ret;
> +	unsigned char data[2];
> +
> +	ret = kstrtoint(buf, 8, &on);
> +	if (ret)
> +		return ret;
> +
> +	if (on) {
> +		ts->debug_dev.debug_enable = 1;
> +		ts->dev_state = state_debugging;
> +	} else {
> +		ts->debug_dev.debug_enable = 0;
> +		ts->dev_state = state_normal;
> +		/* set touch mode */
> +		data[0] = 0x00;
> +		data[1] = 0x00;
> +		ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
> +		if (ts->interrupt_state == 0) {
> +			data[0] = 0x5A;
> +			ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
> +				data);
> +			enable_irq(ts->client->irq);
> +			ts->interrupt_state = 1;
> +		}
> +	}
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
> +static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
> +static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
> +static DEVICE_ATTR(power_en, 0664, power_status, power_control);
> +static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);
> +#ifdef HIDEEP_SUPPORT_STYLUS
> +static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);
> +#endif
> +
> +static struct attribute *hideep_ts_sysfs_entries[] = {
> +	&dev_attr_update_fw.attr,
> +	&dev_attr_version.attr,
> +	&dev_attr_product_id.attr,
> +	&dev_attr_power_en.attr,
> +	&dev_attr_debug_en.attr,
> +#ifdef HIDEEP_SUPPORT_STYLUS
> +	&dev_attr_stylus_en.attr,
> +#endif
> +	NULL
> +};
> +
> +static struct attribute_group hideep_ts_attr_group = {
> +	.attrs = hideep_ts_sysfs_entries,
> +};
> +
> +int hideep_sysfs_init(struct hideep_t *ts)
> +{
> +	int ret;
> +	struct i2c_client *client = ts->client;
> +
> +	/* Create the files associated with this kobject */
> +	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
> +
> +	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
> +
> +	if (ret)
> +		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
> +			 __func__, ret);
> +
> +	return ret;
> +}
> +
> +int hideep_sysfs_exit(struct hideep_t *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +
> +	sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
> +
> +	return 0;
> +}
> -- 
> 2.7.4
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
  2017-07-20  0:22     ` Anthony Kim
       [not found]       ` <1500510154-6661-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-07-25  6:53       ` Anthony Kim
       [not found]         ` <1500965607-2446-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
  2017-08-22  9:03           ` Anthony Kim
  1 sibling, 2 replies; 29+ messages in thread
From: Anthony Kim @ 2017-07-25  6:53 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, rydberg
  Cc: linux-input, devicetree, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |  35 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 drivers/input/touchscreen/Kconfig                  |  11 +
 drivers/input/touchscreen/Makefile                 |   2 +
 drivers/input/touchscreen/hideep.h                 | 318 +++++++
 drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
 drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
 drivers/input/touchscreen/hideep_dbg.h             |  24 +
 drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
 drivers/input/touchscreen/hideep_isp.h             |  96 +++
 drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
 11 files changed, 2645 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.h
 create mode 100644 drivers/input/touchscreen/hideep_core.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.c
 create mode 100644 drivers/input/touchscreen/hideep_dbg.h
 create mode 100644 drivers/input/touchscreen/hideep_isp.c
 create mode 100644 drivers/input/touchscreen/hideep_isp.h
 create mode 100644 drivers/input/touchscreen/hideep_sysfs.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..5270426
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,35 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-crimson"
+					or "hideep,hideep-lime".
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- hideep,max-coords	: Max value for axis X, Y, W, Z.
+
+Example:
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-lime";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 0>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		hideep,max-coords = <1080 1920 65535 65535>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..13e11c7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..3aab466 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
+hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
new file mode 100644
index 0000000..50306a1
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_H
+#define _LINUX_HIDEEP_H
+
+/*************************************************************************
+ * this is include special HEAD file.
+ *************************************************************************/
+#ifdef CONFIG_FB
+#include <linux/fb.h>
+#include <linux/notifier.h>
+#endif
+
+/*************************************************************************
+ * this is include normal HEAD file.
+ *************************************************************************/
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/kthread.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/task_work.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/input/mt.h>
+#include <linux/fs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
+#include <linux/acpi.h>
+
+/*************************************************************************
+ * definition part.
+ * define is (open, set, enable) if not, is (close, clear, disable)
+ * some special switch of functions.
+ *************************************************************************/
+
+/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
+#define HIDEEP_PROTOCOL_2_0
+
+/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
+ * otherwise TYPE_A.
+ */
+#define HIDEEP_TYPE_B_PROTOCOL
+
+/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
+#define HIDEEP_DWZ_VERSION_CHECK
+
+/* HIDEEP_SUPPORT_KE if define, it will use key button. */
+#define HIDEEP_SUPPORT_KEY
+
+/* HIDEEP_SUPPORT_STYLUS if define, it will use stylus mode. */
+#define HIDEEP_SUPPORT_STYLUS
+
+/*************************************************************************
+ * Buffer size
+ *************************************************************************/
+#define FRAME_HEADER_SIZE				8
+/* if size of system i2c buffer is smaller than this value, need to modify */
+#define MAX_I2C_BUFFER_SIZE				512
+
+/*************************************************************************
+ * board porting config
+ *************************************************************************/
+#define HIDEEP_DEBUG_DEVICE_NAME		"hideep_debug"
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+/*************************************************************************
+ * register addr
+ *************************************************************************/
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/*************************************************************************
+ * multi-touch & key definitions.
+ *************************************************************************/
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/*************************************************************************
+ * HIDEEP PROTOCOL
+ *************************************************************************/
+#ifdef HIDEEP_PROTOCOL_2_0
+struct hideep_mt_t {
+	unsigned short x;
+	unsigned short y;
+	unsigned short z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+};
+#else
+struct hideep_mt_t {
+	unsigned char flag;
+	unsigned char index;
+	unsigned short x;
+	unsigned short y;
+	unsigned char z;
+	unsigned char w;
+};
+#endif
+
+struct hideep_key_t {
+	unsigned char flag;
+	unsigned int key;
+};
+
+/*************************************************************************
+ * IC State
+ *************************************************************************/
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+	state_debugging,
+};
+
+/*************************************************************************
+ * Firmware info
+ *************************************************************************/
+struct dwz_info_t {
+	unsigned int code_start;
+	unsigned char code_crc[12];
+
+	unsigned int c_code_start;
+	unsigned short c_code_len;
+	unsigned short gen_ver;
+
+	unsigned int vr_start;
+	unsigned short vr_len;
+	unsigned short rsv0;
+
+	unsigned int ft_start;
+	unsigned short ft_len;
+	unsigned short vr_version;
+
+	unsigned short boot_ver;
+	unsigned short core_ver;
+	unsigned short custom_ver;
+	unsigned short release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	unsigned short product_code;
+	unsigned short extra_option;
+
+	unsigned short product_id;
+	unsigned short vendor_id;
+};
+
+/*************************************************************************
+ * driver information for hideep_t by device tree
+ *************************************************************************/
+struct hideep_platform_data_t {
+	unsigned int max_x;
+	unsigned int max_y;
+	unsigned int max_z;
+	unsigned int max_w;
+
+#ifdef CONFIG_OF
+	int reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+#endif
+};
+
+struct hideep_debug_dev_t {
+	struct miscdevice misc;
+
+	wait_queue_head_t i_packet;
+
+	unsigned int ready;
+	unsigned char *vr_buff;
+	unsigned char *img_buff;
+	unsigned short vr_size;
+	unsigned short img_size;
+
+	bool debug_enable;
+	bool release_flag;
+
+	struct hideep_t *ts;
+};
+
+struct hideep_function_list_t {
+	//core
+	int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
+		unsigned char *);
+	void (*reset_ic)(struct hideep_t *);
+	void (*power)(struct hideep_t *, int);
+
+	//isp
+	int (*update_all)(struct hideep_t *, const char *);
+	int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
+	int (*get_dwz_info)(struct hideep_t *);
+	void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
+};
+
+struct hideep_t {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct hideep_platform_data_t *p_data;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	struct hideep_debug_dev_t debug_dev;
+	struct hideep_function_list_t *hideep_api;
+
+	struct task_struct *hthread_event;
+
+	bool suspended;
+	enum e_dev_state dev_state;
+
+#ifdef CONFIG_FB
+	struct notifier_block fb_notif;
+#endif
+
+	long tch_bit;
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
+
+#ifdef HIDEEP_SUPPORT_KEY
+	struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
+	int key_codes[HIDEEP_KEY_MAX];
+#endif
+
+	unsigned char i2c_buf[256];
+	struct dwz_info_t *dwz_info;
+
+	int interrupt_state;
+	int fw_size;
+};
+
+/*************************************************************************
+ * function define
+ *************************************************************************/
+int hideep_debug_init(struct hideep_t *ts);
+void hideep_debug_uninit(void);
+
+int hideep_sysfs_init(struct hideep_t *ts);
+int hideep_sysfs_exit(struct hideep_t *ts);
+
+void hideep_isp_init(struct hideep_t *ts);
+
+#endif
diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
new file mode 100644
index 0000000..d1b2c44
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.c
@@ -0,0 +1,916 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
+	((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
+
+static int hideep_pwr_on(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (!IS_ERR(pdata->vcc_vdd)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+	}
+	usleep_range(999, 1000);
+
+	if (!IS_ERR(pdata->vcc_vid)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+	usleep_range(2999, 3000);
+#endif
+
+	return ret;
+}
+
+static int hideep_pwr_off(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret = 0;
+
+	pdata = ts->p_data;
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
+		gpio_set_value(pdata->reset_gpio, 0);
+	}
+
+	if (!IS_ERR(pdata->vcc_vid)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
+		ret = regulator_disable(pdata->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+
+	if (!IS_ERR(pdata->vcc_vdd)) {
+		dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
+		ret = regulator_disable(pdata->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd disable failed ret=%d", ret);
+	}
+#endif
+	return ret;
+}
+
+static void hideep_power(struct hideep_t *ts, int on)
+{
+	int ret = 0;
+
+	if (on) {
+		dev_info(&ts->client->dev, "power on");
+		ret = hideep_pwr_on(ts);
+	} else {
+		dev_info(&ts->client->dev, "power off");
+		ret = hideep_pwr_off(ts);
+	}
+}
+
+static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned short r_len, raddr;
+	int length;
+
+	length = len;
+	raddr = addr;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	do {
+		if (length > MAX_I2C_BUFFER_SIZE)
+			r_len = MAX_I2C_BUFFER_SIZE;
+		else
+			r_len = length;
+
+		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
+			raddr, r_len);
+
+		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
+
+		if (ret < 0)
+			goto i2c_err;
+
+		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
+		length -= MAX_I2C_BUFFER_SIZE;
+		buf += MAX_I2C_BUFFER_SIZE;
+		raddr += MAX_I2C_BUFFER_SIZE;
+
+		if (ret < 0)
+			goto i2c_err;
+	} while (length > 0);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	return  0;
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	// data mangling..
+	ts->i2c_buf[0] = (addr >> 0) & 0xFF;
+	ts->i2c_buf[1] = (addr >> 8) & 0xFF;
+	memcpy(&ts->i2c_buf[2], buf, len);
+
+	ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
+
+	if (ret < 0)
+		goto i2c_err;
+
+	mutex_unlock(&ts->i2c_mutex);
+	return  0;
+
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void pops_mt(struct hideep_t *ts)
+{
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	int id;
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+#else
+	input_report_key(ts->input_dev, BTN_TOUCH, false);
+	input_mt_sync(ts->input_dev);
+#endif
+
+	input_sync(ts->input_dev);
+}
+
+static void push_mt(struct hideep_t *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	bool btn_dn = 0;
+	bool btn_mv = 0;
+	int evt = 0;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		id = (ts->touch_evt[i].index >> 0) & 0x0F;
+		btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
+		btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
+			& 0x01;
+		btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
+
+		if (btn_up)
+			clear_bit(id, &ts->tch_bit);
+		else
+			__set_bit(id, &ts->tch_bit);
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			ts->touch_evt[i].type, ts->touch_evt[i].index,
+			i, ts->touch_evt[i].x, ts->touch_evt[i].y,
+			ts->touch_evt[i].z);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			evt++;
+		}
+#else
+		if (btn_up) {
+			input_mt_sync(ts->input_dev);
+		} else {
+			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				ts->touch_evt[i].x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				ts->touch_evt[i].y);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				ts->touch_evt[i].z);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				ts->touch_evt[i].w);
+			input_mt_sync(ts->input_dev);
+			evt++;
+		}
+#endif
+	}
+
+	if (ts->tch_bit == 0)
+		evt = 0;
+
+	input_report_key(ts->input_dev, BTN_TOUCH, evt);
+	input_mt_sync_frame(ts->input_dev);
+	input_sync(ts->input_dev);
+}
+
+#ifdef HIDEEP_SUPPORT_KEY
+static void pops_ky(struct hideep_t *ts)
+{
+	int i;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		input_report_key(ts->input_dev, ts->key_codes[i], false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+
+	input_sync(ts->input_dev);
+}
+
+static void push_ky(struct hideep_t *ts)
+{
+	int i;
+	int pressed;
+	int key_code;
+	int key_status;
+
+	for (i = 0; i < ts->key_count; i++) {
+		key_code = ts->key_evt[i].flag & 0x0F;
+		key_status = ts->key_evt[i].flag & 0xF0;
+		pressed = false;
+
+		if (key_status & HIDEEP_KEY_PRESSED_MASK)
+			pressed = true;
+		else
+			pressed = false;
+
+		input_report_key(ts->input_dev, ts->key_codes[key_code],
+			pressed);
+		input_report_key(ts->input_dev, BTN_TOUCH, pressed);
+	}
+
+	input_sync(ts->input_dev);
+}
+#endif
+
+static void hideep_put_event(struct hideep_t *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0)
+		push_ky(ts);
+#endif
+}
+
+static int hideep_get_event(struct hideep_t *ts)
+{
+	int ret;
+	int touch_count;
+	int info_size;
+
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		info_size = ts->tch_count * sizeof(struct hideep_mt_t);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			info_size, (unsigned char *)ts->touch_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+#ifdef HIDEEP_SUPPORT_KEY
+	if (ts->key_count > 0) {
+		info_size = ts->key_count * sizeof(struct hideep_key_t);
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			info_size, (unsigned char *)ts->key_evt);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+#endif
+
+	return touch_count;
+}
+
+static int hideep_event_thread(void *arg)
+{
+	int t_evt;
+
+	struct hideep_t *ts = arg;
+
+	dev_info(&ts->client->dev, "start event thread");
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		schedule();
+
+		t_evt = hideep_get_event(ts);
+
+		if (t_evt >= 0)
+			hideep_put_event(ts);
+
+		if (kthread_should_stop())
+			break;
+	}
+
+	dev_info(&ts->client->dev, "end thread");
+	return 0;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char i2c_buff[2];
+	int ret;
+
+	struct hideep_t *ts = (struct hideep_t *) handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
+		ts->dev_state, ts->debug_dev.debug_enable);
+
+	if (ts->debug_dev.debug_enable == false &&
+		ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, (u8 *)&i2c_buff);
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			ts->interrupt_state = 0;
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = i2c_buff[0];
+		ts->key_count = i2c_buff[1] & 0x0f;
+		ts->lpm_count = i2c_buff[1] & 0xf0;
+
+		wake_up_process(ts->hthread_event);
+
+	} else if (ts->debug_dev.debug_enable == true
+		&& ts->dev_state == state_debugging) {
+		ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
+			FRAME_HEADER_SIZE + ts->debug_dev.img_size,
+			ts->debug_dev.img_buff);
+		ts->debug_dev.img_size =
+			GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
+		ts->debug_dev.ready = 1;
+		wake_up_interruptible(&ts->debug_dev.i_packet);
+	}
+	dev_dbg(&ts->client->dev, "end.");
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_t *ts)
+{
+#ifdef HIDEEP_SUPPORT_KEY
+	int i;
+
+	ts->key_codes[0] = KEY_MENU;
+	ts->key_codes[1] = KEY_HOME;
+	ts->key_codes[2] = KEY_BACK;
+
+	for (i = 0; i < HIDEEP_KEY_MAX; i++)
+		set_bit(ts->key_codes[i], ts->input_dev->keybit);
+#endif
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	set_bit(EV_ABS, ts->input_dev->evbit);
+	set_bit(EV_KEY, ts->input_dev->evbit);
+	set_bit(EV_SYN, ts->input_dev->evbit);
+	set_bit(BTN_TOUCH, ts->input_dev->keybit);
+	set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
+	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
+		ts->p_data->max_z, 0, 0);
+
+#ifdef HIDEEP_TYPE_B_PROTOCOL
+	input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+#else
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TRACKING_ID, 0, 10, 0, 0);
+#endif
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
+
+	return 0;
+}
+
+static void hideep_reset_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	int ret;
+	unsigned char cmd = 0x01;
+
+	pdata = ts->p_data;
+
+	dev_info(&ts->client->dev, "start!!");
+
+#ifdef CONFIG_OF
+	if (pdata->reset_gpio > 0) {
+		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpio_set_value(pdata->reset_gpio, 0);
+		mdelay(20);
+		gpio_set_value(pdata->reset_gpio, 1);
+	} else
+#endif
+	{
+		ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+	dev_info(&ts->client->dev, "end!!");
+}
+
+static void hideep_get_info(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	unsigned char val[4];
+
+	pdata = ts->p_data;
+	ts->hideep_api->i2c_read(ts, 0x28, 4, val);
+
+	pdata->max_x = val[3] << 8 | val[2];
+	pdata->max_y = val[1] << 8 | val[0];
+	pdata->max_w = 255;
+	pdata->max_z = 255;
+
+	dev_info(&ts->client->dev, "X : %d, Y : %d",
+		pdata->max_x, pdata->max_y);
+}
+
+static void hideep_init_ic(struct hideep_t *ts)
+{
+	struct hideep_platform_data_t *pdata;
+	struct device dev;
+
+	pdata = ts->p_data;
+	dev = ts->client->dev;
+
+	/* power on */
+	ts->hideep_api->power(ts, true);
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	ts->hideep_api->reset_ic(ts);
+}
+
+static int hideep_resume(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state == state_normal)
+		goto hideep_resume_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+hideep_resume_exit:
+	mdelay(10);
+	ts->hideep_api->reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+static int hideep_suspend(struct device *dev)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	dev_info(dev, "enter");
+
+	mutex_lock(&ts->dev_mutex);
+	if (ts->dev_state == state_sleep)
+		goto hideep_suspend_exit;
+
+	dev_info(dev, "not waiting.");
+	ts->dev_state = state_sleep;
+
+	/* send sleep command.. */
+	pops_mt(ts);
+#ifdef HIDEEP_SUPPORT_KEY
+	pops_ky(ts);
+#endif
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+hideep_suspend_exit:
+	mutex_unlock(&ts->dev_mutex);
+	dev_info(dev, "exit.");
+	return 0;
+}
+
+#ifdef CONFIG_FB
+static int fb_notifier_callback(struct notifier_block *self,
+	unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+
+	struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
+
+	if ((evdata) && (evdata->data) &&
+		(event == FB_EVENT_BLANK) &&
+		(ts) && (ts->client)) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			dev_info(&ts->client->dev, "resume");
+			if (ts->suspended == 1) {
+				hideep_resume(&ts->client->dev);
+				ts->suspended = 0;
+			}
+		} else if (*blank == FB_BLANK_POWERDOWN) {
+			dev_info(&ts->client->dev, "suspend");
+			if (ts->suspended == 0) {
+				ts->suspended = 1;
+				hideep_suspend(&ts->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_OF
+static int hideep_parse_dts(struct device *dev,
+	struct hideep_platform_data_t *pdata)
+{
+	int ret = 0;
+	unsigned int coords[4];
+	struct device_node *np;
+
+	dev_info(dev, "enter");
+	np = dev->of_node;
+
+	ret = of_property_read_u32_array(np, "hideep,max-coords", coords, 4);
+	if (ret) {
+		dev_err(dev, "Failed to get max-coords property\n");
+		return ret;
+	}
+
+	pdata->max_x = coords[0];
+	pdata->max_y = coords[1];
+	pdata->max_w = coords[2];
+	pdata->max_z = coords[3];
+
+	dev_info(dev, "max coord data x : %d\n", pdata->max_x);
+	dev_info(dev, "max coord data y : %d\n", pdata->max_y);
+	dev_info(dev, "max coord data w : %d\n", pdata->max_w);
+	dev_info(dev, "max coord data z : %d\n", pdata->max_z);
+
+	/* device tree information get */
+	pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+	dev_info(dev, "reset_gpio = %d, is %s specified",
+		pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
+	if (pdata->reset_gpio)
+		ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
+			"reset-gpios");
+
+	pdata->vcc_vdd = regulator_get(dev, "vdd");
+	pdata->vcc_vid = regulator_get(dev, "vid");
+
+	return ret;
+}
+#endif
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_platform_data_t *p_data;
+	struct dwz_info_t *dwz;
+	struct hideep_function_list_t *function;
+	struct hideep_t *ts;
+
+	dev_info(&client->dev, "enter");
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		ret = -ENODEV;
+		return ret;
+	}
+
+	/* init platform data */
+	p_data = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_platform_data_t), GFP_KERNEL);
+
+#ifdef CONFIG_OF
+	if (client->dev.of_node) {
+		ret = hideep_parse_dts(&client->dev, p_data);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	/* init hideep_t */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_t), GFP_KERNEL);
+	dwz = devm_kzalloc(&client->dev,
+		sizeof(struct dwz_info_t), GFP_KERNEL);
+	function = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_function_list_t), GFP_KERNEL);
+
+	ts->client = client;
+	ts->p_data = p_data;
+	ts->dwz_info = dwz;
+	ts->hideep_api = function;
+
+	ts->hideep_api->i2c_read = hideep_i2c_read;
+	ts->hideep_api->i2c_write = hideep_i2c_write;
+	ts->hideep_api->reset_ic = hideep_reset_ic;
+	ts->hideep_api->power = hideep_power;
+
+	/* init for isp function */
+	hideep_isp_init(ts);
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	if (!client->dev.of_node)
+		hideep_get_info(ts);
+
+	/* hardware init */
+	hideep_init_ic(ts);
+
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+	/* read info */
+	ret = ts->hideep_api->get_dwz_info(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		goto hideep_probe_read_dwz_err;
+	}
+#endif
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_input_dev_memory_err;
+	}
+
+	hideep_capability(ts);
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		ret = -ENOMEM;
+		goto hideep_probe_register_input_dev_err;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	/* init event thread */
+	ts->hthread_event = kthread_run(hideep_event_thread,
+		ts, "hideep_event_thread");
+	if (IS_ERR(ts->hthread_event)) {
+		dev_err(&client->dev, "can't create event thread !!!");
+		ret = PTR_ERR(ts->hthread_event);
+		goto hideep_probe_create_thread_err;
+	}
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (ts->client->irq <= 0) {
+		dev_err(&client->dev, "can't be assigned irq");
+		goto hideep_probe_assigned_irq_err;
+	}
+
+	if (ts->client->irq) {
+		ret = request_threaded_irq(ts->client->irq, NULL,
+			hideep_irq_task,
+			(IRQF_TRIGGER_LOW | IRQF_ONESHOT),
+			ts->client->name, ts);
+		disable_irq(ts->client->irq);
+		ts->interrupt_state = 0;
+		if (ret < 0) {
+			dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+				ret);
+			goto hideep_probe_request_irq_err;
+		}
+	}
+
+	ret = hideep_debug_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_debug_init_err;
+	}
+
+	ret = hideep_sysfs_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		ret = -1;
+		goto hideep_probe_sysfs_init_err;
+	}
+
+#ifdef CONFIG_FB
+	ts->suspended = 0;
+	ts->fb_notif.notifier_call = fb_notifier_callback;
+	ret = fb_register_client(&ts->fb_notif);
+	if (ret) {
+		dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
+			ret);
+		ret = -1;
+		goto hideep_probe_register_fb_err;
+	}
+#endif
+
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+	dev_info(&client->dev, "probe is ok!");
+	return 0;
+
+hideep_probe_register_fb_err:
+	hideep_sysfs_exit(ts);
+
+hideep_probe_sysfs_init_err:
+	hideep_debug_uninit();
+
+hideep_probe_debug_init_err:
+hideep_probe_request_irq_err:
+	free_irq(ts->client->irq, ts);
+
+hideep_probe_assigned_irq_err:
+hideep_probe_create_thread_err:
+	input_unregister_device(ts->input_dev);
+
+hideep_probe_register_input_dev_err:
+	if (ts->input_dev)
+		input_free_device(ts->input_dev);
+
+hideep_probe_input_dev_memory_err:
+#ifdef HIDEEP_DWZ_VERSION_CHECK
+hideep_probe_read_dwz_err:
+#endif
+
+#ifdef CONFIG_OF
+	ts->hideep_api->power(ts, false);
+#endif
+	dev_err(&client->dev, "probe err!");
+	return ret;
+}
+
+static int hideep_remove(struct i2c_client *client)
+{
+	struct hideep_t *ts = i2c_get_clientdata(client);
+
+	kthread_stop(ts->hthread_event);
+
+#ifdef CONFIG_FB
+	if (fb_unregister_client(&ts->fb_notif))
+		dev_info(&client->dev,
+			"Error occurred while unregistering fb_notifier");
+#endif
+
+#ifdef CONFIG_OF
+	ts->hideep_api->power(ts, false);
+#endif
+	free_irq(client->irq, ts);
+
+	input_unregister_device(ts->input_dev);
+	hideep_sysfs_exit(ts);
+
+	hideep_debug_uninit();
+	devm_kfree(&client->dev, ts);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops hideep_pm_ops = {
+	.suspend = hideep_suspend,
+	.resume = hideep_resume,
+};
+#endif
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-lime" },
+	{ .compatible = "hideep,hideep-crimson" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.remove = hideep_remove,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
new file mode 100644
index 0000000..549f2ad2
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+#include "hideep_dbg.h"
+
+static struct hideep_debug_dev_t *hdd;
+
+static int hideep_i2c_recv(unsigned int len)
+{
+	int ret = 0;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_i2c_send(unsigned int len)
+{
+	int ret = 0;
+	unsigned char *buff = hdd->vr_buff;
+
+	mutex_lock(&hdd->ts->i2c_mutex);
+	ret = i2c_master_send(hdd->ts->client, buff, len);
+	mutex_unlock(&hdd->ts->i2c_mutex);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_info(&hdd->ts->client->dev, "(%d)", len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_get_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+
+	ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
+		hdd->vr_buff);
+	if (ret < 0)
+		goto i2c_err;
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_set_vreg(unsigned int addr, unsigned int len)
+{
+	int ret = 0;
+	int wr_remain = len;
+	int vr_addr = addr;
+	int wr_len = len;
+	unsigned char *buff = hdd->vr_buff;
+
+	do {
+		if (wr_remain >=  MAX_VR_BUFF)
+			wr_len = MAX_VR_BUFF;
+		else
+			wr_len = wr_remain;
+
+		ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
+			wr_len, buff);
+		if (ret < 0)
+			goto i2c_err;
+
+		wr_remain -= MAX_VR_BUFF;
+		vr_addr += MAX_VR_BUFF;
+		buff += MAX_VR_BUFF;
+	} while (wr_remain > 0);
+
+	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
+	return ret;
+
+i2c_err:
+	dev_err(&hdd->ts->client->dev, "i2c_err");
+	return ret;
+}
+
+static int hideep_download_uc(const char __user *uc, size_t count, int offset)
+{
+	int ret;
+	unsigned char *ucode;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (count > hdd->ts->fw_size) {
+		dev_err(&hdd->ts->client->dev, "over size data!!!");
+		return -1;
+	}
+
+	ucode = kmalloc(count, GFP_KERNEL);
+
+	ret = copy_from_user(ucode + offset, uc, count);
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
+		kfree(ucode);
+		return 0;
+	}
+
+	disable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 0;
+	hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
+	enable_irq(hdd->ts->client->irq);
+	hdd->ts->interrupt_state = 1;
+	kfree(ucode);
+
+	dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
+
+	return count;
+}
+
+static int hideep_debug_open(struct inode *inode, struct file *file)
+{
+	hdd->release_flag = false;
+
+	file->private_data = hdd;
+	dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
+
+	return 0;
+}
+
+static int hideep_debug_release(struct inode *inode, struct file *file)
+{
+	if (!hdd->release_flag)
+		return -1;
+	hdd->release_flag = false;
+	file->private_data = NULL;
+	return 0;
+}
+
+static unsigned int hideep_debug_poll(struct file *file,
+	struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	struct hideep_debug_dev_t *drv_info;
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info = file->private_data;
+
+	poll_wait(file, &drv_info->i_packet, wait);
+
+	if (drv_info->ready) {
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mask |= POLLIN | POLLRDNORM;
+		drv_info->ready = 0;
+	}
+
+	return mask;
+}
+
+static ssize_t hideep_debug_read(struct file *file, char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret = -1;
+	ssize_t rd_len = 0;
+	unsigned char *rd_buffer = NULL;
+	unsigned char *data = NULL;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+
+	if (file->private_data == NULL)
+		return 0;
+
+	drv_info->vr_size = count;
+	rd_len = count;
+
+	data = kmalloc(rd_len, GFP_KERNEL);
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		if ((*offset & HIDEEP_MAX_RAW_LEN) &&
+			(*offset < HIDEEP_MAX_RAW_LEN) &&
+			(drv_info->debug_enable == true)) {
+			mutex_lock(&drv_info->ts->dev_mutex);
+			rd_buffer = drv_info->img_buff;
+			ret = 0;
+			mutex_unlock(&drv_info->ts->dev_mutex);
+		} else {
+			ret = hideep_get_vreg(*offset, rd_len);
+			rd_buffer = drv_info->vr_buff;
+		}
+		if (ret < 0)
+			rd_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		ret = hideep_i2c_recv(rd_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "ret = %d", ret);
+			rd_len = 0;
+		} else {
+			rd_buffer = drv_info->vr_buff;
+		}
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		rd_len = count;
+		memset(data, 0x0, rd_len);
+
+		disable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 0;
+		mutex_lock(&drv_info->ts->dev_mutex);
+		mutex_lock(&drv_info->ts->i2c_mutex);
+
+		drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
+			*offset & 0xfffff);
+
+		mutex_unlock(&drv_info->ts->dev_mutex);
+		mutex_unlock(&drv_info->ts->i2c_mutex);
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+
+		rd_buffer = data;
+	} else {
+		dev_err(&hdd->ts->client->dev, "undefined address");
+		kfree(data);
+		return 0;
+	}
+
+	ret = copy_to_user(buf, rd_buffer, rd_len);
+
+	if (drv_info->debug_enable == true && drv_info->ready == 0) {
+		enable_irq(drv_info->ts->client->irq);
+		drv_info->ts->interrupt_state = 1;
+	}
+
+	if (ret < 0) {
+		dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+		kfree(data);
+		return -EFAULT;
+	}
+
+	kfree(data);
+	return rd_len;
+}
+
+static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *offset)
+{
+	int ret;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+	int wr_len = 0;
+
+	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
+	if (file->private_data == NULL)
+		return 0;
+
+	if (*offset <= HIDEEP_VR_ADDR_LEN) {
+		// if offset is not belong to any special command
+		wr_len = count;
+
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		if (ret < 0) {
+			dev_err(&hdd->ts->client->dev, "error : copy_to_user");
+			return -EFAULT;
+		}
+
+		ret = hideep_set_vreg(*offset, wr_len);
+		if (ret < 0)
+			wr_len = 0;
+	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
+		// if offset is belong to special command "i2c bypass"
+		wr_len = count;
+		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
+		ret = hideep_i2c_send(wr_len);
+	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
+		// if offset is belong to special command "nvm download"
+		wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
+	} else {
+		dev_err(&hdd->ts->client->dev,
+			"hideep_write : undefined address, 0x%08x",
+			(int)*offset);
+		return 0;
+	}
+
+	return wr_len;
+}
+
+static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
+{
+	loff_t newpos;
+	struct hideep_debug_dev_t *drv_info = file->private_data;
+
+	dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
+		(unsigned int)off, whence);
+	if (file->private_data == NULL)
+		return -EFAULT;
+
+	switch (whence) {
+	/* SEEK_SET */
+	case 0:
+		newpos = off;
+		break;
+	/* SEEK_CUR */
+	case 1:
+		dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
+			(unsigned int)off);
+		if (off & HIDEEP_RELEASE_FLAG) {
+			dev_dbg(&hdd->ts->client->dev, "set release flag");
+			drv_info->release_flag = true;
+		}
+		newpos = file->f_pos;
+		break;
+	/* SEEK_END */
+	case 2:
+		newpos = file->f_pos;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (newpos < 0)
+		return -EINVAL;
+
+	file->f_pos = newpos;
+
+	return newpos;
+}
+
+static const struct file_operations hideep_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = hideep_debug_open,
+	.poll = hideep_debug_poll,
+	.release = hideep_debug_release,
+	.read = hideep_debug_read,
+	.write = hideep_debug_write,
+	.llseek = hideep_debug_llseek,
+};
+
+static struct miscdevice hideep_debug_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = HIDEEP_DEBUG_DEVICE_NAME,
+	.fops = &hideep_debug_fops
+};
+
+void hideep_debug_uninit(void)
+{
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+
+	misc_deregister(&hideep_debug_dev);
+}
+
+int hideep_debug_init(struct hideep_t *ts)
+{
+	int ret = 0;
+
+	hdd = &ts->debug_dev;
+
+	ret = misc_register(&hideep_debug_dev);
+	if (ret) {
+		dev_err(&ts->client->dev,
+			"hideep debug device register fail!!!");
+		goto fail;
+	}
+
+	init_waitqueue_head(&hdd->i_packet);
+
+	hdd->ts = ts;
+	hdd->debug_enable = false;
+
+	hdd->img_size = 0;
+	hdd->vr_size = 0;
+
+	hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
+	hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
+
+	memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
+	memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
+
+	if (!hdd->vr_buff || !hdd->img_buff)
+		goto fail;
+
+	dev_info(&ts->client->dev, "debug init....");
+	return 0;
+
+fail:
+	kfree(hdd->vr_buff);
+	kfree(hdd->img_buff);
+	return ret;
+}
diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
new file mode 100644
index 0000000..f18a09f
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_dbg.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_DBG_H
+#define _LINUX_HIDEEP_DBG_H
+
+/* Device Driver <==> Application */
+/* lseek(), write(), read() command */
+#define HIDEEP_RELEASE_FLAG				0x8000
+#define HIDEEP_VR_ADDR_LEN				0xFFFF
+#define HIDEEP_MAX_RAW_LEN				0x3000
+#define HIDEEP_READ_WRITE_VR			0xF000
+#define HIDEEP_I2C_BYPASS				0x20000000
+
+#define MAX_VR_BUFF						2048
+
+/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
+#define MAX_RAW_SIZE					7369
+#endif /* _LINUX_HIDEEP_DBG_H */
diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
new file mode 100644
index 0000000..c8d2e932
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+#include "hideep_isp.h"
+
+static struct pgm_packet packet_w;
+static struct pgm_packet packet_r;
+
+static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x80 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		(len+5));
+
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	packet->header.w[0] = htonl((0x00 | (len / 4-1)));
+	packet->header.w[1] = htonl(addr);
+
+	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
+		5);
+
+	if (ret < 0)
+		goto err;
+
+	ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
+		len);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
+		packet->payload[i] = htonl(packet->payload[i]);
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x00);
+	packet.header.w[1] = htonl(addr);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	packet.header.w[0] = htonl(0x80);
+	packet.header.w[1] = htonl(addr);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void get_dwz_from_binary(unsigned char *pres,
+	struct dwz_info_t *dwz_info)
+{
+	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
+		sizeof(struct dwz_info_t));
+}
+
+static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
+{
+	SW_RESET_IN_PGM(food);
+}
+
+static int hideep_enter_pgm(struct hideep_t *ts)
+{
+	int ret = 0;
+	int retry_count = 10;
+	int retry = 0;
+	unsigned int status;
+	unsigned int pattern = 0xDF9DAF39;
+
+	while (retry < retry_count) {
+		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
+		mdelay(1);
+
+		/* flush invalid Tx load register */
+		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+		if (status != htonl(pattern)) {
+			retry++;
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				status);
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry < retry_count) {
+		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
+
+		mdelay(1);
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_t *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	mdelay(50);
+
+	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
+		sizeof(struct dwz_info_t));
+
+	memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info_t));
+	hideep_sw_reset(ts, 10);
+
+	if (ts->dwz_info->product_code & 0x40) {
+		/* Crimson IC */
+		ts->fw_size = 1024 * 48;
+	} else {
+		/* default fw size */
+		ts->fw_size = 1024 * 64;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mdelay(50);
+
+	return ret;
+}
+
+static int hideep_nvm_unlock(struct hideep_t *ts)
+{
+	int ret = 0;
+	unsigned int unmask_code = 0;
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != NVM_MASK)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
+	SET_FLASH_HWCONTROL();
+	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	return ret;
+}
+
+static int hideep_program_page(struct hideep_t *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret = 0;
+	unsigned int pio_cmd = WRONLY;
+	unsigned int pio_cmd_page_erase = PERASE;
+	unsigned int status;
+	int time_out = 0;
+	int inf_en = 0;
+	unsigned int end_flag = 124;
+#ifndef PGM_BURST_WR
+	unsigned int i;
+#endif
+
+	hideep_pgm_r_reg(ts, FLASH_STA, &status);
+	ret = (status == 0) ? -1:0;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	if (addr > INF_SECTION) {
+		/* added INF flag set in pio_cmd */
+		addr -= INF_SECTION;
+		pio_cmd |= INF;
+		pio_cmd_page_erase |= INF;
+		inf_en = 1;
+	}
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* first erase */
+	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+	time_out = 0;
+
+	while (1) {
+		mdelay(1);
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+		if (time_out++ > 100)
+			break;
+	}
+	SET_FLASH_PIO(1);
+	/* first erase end*/
+
+	SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
+
+#ifdef PGM_BURST_WR
+	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
+		packet_w, NVM_PAGE_SIZE);
+#else
+	for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
+		SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
+#endif
+	if (inf_en == 0)
+		SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
+	else
+		SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	while (1) {
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+	}
+	/* write routine end */
+
+	SET_FLASH_HWCONTROL();
+
+	return ret;
+}
+
+static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset, unsigned char *old_fw)
+{
+	int i;
+	int ret = 0;
+	int len_r;
+	int len_w;
+	int addr = 0;
+	unsigned int pages;
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	addr = offset;
+	len_r = len;
+	len_w = len_r;
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_w = NVM_PAGE_SIZE;
+
+		/* compare */
+		if (old_fw != NULL)
+			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
+
+		if (ret != 0 || old_fw == NULL) {
+			/* write page */
+			memcpy(packet_w.payload, &(ucode[addr]), len_w);
+
+			ret = hideep_program_page(ts, addr, &packet_w);
+			mdelay(1);
+			if (ret < 0)
+				dev_err(&ts->client->dev,
+					"hideep_program_nvm : error(%08x):",
+					addr);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_w = len_r;
+	}
+
+	return ret;
+}
+
+static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
+	int len, int offset)
+{
+	int i, j;
+	int ret = 0;
+	unsigned char page_chk = 0;
+	unsigned int addr = offset;
+	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	int len_r = len;
+	int len_v = len_r;
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
+
+		if (page_chk != 0) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++)
+				dev_err(&ts->client->dev, "%02x : %02x",
+						ucode[addr+j], read[j]);
+
+			dev_err(&ts->client->dev, "verify : error(addr : %d)",
+				addr);
+
+			ret = -1;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	return ret;
+}
+
+static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
+	int offset)
+{
+	int ret = 0;
+	int pages, i;
+	int len_r, len_v;
+	int addr = offset;
+#ifndef PGM_BURST_WR
+	int j;
+#endif
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	len_r = len;
+	len_v = len_r;
+
+	ts->hideep_api->reset_ic(ts);
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+#ifdef PGM_BURST_WR
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+#else
+		for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
+			hideep_pgm_r_reg(ts, addr + (j << 2),
+				&(packet_r.payload[j]));
+#endif
+		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	hideep_sw_reset(ts, 1000);
+}
+
+static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
+	size_t len, int offset)
+{
+	int ret = 0;
+	int retry = 3;
+
+	while (retry--) {
+		ret = hideep_verify_nvm(ts, fw, len, offset);
+		if (ret == 0) {
+			dev_dbg(&ts->client->dev, "update success");
+			break;
+		}
+		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
+	}
+
+	ret = (retry == 0) ? -1:0;
+
+	return ret;
+}
+
+static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
+	int len, int offset, bool mode)
+{
+	int ret = 0;
+	int firm_len;
+	unsigned char *ic_fw;
+	unsigned char *dwz_info;
+
+	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
+	dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
+
+	memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
+
+	firm_len = len;
+	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
+		firm_len, ts->fw_size);
+
+	if (firm_len > ts->fw_size)
+		firm_len = ts->fw_size;
+
+	dev_dbg(&ts->client->dev, "enter");
+	/* memory dump of target IC */
+	hideep_read_nvm(ts, ic_fw, firm_len, offset);
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
+
+#ifdef PGM_VERIFY
+	hideep_fw_verify_run(ts, code, firm_len, offset);
+	if (ret < 0) {
+		if (mode == true) {
+			/* clear dwz version, it will be store once again
+			 * after update success
+			 */
+			ts->dwz_info->release_ver = 0;
+			memcpy(&dwz_info[64], ts->dwz_info,
+				sizeof(struct dwz_info_t));
+			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
+				0x280, NULL);
+		}
+	}
+#endif
+	get_dwz_from_binary(code, ts->dwz_info);
+
+	hideep_sw_reset(ts, 1000);
+
+	kfree(ic_fw);
+	kfree(dwz_info);
+
+	return ret;
+}
+
+static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
+{
+	int ret = 0;
+	const struct firmware *fw_entry;
+	unsigned char *fw_buf;
+	unsigned int fw_length;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	fw_buf = (unsigned char *)fw_entry->data;
+	fw_length = (unsigned int)fw_entry->size;
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_update_part_firmware(struct hideep_t *ts,
+	unsigned char *code, int len, int offset, bool mode)
+{
+	int ret = 0;
+
+	mutex_lock(&ts->dev_mutex);
+
+	ret = hideep_wr_firmware(ts, code, len, offset, false);
+
+	mutex_unlock(&ts->dev_mutex);
+
+	return ret;
+}
+
+void hideep_isp_init(struct hideep_t *ts)
+{
+	ts->hideep_api->update_all = hideep_update_all_firmware;
+	ts->hideep_api->update_part = hideep_update_part_firmware;
+	ts->hideep_api->get_dwz_info = hideep_load_dwz;
+	ts->hideep_api->sp_func = hideep_read_nvm;
+}
diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
new file mode 100644
index 0000000..648f271
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_isp.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_ISP_H
+#define _LINUX_HIDEEP_ISP_H
+
+#define PGM_BURST_WR
+#define PGM_VERIFY
+
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_NVM_DOWNLOAD		0x10000000
+
+/*************************************************************************
+ * register map
+ *************************************************************************/
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+/*************************************************************************
+ * flash commands
+ *************************************************************************/
+#define MERASE					0x00010000
+#define SERASE					0x00020000
+#define PERASE					0x00040000
+#define PROG					0x00080000
+#define WRONLY					0x00100000
+#define INF						0x00200000
+
+/*************************************************************************
+ * NVM Mask
+ *************************************************************************/
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x00310000
+#define INF_SECTION				0x0000C000
+#endif
+#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_MASK				0x0030027B
+#define INF_SECTION				0x00010000
+#endif
+
+/*************************************************************************
+ * DWZ info
+ *************************************************************************/
+#define HIDEEP_BOOT_SECTION		0x00000400
+#define HIDEEP_BOOT_LEN			0x00000400
+#define HIDEEP_DWZ_SECTION		0x00000280
+#define HIDEEP_DWZ_INFO_OFS		0x000002C0
+#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
+							- HIDEEP_DWZ_SECTION)
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		unsigned int w[2];
+	} header;
+
+	unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+#endif /* _LINUX_HIDEEP_ISP_H */
diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
new file mode 100644
index 0000000..7f28fb4
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_sysfs.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include "hideep.h"
+
+static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	if (mode == 1) {
+		disable_irq(ts->client->irq);
+
+		ts->dev_state = state_updating;
+		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+			ts->dwz_info->product_id);
+		ret = ts->hideep_api->update_all(ts, fw_name);
+
+		kfree(fw_name);
+
+		enable_irq(ts->client->irq);
+
+		ts->dev_state = state_normal;
+		if (ret != 0)
+			dev_err(dev, "The firmware update failed(%d)", ret);
+	}
+
+	return count;
+}
+
+static ssize_t fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		ts->dwz_info->release_ver);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->release_ver);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_t *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		ts->dwz_info->product_id);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", ts->dwz_info->product_id);
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t power_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
+		(ts->dev_state == state_init) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t power_control(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on, ret;
+
+	ret = kstrtoint(buf, 8, &on);
+	if (ret)
+		return ret;
+
+	if (on) {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_normal;
+		ts->hideep_api->reset_ic(ts);
+	} else {
+		ts->hideep_api->power(ts, on);
+		ts->dev_state = state_init;
+	}
+
+	return count;
+}
+
+#ifdef HIDEEP_SUPPORT_STYLUS
+static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+	unsigned char status[1];
+
+	ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
+
+	len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
+		(status[0] == 0x00) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on, ret;
+	unsigned char data[2];
+
+	ret = kstrtoint(buf, 8, &on);
+	if (ret)
+		return ret;
+
+	data[0] = 0x04;
+
+	if (on) {
+		data[1] = 0x01;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	} else {
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
+	}
+
+	return count;
+}
+#endif
+
+static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
+		(ts->debug_dev.debug_enable == 0) ? "off" : "on");
+
+	return len;
+}
+
+static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct hideep_t *ts = dev_get_drvdata(dev);
+	int on, ret;
+	unsigned char data[2];
+
+	ret = kstrtoint(buf, 8, &on);
+	if (ret)
+		return ret;
+
+	if (on) {
+		ts->debug_dev.debug_enable = 1;
+		ts->dev_state = state_debugging;
+	} else {
+		ts->debug_dev.debug_enable = 0;
+		ts->dev_state = state_normal;
+		/* set touch mode */
+		data[0] = 0x00;
+		data[1] = 0x00;
+		ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
+		if (ts->interrupt_state == 0) {
+			data[0] = 0x5A;
+			ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
+				data);
+			enable_irq(ts->client->irq);
+			ts->interrupt_state = 1;
+		}
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
+static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
+static DEVICE_ATTR(power_en, 0664, power_status, power_control);
+static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);
+#ifdef HIDEEP_SUPPORT_STYLUS
+static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);
+#endif
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_update_fw.attr,
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_power_en.attr,
+	&dev_attr_debug_en.attr,
+#ifdef HIDEEP_SUPPORT_STYLUS
+	&dev_attr_stylus_en.attr,
+#endif
+	NULL
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+int hideep_sysfs_init(struct hideep_t *ts)
+{
+	int ret;
+	struct i2c_client *client = ts->client;
+
+	/* Create the files associated with this kobject */
+	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
+
+	if (ret)
+		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
+			 __func__, ret);
+
+	return ret;
+}
+
+int hideep_sysfs_exit(struct hideep_t *ts)
+{
+	struct i2c_client *client = ts->client;
+
+	sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	return 0;
+}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
       [not found]         ` <1500965607-2446-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-08-03 17:50           ` Rob Herring
  2017-08-09  7:54             ` Anthony Kim
  2017-08-09 23:49           ` Dmitry Torokhov
  1 sibling, 1 reply; 29+ messages in thread
From: Rob Herring @ 2017-08-03 17:50 UTC (permalink / raw)
  To: Anthony Kim
  Cc: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	rydberg-FFUHeuDi6mxAfugRpC6u6w,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

On Tue, Jul 25, 2017 at 03:53:27PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |  35 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +

For the binding,

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

>  drivers/input/touchscreen/Kconfig                  |  11 +
>  drivers/input/touchscreen/Makefile                 |   2 +
>  drivers/input/touchscreen/hideep.h                 | 318 +++++++
>  drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
>  drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
>  drivers/input/touchscreen/hideep_dbg.h             |  24 +
>  drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
>  drivers/input/touchscreen/hideep_isp.h             |  96 +++
>  drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
>  11 files changed, 2645 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.h
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-08-03 17:50           ` Rob Herring
@ 2017-08-09  7:54             ` Anthony Kim
  0 siblings, 0 replies; 29+ messages in thread
From: Anthony Kim @ 2017-08-09  7:54 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Rob Herring, mark.rutland, rydberg, linux-input, devicetree

Hi, Dmitry.
My company is registering the Approved Vendor List for the Chrome OS.
So I need to upstream my driver code. For it, I need your confirm.
Please confirm my driver code.
Thank You.
Anthony.

2017-08-04 2:50 GMT+09:00 Rob Herring <robh@kernel.org>:
> On Tue, Jul 25, 2017 at 03:53:27PM +0900, Anthony Kim wrote:
>> The HiDeep touchscreen device is a capacitive multi-touch controller
>> mainly for multi-touch supported devices use. It use I2C interface for
>> communication to IC and provide axis X, Y, Z locations for ten finger
>> touch through input event interface to userspace.
>>
>> It support the Crimson and the Lime two type IC. They are different
>> the number of channel supported and FW size. But the working protocol
>> is same.
>>
>> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
>> ---
>>  .../bindings/input/touchscreen/hideep.txt          |  35 +
>>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>
> For the binding,
>
> Acked-by: Rob Herring <robh@kernel.org>
>
>>  drivers/input/touchscreen/Kconfig                  |  11 +
>>  drivers/input/touchscreen/Makefile                 |   2 +
>>  drivers/input/touchscreen/hideep.h                 | 318 +++++++
>>  drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
>>  drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
>>  drivers/input/touchscreen/hideep_dbg.h             |  24 +
>>  drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
>>  drivers/input/touchscreen/hideep_isp.h             |  96 +++
>>  drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
>>  11 files changed, 2645 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>>  create mode 100644 drivers/input/touchscreen/hideep.h
>>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
       [not found]         ` <1500965607-2446-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
  2017-08-03 17:50           ` Rob Herring
@ 2017-08-09 23:49           ` Dmitry Torokhov
  2017-08-10  8:01             ` Anthony Kim
  1 sibling, 1 reply; 29+ messages in thread
From: Dmitry Torokhov @ 2017-08-09 23:49 UTC (permalink / raw)
  To: Anthony Kim
  Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	rydberg-FFUHeuDi6mxAfugRpC6u6w,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

Hi Anthony,

On Tue, Jul 25, 2017 at 03:53:27PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |  35 +
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  drivers/input/touchscreen/Kconfig                  |  11 +
>  drivers/input/touchscreen/Makefile                 |   2 +
>  drivers/input/touchscreen/hideep.h                 | 318 +++++++
>  drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
>  drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
>  drivers/input/touchscreen/hideep_dbg.h             |  24 +
>  drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
>  drivers/input/touchscreen/hideep_isp.h             |  96 +++
>  drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
>  11 files changed, 2645 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.h
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..5270426
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,35 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible		: must be "hideep,hideep-crimson"
> +					or "hideep,hideep-lime".
> +- reg			: I2C slave address, (e.g. 0x6C).
> +- interrupt-parent : Interrupt controller to which the chip is connected.
> +- interrupts : Interrupt to which the chip is connected.
> +
> +Optional properties:
> +- vdd-supply	: It is the controller supply for controlling
> +					 main voltage(3.3V) through the regulator.
> +- vid-supply	: It is the controller supply for controlling
> +					IO voltage(1.8V) through the regulator.
> +- reset-gpios	: Define for reset gpio pin.
> +						It is to use for reset IC.
> +- hideep,max-coords	: Max value for axis X, Y, W, Z.
> +
> +Example:
> +
> +i2c@00000000 {
> +
> +	/* ... */
> +
> +	touchscreen@6c {
> +		compatible = "hideep,hideep-lime";
> +		reg = <0x6c>;
> +		interrupt-parent = <&gpx1>;
> +		interrupts = <2 0>;
> +		vdd-supply = <&ldo15_reg>";
> +		vid-supply = <&ldo18_reg>;
> +		reset-gpios = <&gpx1 5 0>;
> +		hideep,max-coords = <1080 1920 65535 65535>;
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index c03d201..aa2a301 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -131,6 +131,7 @@ gw	Gateworks Corporation
>  hannstar	HannStar Display Corporation
>  haoyu	Haoyu Microelectronic Co. Ltd.
>  hardkernel	Hardkernel Co., Ltd
> +hideep	HiDeep Inc.
>  himax	Himax Technologies, Inc.
>  hisilicon	Hisilicon Limited.
>  hit	Hitachi Ltd.
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 64b30fe..13e11c7 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called bu21023_ts.
>  
> +config TOUCHSCREEN_HIDEEP
> +	tristate "HiDeep Touch IC"
> +	depends on I2C
> +	help
> +	  Say Y here if you have a touchscreen using HiDeep.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a moudle, choose M here : the
> +	  module will be called hideep_ts.
> +
>  endif
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 6badce8..3aab466 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
> +obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
> +hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
> diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
> new file mode 100644
> index 0000000..50306a1
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep.h
> @@ -0,0 +1,318 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_H
> +#define _LINUX_HIDEEP_H
> +
> +/*************************************************************************
> + * this is include special HEAD file.
> + *************************************************************************/
> +#ifdef CONFIG_FB
> +#include <linux/fb.h>
> +#include <linux/notifier.h>
> +#endif
> +
> +/*************************************************************************
> + * this is include normal HEAD file.
> + *************************************************************************/
> +#include <linux/of_gpio.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/input.h>
> +#include <linux/uaccess.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/proc_fs.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/firmware.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/kthread.h>
> +#include <linux/random.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/sched/rt.h>
> +#include <linux/task_work.h>
> +#include <linux/rtc.h>
> +#include <linux/syscalls.h>
> +#include <linux/timer.h>
> +#include <linux/time.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/hrtimer.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/input/mt.h>
> +#include <linux/fs.h>
> +#include <linux/completion.h>
> +#include <linux/platform_device.h>
> +#include <linux/miscdevice.h>
> +#include <linux/init.h>
> +#include <linux/gfp.h>
> +#include <linux/kobject.h>
> +#include <linux/string.h>
> +#include <linux/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/poll.h>
> +#include <linux/fcntl.h>
> +#include <linux/unistd.h>
> +#include <linux/version.h>
> +#include <linux/acpi.h>
> +
> +/*************************************************************************
> + * definition part.
> + * define is (open, set, enable) if not, is (close, clear, disable)
> + * some special switch of functions.
> + *************************************************************************/
> +
> +/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
> +#define HIDEEP_PROTOCOL_2_0

We should support both protocols simultaneously instead of being compile
time option.

> +
> +/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
> + * otherwise TYPE_A.
> + */
> +#define HIDEEP_TYPE_B_PROTOCOL

We only need Type-B support in new drivers.

> +
> +/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
> +#define HIDEEP_DWZ_VERSION_CHECK

Why is this conditional?

> +
> +/* HIDEEP_SUPPORT_KE if define, it will use key button. */
> +#define HIDEEP_SUPPORT_KEY

Why is this compile-time decision?

> +
> +/* HIDEEP_SUPPORT_STYLUS if define, it will use stylus mode. */
> +#define HIDEEP_SUPPORT_STYLUS

Same here. We are looking for drivers to be usable on multitude of
boards. The data should be comping form the board desciption (device
tree, ACPI) so that the same kernel can work on multitude of boards.

> +
> +/*************************************************************************
> + * Buffer size
> + *************************************************************************/
> +#define FRAME_HEADER_SIZE				8
> +/* if size of system i2c buffer is smaller than this value, need to modify */
> +#define MAX_I2C_BUFFER_SIZE				512
> +
> +/*************************************************************************
> + * board porting config
> + *************************************************************************/
> +#define HIDEEP_DEBUG_DEVICE_NAME		"hideep_debug"

Why is this needed? I normally recommend userspace access over /dev/i2c
if tuning is needed so that we do not have a large chunk of effectively
dead code in the driver in production images.

> +#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
> +#define HIDEEP_I2C_NAME					"hideep_ts"
> +
> +/*************************************************************************
> + * register addr
> + *************************************************************************/
> +/* Touch & key event */
> +#define HIDEEP_EVENT_COUNT_ADDR			0x240
> +#define HIDEEP_TOUCH_DATA_ADDR			0x242
> +#define HIDEEP_KEY_DATA_ADDR			0x2A6
> +#define HIDEEP_RAW_DATA_ADDR			0x1000
> +
> +/* command list */
> +#define HIDEEP_RESET_CMD				0x9800
> +#define HIDEEP_INTCLR_CMD				0x9802
> +#define HIDEEP_OPMODE_CMD				0x9804
> +#define HIDEEP_SWTICH_CMD				0x9805
> +#define HIDEEP_SLEEP_CMD				0x980D
> +
> +/*************************************************************************
> + * multi-touch & key definitions.
> + *************************************************************************/
> +#define HIDEEP_MT_MAX					10
> +#define HIDEEP_KEY_MAX					3
> +
> +/* multi touch event bit */
> +#define HIDEEP_MT_ALWAYS_REPORT			0
> +#define HIDEEP_MT_TOUCHED				1
> +#define HIDEEP_MT_FIRST_CONTACT			2
> +#define HIDEEP_MT_DRAG_MOVE				3
> +#define HIDEEP_MT_RELEASED				4
> +#define HIDEEP_MT_PINCH					5
> +#define HIDEEP_MT_PRESSURE				6
> +
> +/* key event bit */
> +#define HIDEEP_KEY_RELEASED				0x20
> +#define HIDEEP_KEY_PRESSED				0x40
> +#define HIDEEP_KEY_FIRST_PRESSED		0x80
> +#define HIDEEP_KEY_PRESSED_MASK			0xC0
> +
> +/*************************************************************************
> + * HIDEEP PROTOCOL
> + *************************************************************************/
> +#ifdef HIDEEP_PROTOCOL_2_0
> +struct hideep_mt_t {

Just drop _t in structure names. You can see that you are dealing with
the type because there is "struct" preceding the name.

> +	unsigned short x;
> +	unsigned short y;
> +	unsigned short z;
> +	unsigned char w;
> +	unsigned char flag;
> +	unsigned char type;
> +	unsigned char index;
> +};
> +#else
> +struct hideep_mt_t {
> +	unsigned char flag;
> +	unsigned char index;
> +	unsigned short x;
> +	unsigned short y;
> +	unsigned char z;
> +	unsigned char w;
> +};
> +#endif
> +
> +struct hideep_key_t {
> +	unsigned char flag;
> +	unsigned int key;
> +};
> +
> +/*************************************************************************
> + * IC State
> + *************************************************************************/
> +enum e_dev_state {
> +	state_init = 1,
> +	state_normal,
> +	state_sleep,
> +	state_updating,
> +	state_debugging,
> +};
> +
> +/*************************************************************************
> + * Firmware info
> + *************************************************************************/
> +struct dwz_info_t {
> +	unsigned int code_start;
> +	unsigned char code_crc[12];
> +
> +	unsigned int c_code_start;
> +	unsigned short c_code_len;
> +	unsigned short gen_ver;

I think you need to annotate with proper endianness (__le16 I assume)
and convert to CPU endianness when using. Same goes for other
firmware/config/nvm data.

> +
> +	unsigned int vr_start;
> +	unsigned short vr_len;
> +	unsigned short rsv0;
> +
> +	unsigned int ft_start;
> +	unsigned short ft_len;
> +	unsigned short vr_version;
> +
> +	unsigned short boot_ver;
> +	unsigned short core_ver;
> +	unsigned short custom_ver;
> +	unsigned short release_ver;
> +
> +	unsigned char factory_id;
> +	unsigned char panel_type;
> +	unsigned char model_name[6];
> +	unsigned short product_code;
> +	unsigned short extra_option;
> +
> +	unsigned short product_id;
> +	unsigned short vendor_id;
> +};
> +
> +/*************************************************************************
> + * driver information for hideep_t by device tree
> + *************************************************************************/
> +struct hideep_platform_data_t {
> +	unsigned int max_x;
> +	unsigned int max_y;
> +	unsigned int max_z;
> +	unsigned int max_w;
> +
> +#ifdef CONFIG_OF
> +	int reset_gpio;
> +
> +	struct regulator *vcc_vdd;
> +	struct regulator *vcc_vid;
> +#endif
> +};
> +
> +struct hideep_debug_dev_t {
> +	struct miscdevice misc;
> +
> +	wait_queue_head_t i_packet;
> +
> +	unsigned int ready;
> +	unsigned char *vr_buff;
> +	unsigned char *img_buff;
> +	unsigned short vr_size;
> +	unsigned short img_size;
> +
> +	bool debug_enable;
> +	bool release_flag;
> +
> +	struct hideep_t *ts;
> +};
> +
> +struct hideep_function_list_t {
> +	//core
> +	int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
> +		unsigned char *);
> +	int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
> +		unsigned char *);
> +	void (*reset_ic)(struct hideep_t *);
> +	void (*power)(struct hideep_t *, int);
> +
> +	//isp
> +	int (*update_all)(struct hideep_t *, const char *);
> +	int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
> +	int (*get_dwz_info)(struct hideep_t *);
> +	void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
> +};
> +
> +struct hideep_t {
> +	struct i2c_client *client;
> +	struct input_dev *input_dev;
> +	struct hideep_platform_data_t *p_data;
> +
> +	struct mutex dev_mutex;
> +	struct mutex i2c_mutex;
> +
> +	struct hideep_debug_dev_t debug_dev;
> +	struct hideep_function_list_t *hideep_api;
> +
> +	struct task_struct *hthread_event;

Why do you need both threaded interrupt and kernel therad? The threaded
interrupt should suffice.

> +
> +	bool suspended;
> +	enum e_dev_state dev_state;
> +
> +#ifdef CONFIG_FB
> +	struct notifier_block fb_notif;
> +#endif
> +
> +	long tch_bit;
> +	unsigned int tch_count;
> +	unsigned int key_count;
> +	unsigned int lpm_count;
> +
> +	struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +	struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
> +	int key_codes[HIDEEP_KEY_MAX];
> +#endif
> +
> +	unsigned char i2c_buf[256];
> +	struct dwz_info_t *dwz_info;
> +
> +	int interrupt_state;
> +	int fw_size;
> +};
> +
> +/*************************************************************************
> + * function define
> + *************************************************************************/
> +int hideep_debug_init(struct hideep_t *ts);
> +void hideep_debug_uninit(void);
> +
> +int hideep_sysfs_init(struct hideep_t *ts);
> +int hideep_sysfs_exit(struct hideep_t *ts);
> +
> +void hideep_isp_init(struct hideep_t *ts);
> +
> +#endif
> diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
> new file mode 100644
> index 0000000..d1b2c44
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_core.c
> @@ -0,0 +1,916 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +
> +#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
> +	((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
> +
> +static int hideep_pwr_on(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	int ret = 0;
> +
> +	pdata = ts->p_data;
> +
> +#ifdef CONFIG_OF
> +	if (!IS_ERR(pdata->vcc_vdd)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
> +		ret = regulator_enable(pdata->vcc_vdd);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vdd enable failed ret=%d", ret);
> +	}
> +	usleep_range(999, 1000);
> +
> +	if (!IS_ERR(pdata->vcc_vid)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
> +		ret = regulator_enable(pdata->vcc_vid);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vcc_vid enable failed ret=%d", ret);
> +	}
> +	usleep_range(2999, 3000);
> +#endif
> +
> +	return ret;
> +}
> +
> +static int hideep_pwr_off(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	int ret = 0;
> +
> +	pdata = ts->p_data;
> +
> +#ifdef CONFIG_OF
> +	if (pdata->reset_gpio > 0) {
> +		dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
> +		gpio_set_value(pdata->reset_gpio, 0);
> +	}
> +
> +	if (!IS_ERR(pdata->vcc_vid)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
> +		ret = regulator_disable(pdata->vcc_vid);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vcc_vid enable failed ret=%d", ret);
> +	}
> +
> +	if (!IS_ERR(pdata->vcc_vdd)) {
> +		dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
> +		ret = regulator_disable(pdata->vcc_vdd);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vdd disable failed ret=%d", ret);
> +	}
> +#endif
> +	return ret;
> +}
> +
> +static void hideep_power(struct hideep_t *ts, int on)
> +{
> +	int ret = 0;
> +
> +	if (on) {
> +		dev_info(&ts->client->dev, "power on");
> +		ret = hideep_pwr_on(ts);
> +	} else {
> +		dev_info(&ts->client->dev, "power off");
> +		ret = hideep_pwr_off(ts);
> +	}
> +}
> +
> +static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret = -1;
> +	unsigned short r_len, raddr;
> +	int length;
> +
> +	length = len;
> +	raddr = addr;
> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	do {
> +		if (length > MAX_I2C_BUFFER_SIZE)
> +			r_len = MAX_I2C_BUFFER_SIZE;
> +		else
> +			r_len = length;
> +
> +		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
> +			raddr, r_len);
> +
> +		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
> +
> +		if (ret < 0)
> +			goto i2c_err;
> +
> +		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
> +		length -= MAX_I2C_BUFFER_SIZE;
> +		buf += MAX_I2C_BUFFER_SIZE;
> +		raddr += MAX_I2C_BUFFER_SIZE;
> +
> +		if (ret < 0)
> +			goto i2c_err;
> +	} while (length > 0);
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +
> +	return  0;
> +i2c_err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return -1;
> +}
> +
> +static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret = -1;
> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	// data mangling..
> +	ts->i2c_buf[0] = (addr >> 0) & 0xFF;
> +	ts->i2c_buf[1] = (addr >> 8) & 0xFF;
> +	memcpy(&ts->i2c_buf[2], buf, len);
> +
> +	ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
> +
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +	return  0;
> +
> +i2c_err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return -1;
> +}
> +
> +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
> +
> +static void pops_mt(struct hideep_t *ts)
> +{
> +#ifdef HIDEEP_TYPE_B_PROTOCOL
> +	int id;
> +	int i;
> +
> +	for (i = 0; i < ts->tch_count; i++) {
> +		id = (ts->touch_evt[i].index >> 0) & 0x0F;
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
> +		input_report_key(ts->input_dev, BTN_TOUCH, false);
> +	}
> +#else
> +	input_report_key(ts->input_dev, BTN_TOUCH, false);
> +	input_mt_sync(ts->input_dev);
> +#endif
> +
> +	input_sync(ts->input_dev);
> +}
> +
> +static void push_mt(struct hideep_t *ts)
> +{
> +	int id;
> +	int i;
> +	bool btn_up = 0;
> +	bool btn_dn = 0;
> +	bool btn_mv = 0;
> +	int evt = 0;
> +
> +	/* load multi-touch event to input system */
> +	for (i = 0; i < ts->tch_count; i++) {
> +		id = (ts->touch_evt[i].index >> 0) & 0x0F;
> +		btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
> +		btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
> +			& 0x01;
> +		btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
> +
> +		if (btn_up)
> +			clear_bit(id, &ts->tch_bit);
> +		else
> +			__set_bit(id, &ts->tch_bit);
> +
> +		dev_dbg(&ts->client->dev,
> +			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
> +			ts->touch_evt[i].type, ts->touch_evt[i].index,
> +			i, ts->touch_evt[i].x, ts->touch_evt[i].y,
> +			ts->touch_evt[i].z);
> +
> +#ifdef HIDEEP_TYPE_B_PROTOCOL
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
> +			(btn_up == 0));
> +
> +		if (btn_up == 0) {
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +				ts->touch_evt[i].x);
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +				ts->touch_evt[i].y);
> +			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +				ts->touch_evt[i].z);
> +			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +				ts->touch_evt[i].w);
> +			evt++;
> +		}
> +#else
> +		if (btn_up) {
> +			input_mt_sync(ts->input_dev);
> +		} else {
> +			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +				ts->touch_evt[i].x);
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +				ts->touch_evt[i].y);
> +			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +				ts->touch_evt[i].z);
> +			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +				ts->touch_evt[i].w);
> +			input_mt_sync(ts->input_dev);
> +			evt++;
> +		}
> +#endif
> +	}
> +
> +	if (ts->tch_bit == 0)
> +		evt = 0;
> +
> +	input_report_key(ts->input_dev, BTN_TOUCH, evt);
> +	input_mt_sync_frame(ts->input_dev);
> +	input_sync(ts->input_dev);
> +}
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +static void pops_ky(struct hideep_t *ts)
> +{
> +	int i;
> +
> +	for (i = 0; i < ts->tch_count; i++) {
> +		input_report_key(ts->input_dev, ts->key_codes[i], false);
> +		input_report_key(ts->input_dev, BTN_TOUCH, false);
> +	}
> +
> +	input_sync(ts->input_dev);
> +}
> +
> +static void push_ky(struct hideep_t *ts)
> +{
> +	int i;
> +	int pressed;
> +	int key_code;
> +	int key_status;
> +
> +	for (i = 0; i < ts->key_count; i++) {
> +		key_code = ts->key_evt[i].flag & 0x0F;
> +		key_status = ts->key_evt[i].flag & 0xF0;
> +		pressed = false;
> +
> +		if (key_status & HIDEEP_KEY_PRESSED_MASK)
> +			pressed = true;
> +		else
> +			pressed = false;
> +
> +		input_report_key(ts->input_dev, ts->key_codes[key_code],
> +			pressed);
> +		input_report_key(ts->input_dev, BTN_TOUCH, pressed);
> +	}
> +
> +	input_sync(ts->input_dev);
> +}
> +#endif
> +
> +static void hideep_put_event(struct hideep_t *ts)
> +{
> +	/* mangling touch information */
> +	if (ts->tch_count > 0)
> +		push_mt(ts);
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +	if (ts->key_count > 0)
> +		push_ky(ts);
> +#endif
> +}
> +
> +static int hideep_get_event(struct hideep_t *ts)
> +{
> +	int ret;
> +	int touch_count;
> +	int info_size;
> +
> +
> +	/* get touch event count */
> +	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
> +		ts->tch_count, ts->key_count, ts->lpm_count);
> +
> +	/* get touch event information */
> +	if (ts->tch_count > HIDEEP_MT_MAX)
> +		ts->tch_count = 0;
> +
> +	if (ts->key_count > HIDEEP_KEY_MAX)
> +		ts->key_count = 0;
> +
> +	touch_count = ts->tch_count + ts->key_count;
> +
> +	if (ts->tch_count > 0) {
> +		info_size = ts->tch_count * sizeof(struct hideep_mt_t);
> +		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
> +			info_size, (unsigned char *)ts->touch_evt);
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return -1;
> +		}
> +	}
> +
> +#ifdef HIDEEP_SUPPORT_KEY
> +	if (ts->key_count > 0) {
> +		info_size = ts->key_count * sizeof(struct hideep_key_t);
> +		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
> +			info_size, (unsigned char *)ts->key_evt);
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return -1;
> +		}
> +	}
> +#endif
> +
> +	return touch_count;
> +}
> +
> +static int hideep_event_thread(void *arg)
> +{
> +	int t_evt;
> +
> +	struct hideep_t *ts = arg;
> +
> +	dev_info(&ts->client->dev, "start event thread");
> +
> +	while (!kthread_should_stop()) {
> +		set_current_state(TASK_INTERRUPTIBLE);
> +
> +		schedule();
> +
> +		t_evt = hideep_get_event(ts);
> +
> +		if (t_evt >= 0)
> +			hideep_put_event(ts);
> +
> +		if (kthread_should_stop())
> +			break;
> +	}
> +
> +	dev_info(&ts->client->dev, "end thread");
> +	return 0;
> +}
> +
> +static irqreturn_t hideep_irq_task(int irq, void *handle)
> +{
> +	unsigned char i2c_buff[2];
> +	int ret;
> +
> +	struct hideep_t *ts = (struct hideep_t *) handle;
> +
> +	dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
> +		ts->dev_state, ts->debug_dev.debug_enable);
> +
> +	if (ts->debug_dev.debug_enable == false &&
> +		ts->dev_state == state_normal) {
> +		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
> +			2, (u8 *)&i2c_buff);
> +		if (ret < 0) {
> +			disable_irq(ts->client->irq);
> +			ts->interrupt_state = 0;
> +			return IRQ_HANDLED;
> +		}
> +
> +		ts->tch_count = i2c_buff[0];
> +		ts->key_count = i2c_buff[1] & 0x0f;
> +		ts->lpm_count = i2c_buff[1] & 0xf0;
> +
> +		wake_up_process(ts->hthread_event);
> +
> +	} else if (ts->debug_dev.debug_enable == true
> +		&& ts->dev_state == state_debugging) {
> +		ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
> +			FRAME_HEADER_SIZE + ts->debug_dev.img_size,
> +			ts->debug_dev.img_buff);
> +		ts->debug_dev.img_size =
> +			GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
> +		ts->debug_dev.ready = 1;
> +		wake_up_interruptible(&ts->debug_dev.i_packet);
> +	}
> +	dev_dbg(&ts->client->dev, "end.");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int hideep_capability(struct hideep_t *ts)
> +{
> +#ifdef HIDEEP_SUPPORT_KEY

As I mentioned, read from device properties.

> +	int i;
> +
> +	ts->key_codes[0] = KEY_MENU;
> +	ts->key_codes[1] = KEY_HOME;
> +	ts->key_codes[2] = KEY_BACK;
> +
> +	for (i = 0; i < HIDEEP_KEY_MAX; i++)
> +		set_bit(ts->key_codes[i], ts->input_dev->keybit);
> +#endif
> +
> +	ts->input_dev->name = HIDEEP_TS_NAME;
> +	ts->input_dev->id.bustype = BUS_I2C;
> +
> +	set_bit(EV_ABS, ts->input_dev->evbit);
> +	set_bit(EV_KEY, ts->input_dev->evbit);
> +	set_bit(EV_SYN, ts->input_dev->evbit);
> +	set_bit(BTN_TOUCH, ts->input_dev->keybit);

Please use input_set_capability().

> +	set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);

There is no such event.

> +	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
> +
> +	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
> +	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
> +	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
> +		ts->p_data->max_z, 0, 0);
> +
> +#ifdef HIDEEP_TYPE_B_PROTOCOL

Should not be conditional.

> +	input_mt_init_slots(ts->input_dev,
> +		HIDEEP_MT_MAX, INPUT_MT_DIRECT);

Error handling needed.

> +#else
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TRACKING_ID, 0, 10, 0, 0);
> +#endif
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
> +
> +	return 0;
> +}
> +
> +static void hideep_reset_ic(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	int ret;
> +	unsigned char cmd = 0x01;
> +
> +	pdata = ts->p_data;

Move together with declaration:

	struct hideep_platform_data_t *pdata = ts->p_data;

> +
> +	dev_info(&ts->client->dev, "start!!");

dev_dbg() or drop.

> +
> +#ifdef CONFIG_OF

This should not depend on CONFIG_OF.

> +	if (pdata->reset_gpio > 0) {
> +		dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
> +		gpio_set_value(pdata->reset_gpio, 0);
> +		mdelay(20);
> +		gpio_set_value(pdata->reset_gpio, 1);
> +	} else
> +#endif
> +	{
> +		ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
> +	}
> +	mdelay(50);
> +	dev_info(&ts->client->dev, "end!!");

dev_dbg() or drop.

> +}
> +
> +static void hideep_get_info(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	unsigned char val[4];
> +
> +	pdata = ts->p_data;
> +	ts->hideep_api->i2c_read(ts, 0x28, 4, val);
> +
> +	pdata->max_x = val[3] << 8 | val[2];
> +	pdata->max_y = val[1] << 8 | val[0];
> +	pdata->max_w = 255;
> +	pdata->max_z = 255;
> +
> +	dev_info(&ts->client->dev, "X : %d, Y : %d",
> +		pdata->max_x, pdata->max_y);
> +}
> +
> +static void hideep_init_ic(struct hideep_t *ts)
> +{
> +	struct hideep_platform_data_t *pdata;
> +	struct device dev;
> +
> +	pdata = ts->p_data;
> +	dev = ts->client->dev;
> +
> +	/* power on */
> +	ts->hideep_api->power(ts, true);
> +	ts->dev_state = state_init;
> +	mdelay(30);
> +
> +	/* ic reset */
> +	ts->hideep_api->reset_ic(ts);
> +}
> +
> +static int hideep_resume(struct device *dev)

__maybe_unused

> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x00;
> +
> +	dev_info(dev, "enter");
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	if (ts->dev_state == state_normal)
> +		goto hideep_resume_exit;
> +
> +	dev_info(dev, "not waiting.");
> +	ts->dev_state = state_normal;
> +
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	enable_irq(ts->client->irq);
> +	ts->interrupt_state = 1;
> +
> +hideep_resume_exit:
> +	mdelay(10);
> +	ts->hideep_api->reset_ic(ts);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +	dev_info(dev, "exit.");
> +	return 0;
> +}
> +
> +static int hideep_suspend(struct device *dev)

__maybe_unused

> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x01;
> +
> +	dev_info(dev, "enter");
> +
> +	mutex_lock(&ts->dev_mutex);
> +	if (ts->dev_state == state_sleep)
> +		goto hideep_suspend_exit;
> +
> +	dev_info(dev, "not waiting.");
> +	ts->dev_state = state_sleep;
> +
> +	/* send sleep command.. */
> +	pops_mt(ts);
> +#ifdef HIDEEP_SUPPORT_KEY
> +	pops_ky(ts);
> +#endif
> +
> +	/* default deep sleep */
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	disable_irq(ts->client->irq);
> +	ts->interrupt_state = 0;
> +
> +hideep_suspend_exit:
> +	mutex_unlock(&ts->dev_mutex);
> +	dev_info(dev, "exit.");
> +	return 0;
> +}
> +
> +#ifdef CONFIG_FB
> +static int fb_notifier_callback(struct notifier_block *self,
> +	unsigned long event, void *data)
> +{
> +	struct fb_event *evdata = data;
> +	int *blank;
> +
> +	struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
> +
> +	if ((evdata) && (evdata->data) &&
> +		(event == FB_EVENT_BLANK) &&
> +		(ts) && (ts->client)) {
> +		blank = evdata->data;
> +		if (*blank == FB_BLANK_UNBLANK) {
> +			dev_info(&ts->client->dev, "resume");
> +			if (ts->suspended == 1) {
> +				hideep_resume(&ts->client->dev);
> +				ts->suspended = 0;
> +			}
> +		} else if (*blank == FB_BLANK_POWERDOWN) {
> +			dev_info(&ts->client->dev, "suspend");
> +			if (ts->suspended == 0) {
> +				ts->suspended = 1;
> +				hideep_suspend(&ts->client->dev);
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_OF
> +static int hideep_parse_dts(struct device *dev,
> +	struct hideep_platform_data_t *pdata)
> +{
> +	int ret = 0;
> +	unsigned int coords[4];
> +	struct device_node *np;
> +
> +	dev_info(dev, "enter");
> +	np = dev->of_node;
> +
> +	ret = of_property_read_u32_array(np, "hideep,max-coords", coords, 4);
> +	if (ret) {
> +		dev_err(dev, "Failed to get max-coords property\n");
> +		return ret;
> +	}

This should be using the standard touchscreen bindings from
Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt

> +
> +	pdata->max_x = coords[0];
> +	pdata->max_y = coords[1];
> +	pdata->max_w = coords[2];
> +	pdata->max_z = coords[3];
> +
> +	dev_info(dev, "max coord data x : %d\n", pdata->max_x);
> +	dev_info(dev, "max coord data y : %d\n", pdata->max_y);
> +	dev_info(dev, "max coord data w : %d\n", pdata->max_w);
> +	dev_info(dev, "max coord data z : %d\n", pdata->max_z);
> +
> +	/* device tree information get */
> +	pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
> +	dev_info(dev, "reset_gpio = %d, is %s specified",
> +		pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
> +	if (pdata->reset_gpio)
> +		ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
> +			"reset-gpios");

Please use gpiod API and request with:

	pdata->reset_gpio = devm_gpiod_get_optional(dev, "reset",
						    GPIOD_OUT_LOW);
	if (IS_ERR(pdata->reset_gpio))
		return PTR_ERR(pdata->reset_gpio));

> +
> +	pdata->vcc_vdd = regulator_get(dev, "vdd");
> +	pdata->vcc_vid = regulator_get(dev, "vid");

Error handling is missing.

> +
> +	return ret;
> +}
> +#endif
> +
> +static int hideep_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
> +{
> +	int ret = 0;
> +	struct hideep_platform_data_t *p_data;
> +	struct dwz_info_t *dwz;
> +	struct hideep_function_list_t *function;
> +	struct hideep_t *ts;
> +
> +	dev_info(&client->dev, "enter");

dev_dbg() or drop. Same goes for similar dev_info() uses.

> +
> +	/* check i2c bus */
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_I2C)) {
> +		dev_err(&client->dev, "check i2c device error");
> +		ret = -ENODEV;
> +		return ret;

		return -ENODEV;

> +	}
> +
> +	/* init platform data */
> +	p_data = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_platform_data_t), GFP_KERNEL);
> +
> +#ifdef CONFIG_OF
> +	if (client->dev.of_node) {
> +		ret = hideep_parse_dts(&client->dev, p_data);
> +		if (ret)
> +			return ret;
> +	}

Use generic device properties and drop dependency on CONFIG_OF.

> +#endif
> +
> +	/* init hideep_t */
> +	ts = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_t), GFP_KERNEL);
> +	dwz = devm_kzalloc(&client->dev,
> +		sizeof(struct dwz_info_t), GFP_KERNEL);
> +	function = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_function_list_t), GFP_KERNEL);

Why are the last 2 allocated separately instead of being sub-structures
in hideep_t.

Also, error handling is missing.

> +
> +	ts->client = client;
> +	ts->p_data = p_data;
> +	ts->dwz_info = dwz;
> +	ts->hideep_api = function;
> +
> +	ts->hideep_api->i2c_read = hideep_i2c_read;
> +	ts->hideep_api->i2c_write = hideep_i2c_write;

Consider using regmap if you want to support SPI down the road.

> +	ts->hideep_api->reset_ic = hideep_reset_ic;
> +	ts->hideep_api->power = hideep_power;
> +
> +	/* init for isp function */
> +	hideep_isp_init(ts);
> +
> +	i2c_set_clientdata(client, ts);
> +
> +	mutex_init(&ts->i2c_mutex);
> +	mutex_init(&ts->dev_mutex);
> +
> +	if (!client->dev.of_node)
> +		hideep_get_info(ts);
> +
> +	/* hardware init */
> +	hideep_init_ic(ts);
> +
> +#ifdef HIDEEP_DWZ_VERSION_CHECK
> +	/* read info */
> +	ret = ts->hideep_api->get_dwz_info(ts);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
> +		goto hideep_probe_read_dwz_err;
> +	}
> +#endif
> +
> +	/* init input device */
> +	ts->input_dev = devm_input_allocate_device(&client->dev);
> +	if (!ts->input_dev) {
> +		dev_err(&client->dev, "can't allocate memory for input_dev");
> +		ret = -ENOMEM;
> +		goto hideep_probe_input_dev_memory_err;
> +	}
> +
> +	hideep_capability(ts);
> +
> +	ret = input_register_device(ts->input_dev);
> +	if (ret) {
> +		dev_err(&client->dev, "can't register input_dev");
> +		ret = -ENOMEM;
> +		goto hideep_probe_register_input_dev_err;
> +	}
> +
> +	input_set_drvdata(ts->input_dev, ts);
> +
> +	/* init event thread */
> +	ts->hthread_event = kthread_run(hideep_event_thread,
> +		ts, "hideep_event_thread");
> +	if (IS_ERR(ts->hthread_event)) {
> +		dev_err(&client->dev, "can't create event thread !!!");
> +		ret = PTR_ERR(ts->hthread_event);
> +		goto hideep_probe_create_thread_err;
> +	}
> +
> +	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
> +	if (ts->client->irq <= 0) {
> +		dev_err(&client->dev, "can't be assigned irq");
> +		goto hideep_probe_assigned_irq_err;
> +	}
> +
> +	if (ts->client->irq) {

This check is useless.

> +		ret = request_threaded_irq(ts->client->irq, NULL,

devm_request_threaded_irq

> +			hideep_irq_task,
> +			(IRQF_TRIGGER_LOW | IRQF_ONESHOT),

Reply on platform supplying proper trigger, so simply IRQF_ONESHOT.

> +			ts->client->name, ts);
> +		disable_irq(ts->client->irq);
> +		ts->interrupt_state = 0;
> +		if (ret < 0) {
> +			dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
> +				ret);
> +			goto hideep_probe_request_irq_err;
> +		}
> +	}
> +
> +	ret = hideep_debug_init(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
> +		ret = -1;
> +		goto hideep_probe_debug_init_err;
> +	}
> +
> +	ret = hideep_sysfs_init(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
> +		ret = -1;
> +		goto hideep_probe_sysfs_init_err;
> +	}
> +
> +#ifdef CONFIG_FB
> +	ts->suspended = 0;
> +	ts->fb_notif.notifier_call = fb_notifier_callback;
> +	ret = fb_register_client(&ts->fb_notif);
> +	if (ret) {
> +		dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
> +			ret);
> +		ret = -1;
> +		goto hideep_probe_register_fb_err;
> +	}

This does not belong to touchscreen driver at all. Kernel driver should
not establish policy.

> +#endif
> +
> +
> +	ts->dev_state = state_normal;
> +	enable_irq(ts->client->irq);
> +	ts->interrupt_state = 1;
> +
> +	dev_info(&client->dev, "probe is ok!");
> +	return 0;
> +
> +hideep_probe_register_fb_err:
> +	hideep_sysfs_exit(ts);
> +
> +hideep_probe_sysfs_init_err:
> +	hideep_debug_uninit();
> +
> +hideep_probe_debug_init_err:
> +hideep_probe_request_irq_err:
> +	free_irq(ts->client->irq, ts);
> +
> +hideep_probe_assigned_irq_err:
> +hideep_probe_create_thread_err:
> +	input_unregister_device(ts->input_dev);
> +
> +hideep_probe_register_input_dev_err:
> +	if (ts->input_dev)
> +		input_free_device(ts->input_dev);

No calls to input_free_device() after input_unregister_device().

Additionally, you are using devm, why do you unregister/free manually?

> +
> +hideep_probe_input_dev_memory_err:
> +#ifdef HIDEEP_DWZ_VERSION_CHECK
> +hideep_probe_read_dwz_err:
> +#endif
> +
> +#ifdef CONFIG_OF
> +	ts->hideep_api->power(ts, false);
> +#endif
> +	dev_err(&client->dev, "probe err!");
> +	return ret;
> +}
> +
> +static int hideep_remove(struct i2c_client *client)
> +{
> +	struct hideep_t *ts = i2c_get_clientdata(client);
> +
> +	kthread_stop(ts->hthread_event);
> +
> +#ifdef CONFIG_FB
> +	if (fb_unregister_client(&ts->fb_notif))
> +		dev_info(&client->dev,
> +			"Error occurred while unregistering fb_notifier");
> +#endif
> +
> +#ifdef CONFIG_OF
> +	ts->hideep_api->power(ts, false);
> +#endif
> +	free_irq(client->irq, ts);
> +
> +	input_unregister_device(ts->input_dev);
> +	hideep_sysfs_exit(ts);
> +
> +	hideep_debug_uninit();
> +	devm_kfree(&client->dev, ts);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static const struct dev_pm_ops hideep_pm_ops = {
> +	.suspend = hideep_suspend,
> +	.resume = hideep_resume,
> +};
> +#endif

Use SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);


> +
> +static const struct i2c_device_id hideep_dev_idtable[] = {
> +	{ HIDEEP_I2C_NAME, 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id hideep_acpi_id[] = {
> +	{ "HIDP0001", 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id hideep_match_table[] = {
> +	{ .compatible = "hideep,hideep-lime" },
> +	{ .compatible = "hideep,hideep-crimson" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hideep_match_table);
> +#endif
> +
> +static struct i2c_driver hideep_driver = {
> +	.probe = hideep_probe,
> +	.remove = hideep_remove,
> +	.id_table = hideep_dev_idtable,
> +	.driver = {
> +		.name = HIDEEP_I2C_NAME,
> +		.owner = THIS_MODULE,

No need to set owner explicitly.

> +		.of_match_table = of_match_ptr(hideep_match_table),
> +		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
> +		.pm = &hideep_pm_ops,
> +	},
> +};
> +
> +module_i2c_driver(hideep_driver);
> +
> +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
> +MODULE_AUTHOR("anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
> new file mode 100644
> index 0000000..549f2ad2
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_dbg.c
> @@ -0,0 +1,405 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +#include "hideep_isp.h"
> +#include "hideep_dbg.h"
> +
> +static struct hideep_debug_dev_t *hdd;
> +
> +static int hideep_i2c_recv(unsigned int len)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&hdd->ts->i2c_mutex);
> +	ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
> +	mutex_unlock(&hdd->ts->i2c_mutex);
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	dev_info(&hdd->ts->client->dev, "(%d)", len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_i2c_send(unsigned int len)
> +{
> +	int ret = 0;
> +	unsigned char *buff = hdd->vr_buff;
> +
> +	mutex_lock(&hdd->ts->i2c_mutex);
> +	ret = i2c_master_send(hdd->ts->client, buff, len);
> +	mutex_unlock(&hdd->ts->i2c_mutex);
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	dev_info(&hdd->ts->client->dev, "(%d)", len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_get_vreg(unsigned int addr, unsigned int len)
> +{
> +	int ret = 0;
> +
> +	ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
> +		hdd->vr_buff);
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_set_vreg(unsigned int addr, unsigned int len)
> +{
> +	int ret = 0;
> +	int wr_remain = len;
> +	int vr_addr = addr;
> +	int wr_len = len;
> +	unsigned char *buff = hdd->vr_buff;
> +
> +	do {
> +		if (wr_remain >=  MAX_VR_BUFF)
> +			wr_len = MAX_VR_BUFF;
> +		else
> +			wr_len = wr_remain;
> +
> +		ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
> +			wr_len, buff);
> +		if (ret < 0)
> +			goto i2c_err;
> +
> +		wr_remain -= MAX_VR_BUFF;
> +		vr_addr += MAX_VR_BUFF;
> +		buff += MAX_VR_BUFF;
> +	} while (wr_remain > 0);
> +
> +	dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
> +	return ret;
> +
> +i2c_err:
> +	dev_err(&hdd->ts->client->dev, "i2c_err");
> +	return ret;
> +}
> +
> +static int hideep_download_uc(const char __user *uc, size_t count, int offset)
> +{
> +	int ret;
> +	unsigned char *ucode;
> +
> +	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
> +
> +	if (count > hdd->ts->fw_size) {
> +		dev_err(&hdd->ts->client->dev, "over size data!!!");
> +		return -1;
> +	}
> +
> +	ucode = kmalloc(count, GFP_KERNEL);
> +
> +	ret = copy_from_user(ucode + offset, uc, count);
> +	if (ret < 0) {
> +		dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
> +		kfree(ucode);
> +		return 0;
> +	}
> +
> +	disable_irq(hdd->ts->client->irq);
> +	hdd->ts->interrupt_state = 0;
> +	hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
> +	enable_irq(hdd->ts->client->irq);
> +	hdd->ts->interrupt_state = 1;
> +	kfree(ucode);
> +
> +	dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
> +
> +	return count;
> +}
> +
> +static int hideep_debug_open(struct inode *inode, struct file *file)
> +{
> +	hdd->release_flag = false;
> +
> +	file->private_data = hdd;
> +	dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
> +
> +	return 0;
> +}
> +
> +static int hideep_debug_release(struct inode *inode, struct file *file)
> +{
> +	if (!hdd->release_flag)
> +		return -1;
> +	hdd->release_flag = false;
> +	file->private_data = NULL;
> +	return 0;
> +}
> +
> +static unsigned int hideep_debug_poll(struct file *file,
> +	struct poll_table_struct *wait)
> +{
> +	unsigned int mask = 0;
> +	struct hideep_debug_dev_t *drv_info;
> +
> +	if (file->private_data == NULL)
> +		return 0;
> +
> +	drv_info = file->private_data;
> +
> +	poll_wait(file, &drv_info->i_packet, wait);
> +
> +	if (drv_info->ready) {
> +		disable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 0;
> +		mask |= POLLIN | POLLRDNORM;
> +		drv_info->ready = 0;
> +	}
> +
> +	return mask;
> +}
> +
> +static ssize_t hideep_debug_read(struct file *file, char __user *buf,
> +	size_t count, loff_t *offset)
> +{
> +	int ret = -1;
> +	ssize_t rd_len = 0;
> +	unsigned char *rd_buffer = NULL;
> +	unsigned char *data = NULL;
> +	struct hideep_debug_dev_t *drv_info = file->private_data;
> +
> +	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
> +
> +	if (file->private_data == NULL)
> +		return 0;
> +
> +	drv_info->vr_size = count;
> +	rd_len = count;
> +
> +	data = kmalloc(rd_len, GFP_KERNEL);
> +
> +	if (*offset <= HIDEEP_VR_ADDR_LEN) {
> +		// if offset is not belong to any special command
> +		if ((*offset & HIDEEP_MAX_RAW_LEN) &&
> +			(*offset < HIDEEP_MAX_RAW_LEN) &&
> +			(drv_info->debug_enable == true)) {
> +			mutex_lock(&drv_info->ts->dev_mutex);
> +			rd_buffer = drv_info->img_buff;
> +			ret = 0;
> +			mutex_unlock(&drv_info->ts->dev_mutex);
> +		} else {
> +			ret = hideep_get_vreg(*offset, rd_len);
> +			rd_buffer = drv_info->vr_buff;
> +		}
> +		if (ret < 0)
> +			rd_len = 0;
> +	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
> +		// if offset is belong to special command "i2c bypass"
> +		ret = hideep_i2c_recv(rd_len);
> +		if (ret < 0) {
> +			dev_err(&hdd->ts->client->dev, "ret = %d", ret);
> +			rd_len = 0;
> +		} else {
> +			rd_buffer = drv_info->vr_buff;
> +		}
> +	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
> +		// if offset is belong to special command "nvm download"
> +		rd_len = count;
> +		memset(data, 0x0, rd_len);
> +
> +		disable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 0;
> +		mutex_lock(&drv_info->ts->dev_mutex);
> +		mutex_lock(&drv_info->ts->i2c_mutex);
> +
> +		drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
> +			*offset & 0xfffff);
> +
> +		mutex_unlock(&drv_info->ts->dev_mutex);
> +		mutex_unlock(&drv_info->ts->i2c_mutex);
> +		enable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 1;
> +
> +		rd_buffer = data;
> +	} else {
> +		dev_err(&hdd->ts->client->dev, "undefined address");
> +		kfree(data);
> +		return 0;
> +	}
> +
> +	ret = copy_to_user(buf, rd_buffer, rd_len);
> +
> +	if (drv_info->debug_enable == true && drv_info->ready == 0) {
> +		enable_irq(drv_info->ts->client->irq);
> +		drv_info->ts->interrupt_state = 1;
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&hdd->ts->client->dev, "error : copy_to_user");
> +		kfree(data);
> +		return -EFAULT;
> +	}
> +
> +	kfree(data);
> +	return rd_len;
> +}
> +
> +static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
> +	size_t count, loff_t *offset)
> +{
> +	int ret;
> +	struct hideep_debug_dev_t *drv_info = file->private_data;
> +	int wr_len = 0;
> +
> +	dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
> +	if (file->private_data == NULL)
> +		return 0;
> +
> +	if (*offset <= HIDEEP_VR_ADDR_LEN) {
> +		// if offset is not belong to any special command
> +		wr_len = count;
> +
> +		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
> +		if (ret < 0) {
> +			dev_err(&hdd->ts->client->dev, "error : copy_to_user");
> +			return -EFAULT;
> +		}
> +
> +		ret = hideep_set_vreg(*offset, wr_len);
> +		if (ret < 0)
> +			wr_len = 0;
> +	} else if (*offset & (HIDEEP_I2C_BYPASS)) {
> +		// if offset is belong to special command "i2c bypass"
> +		wr_len = count;
> +		ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
> +		ret = hideep_i2c_send(wr_len);
> +	} else if (*offset & HIDEEP_NVM_DOWNLOAD) {
> +		// if offset is belong to special command "nvm download"
> +		wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
> +	} else {
> +		dev_err(&hdd->ts->client->dev,
> +			"hideep_write : undefined address, 0x%08x",
> +			(int)*offset);
> +		return 0;
> +	}
> +
> +	return wr_len;
> +}
> +
> +static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
> +{
> +	loff_t newpos;
> +	struct hideep_debug_dev_t *drv_info = file->private_data;
> +
> +	dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
> +		(unsigned int)off, whence);
> +	if (file->private_data == NULL)
> +		return -EFAULT;
> +
> +	switch (whence) {
> +	/* SEEK_SET */
> +	case 0:
> +		newpos = off;
> +		break;
> +	/* SEEK_CUR */
> +	case 1:
> +		dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
> +			(unsigned int)off);
> +		if (off & HIDEEP_RELEASE_FLAG) {
> +			dev_dbg(&hdd->ts->client->dev, "set release flag");
> +			drv_info->release_flag = true;
> +		}
> +		newpos = file->f_pos;
> +		break;
> +	/* SEEK_END */
> +	case 2:
> +		newpos = file->f_pos;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (newpos < 0)
> +		return -EINVAL;
> +
> +	file->f_pos = newpos;
> +
> +	return newpos;
> +}
> +
> +static const struct file_operations hideep_debug_fops = {
> +	.owner = THIS_MODULE,
> +	.open = hideep_debug_open,
> +	.poll = hideep_debug_poll,
> +	.release = hideep_debug_release,
> +	.read = hideep_debug_read,
> +	.write = hideep_debug_write,
> +	.llseek = hideep_debug_llseek,
> +};
> +
> +static struct miscdevice hideep_debug_dev = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = HIDEEP_DEBUG_DEVICE_NAME,
> +	.fops = &hideep_debug_fops
> +};
> +
> +void hideep_debug_uninit(void)
> +{
> +	kfree(hdd->vr_buff);
> +	kfree(hdd->img_buff);
> +
> +	misc_deregister(&hideep_debug_dev);
> +}
> +
> +int hideep_debug_init(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +
> +	hdd = &ts->debug_dev;
> +
> +	ret = misc_register(&hideep_debug_dev);
> +	if (ret) {
> +		dev_err(&ts->client->dev,
> +			"hideep debug device register fail!!!");
> +		goto fail;
> +	}
> +
> +	init_waitqueue_head(&hdd->i_packet);
> +
> +	hdd->ts = ts;
> +	hdd->debug_enable = false;
> +
> +	hdd->img_size = 0;
> +	hdd->vr_size = 0;
> +
> +	hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
> +	hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
> +
> +	memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
> +	memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
> +
> +	if (!hdd->vr_buff || !hdd->img_buff)
> +		goto fail;
> +
> +	dev_info(&ts->client->dev, "debug init....");
> +	return 0;
> +
> +fail:
> +	kfree(hdd->vr_buff);
> +	kfree(hdd->img_buff);
> +	return ret;
> +}
> diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
> new file mode 100644
> index 0000000..f18a09f
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_dbg.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_DBG_H
> +#define _LINUX_HIDEEP_DBG_H
> +
> +/* Device Driver <==> Application */
> +/* lseek(), write(), read() command */
> +#define HIDEEP_RELEASE_FLAG				0x8000
> +#define HIDEEP_VR_ADDR_LEN				0xFFFF
> +#define HIDEEP_MAX_RAW_LEN				0x3000
> +#define HIDEEP_READ_WRITE_VR			0xF000
> +#define HIDEEP_I2C_BYPASS				0x20000000
> +
> +#define MAX_VR_BUFF						2048
> +
> +/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
> +#define MAX_RAW_SIZE					7369
> +#endif /* _LINUX_HIDEEP_DBG_H */
> diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
> new file mode 100644
> index 0000000..c8d2e932
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_isp.c
> @@ -0,0 +1,592 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +#include "hideep_isp.h"
> +
> +static struct pgm_packet packet_w;
> +static struct pgm_packet packet_r;
> +
> +static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	if ((len % 4) != 0)
> +		return -1;
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	packet->header.w[0] = htonl((0x80 | (len / 4-1)));
> +	packet->header.w[1] = htonl(addr);
> +
> +	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
> +		packet->payload[i] = htonl(packet->payload[i]);
> +
> +	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
> +		(len+5));
> +
> +	if (ret < 0)
> +		goto err;
> +
> +err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	if ((len % 4) != 0)
> +		return -1;
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	packet->header.w[0] = htonl((0x00 | (len / 4-1)));
> +	packet->header.w[1] = htonl(addr);
> +
> +	ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
> +		5);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
> +		len);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
> +		packet->payload[i] = htonl(packet->payload[i]);
> +
> +err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
> +	unsigned int *val)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet;
> +
> +	packet.header.w[0] = htonl(0x00);
> +	packet.header.w[1] = htonl(addr);
> +
> +	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	*val = packet.payload[0];
> +
> +err:
> +	return ret;
> +}
> +
> +static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
> +	unsigned int data)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet;
> +
> +	packet.header.w[0] = htonl(0x80);
> +	packet.header.w[1] = htonl(addr);
> +	packet.payload[0] = data;
> +
> +	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
> +
> +	return ret;
> +}
> +
> +#define SW_RESET_IN_PGM(CLK) \
> +{ \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
> +}
> +
> +#define SET_FLASH_PIO(CE) \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
> +#define SET_PIO_SIG(X, Y) \
> +	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
> +#define SET_FLASH_HWCONTROL() \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
> +
> +#define NVM_W_SFR(x, y) \
> +{ \
> +	SET_FLASH_PIO(1); \
> +	SET_PIO_SIG(x, y); \
> +	SET_FLASH_PIO(0); \
> +}
> +
> +static void get_dwz_from_binary(unsigned char *pres,
> +	struct dwz_info_t *dwz_info)
> +{
> +	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
> +		sizeof(struct dwz_info_t));
> +}
> +
> +static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
> +{
> +	SW_RESET_IN_PGM(food);
> +}
> +
> +static int hideep_enter_pgm(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +	int retry_count = 10;
> +	int retry = 0;
> +	unsigned int status;
> +	unsigned int pattern = 0xDF9DAF39;
> +
> +	while (retry < retry_count) {
> +		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
> +		mdelay(1);
> +
> +		/* flush invalid Tx load register */
> +		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
> +
> +		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
> +
> +		if (status != htonl(pattern)) {
> +			retry++;
> +			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
> +				status);
> +		} else {
> +			dev_dbg(&ts->client->dev, "found magic code");
> +			break;
> +		}
> +	}
> +
> +	if (retry < retry_count) {
> +		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
> +		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
> +		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
> +		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
> +		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
> +		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
> +		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
> +		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
> +
> +		mdelay(1);
> +	} else {
> +		ret = -1;
> +	}
> +
> +	return ret;
> +}
> +
> +static int hideep_load_dwz(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet_r;
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	mdelay(50);
> +
> +	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
> +		sizeof(struct dwz_info_t));
> +
> +	memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
> +		sizeof(struct dwz_info_t));
> +	hideep_sw_reset(ts, 10);
> +
> +	if (ts->dwz_info->product_code & 0x40) {
> +		/* Crimson IC */
> +		ts->fw_size = 1024 * 48;
> +	} else {
> +		/* default fw size */
> +		ts->fw_size = 1024 * 64;
> +	}
> +
> +	dev_dbg(&ts->client->dev, "firmware release version : %04x",
> +		ts->dwz_info->release_ver);
> +
> +	mdelay(50);
> +
> +	return ret;
> +}
> +
> +static int hideep_nvm_unlock(struct hideep_t *ts)
> +{
> +	int ret = 0;
> +	unsigned int unmask_code = 0;
> +
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
> +
> +	ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
> +
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +
> +	/* make it unprotected code */
> +	unmask_code &= (~_PROT_MODE);
> +
> +	/* compare unmask code */
> +	if (unmask_code != NVM_MASK)
> +		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
> +			unmask_code);
> +
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
> +	SET_FLASH_PIO(0);
> +
> +	NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
> +	SET_FLASH_HWCONTROL();
> +	ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +
> +	return ret;
> +}
> +
> +static int hideep_program_page(struct hideep_t *ts,
> +	unsigned int addr, struct pgm_packet *packet_w)
> +{
> +	int ret = 0;
> +	unsigned int pio_cmd = WRONLY;
> +	unsigned int pio_cmd_page_erase = PERASE;
> +	unsigned int status;
> +	int time_out = 0;
> +	int inf_en = 0;
> +	unsigned int end_flag = 124;
> +#ifndef PGM_BURST_WR
> +	unsigned int i;
> +#endif
> +
> +	hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +	ret = (status == 0) ? -1:0;
> +
> +	addr = addr & ~(NVM_PAGE_SIZE - 1);
> +
> +	if (addr > INF_SECTION) {
> +		/* added INF flag set in pio_cmd */
> +		addr -= INF_SECTION;
> +		pio_cmd |= INF;
> +		pio_cmd_page_erase |= INF;
> +		inf_en = 1;
> +	}
> +
> +	SET_FLASH_PIO(0);
> +	SET_FLASH_PIO(1);
> +
> +	/* first erase */
> +	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
> +
> +	SET_FLASH_PIO(0);
> +	time_out = 0;
> +
> +	while (1) {
> +		mdelay(1);
> +		hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +		if ((status) != 0)
> +			break;
> +		if (time_out++ > 100)
> +			break;
> +	}
> +	SET_FLASH_PIO(1);
> +	/* first erase end*/
> +
> +	SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
> +
> +#ifdef PGM_BURST_WR
> +	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
> +		packet_w, NVM_PAGE_SIZE);
> +#else
> +	for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
> +		SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
> +#endif
> +	if (inf_en == 0)
> +		SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
> +	else
> +		SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
> +
> +	SET_FLASH_PIO(0);
> +
> +	mdelay(1);
> +
> +	while (1) {
> +		hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +		if ((status) != 0)
> +			break;
> +	}
> +	/* write routine end */
> +
> +	SET_FLASH_HWCONTROL();
> +
> +	return ret;
> +}
> +
> +static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
> +	int len, int offset, unsigned char *old_fw)
> +{
> +	int i;
> +	int ret = 0;
> +	int len_r;
> +	int len_w;
> +	int addr = 0;
> +	unsigned int pages;
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	hideep_nvm_unlock(ts);
> +
> +	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	addr = offset;
> +	len_r = len;
> +	len_w = len_r;
> +
> +	dev_dbg(&ts->client->dev, "pages : %d", pages);
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_w = NVM_PAGE_SIZE;
> +
> +		/* compare */
> +		if (old_fw != NULL)
> +			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
> +
> +		if (ret != 0 || old_fw == NULL) {
> +			/* write page */
> +			memcpy(packet_w.payload, &(ucode[addr]), len_w);
> +
> +			ret = hideep_program_page(ts, addr, &packet_w);
> +			mdelay(1);
> +			if (ret < 0)
> +				dev_err(&ts->client->dev,
> +					"hideep_program_nvm : error(%08x):",
> +					addr);
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_w = len_r;
> +	}
> +
> +	return ret;
> +}
> +
> +static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
> +	int len, int offset)
> +{
> +	int i, j;
> +	int ret = 0;
> +	unsigned char page_chk = 0;
> +	unsigned int addr = offset;
> +	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	int len_r = len;
> +	int len_v = len_r;
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_v = NVM_PAGE_SIZE;
> +
> +#ifdef PGM_BURST_WR
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +#else
> +		for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
> +			hideep_pgm_r_reg(ts, addr + (j << 2),
> +				&(packet_r.payload[j]));
> +#endif
> +		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
> +
> +		if (page_chk != 0) {
> +			u8 *read = (u8 *)packet_r.payload;
> +
> +			for (j = 0; j < NVM_PAGE_SIZE; j++)
> +				dev_err(&ts->client->dev, "%02x : %02x",
> +						ucode[addr+j], read[j]);
> +
> +			dev_err(&ts->client->dev, "verify : error(addr : %d)",
> +				addr);
> +
> +			ret = -1;
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_v = len_r;
> +	}
> +
> +	return ret;
> +}
> +
> +static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
> +	int offset)
> +{
> +	int ret = 0;
> +	int pages, i;
> +	int len_r, len_v;
> +	int addr = offset;
> +#ifndef PGM_BURST_WR
> +	int j;
> +#endif
> +
> +	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	len_r = len;
> +	len_v = len_r;
> +
> +	ts->hideep_api->reset_ic(ts);
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	hideep_nvm_unlock(ts);
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_v = NVM_PAGE_SIZE;
> +
> +#ifdef PGM_BURST_WR
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +#else
> +		for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
> +			hideep_pgm_r_reg(ts, addr + (j << 2),
> +				&(packet_r.payload[j]));
> +#endif
> +		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_v = len_r;
> +	}
> +
> +	hideep_sw_reset(ts, 1000);
> +}
> +
> +static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
> +	size_t len, int offset)
> +{
> +	int ret = 0;
> +	int retry = 3;
> +
> +	while (retry--) {
> +		ret = hideep_verify_nvm(ts, fw, len, offset);
> +		if (ret == 0) {
> +			dev_dbg(&ts->client->dev, "update success");
> +			break;
> +		}
> +		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
> +	}
> +
> +	ret = (retry == 0) ? -1:0;
> +
> +	return ret;
> +}
> +
> +static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
> +	int len, int offset, bool mode)
> +{
> +	int ret = 0;
> +	int firm_len;
> +	unsigned char *ic_fw;
> +	unsigned char *dwz_info;
> +
> +	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
> +	dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
> +
> +	memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
> +
> +	firm_len = len;
> +	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
> +		firm_len, ts->fw_size);
> +
> +	if (firm_len > ts->fw_size)
> +		firm_len = ts->fw_size;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	/* memory dump of target IC */
> +	hideep_read_nvm(ts, ic_fw, firm_len, offset);
> +	/* comparing & programming each page, if the memory of specified
> +	 * page is exactly same, no need to update.
> +	 */
> +	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
> +
> +#ifdef PGM_VERIFY
> +	hideep_fw_verify_run(ts, code, firm_len, offset);
> +	if (ret < 0) {
> +		if (mode == true) {
> +			/* clear dwz version, it will be store once again
> +			 * after update success
> +			 */
> +			ts->dwz_info->release_ver = 0;
> +			memcpy(&dwz_info[64], ts->dwz_info,
> +				sizeof(struct dwz_info_t));
> +			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
> +				0x280, NULL);
> +		}
> +	}
> +#endif
> +	get_dwz_from_binary(code, ts->dwz_info);
> +
> +	hideep_sw_reset(ts, 1000);
> +
> +	kfree(ic_fw);
> +	kfree(dwz_info);
> +
> +	return ret;
> +}
> +
> +static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
> +{
> +	int ret = 0;
> +	const struct firmware *fw_entry;
> +	unsigned char *fw_buf;
> +	unsigned int fw_length;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
> +
> +	if (ret != 0) {
> +		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
> +		return ret;
> +	}
> +
> +	fw_buf = (unsigned char *)fw_entry->data;
> +	fw_length = (unsigned int)fw_entry->size;
> +
> +	/* chip specific code for flash fuse */
> +	mutex_lock(&ts->dev_mutex);
> +
> +	ts->dev_state = state_updating;
> +
> +	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
> +
> +	ts->dev_state = state_normal;
> +
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	release_firmware(fw_entry);
> +
> +	return ret;
> +}
> +
> +static int hideep_update_part_firmware(struct hideep_t *ts,
> +	unsigned char *code, int len, int offset, bool mode)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	ret = hideep_wr_firmware(ts, code, len, offset, false);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return ret;
> +}
> +
> +void hideep_isp_init(struct hideep_t *ts)
> +{
> +	ts->hideep_api->update_all = hideep_update_all_firmware;
> +	ts->hideep_api->update_part = hideep_update_part_firmware;
> +	ts->hideep_api->get_dwz_info = hideep_load_dwz;
> +	ts->hideep_api->sp_func = hideep_read_nvm;
> +}
> diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
> new file mode 100644
> index 0000000..648f271
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_isp.h
> @@ -0,0 +1,96 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_ISP_H
> +#define _LINUX_HIDEEP_ISP_H
> +
> +#define PGM_BURST_WR
> +#define PGM_VERIFY
> +
> +#define NVM_DEFAULT_PAGE		0
> +#define NVM_SFR_WPAGE			1
> +#define NVM_SFR_RPAGE			2
> +
> +#define PIO_SIG					0x00400000
> +#define _PROT_MODE				0x03400000
> +
> +#define NVM_PAGE_SIZE			128
> +
> +#define HIDEEP_NVM_DOWNLOAD		0x10000000
> +
> +/*************************************************************************
> + * register map
> + *************************************************************************/
> +#define YRAM_BASE				0x40000000
> +#define PERIPHERAL_BASE			0x50000000
> +#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
> +#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
> +#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
> +
> +#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
> +#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
> +#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
> +#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
> +#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
> +#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
> +#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
> +#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
> +#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
> +
> +#define FLASH_CON				(FLASH_BASE + 0x0000)
> +#define FLASH_STA				(FLASH_BASE + 0x0004)
> +#define FLASH_CFG				(FLASH_BASE + 0x0008)
> +#define FLASH_TIM				(FLASH_BASE + 0x000C)
> +#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
> +
> +#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
> +
> +/*************************************************************************
> + * flash commands
> + *************************************************************************/
> +#define MERASE					0x00010000
> +#define SERASE					0x00020000
> +#define PERASE					0x00040000
> +#define PROG					0x00080000
> +#define WRONLY					0x00100000
> +#define INF						0x00200000
> +
> +/*************************************************************************
> + * NVM Mask
> + *************************************************************************/
> +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
> +#define NVM_MASK_OFS			0x0000000C
> +#define NVM_MASK				0x00310000
> +#define INF_SECTION				0x0000C000
> +#endif
> +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
> +#define NVM_MASK_OFS			0x0000000C
> +#define NVM_MASK				0x0030027B
> +#define INF_SECTION				0x00010000
> +#endif
> +
> +/*************************************************************************
> + * DWZ info
> + *************************************************************************/
> +#define HIDEEP_BOOT_SECTION		0x00000400
> +#define HIDEEP_BOOT_LEN			0x00000400
> +#define HIDEEP_DWZ_SECTION		0x00000280
> +#define HIDEEP_DWZ_INFO_OFS		0x000002C0
> +#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
> +							- HIDEEP_DWZ_SECTION)
> +
> +struct pgm_packet {
> +	union {
> +		unsigned char b[8];
> +		unsigned int w[2];
> +	} header;
> +
> +	unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
> +};
> +
> +#endif /* _LINUX_HIDEEP_ISP_H */
> diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
> new file mode 100644
> index 0000000..7f28fb4
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_sysfs.c
> @@ -0,0 +1,245 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include "hideep.h"
> +
> +static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int mode, ret;
> +	char *fw_name;
> +
> +	ret = kstrtoint(buf, 8, &mode);
> +	if (ret)
> +		return ret;
> +
> +	if (mode == 1) {
> +		disable_irq(ts->client->irq);
> +
> +		ts->dev_state = state_updating;
> +		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
> +			ts->dwz_info->product_id);
> +		ret = ts->hideep_api->update_all(ts, fw_name);
> +
> +		kfree(fw_name);
> +
> +		enable_irq(ts->client->irq);
> +
> +		ts->dev_state = state_normal;
> +		if (ret != 0)
> +			dev_err(dev, "The firmware update failed(%d)", ret);
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t fw_version_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "release version : %04x",
> +		ts->dwz_info->release_ver);
> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", ts->dwz_info->release_ver);
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static ssize_t product_id_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "product id : %04x",
> +		ts->dwz_info->product_id);
> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", ts->dwz_info->product_id);
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static ssize_t power_status(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int len;
> +
> +	len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
> +		(ts->dev_state == state_init) ? "off" : "on");
> +
> +	return len;
> +}
> +
> +static ssize_t power_control(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int on, ret;
> +
> +	ret = kstrtoint(buf, 8, &on);
> +	if (ret)
> +		return ret;
> +
> +	if (on) {
> +		ts->hideep_api->power(ts, on);
> +		ts->dev_state = state_normal;
> +		ts->hideep_api->reset_ic(ts);
> +	} else {
> +		ts->hideep_api->power(ts, on);
> +		ts->dev_state = state_init;
> +	}
> +
> +	return count;
> +}
> +
> +#ifdef HIDEEP_SUPPORT_STYLUS
> +static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int len;
> +	unsigned char status[1];
> +
> +	ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
> +
> +	len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
> +		(status[0] == 0x00) ? "off" : "on");
> +
> +	return len;
> +}
> +
> +static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int on, ret;
> +	unsigned char data[2];
> +
> +	ret = kstrtoint(buf, 8, &on);
> +	if (ret)
> +		return ret;
> +
> +	data[0] = 0x04;
> +
> +	if (on) {
> +		data[1] = 0x01;
> +		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
> +	} else {
> +		data[1] = 0x00;
> +		ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
> +	}
> +
> +	return count;
> +}
> +#endif
> +
> +static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int len;
> +
> +	len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
> +		(ts->debug_dev.debug_enable == 0) ? "off" : "on");
> +
> +	return len;
> +}
> +
> +static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
> +	const char *buf, size_t count)
> +{
> +	struct hideep_t *ts = dev_get_drvdata(dev);
> +	int on, ret;
> +	unsigned char data[2];
> +
> +	ret = kstrtoint(buf, 8, &on);
> +	if (ret)
> +		return ret;
> +
> +	if (on) {
> +		ts->debug_dev.debug_enable = 1;
> +		ts->dev_state = state_debugging;
> +	} else {
> +		ts->debug_dev.debug_enable = 0;
> +		ts->dev_state = state_normal;
> +		/* set touch mode */
> +		data[0] = 0x00;
> +		data[1] = 0x00;
> +		ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
> +		if (ts->interrupt_state == 0) {
> +			data[0] = 0x5A;
> +			ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
> +				data);
> +			enable_irq(ts->client->irq);
> +			ts->interrupt_state = 1;
> +		}
> +	}
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
> +static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
> +static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
> +static DEVICE_ATTR(power_en, 0664, power_status, power_control);

Why does this need to be exported?

> +static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);

I do not think we want it in mainline.

> +#ifdef HIDEEP_SUPPORT_STYLUS
> +static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);

Why do we need to disable stylus?

> +#endif
> +
> +static struct attribute *hideep_ts_sysfs_entries[] = {
> +	&dev_attr_update_fw.attr,
> +	&dev_attr_version.attr,
> +	&dev_attr_product_id.attr,
> +	&dev_attr_power_en.attr,
> +	&dev_attr_debug_en.attr,
> +#ifdef HIDEEP_SUPPORT_STYLUS
> +	&dev_attr_stylus_en.attr,
> +#endif
> +	NULL
> +};
> +
> +static struct attribute_group hideep_ts_attr_group = {
> +	.attrs = hideep_ts_sysfs_entries,
> +};
> +
> +int hideep_sysfs_init(struct hideep_t *ts)
> +{
> +	int ret;
> +	struct i2c_client *client = ts->client;
> +
> +	/* Create the files associated with this kobject */
> +	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
> +
> +	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
> +
> +	if (ret)
> +		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
> +			 __func__, ret);
> +
> +	return ret;
> +}
> +
> +int hideep_sysfs_exit(struct hideep_t *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +
> +	sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
> +
> +	return 0;
> +}
> -- 
> 2.7.4
> 

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-08-09 23:49           ` Dmitry Torokhov
@ 2017-08-10  8:01             ` Anthony Kim
  0 siblings, 0 replies; 29+ messages in thread
From: Anthony Kim @ 2017-08-10  8:01 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: robh+dt, mark.rutland, rydberg, linux-input, devicetree

Hi Dmitry,

Thank you very much for your mention.
I  feel to need answer of some your mention, so I reply.
And I will modify for all your mentions and resend patch soon.
Sure, I will apply most of the content your mentioned.

Thank you.
Anthony.

2017-08-10 8:49 GMT+09:00 Dmitry Torokhov <dmitry.torokhov@gmail.com>:
> Hi Anthony,
>
> On Tue, Jul 25, 2017 at 03:53:27PM +0900, Anthony Kim wrote:
>> The HiDeep touchscreen device is a capacitive multi-touch controller
>> mainly for multi-touch supported devices use. It use I2C interface for
>> communication to IC and provide axis X, Y, Z locations for ten finger
>> touch through input event interface to userspace.
>>
>> It support the Crimson and the Lime two type IC. They are different
>> the number of channel supported and FW size. But the working protocol
>> is same.
>>
>> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
>> ---
>>  .../bindings/input/touchscreen/hideep.txt          |  35 +
>>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>>  drivers/input/touchscreen/Kconfig                  |  11 +
>>  drivers/input/touchscreen/Makefile                 |   2 +
>>  drivers/input/touchscreen/hideep.h                 | 318 +++++++
>>  drivers/input/touchscreen/hideep_core.c            | 916 +++++++++++++++++++++
>>  drivers/input/touchscreen/hideep_dbg.c             | 405 +++++++++
>>  drivers/input/touchscreen/hideep_dbg.h             |  24 +
>>  drivers/input/touchscreen/hideep_isp.c             | 592 +++++++++++++
>>  drivers/input/touchscreen/hideep_isp.h             |  96 +++
>>  drivers/input/touchscreen/hideep_sysfs.c           | 245 ++++++
>>  11 files changed, 2645 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>>  create mode 100644 drivers/input/touchscreen/hideep.h
>>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>>  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
>>  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
>>  create mode 100644 drivers/input/touchscreen/hideep_isp.c
>>  create mode 100644 drivers/input/touchscreen/hideep_isp.h
>>  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
>>
>> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>> new file mode 100644
>> index 0000000..5270426
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>> @@ -0,0 +1,35 @@
>> +* HiDeep Finger and Stylus touchscreen controller
>> +
>> +Required properties:
>> +- compatible         : must be "hideep,hideep-crimson"
>> +                                     or "hideep,hideep-lime".
>> +- reg                        : I2C slave address, (e.g. 0x6C).
>> +- interrupt-parent : Interrupt controller to which the chip is connected.
>> +- interrupts : Interrupt to which the chip is connected.
>> +
>> +Optional properties:
>> +- vdd-supply : It is the controller supply for controlling
>> +                                      main voltage(3.3V) through the regulator.
>> +- vid-supply : It is the controller supply for controlling
>> +                                     IO voltage(1.8V) through the regulator.
>> +- reset-gpios        : Define for reset gpio pin.
>> +                                             It is to use for reset IC.
>> +- hideep,max-coords  : Max value for axis X, Y, W, Z.
>> +
>> +Example:
>> +
>> +i2c@00000000 {
>> +
>> +     /* ... */
>> +
>> +     touchscreen@6c {
>> +             compatible = "hideep,hideep-lime";
>> +             reg = <0x6c>;
>> +             interrupt-parent = <&gpx1>;
>> +             interrupts = <2 0>;
>> +             vdd-supply = <&ldo15_reg>";
>> +             vid-supply = <&ldo18_reg>;
>> +             reset-gpios = <&gpx1 5 0>;
>> +             hideep,max-coords = <1080 1920 65535 65535>;
>> +     };
>> +};
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index c03d201..aa2a301 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -131,6 +131,7 @@ gw        Gateworks Corporation
>>  hannstar     HannStar Display Corporation
>>  haoyu        Haoyu Microelectronic Co. Ltd.
>>  hardkernel   Hardkernel Co., Ltd
>> +hideep       HiDeep Inc.
>>  himax        Himax Technologies, Inc.
>>  hisilicon    Hisilicon Limited.
>>  hit  Hitachi Ltd.
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 64b30fe..13e11c7 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
>>         To compile this driver as a module, choose M here: the
>>         module will be called bu21023_ts.
>>
>> +config TOUCHSCREEN_HIDEEP
>> +     tristate "HiDeep Touch IC"
>> +     depends on I2C
>> +     help
>> +       Say Y here if you have a touchscreen using HiDeep.
>> +
>> +       If unsure, say N.
>> +
>> +       To compile this driver as a moudle, choose M here : the
>> +       module will be called hideep_ts.
>> +
>>  endif
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index 6badce8..3aab466 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -103,3 +103,5 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223) += zet6223.o
>>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)     += zforce_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)       += colibri-vf50-ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)       += rohm_bu21023.o
>> +obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_ts.o
>> +hideep_ts-$(CONFIG_TOUCHSCREEN_HIDEEP) := hideep_core.o hideep_sysfs.o hideep_isp.o hideep_dbg.o
>> diff --git a/drivers/input/touchscreen/hideep.h b/drivers/input/touchscreen/hideep.h
>> new file mode 100644
>> index 0000000..50306a1
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep.h
>> @@ -0,0 +1,318 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#ifndef _LINUX_HIDEEP_H
>> +#define _LINUX_HIDEEP_H
>> +
>> +/*************************************************************************
>> + * this is include special HEAD file.
>> + *************************************************************************/
>> +#ifdef CONFIG_FB
>> +#include <linux/fb.h>
>> +#include <linux/notifier.h>
>> +#endif
>> +
>> +/*************************************************************************
>> + * this is include normal HEAD file.
>> + *************************************************************************/
>> +#include <linux/of_gpio.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/input.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/proc_fs.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/firmware.h>
>> +#include <linux/pinctrl/consumer.h>
>> +#include <linux/pinctrl/pinctrl.h>
>> +#include <linux/pinctrl/machine.h>
>> +#include <linux/kthread.h>
>> +#include <linux/random.h>
>> +#include <linux/slab.h>
>> +#include <linux/sched.h>
>> +#include <linux/sched/rt.h>
>> +#include <linux/task_work.h>
>> +#include <linux/rtc.h>
>> +#include <linux/syscalls.h>
>> +#include <linux/timer.h>
>> +#include <linux/time.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/input/mt.h>
>> +#include <linux/fs.h>
>> +#include <linux/completion.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/miscdevice.h>
>> +#include <linux/init.h>
>> +#include <linux/gfp.h>
>> +#include <linux/kobject.h>
>> +#include <linux/string.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/module.h>
>> +#include <linux/poll.h>
>> +#include <linux/fcntl.h>
>> +#include <linux/unistd.h>
>> +#include <linux/version.h>
>> +#include <linux/acpi.h>
>> +
>> +/*************************************************************************
>> + * definition part.
>> + * define is (open, set, enable) if not, is (close, clear, disable)
>> + * some special switch of functions.
>> + *************************************************************************/
>> +
>> +/* HIDEEP_PROTOCOL_2_0 is for protocol 2.0. */
>> +#define HIDEEP_PROTOCOL_2_0
>
> We should support both protocols simultaneously instead of being compile
> time option.
>
>> +
>> +/* HIDEEP_TYPE_B_PROTOCOL is for input_dev, if define , using TYPE_B,
>> + * otherwise TYPE_A.
>> + */
>> +#define HIDEEP_TYPE_B_PROTOCOL
>
> We only need Type-B support in new drivers.
>
>> +
>> +/* HIDEEP_DWZ_VERSION_CHECK if define, it will check dwz version. */
>> +#define HIDEEP_DWZ_VERSION_CHECK
>
> Why is this conditional?
>
>> +
>> +/* HIDEEP_SUPPORT_KE if define, it will use key button. */
>> +#define HIDEEP_SUPPORT_KEY
>
> Why is this compile-time decision?
>
>> +
>> +/* HIDEEP_SUPPORT_STYLUS if define, it will use stylus mode. */
>> +#define HIDEEP_SUPPORT_STYLUS
>
> Same here. We are looking for drivers to be usable on multitude of
> boards. The data should be comping form the board desciption (device
> tree, ACPI) so that the same kernel can work on multitude of boards.
>

Ok, got it. I will check again for many condition of my driver code and
modify all for these.

>> +
>> +/*************************************************************************
>> + * Buffer size
>> + *************************************************************************/
>> +#define FRAME_HEADER_SIZE                            8
>> +/* if size of system i2c buffer is smaller than this value, need to modify */
>> +#define MAX_I2C_BUFFER_SIZE                          512
>> +
>> +/*************************************************************************
>> + * board porting config
>> + *************************************************************************/
>> +#define HIDEEP_DEBUG_DEVICE_NAME             "hideep_debug"
>
> Why is this needed? I normally recommend userspace access over /dev/i2c
> if tuning is needed so that we do not have a large chunk of effectively
> dead code in the driver in production images.

Through the device, I get raw data of IC and access to registry of IC for
debugging and data analysis. Could I not use this device? If can not use,
I will remove it.

>
>> +#define HIDEEP_TS_NAME                                       "HiDeep Touchscreen"
>> +#define HIDEEP_I2C_NAME                                      "hideep_ts"
>> +
>> +/*************************************************************************
>> + * register addr
>> + *************************************************************************/
>> +/* Touch & key event */
>> +#define HIDEEP_EVENT_COUNT_ADDR                      0x240
>> +#define HIDEEP_TOUCH_DATA_ADDR                       0x242
>> +#define HIDEEP_KEY_DATA_ADDR                 0x2A6
>> +#define HIDEEP_RAW_DATA_ADDR                 0x1000
>> +
>> +/* command list */
>> +#define HIDEEP_RESET_CMD                             0x9800
>> +#define HIDEEP_INTCLR_CMD                            0x9802
>> +#define HIDEEP_OPMODE_CMD                            0x9804
>> +#define HIDEEP_SWTICH_CMD                            0x9805
>> +#define HIDEEP_SLEEP_CMD                             0x980D
>> +
>> +/*************************************************************************
>> + * multi-touch & key definitions.
>> + *************************************************************************/
>> +#define HIDEEP_MT_MAX                                        10
>> +#define HIDEEP_KEY_MAX                                       3
>> +
>> +/* multi touch event bit */
>> +#define HIDEEP_MT_ALWAYS_REPORT                      0
>> +#define HIDEEP_MT_TOUCHED                            1
>> +#define HIDEEP_MT_FIRST_CONTACT                      2
>> +#define HIDEEP_MT_DRAG_MOVE                          3
>> +#define HIDEEP_MT_RELEASED                           4
>> +#define HIDEEP_MT_PINCH                                      5
>> +#define HIDEEP_MT_PRESSURE                           6
>> +
>> +/* key event bit */
>> +#define HIDEEP_KEY_RELEASED                          0x20
>> +#define HIDEEP_KEY_PRESSED                           0x40
>> +#define HIDEEP_KEY_FIRST_PRESSED             0x80
>> +#define HIDEEP_KEY_PRESSED_MASK                      0xC0
>> +
>> +/*************************************************************************
>> + * HIDEEP PROTOCOL
>> + *************************************************************************/
>> +#ifdef HIDEEP_PROTOCOL_2_0
>> +struct hideep_mt_t {
>
> Just drop _t in structure names. You can see that you are dealing with
> the type because there is "struct" preceding the name.
>
>> +     unsigned short x;
>> +     unsigned short y;
>> +     unsigned short z;
>> +     unsigned char w;
>> +     unsigned char flag;
>> +     unsigned char type;
>> +     unsigned char index;
>> +};
>> +#else
>> +struct hideep_mt_t {
>> +     unsigned char flag;
>> +     unsigned char index;
>> +     unsigned short x;
>> +     unsigned short y;
>> +     unsigned char z;
>> +     unsigned char w;
>> +};
>> +#endif
>> +
>> +struct hideep_key_t {
>> +     unsigned char flag;
>> +     unsigned int key;
>> +};
>> +
>> +/*************************************************************************
>> + * IC State
>> + *************************************************************************/
>> +enum e_dev_state {
>> +     state_init = 1,
>> +     state_normal,
>> +     state_sleep,
>> +     state_updating,
>> +     state_debugging,
>> +};
>> +
>> +/*************************************************************************
>> + * Firmware info
>> + *************************************************************************/
>> +struct dwz_info_t {
>> +     unsigned int code_start;
>> +     unsigned char code_crc[12];
>> +
>> +     unsigned int c_code_start;
>> +     unsigned short c_code_len;
>> +     unsigned short gen_ver;
>
> I think you need to annotate with proper endianness (__le16 I assume)
> and convert to CPU endianness when using. Same goes for other
> firmware/config/nvm data.
>
>> +
>> +     unsigned int vr_start;
>> +     unsigned short vr_len;
>> +     unsigned short rsv0;
>> +
>> +     unsigned int ft_start;
>> +     unsigned short ft_len;
>> +     unsigned short vr_version;
>> +
>> +     unsigned short boot_ver;
>> +     unsigned short core_ver;
>> +     unsigned short custom_ver;
>> +     unsigned short release_ver;
>> +
>> +     unsigned char factory_id;
>> +     unsigned char panel_type;
>> +     unsigned char model_name[6];
>> +     unsigned short product_code;
>> +     unsigned short extra_option;
>> +
>> +     unsigned short product_id;
>> +     unsigned short vendor_id;
>> +};
>> +
>> +/*************************************************************************
>> + * driver information for hideep_t by device tree
>> + *************************************************************************/
>> +struct hideep_platform_data_t {
>> +     unsigned int max_x;
>> +     unsigned int max_y;
>> +     unsigned int max_z;
>> +     unsigned int max_w;
>> +
>> +#ifdef CONFIG_OF
>> +     int reset_gpio;
>> +
>> +     struct regulator *vcc_vdd;
>> +     struct regulator *vcc_vid;
>> +#endif
>> +};
>> +
>> +struct hideep_debug_dev_t {
>> +     struct miscdevice misc;
>> +
>> +     wait_queue_head_t i_packet;
>> +
>> +     unsigned int ready;
>> +     unsigned char *vr_buff;
>> +     unsigned char *img_buff;
>> +     unsigned short vr_size;
>> +     unsigned short img_size;
>> +
>> +     bool debug_enable;
>> +     bool release_flag;
>> +
>> +     struct hideep_t *ts;
>> +};
>> +
>> +struct hideep_function_list_t {
>> +     //core
>> +     int (*i2c_read)(struct hideep_t *, unsigned short, unsigned short,
>> +             unsigned char *);
>> +     int (*i2c_write)(struct hideep_t *, unsigned short, unsigned short,
>> +             unsigned char *);
>> +     void (*reset_ic)(struct hideep_t *);
>> +     void (*power)(struct hideep_t *, int);
>> +
>> +     //isp
>> +     int (*update_all)(struct hideep_t *, const char *);
>> +     int (*update_part)(struct hideep_t *, unsigned char *, int, int, bool);
>> +     int (*get_dwz_info)(struct hideep_t *);
>> +     void (*sp_func)(struct hideep_t *, unsigned char *, int, int);
>> +};
>> +
>> +struct hideep_t {
>> +     struct i2c_client *client;
>> +     struct input_dev *input_dev;
>> +     struct hideep_platform_data_t *p_data;
>> +
>> +     struct mutex dev_mutex;
>> +     struct mutex i2c_mutex;
>> +
>> +     struct hideep_debug_dev_t debug_dev;
>> +     struct hideep_function_list_t *hideep_api;
>> +
>> +     struct task_struct *hthread_event;
>
> Why do you need both threaded interrupt and kernel therad? The threaded
> interrupt should suffice.
>

Sometimes, my driver interrupt process time was long at some system,
so I used kernel thread for less interrupt process time. If there have
any problem,
I will use only interrupt thread.

>> +
>> +     bool suspended;
>> +     enum e_dev_state dev_state;
>> +
>> +#ifdef CONFIG_FB
>> +     struct notifier_block fb_notif;
>> +#endif
>> +
>> +     long tch_bit;
>> +     unsigned int tch_count;
>> +     unsigned int key_count;
>> +     unsigned int lpm_count;
>> +
>> +     struct hideep_mt_t touch_evt[HIDEEP_MT_MAX];
>> +
>> +#ifdef HIDEEP_SUPPORT_KEY
>> +     struct hideep_key_t key_evt[HIDEEP_KEY_MAX];
>> +     int key_codes[HIDEEP_KEY_MAX];
>> +#endif
>> +
>> +     unsigned char i2c_buf[256];
>> +     struct dwz_info_t *dwz_info;
>> +
>> +     int interrupt_state;
>> +     int fw_size;
>> +};
>> +
>> +/*************************************************************************
>> + * function define
>> + *************************************************************************/
>> +int hideep_debug_init(struct hideep_t *ts);
>> +void hideep_debug_uninit(void);
>> +
>> +int hideep_sysfs_init(struct hideep_t *ts);
>> +int hideep_sysfs_exit(struct hideep_t *ts);
>> +
>> +void hideep_isp_init(struct hideep_t *ts);
>> +
>> +#endif
>> diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
>> new file mode 100644
>> index 0000000..d1b2c44
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep_core.c
>> @@ -0,0 +1,916 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#include "hideep.h"
>> +
>> +#define GET_PAYLOAD_SIZE_FROM_HEADER(x) \
>> +     ((x[1]>>6) ? ((x[2]*256+x[3])*(x[7]+1)):(x[2]*x[3]*(x[7]+1)))
>> +
>> +static int hideep_pwr_on(struct hideep_t *ts)
>> +{
>> +     struct hideep_platform_data_t *pdata;
>> +     int ret = 0;
>> +
>> +     pdata = ts->p_data;
>> +
>> +#ifdef CONFIG_OF
>> +     if (!IS_ERR(pdata->vcc_vdd)) {
>> +             dev_info(&ts->client->dev, "hideep:vcc_vdd is enable");
>> +             ret = regulator_enable(pdata->vcc_vdd);
>> +             if (ret)
>> +                     dev_err(&ts->client->dev,
>> +                             "Regulator vdd enable failed ret=%d", ret);
>> +     }
>> +     usleep_range(999, 1000);
>> +
>> +     if (!IS_ERR(pdata->vcc_vid)) {
>> +             dev_info(&ts->client->dev, "hideep:vcc_vid is enable");
>> +             ret = regulator_enable(pdata->vcc_vid);
>> +             if (ret)
>> +                     dev_err(&ts->client->dev,
>> +                             "Regulator vcc_vid enable failed ret=%d", ret);
>> +     }
>> +     usleep_range(2999, 3000);
>> +#endif
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_pwr_off(struct hideep_t *ts)
>> +{
>> +     struct hideep_platform_data_t *pdata;
>> +     int ret = 0;
>> +
>> +     pdata = ts->p_data;
>> +
>> +#ifdef CONFIG_OF
>> +     if (pdata->reset_gpio > 0) {
>> +             dev_info(&ts->client->dev, "hideep:disable the reset_gpio");
>> +             gpio_set_value(pdata->reset_gpio, 0);
>> +     }
>> +
>> +     if (!IS_ERR(pdata->vcc_vid)) {
>> +             dev_info(&ts->client->dev, "hideep:vcc_vid is disable");
>> +             ret = regulator_disable(pdata->vcc_vid);
>> +             if (ret)
>> +                     dev_err(&ts->client->dev,
>> +                             "Regulator vcc_vid enable failed ret=%d", ret);
>> +     }
>> +
>> +     if (!IS_ERR(pdata->vcc_vdd)) {
>> +             dev_info(&ts->client->dev, "hideep:vcc_vdd is disable");
>> +             ret = regulator_disable(pdata->vcc_vdd);
>> +             if (ret)
>> +                     dev_err(&ts->client->dev,
>> +                             "Regulator vdd disable failed ret=%d", ret);
>> +     }
>> +#endif
>> +     return ret;
>> +}
>> +
>> +static void hideep_power(struct hideep_t *ts, int on)
>> +{
>> +     int ret = 0;
>> +
>> +     if (on) {
>> +             dev_info(&ts->client->dev, "power on");
>> +             ret = hideep_pwr_on(ts);
>> +     } else {
>> +             dev_info(&ts->client->dev, "power off");
>> +             ret = hideep_pwr_off(ts);
>> +     }
>> +}
>> +
>> +static int hideep_i2c_read(struct hideep_t *ts, unsigned short addr,
>> +     unsigned short len, unsigned char *buf)
>> +{
>> +     int ret = -1;
>> +     unsigned short r_len, raddr;
>> +     int length;
>> +
>> +     length = len;
>> +     raddr = addr;
>> +
>> +     dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
>> +
>> +     mutex_lock(&ts->i2c_mutex);
>> +
>> +     do {
>> +             if (length > MAX_I2C_BUFFER_SIZE)
>> +                     r_len = MAX_I2C_BUFFER_SIZE;
>> +             else
>> +                     r_len = length;
>> +
>> +             dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
>> +                     raddr, r_len);
>> +
>> +             ret = i2c_master_send(ts->client, (char *)&raddr, 2);
>> +
>> +             if (ret < 0)
>> +                     goto i2c_err;
>> +
>> +             ret = i2c_master_recv(ts->client, (char *)buf, r_len);
>> +             length -= MAX_I2C_BUFFER_SIZE;
>> +             buf += MAX_I2C_BUFFER_SIZE;
>> +             raddr += MAX_I2C_BUFFER_SIZE;
>> +
>> +             if (ret < 0)
>> +                     goto i2c_err;
>> +     } while (length > 0);
>> +
>> +     mutex_unlock(&ts->i2c_mutex);
>> +
>> +     return  0;
>> +i2c_err:
>> +     mutex_unlock(&ts->i2c_mutex);
>> +     return -1;
>> +}
>> +
>> +static int hideep_i2c_write(struct hideep_t *ts, unsigned short addr,
>> +     unsigned short len, unsigned char *buf)
>> +{
>> +     int ret = -1;
>> +
>> +     dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
>> +
>> +     mutex_lock(&ts->i2c_mutex);
>> +
>> +     // data mangling..
>> +     ts->i2c_buf[0] = (addr >> 0) & 0xFF;
>> +     ts->i2c_buf[1] = (addr >> 8) & 0xFF;
>> +     memcpy(&ts->i2c_buf[2], buf, len);
>> +
>> +     ret = i2c_master_send(ts->client, (char *)ts->i2c_buf, len + 2);
>> +
>> +     if (ret < 0)
>> +             goto i2c_err;
>> +
>> +     mutex_unlock(&ts->i2c_mutex);
>> +     return  0;
>> +
>> +i2c_err:
>> +     mutex_unlock(&ts->i2c_mutex);
>> +     return -1;
>> +}
>> +
>> +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
>> +
>> +static void pops_mt(struct hideep_t *ts)
>> +{
>> +#ifdef HIDEEP_TYPE_B_PROTOCOL
>> +     int id;
>> +     int i;
>> +
>> +     for (i = 0; i < ts->tch_count; i++) {
>> +             id = (ts->touch_evt[i].index >> 0) & 0x0F;
>> +             input_mt_slot(ts->input_dev, id);
>> +             input_mt_report_slot_state(ts->input_dev,
>> +                     __GET_MT_TOOL_TYPE(ts->touch_evt[i].type), false);
>> +             input_report_key(ts->input_dev, BTN_TOUCH, false);
>> +     }
>> +#else
>> +     input_report_key(ts->input_dev, BTN_TOUCH, false);
>> +     input_mt_sync(ts->input_dev);
>> +#endif
>> +
>> +     input_sync(ts->input_dev);
>> +}
>> +
>> +static void push_mt(struct hideep_t *ts)
>> +{
>> +     int id;
>> +     int i;
>> +     bool btn_up = 0;
>> +     bool btn_dn = 0;
>> +     bool btn_mv = 0;
>> +     int evt = 0;
>> +
>> +     /* load multi-touch event to input system */
>> +     for (i = 0; i < ts->tch_count; i++) {
>> +             id = (ts->touch_evt[i].index >> 0) & 0x0F;
>> +             btn_up = (ts->touch_evt[i].flag >> HIDEEP_MT_RELEASED) & 0x01;
>> +             btn_dn = (ts->touch_evt[i].flag >> HIDEEP_MT_FIRST_CONTACT)
>> +                     & 0x01;
>> +             btn_mv = (ts->touch_evt[i].flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
>> +
>> +             if (btn_up)
>> +                     clear_bit(id, &ts->tch_bit);
>> +             else
>> +                     __set_bit(id, &ts->tch_bit);
>> +
>> +             dev_dbg(&ts->client->dev,
>> +                     "type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
>> +                     ts->touch_evt[i].type, ts->touch_evt[i].index,
>> +                     i, ts->touch_evt[i].x, ts->touch_evt[i].y,
>> +                     ts->touch_evt[i].z);
>> +
>> +#ifdef HIDEEP_TYPE_B_PROTOCOL
>> +             input_mt_slot(ts->input_dev, id);
>> +             input_mt_report_slot_state(ts->input_dev,
>> +                     __GET_MT_TOOL_TYPE(ts->touch_evt[i].type),
>> +                     (btn_up == 0));
>> +
>> +             if (btn_up == 0) {
>> +                     input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
>> +                             ts->touch_evt[i].x);
>> +                     input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
>> +                             ts->touch_evt[i].y);
>> +                     input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
>> +                             ts->touch_evt[i].z);
>> +                     input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
>> +                             ts->touch_evt[i].w);
>> +                     evt++;
>> +             }
>> +#else
>> +             if (btn_up) {
>> +                     input_mt_sync(ts->input_dev);
>> +             } else {
>> +                     input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
>> +                     input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
>> +                             ts->touch_evt[i].x);
>> +                     input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
>> +                             ts->touch_evt[i].y);
>> +                     input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
>> +                             ts->touch_evt[i].z);
>> +                     input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
>> +                             ts->touch_evt[i].w);
>> +                     input_mt_sync(ts->input_dev);
>> +                     evt++;
>> +             }
>> +#endif
>> +     }
>> +
>> +     if (ts->tch_bit == 0)
>> +             evt = 0;
>> +
>> +     input_report_key(ts->input_dev, BTN_TOUCH, evt);
>> +     input_mt_sync_frame(ts->input_dev);
>> +     input_sync(ts->input_dev);
>> +}
>> +
>> +#ifdef HIDEEP_SUPPORT_KEY
>> +static void pops_ky(struct hideep_t *ts)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < ts->tch_count; i++) {
>> +             input_report_key(ts->input_dev, ts->key_codes[i], false);
>> +             input_report_key(ts->input_dev, BTN_TOUCH, false);
>> +     }
>> +
>> +     input_sync(ts->input_dev);
>> +}
>> +
>> +static void push_ky(struct hideep_t *ts)
>> +{
>> +     int i;
>> +     int pressed;
>> +     int key_code;
>> +     int key_status;
>> +
>> +     for (i = 0; i < ts->key_count; i++) {
>> +             key_code = ts->key_evt[i].flag & 0x0F;
>> +             key_status = ts->key_evt[i].flag & 0xF0;
>> +             pressed = false;
>> +
>> +             if (key_status & HIDEEP_KEY_PRESSED_MASK)
>> +                     pressed = true;
>> +             else
>> +                     pressed = false;
>> +
>> +             input_report_key(ts->input_dev, ts->key_codes[key_code],
>> +                     pressed);
>> +             input_report_key(ts->input_dev, BTN_TOUCH, pressed);
>> +     }
>> +
>> +     input_sync(ts->input_dev);
>> +}
>> +#endif
>> +
>> +static void hideep_put_event(struct hideep_t *ts)
>> +{
>> +     /* mangling touch information */
>> +     if (ts->tch_count > 0)
>> +             push_mt(ts);
>> +
>> +#ifdef HIDEEP_SUPPORT_KEY
>> +     if (ts->key_count > 0)
>> +             push_ky(ts);
>> +#endif
>> +}
>> +
>> +static int hideep_get_event(struct hideep_t *ts)
>> +{
>> +     int ret;
>> +     int touch_count;
>> +     int info_size;
>> +
>> +
>> +     /* get touch event count */
>> +     dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
>> +             ts->tch_count, ts->key_count, ts->lpm_count);
>> +
>> +     /* get touch event information */
>> +     if (ts->tch_count > HIDEEP_MT_MAX)
>> +             ts->tch_count = 0;
>> +
>> +     if (ts->key_count > HIDEEP_KEY_MAX)
>> +             ts->key_count = 0;
>> +
>> +     touch_count = ts->tch_count + ts->key_count;
>> +
>> +     if (ts->tch_count > 0) {
>> +             info_size = ts->tch_count * sizeof(struct hideep_mt_t);
>> +             ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
>> +                     info_size, (unsigned char *)ts->touch_evt);
>> +             if (ret < 0) {
>> +                     dev_err(&ts->client->dev, "read I2C error.");
>> +                     return -1;
>> +             }
>> +     }
>> +
>> +#ifdef HIDEEP_SUPPORT_KEY
>> +     if (ts->key_count > 0) {
>> +             info_size = ts->key_count * sizeof(struct hideep_key_t);
>> +             ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
>> +                     info_size, (unsigned char *)ts->key_evt);
>> +             if (ret < 0) {
>> +                     dev_err(&ts->client->dev, "read I2C error.");
>> +                     return -1;
>> +             }
>> +     }
>> +#endif
>> +
>> +     return touch_count;
>> +}
>> +
>> +static int hideep_event_thread(void *arg)
>> +{
>> +     int t_evt;
>> +
>> +     struct hideep_t *ts = arg;
>> +
>> +     dev_info(&ts->client->dev, "start event thread");
>> +
>> +     while (!kthread_should_stop()) {
>> +             set_current_state(TASK_INTERRUPTIBLE);
>> +
>> +             schedule();
>> +
>> +             t_evt = hideep_get_event(ts);
>> +
>> +             if (t_evt >= 0)
>> +                     hideep_put_event(ts);
>> +
>> +             if (kthread_should_stop())
>> +                     break;
>> +     }
>> +
>> +     dev_info(&ts->client->dev, "end thread");
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t hideep_irq_task(int irq, void *handle)
>> +{
>> +     unsigned char i2c_buff[2];
>> +     int ret;
>> +
>> +     struct hideep_t *ts = (struct hideep_t *) handle;
>> +
>> +     dev_dbg(&ts->client->dev, "state = 0x%x, debug = %d",
>> +             ts->dev_state, ts->debug_dev.debug_enable);
>> +
>> +     if (ts->debug_dev.debug_enable == false &&
>> +             ts->dev_state == state_normal) {
>> +             ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
>> +                     2, (u8 *)&i2c_buff);
>> +             if (ret < 0) {
>> +                     disable_irq(ts->client->irq);
>> +                     ts->interrupt_state = 0;
>> +                     return IRQ_HANDLED;
>> +             }
>> +
>> +             ts->tch_count = i2c_buff[0];
>> +             ts->key_count = i2c_buff[1] & 0x0f;
>> +             ts->lpm_count = i2c_buff[1] & 0xf0;
>> +
>> +             wake_up_process(ts->hthread_event);
>> +
>> +     } else if (ts->debug_dev.debug_enable == true
>> +             && ts->dev_state == state_debugging) {
>> +             ret = ts->hideep_api->i2c_read(ts, HIDEEP_RAW_DATA_ADDR,
>> +                     FRAME_HEADER_SIZE + ts->debug_dev.img_size,
>> +                     ts->debug_dev.img_buff);
>> +             ts->debug_dev.img_size =
>> +                     GET_PAYLOAD_SIZE_FROM_HEADER(ts->debug_dev.img_buff);
>> +             ts->debug_dev.ready = 1;
>> +             wake_up_interruptible(&ts->debug_dev.i_packet);
>> +     }
>> +     dev_dbg(&ts->client->dev, "end.");
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int hideep_capability(struct hideep_t *ts)
>> +{
>> +#ifdef HIDEEP_SUPPORT_KEY
>
> As I mentioned, read from device properties.
>
>> +     int i;
>> +
>> +     ts->key_codes[0] = KEY_MENU;
>> +     ts->key_codes[1] = KEY_HOME;
>> +     ts->key_codes[2] = KEY_BACK;
>> +
>> +     for (i = 0; i < HIDEEP_KEY_MAX; i++)
>> +             set_bit(ts->key_codes[i], ts->input_dev->keybit);
>> +#endif
>> +
>> +     ts->input_dev->name = HIDEEP_TS_NAME;
>> +     ts->input_dev->id.bustype = BUS_I2C;
>> +
>> +     set_bit(EV_ABS, ts->input_dev->evbit);
>> +     set_bit(EV_KEY, ts->input_dev->evbit);
>> +     set_bit(EV_SYN, ts->input_dev->evbit);
>> +     set_bit(BTN_TOUCH, ts->input_dev->keybit);
>
> Please use input_set_capability().
>
>> +     set_bit(MT_TOOL_FINGER, ts->input_dev->keybit);
>
> There is no such event.
>
>> +     set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
>> +
>> +     input_set_abs_params(ts->input_dev, ABS_X, 0, ts->p_data->max_x, 0, 0);
>> +     input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->p_data->max_y, 0, 0);
>> +     input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
>> +             ts->p_data->max_z, 0, 0);
>> +
>> +#ifdef HIDEEP_TYPE_B_PROTOCOL
>
> Should not be conditional.
>
>> +     input_mt_init_slots(ts->input_dev,
>> +             HIDEEP_MT_MAX, INPUT_MT_DIRECT);
>
> Error handling needed.
>
>> +#else
>> +     input_set_abs_params(ts->input_dev,
>> +             ABS_MT_TRACKING_ID, 0, 10, 0, 0);
>> +#endif
>> +     input_set_abs_params(ts->input_dev,
>> +             ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
>> +     input_set_abs_params(ts->input_dev,
>> +             ABS_MT_POSITION_X, 0, ts->p_data->max_x - 1, 0, 0);
>> +     input_set_abs_params(ts->input_dev,
>> +             ABS_MT_POSITION_Y, 0, ts->p_data->max_y - 1, 0, 0);
>> +     input_set_abs_params(ts->input_dev,
>> +             ABS_MT_PRESSURE, 0, ts->p_data->max_z, 0, 0);
>> +     input_set_abs_params(ts->input_dev,
>> +             ABS_MT_TOUCH_MAJOR, 0, ts->p_data->max_w, 0, 0);
>> +
>> +     return 0;
>> +}
>> +
>> +static void hideep_reset_ic(struct hideep_t *ts)
>> +{
>> +     struct hideep_platform_data_t *pdata;
>> +     int ret;
>> +     unsigned char cmd = 0x01;
>> +
>> +     pdata = ts->p_data;
>
> Move together with declaration:
>
>         struct hideep_platform_data_t *pdata = ts->p_data;
>
>> +
>> +     dev_info(&ts->client->dev, "start!!");
>
> dev_dbg() or drop.
>
>> +
>> +#ifdef CONFIG_OF
>
> This should not depend on CONFIG_OF.
>
>> +     if (pdata->reset_gpio > 0) {
>> +             dev_info(&ts->client->dev, "hideep:enable the reset_gpio");
>> +             gpio_set_value(pdata->reset_gpio, 0);
>> +             mdelay(20);
>> +             gpio_set_value(pdata->reset_gpio, 1);
>> +     } else
>> +#endif
>> +     {
>> +             ret = hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
>> +     }
>> +     mdelay(50);
>> +     dev_info(&ts->client->dev, "end!!");
>
> dev_dbg() or drop.
>
>> +}
>> +
>> +static void hideep_get_info(struct hideep_t *ts)
>> +{
>> +     struct hideep_platform_data_t *pdata;
>> +     unsigned char val[4];
>> +
>> +     pdata = ts->p_data;
>> +     ts->hideep_api->i2c_read(ts, 0x28, 4, val);
>> +
>> +     pdata->max_x = val[3] << 8 | val[2];
>> +     pdata->max_y = val[1] << 8 | val[0];
>> +     pdata->max_w = 255;
>> +     pdata->max_z = 255;
>> +
>> +     dev_info(&ts->client->dev, "X : %d, Y : %d",
>> +             pdata->max_x, pdata->max_y);
>> +}
>> +
>> +static void hideep_init_ic(struct hideep_t *ts)
>> +{
>> +     struct hideep_platform_data_t *pdata;
>> +     struct device dev;
>> +
>> +     pdata = ts->p_data;
>> +     dev = ts->client->dev;
>> +
>> +     /* power on */
>> +     ts->hideep_api->power(ts, true);
>> +     ts->dev_state = state_init;
>> +     mdelay(30);
>> +
>> +     /* ic reset */
>> +     ts->hideep_api->reset_ic(ts);
>> +}
>> +
>> +static int hideep_resume(struct device *dev)
>
> __maybe_unused
>
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     unsigned char sleep_cmd = 0x00;
>> +
>> +     dev_info(dev, "enter");
>> +
>> +     mutex_lock(&ts->dev_mutex);
>> +
>> +     if (ts->dev_state == state_normal)
>> +             goto hideep_resume_exit;
>> +
>> +     dev_info(dev, "not waiting.");
>> +     ts->dev_state = state_normal;
>> +
>> +     hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
>> +     enable_irq(ts->client->irq);
>> +     ts->interrupt_state = 1;
>> +
>> +hideep_resume_exit:
>> +     mdelay(10);
>> +     ts->hideep_api->reset_ic(ts);
>> +
>> +     mutex_unlock(&ts->dev_mutex);
>> +     dev_info(dev, "exit.");
>> +     return 0;
>> +}
>> +
>> +static int hideep_suspend(struct device *dev)
>
> __maybe_unused
>
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     unsigned char sleep_cmd = 0x01;
>> +
>> +     dev_info(dev, "enter");
>> +
>> +     mutex_lock(&ts->dev_mutex);
>> +     if (ts->dev_state == state_sleep)
>> +             goto hideep_suspend_exit;
>> +
>> +     dev_info(dev, "not waiting.");
>> +     ts->dev_state = state_sleep;
>> +
>> +     /* send sleep command.. */
>> +     pops_mt(ts);
>> +#ifdef HIDEEP_SUPPORT_KEY
>> +     pops_ky(ts);
>> +#endif
>> +
>> +     /* default deep sleep */
>> +     hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
>> +     disable_irq(ts->client->irq);
>> +     ts->interrupt_state = 0;
>> +
>> +hideep_suspend_exit:
>> +     mutex_unlock(&ts->dev_mutex);
>> +     dev_info(dev, "exit.");
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_FB
>> +static int fb_notifier_callback(struct notifier_block *self,
>> +     unsigned long event, void *data)
>> +{
>> +     struct fb_event *evdata = data;
>> +     int *blank;
>> +
>> +     struct hideep_t *ts = container_of(self, struct hideep_t, fb_notif);
>> +I have some question. As I remember, I got the consent of Rob about this.
Should I still use the standard method? What should I prioritize?

>> +     if ((evdata) && (evdata->data) &&
>> +             (event == FB_EVENT_BLANK) &&
>> +             (ts) && (ts->client)) {
>> +             blank = evdata->data;
>> +             if (*blank == FB_BLANK_UNBLANK) {
>> +                     dev_info(&ts->client->dev, "resume");
>> +                     if (ts->suspended == 1) {
>> +                             hideep_resume(&ts->client->dev);
>> +                             ts->suspended = 0;
>> +                     }
>> +             } else if (*blank == FB_BLANK_POWERDOWN) {
>> +                     dev_info(&ts->client->dev, "suspend");
>> +                     if (ts->suspended == 0) {
>> +                             ts->suspended = 1;
>> +                             hideep_suspend(&ts->client->dev);
>> +                     }
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +#endif
>> +
>> +#ifdef CONFIG_OF
>> +static int hideep_parse_dts(struct device *dev,
>> +     struct hideep_platform_data_t *pdata)
>> +{
>> +     int ret = 0;
>> +     unsigned int coords[4];
>> +     struct device_node *np;
>> +
>> +     dev_info(dev, "enter");
>> +     np = dev->of_node;
>> +
>> +     ret = of_property_read_u32_array(np, "hideep,max-coords", coords, 4);
>> +     if (ret) {
>> +             dev_err(dev, "Failed to get max-coords property\n");
>> +             return ret;
>> +     }
>
> This should be using the standard touchscreen bindings from
> Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
>
>> +
>> +     pdata->max_x = coords[0];
>> +     pdata->max_y = coords[1];
>> +     pdata->max_w = coords[2];
>> +     pdata->max_z = coords[3];
>> +
>> +     dev_info(dev, "max coord data x : %d\n", pdata->max_x);
>> +     dev_info(dev, "max coord data y : %d\n", pdata->max_y);
>> +     dev_info(dev, "max coord data w : %d\n", pdata->max_w);
>> +     dev_info(dev, "max coord data z : %d\n", pdata->max_z);
>> +
>> +     /* device tree information get */
>> +     pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
>> +     dev_info(dev, "reset_gpio = %d, is %s specified",
>> +             pdata->reset_gpio, pdata->reset_gpio < 0 ? "not" : "");
>> +     if (pdata->reset_gpio)
>> +             ret = gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH,
>> +                     "reset-gpios");
>
> Please use gpiod API and request with:
>
>         pdata->reset_gpio = devm_gpiod_get_optional(dev, "reset",
>                                                     GPIOD_OUT_LOW);
>         if (IS_ERR(pdata->reset_gpio))
>                 return PTR_ERR(pdata->reset_gpio));
>
>> +
>> +     pdata->vcc_vdd = regulator_get(dev, "vdd");
>> +     pdata->vcc_vid = regulator_get(dev, "vid");
>
> Error handling is missing.
>
>> +
>> +     return ret;
>> +}
>> +#endif
>> +
>> +static int hideep_probe(struct i2c_client *client,
>> +     const struct i2c_device_id *id)
>> +{
>> +     int ret = 0;
>> +     struct hideep_platform_data_t *p_data;
>> +     struct dwz_info_t *dwz;
>> +     struct hideep_function_list_t *function;
>> +     struct hideep_t *ts;
>> +
>> +     dev_info(&client->dev, "enter");
>
> dev_dbg() or drop. Same goes for similar dev_info() uses.
>
>> +
>> +     /* check i2c bus */
>> +     if (!i2c_check_functionality(client->adapter,
>> +             I2C_FUNC_I2C)) {
>> +             dev_err(&client->dev, "check i2c device error");
>> +             ret = -ENODEV;
>> +             return ret;
>
>                 return -ENODEV;
>
>> +     }
>> +
>> +     /* init platform data */
>> +     p_data = devm_kzalloc(&client->dev,
>> +             sizeof(struct hideep_platform_data_t), GFP_KERNEL);
>> +
>> +#ifdef CONFIG_OF
>> +     if (client->dev.of_node) {
>> +             ret = hideep_parse_dts(&client->dev, p_data);
>> +             if (ret)
>> +                     return ret;
>> +     }
>
> Use generic device properties and drop dependency on CONFIG_OF.
>
>> +#endif
>> +
>> +     /* init hideep_t */
>> +     ts = devm_kzalloc(&client->dev,
>> +             sizeof(struct hideep_t), GFP_KERNEL);
>> +     dwz = devm_kzalloc(&client->dev,
>> +             sizeof(struct dwz_info_t), GFP_KERNEL);
>> +     function = devm_kzalloc(&client->dev,
>> +             sizeof(struct hideep_function_list_t), GFP_KERNEL);
>
> Why are the last 2 allocated separately instead of being sub-structures
> in hideep_t.
>
> Also, error handling is missing.
>
>> +
>> +     ts->client = client;
>> +     ts->p_data = p_data;
>> +     ts->dwz_info = dwz;
>> +     ts->hideep_api = function;
>> +
>> +     ts->hideep_api->i2c_read = hideep_i2c_read;
>> +     ts->hideep_api->i2c_write = hideep_i2c_write;
>
> Consider using regmap if you want to support SPI down the road.
>
>> +     ts->hideep_api->reset_ic = hideep_reset_ic;
>> +     ts->hideep_api->power = hideep_power;
>> +
>> +     /* init for isp function */
>> +     hideep_isp_init(ts);
>> +
>> +     i2c_set_clientdata(client, ts);
>> +
>> +     mutex_init(&ts->i2c_mutex);
>> +     mutex_init(&ts->dev_mutex);
>> +
>> +     if (!client->dev.of_node)
>> +             hideep_get_info(ts);
>> +
>> +     /* hardware init */
>> +     hideep_init_ic(ts);
>> +
>> +#ifdef HIDEEP_DWZ_VERSION_CHECK
>> +     /* read info */
>> +     ret = ts->hideep_api->get_dwz_info(ts);
>> +     if (ret < 0) {
>> +             dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
>> +             goto hideep_probe_read_dwz_err;
>> +     }
>> +#endif
>> +
>> +     /* init input device */
>> +     ts->input_dev = devm_input_allocate_device(&client->dev);
>> +     if (!ts->input_dev) {
>> +             dev_err(&client->dev, "can't allocate memory for input_dev");
>> +             ret = -ENOMEM;
>> +             goto hideep_probe_input_dev_memory_err;
>> +     }
>> +
>> +     hideep_capability(ts);
>> +
>> +     ret = input_register_device(ts->input_dev);
>> +     if (ret) {
>> +             dev_err(&client->dev, "can't register input_dev");
>> +             ret = -ENOMEM;
>> +             goto hideep_probe_register_input_dev_err;
>> +     }
>> +
>> +     input_set_drvdata(ts->input_dev, ts);
>> +
>> +     /* init event thread */
>> +     ts->hthread_event = kthread_run(hideep_event_thread,
>> +             ts, "hideep_event_thread");
>> +     if (IS_ERR(ts->hthread_event)) {
>> +             dev_err(&client->dev, "can't create event thread !!!");
>> +             ret = PTR_ERR(ts->hthread_event);
>> +             goto hideep_probe_create_thread_err;
>> +     }
>> +
>> +     dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
>> +     if (ts->client->irq <= 0) {
>> +             dev_err(&client->dev, "can't be assigned irq");
>> +             goto hideep_probe_assigned_irq_err;
>> +     }
>> +
>> +     if (ts->client->irq) {
>
> This check is useless.
>
>> +             ret = request_threaded_irq(ts->client->irq, NULL,
>
> devm_request_threaded_irq
>
>> +                     hideep_irq_task,
>> +                     (IRQF_TRIGGER_LOW | IRQF_ONESHOT),
>
> Reply on platform supplying proper trigger, so simply IRQF_ONESHOT.
>
>> +                     ts->client->name, ts);
>> +             disable_irq(ts->client->irq);
>> +             ts->interrupt_state = 0;
>> +             if (ret < 0) {
>> +                     dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
>> +                             ret);
>> +                     goto hideep_probe_request_irq_err;
>> +             }
>> +     }
>> +
>> +     ret = hideep_debug_init(ts);
>> +     if (ret) {
>> +             dev_err(&client->dev, "fail init debug, ret = 0x%x", ret);
>> +             ret = -1;
>> +             goto hideep_probe_debug_init_err;
>> +     }
>> +
>> +     ret = hideep_sysfs_init(ts);
>> +     if (ret) {
>> +             dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
>> +             ret = -1;
>> +             goto hideep_probe_sysfs_init_err;
>> +     }
>> +
>> +#ifdef CONFIG_FB
>> +     ts->suspended = 0;
>> +     ts->fb_notif.notifier_call = fb_notifier_callback;
>> +     ret = fb_register_client(&ts->fb_notif);
>> +     if (ret) {
>> +             dev_err(&client->dev, "Unable to register fb_notifier: ret = %d",
>> +                     ret);
>> +             ret = -1;
>> +             goto hideep_probe_register_fb_err;
>> +     }
>
> This does not belong to touchscreen driver at all. Kernel driver should
> not establish policy.
>
>> +#endif
>> +
>> +
>> +     ts->dev_state = state_normal;
>> +     enable_irq(ts->client->irq);
>> +     ts->interrupt_state = 1;
>> +
>> +     dev_info(&client->dev, "probe is ok!");
>> +     return 0;
>> +
>> +hideep_probe_register_fb_err:
>> +     hideep_sysfs_exit(ts);
>> +
>> +hideep_probe_sysfs_init_err:
>> +     hideep_debug_uninit();
>> +
>> +hideep_probe_debug_init_err:
>> +hideep_probe_request_irq_err:
>> +     free_irq(ts->client->irq, ts);
>> +
>> +hideep_probe_assigned_irq_err:
>> +hideep_probe_create_thread_err:
>> +     input_unregister_device(ts->input_dev);
>> +
>> +hideep_probe_register_input_dev_err:
>> +     if (ts->input_dev)
>> +             input_free_device(ts->input_dev);
>
> No calls to input_free_device() after input_unregister_device().
>
> Additionally, you are using devm, why do you unregister/free manually?

Ok, I will change to using devm.

>
>> +
>> +hideep_probe_input_dev_memory_err:
>> +#ifdef HIDEEP_DWZ_VERSION_CHECK
>> +hideep_probe_read_dwz_err:
>> +#endif
>> +
>> +#ifdef CONFIG_OF
>> +     ts->hideep_api->power(ts, false);
>> +#endif
>> +     dev_err(&client->dev, "probe err!");
>> +     return ret;
>> +}
>> +
>> +static int hideep_remove(struct i2c_client *client)
>> +{
>> +     struct hideep_t *ts = i2c_get_clientdata(client);
>> +
>> +     kthread_stop(ts->hthread_event);
>> +
>> +#ifdef CONFIG_FB
>> +     if (fb_unregister_client(&ts->fb_notif))
>> +             dev_info(&client->dev,
>> +                     "Error occurred while unregistering fb_notifier");
>> +#endif
>> +
>> +#ifdef CONFIG_OF
>> +     ts->hideep_api->power(ts, false);
>> +#endif
>> +     free_irq(client->irq, ts);
>> +
>> +     input_unregister_device(ts->input_dev);
>> +     hideep_sysfs_exit(ts);
>> +
>> +     hideep_debug_uninit();
>> +     devm_kfree(&client->dev, ts);
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static const struct dev_pm_ops hideep_pm_ops = {
>> +     .suspend = hideep_suspend,
>> +     .resume = hideep_resume,
>> +};
>> +#endif
>
> Use SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
>
>
>> +
>> +static const struct i2c_device_id hideep_dev_idtable[] = {
>> +     { HIDEEP_I2C_NAME, 0 },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
>> +
>> +#ifdef CONFIG_ACPI
>> +static const struct acpi_device_id hideep_acpi_id[] = {
>> +     { "HIDP0001", 0 },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
>> +#endif
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id hideep_match_table[] = {
>> +     { .compatible = "hideep,hideep-lime" },
>> +     { .compatible = "hideep,hideep-crimson" },
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(of, hideep_match_table);
>> +#endif
>> +
>> +static struct i2c_driver hideep_driver = {
>> +     .probe = hideep_probe,
>> +     .remove = hideep_remove,
>> +     .id_table = hideep_dev_idtable,
>> +     .driver = {
>> +             .name = HIDEEP_I2C_NAME,
>> +             .owner = THIS_MODULE,
>
> No need to set owner explicitly.
>
>> +             .of_match_table = of_match_ptr(hideep_match_table),
>> +             .acpi_match_table = ACPI_PTR(hideep_acpi_id),
>> +             .pm = &hideep_pm_ops,
>> +     },
>> +};
>> +
>> +module_i2c_driver(hideep_driver);
>> +
>> +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
>> +MODULE_AUTHOR("anthony.kim@hideep.com");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/input/touchscreen/hideep_dbg.c b/drivers/input/touchscreen/hideep_dbg.c
>> new file mode 100644
>> index 0000000..549f2ad2
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep_dbg.c
>> @@ -0,0 +1,405 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#include "hideep.h"
>> +#include "hideep_isp.h"
>> +#include "hideep_dbg.h"
>> +
>> +static struct hideep_debug_dev_t *hdd;
>> +
>> +static int hideep_i2c_recv(unsigned int len)
>> +{
>> +     int ret = 0;
>> +
>> +     mutex_lock(&hdd->ts->i2c_mutex);
>> +     ret = i2c_master_recv(hdd->ts->client, hdd->vr_buff, len);
>> +     mutex_unlock(&hdd->ts->i2c_mutex);
>> +     if (ret < 0)
>> +             goto i2c_err;
>> +
>> +     dev_info(&hdd->ts->client->dev, "(%d)", len);
>> +     return ret;
>> +
>> +i2c_err:
>> +     dev_err(&hdd->ts->client->dev, "i2c_err");
>> +     return ret;
>> +}
>> +
>> +static int hideep_i2c_send(unsigned int len)
>> +{
>> +     int ret = 0;
>> +     unsigned char *buff = hdd->vr_buff;
>> +
>> +     mutex_lock(&hdd->ts->i2c_mutex);
>> +     ret = i2c_master_send(hdd->ts->client, buff, len);
>> +     mutex_unlock(&hdd->ts->i2c_mutex);
>> +     if (ret < 0)
>> +             goto i2c_err;
>> +
>> +     dev_info(&hdd->ts->client->dev, "(%d)", len);
>> +     return ret;
>> +
>> +i2c_err:
>> +     dev_err(&hdd->ts->client->dev, "i2c_err");
>> +     return ret;
>> +}
>> +
>> +static int hideep_get_vreg(unsigned int addr, unsigned int len)
>> +{
>> +     int ret = 0;
>> +
>> +     ret = hdd->ts->hideep_api->i2c_read(hdd->ts, addr, len,
>> +             hdd->vr_buff);
>> +     if (ret < 0)
>> +             goto i2c_err;
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
>> +     return ret;
>> +
>> +i2c_err:
>> +     dev_err(&hdd->ts->client->dev, "i2c_err");
>> +     return ret;
>> +}
>> +
>> +static int hideep_set_vreg(unsigned int addr, unsigned int len)
>> +{
>> +     int ret = 0;
>> +     int wr_remain = len;
>> +     int vr_addr = addr;
>> +     int wr_len = len;
>> +     unsigned char *buff = hdd->vr_buff;
>> +
>> +     do {
>> +             if (wr_remain >=  MAX_VR_BUFF)
>> +                     wr_len = MAX_VR_BUFF;
>> +             else
>> +                     wr_len = wr_remain;
>> +
>> +             ret = hdd->ts->hideep_api->i2c_write(hdd->ts, vr_addr,
>> +                     wr_len, buff);
>> +             if (ret < 0)
>> +                     goto i2c_err;
>> +
>> +             wr_remain -= MAX_VR_BUFF;
>> +             vr_addr += MAX_VR_BUFF;
>> +             buff += MAX_VR_BUFF;
>> +     } while (wr_remain > 0);
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "(0x%02x:%d)", addr, len);
>> +     return ret;
>> +
>> +i2c_err:
>> +     dev_err(&hdd->ts->client->dev, "i2c_err");
>> +     return ret;
>> +}
>> +
>> +static int hideep_download_uc(const char __user *uc, size_t count, int offset)
>> +{
>> +     int ret;
>> +     unsigned char *ucode;
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
>> +
>> +     if (count > hdd->ts->fw_size) {
>> +             dev_err(&hdd->ts->client->dev, "over size data!!!");
>> +             return -1;
>> +     }
>> +
>> +     ucode = kmalloc(count, GFP_KERNEL);
>> +
>> +     ret = copy_from_user(ucode + offset, uc, count);
>> +     if (ret < 0) {
>> +             dev_err(&hdd->ts->client->dev, "ADDR_UC : copy_to_user");
>> +             kfree(ucode);
>> +             return 0;
>> +     }
>> +
>> +     disable_irq(hdd->ts->client->irq);
>> +     hdd->ts->interrupt_state = 0;
>> +     hdd->ts->hideep_api->update_part(hdd->ts, ucode, count, offset, false);
>> +     enable_irq(hdd->ts->client->irq);
>> +     hdd->ts->interrupt_state = 1;
>> +     kfree(ucode);
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "Download_uc(%zu)", count);
>> +
>> +     return count;
>> +}
>> +
>> +static int hideep_debug_open(struct inode *inode, struct file *file)
>> +{
>> +     hdd->release_flag = false;
>> +
>> +     file->private_data = hdd;
>> +     dev_dbg(&hdd->ts->client->dev, "hideep_debug_open");
>> +
>> +     return 0;
>> +}
>> +
>> +static int hideep_debug_release(struct inode *inode, struct file *file)
>> +{
>> +     if (!hdd->release_flag)
>> +             return -1;
>> +     hdd->release_flag = false;
>> +     file->private_data = NULL;
>> +     return 0;
>> +}
>> +
>> +static unsigned int hideep_debug_poll(struct file *file,
>> +     struct poll_table_struct *wait)
>> +{
>> +     unsigned int mask = 0;
>> +     struct hideep_debug_dev_t *drv_info;
>> +
>> +     if (file->private_data == NULL)
>> +             return 0;
>> +
>> +     drv_info = file->private_data;
>> +
>> +     poll_wait(file, &drv_info->i_packet, wait);
>> +
>> +     if (drv_info->ready) {
>> +             disable_irq(drv_info->ts->client->irq);
>> +             drv_info->ts->interrupt_state = 0;
>> +             mask |= POLLIN | POLLRDNORM;
>> +             drv_info->ready = 0;
>> +     }
>> +
>> +     return mask;
>> +}
>> +
>> +static ssize_t hideep_debug_read(struct file *file, char __user *buf,
>> +     size_t count, loff_t *offset)
>> +{
>> +     int ret = -1;
>> +     ssize_t rd_len = 0;
>> +     unsigned char *rd_buffer = NULL;
>> +     unsigned char *data = NULL;
>> +     struct hideep_debug_dev_t *drv_info = file->private_data;
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
>> +
>> +     if (file->private_data == NULL)
>> +             return 0;
>> +
>> +     drv_info->vr_size = count;
>> +     rd_len = count;
>> +
>> +     data = kmalloc(rd_len, GFP_KERNEL);
>> +
>> +     if (*offset <= HIDEEP_VR_ADDR_LEN) {
>> +             // if offset is not belong to any special command
>> +             if ((*offset & HIDEEP_MAX_RAW_LEN) &&
>> +                     (*offset < HIDEEP_MAX_RAW_LEN) &&
>> +                     (drv_info->debug_enable == true)) {
>> +                     mutex_lock(&drv_info->ts->dev_mutex);
>> +                     rd_buffer = drv_info->img_buff;
>> +                     ret = 0;
>> +                     mutex_unlock(&drv_info->ts->dev_mutex);
>> +             } else {
>> +                     ret = hideep_get_vreg(*offset, rd_len);
>> +                     rd_buffer = drv_info->vr_buff;
>> +             }
>> +             if (ret < 0)
>> +                     rd_len = 0;
>> +     } else if (*offset & (HIDEEP_I2C_BYPASS)) {
>> +             // if offset is belong to special command "i2c bypass"
>> +             ret = hideep_i2c_recv(rd_len);
>> +             if (ret < 0) {
>> +                     dev_err(&hdd->ts->client->dev, "ret = %d", ret);
>> +                     rd_len = 0;
>> +             } else {
>> +                     rd_buffer = drv_info->vr_buff;
>> +             }
>> +     } else if (*offset & HIDEEP_NVM_DOWNLOAD) {
>> +             // if offset is belong to special command "nvm download"
>> +             rd_len = count;
>> +             memset(data, 0x0, rd_len);
>> +
>> +             disable_irq(drv_info->ts->client->irq);
>> +             drv_info->ts->interrupt_state = 0;
>> +             mutex_lock(&drv_info->ts->dev_mutex);
>> +             mutex_lock(&drv_info->ts->i2c_mutex);
>> +
>> +             drv_info->ts->hideep_api->sp_func(drv_info->ts, data, rd_len,
>> +                     *offset & 0xfffff);
>> +
>> +             mutex_unlock(&drv_info->ts->dev_mutex);
>> +             mutex_unlock(&drv_info->ts->i2c_mutex);
>> +             enable_irq(drv_info->ts->client->irq);
>> +             drv_info->ts->interrupt_state = 1;
>> +
>> +             rd_buffer = data;
>> +     } else {
>> +             dev_err(&hdd->ts->client->dev, "undefined address");
>> +             kfree(data);
>> +             return 0;
>> +     }
>> +
>> +     ret = copy_to_user(buf, rd_buffer, rd_len);
>> +
>> +     if (drv_info->debug_enable == true && drv_info->ready == 0) {
>> +             enable_irq(drv_info->ts->client->irq);
>> +             drv_info->ts->interrupt_state = 1;
>> +     }
>> +
>> +     if (ret < 0) {
>> +             dev_err(&hdd->ts->client->dev, "error : copy_to_user");
>> +             kfree(data);
>> +             return -EFAULT;
>> +     }
>> +
>> +     kfree(data);
>> +     return rd_len;
>> +}
>> +
>> +static ssize_t hideep_debug_write(struct file *file, const char __user *buf,
>> +     size_t count, loff_t *offset)
>> +{
>> +     int ret;
>> +     struct hideep_debug_dev_t *drv_info = file->private_data;
>> +     int wr_len = 0;
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "count = %zu", count);
>> +     if (file->private_data == NULL)
>> +             return 0;
>> +
>> +     if (*offset <= HIDEEP_VR_ADDR_LEN) {
>> +             // if offset is not belong to any special command
>> +             wr_len = count;
>> +
>> +             ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
>> +             if (ret < 0) {
>> +                     dev_err(&hdd->ts->client->dev, "error : copy_to_user");
>> +                     return -EFAULT;
>> +             }
>> +
>> +             ret = hideep_set_vreg(*offset, wr_len);
>> +             if (ret < 0)
>> +                     wr_len = 0;
>> +     } else if (*offset & (HIDEEP_I2C_BYPASS)) {
>> +             // if offset is belong to special command "i2c bypass"
>> +             wr_len = count;
>> +             ret = copy_from_user(drv_info->vr_buff, buf, wr_len);
>> +             ret = hideep_i2c_send(wr_len);
>> +     } else if (*offset & HIDEEP_NVM_DOWNLOAD) {
>> +             // if offset is belong to special command "nvm download"
>> +             wr_len = hideep_download_uc(buf, count, *offset & 0xfffff);
>> +     } else {
>> +             dev_err(&hdd->ts->client->dev,
>> +                     "hideep_write : undefined address, 0x%08x",
>> +                     (int)*offset);
>> +             return 0;
>> +     }
>> +
>> +     return wr_len;
>> +}
>> +
>> +static loff_t hideep_debug_llseek(struct file *file, loff_t off, int whence)
>> +{
>> +     loff_t newpos;
>> +     struct hideep_debug_dev_t *drv_info = file->private_data;
>> +
>> +     dev_dbg(&hdd->ts->client->dev, "off = 0x%08x, whence = %d",
>> +             (unsigned int)off, whence);
>> +     if (file->private_data == NULL)
>> +             return -EFAULT;
>> +
>> +     switch (whence) {
>> +     /* SEEK_SET */
>> +     case 0:
>> +             newpos = off;
>> +             break;
>> +     /* SEEK_CUR */
>> +     case 1:
>> +             dev_dbg(&hdd->ts->client->dev, "set mode off = 0x%08x",
>> +                     (unsigned int)off);
>> +             if (off & HIDEEP_RELEASE_FLAG) {
>> +                     dev_dbg(&hdd->ts->client->dev, "set release flag");
>> +                     drv_info->release_flag = true;
>> +             }
>> +             newpos = file->f_pos;
>> +             break;
>> +     /* SEEK_END */
>> +     case 2:
>> +             newpos = file->f_pos;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (newpos < 0)
>> +             return -EINVAL;
>> +
>> +     file->f_pos = newpos;
>> +
>> +     return newpos;
>> +}
>> +
>> +static const struct file_operations hideep_debug_fops = {
>> +     .owner = THIS_MODULE,
>> +     .open = hideep_debug_open,
>> +     .poll = hideep_debug_poll,
>> +     .release = hideep_debug_release,
>> +     .read = hideep_debug_read,
>> +     .write = hideep_debug_write,
>> +     .llseek = hideep_debug_llseek,
>> +};
>> +
>> +static struct miscdevice hideep_debug_dev = {
>> +     .minor = MISC_DYNAMIC_MINOR,
>> +     .name = HIDEEP_DEBUG_DEVICE_NAME,
>> +     .fops = &hideep_debug_fops
>> +};
>> +
>> +void hideep_debug_uninit(void)
>> +{
>> +     kfree(hdd->vr_buff);
>> +     kfree(hdd->img_buff);
>> +
>> +     misc_deregister(&hideep_debug_dev);
>> +}
>> +
>> +int hideep_debug_init(struct hideep_t *ts)
>> +{
>> +     int ret = 0;
>> +
>> +     hdd = &ts->debug_dev;
>> +
>> +     ret = misc_register(&hideep_debug_dev);
>> +     if (ret) {
>> +             dev_err(&ts->client->dev,
>> +                     "hideep debug device register fail!!!");
>> +             goto fail;
>> +     }
>> +
>> +     init_waitqueue_head(&hdd->i_packet);
>> +
>> +     hdd->ts = ts;
>> +     hdd->debug_enable = false;
>> +
>> +     hdd->img_size = 0;
>> +     hdd->vr_size = 0;
>> +
>> +     hdd->vr_buff = kmalloc(MAX_VR_BUFF, GFP_KERNEL);
>> +     hdd->img_buff = kmalloc(MAX_RAW_SIZE, GFP_KERNEL);
>> +
>> +     memset(hdd->vr_buff, 0x0, MAX_VR_BUFF);
>> +     memset(hdd->img_buff, 0x0, MAX_RAW_SIZE);
>> +
>> +     if (!hdd->vr_buff || !hdd->img_buff)
>> +             goto fail;
>> +
>> +     dev_info(&ts->client->dev, "debug init....");
>> +     return 0;
>> +
>> +fail:
>> +     kfree(hdd->vr_buff);
>> +     kfree(hdd->img_buff);
>> +     return ret;
>> +}
>> diff --git a/drivers/input/touchscreen/hideep_dbg.h b/drivers/input/touchscreen/hideep_dbg.h
>> new file mode 100644
>> index 0000000..f18a09f
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep_dbg.h
>> @@ -0,0 +1,24 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#ifndef _LINUX_HIDEEP_DBG_H
>> +#define _LINUX_HIDEEP_DBG_H
>> +
>> +/* Device Driver <==> Application */
>> +/* lseek(), write(), read() command */
>> +#define HIDEEP_RELEASE_FLAG                          0x8000
>> +#define HIDEEP_VR_ADDR_LEN                           0xFFFF
>> +#define HIDEEP_MAX_RAW_LEN                           0x3000
>> +#define HIDEEP_READ_WRITE_VR                 0xF000
>> +#define HIDEEP_I2C_BYPASS                            0x20000000
>> +
>> +#define MAX_VR_BUFF                                          2048
>> +
>> +/* max tx * max rx * 2 + 8 + 1, 1 is magin size */
>> +#define MAX_RAW_SIZE                                 7369
>> +#endif /* _LINUX_HIDEEP_DBG_H */
>> diff --git a/drivers/input/touchscreen/hideep_isp.c b/drivers/input/touchscreen/hideep_isp.c
>> new file mode 100644
>> index 0000000..c8d2e932
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep_isp.c
>> @@ -0,0 +1,592 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#include "hideep.h"
>> +#include "hideep_isp.h"
>> +
>> +static struct pgm_packet packet_w;
>> +static struct pgm_packet packet_r;
>> +
>> +static int hideep_pgm_w_mem(struct hideep_t *ts, unsigned int addr,
>> +     struct pgm_packet *packet, unsigned int len)
>> +{
>> +     int ret = 0;
>> +     int i;
>> +
>> +     if ((len % 4) != 0)
>> +             return -1;
>> +
>> +     mutex_lock(&ts->i2c_mutex);
>> +
>> +     packet->header.w[0] = htonl((0x80 | (len / 4-1)));
>> +     packet->header.w[1] = htonl(addr);
>> +
>> +     for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
>> +             packet->payload[i] = htonl(packet->payload[i]);
>> +
>> +     ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
>> +             (len+5));
>> +
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +err:
>> +     mutex_unlock(&ts->i2c_mutex);
>> +     return ret;
>> +}
>> +
>> +static int hideep_pgm_r_mem(struct hideep_t *ts, unsigned int addr,
>> +     struct pgm_packet *packet, unsigned int len)
>> +{
>> +     int ret = 0;
>> +     int i;
>> +
>> +     if ((len % 4) != 0)
>> +             return -1;
>> +
>> +     mutex_lock(&ts->i2c_mutex);
>> +
>> +     packet->header.w[0] = htonl((0x00 | (len / 4-1)));
>> +     packet->header.w[1] = htonl(addr);
>> +
>> +     ret = i2c_master_send(ts->client, (unsigned char *)&packet->header.b[3],
>> +             5);
>> +
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     ret = i2c_master_recv(ts->client, (unsigned char *)packet->payload,
>> +             len);
>> +
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     for (i = 0; i < NVM_PAGE_SIZE / sizeof(unsigned int); i++)
>> +             packet->payload[i] = htonl(packet->payload[i]);
>> +
>> +err:
>> +     mutex_unlock(&ts->i2c_mutex);
>> +     return ret;
>> +}
>> +
>> +static int hideep_pgm_r_reg(struct hideep_t *ts, unsigned int addr,
>> +     unsigned int *val)
>> +{
>> +     int ret = 0;
>> +     struct pgm_packet packet;
>> +
>> +     packet.header.w[0] = htonl(0x00);
>> +     packet.header.w[1] = htonl(addr);
>> +
>> +     ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
>> +
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     *val = packet.payload[0];
>> +
>> +err:
>> +     return ret;
>> +}
>> +
>> +static int hideep_pgm_w_reg(struct hideep_t *ts, unsigned int addr,
>> +     unsigned int data)
>> +{
>> +     int ret = 0;
>> +     struct pgm_packet packet;
>> +
>> +     packet.header.w[0] = htonl(0x80);
>> +     packet.header.w[1] = htonl(addr);
>> +     packet.payload[0] = data;
>> +
>> +     ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
>> +
>> +     return ret;
>> +}
>> +
>> +#define SW_RESET_IN_PGM(CLK) \
>> +{ \
>> +     hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
>> +     hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
>> +     hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
>> +}
>> +
>> +#define SET_FLASH_PIO(CE) \
>> +     hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
>> +#define SET_PIO_SIG(X, Y) \
>> +     hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
>> +#define SET_FLASH_HWCONTROL() \
>> +     hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
>> +
>> +#define NVM_W_SFR(x, y) \
>> +{ \
>> +     SET_FLASH_PIO(1); \
>> +     SET_PIO_SIG(x, y); \
>> +     SET_FLASH_PIO(0); \
>> +}
>> +
>> +static void get_dwz_from_binary(unsigned char *pres,
>> +     struct dwz_info_t *dwz_info)
>> +{
>> +     memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
>> +             sizeof(struct dwz_info_t));
>> +}
>> +
>> +static void hideep_sw_reset(struct hideep_t *ts, unsigned int food)
>> +{
>> +     SW_RESET_IN_PGM(food);
>> +}
>> +
>> +static int hideep_enter_pgm(struct hideep_t *ts)
>> +{
>> +     int ret = 0;
>> +     int retry_count = 10;
>> +     int retry = 0;
>> +     unsigned int status;
>> +     unsigned int pattern = 0xDF9DAF39;
>> +
>> +     while (retry < retry_count) {
>> +             i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
>> +             mdelay(1);
>> +
>> +             /* flush invalid Tx load register */
>> +             hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
>> +
>> +             hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
>> +
>> +             if (status != htonl(pattern)) {
>> +                     retry++;
>> +                     dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
>> +                             status);
>> +             } else {
>> +                     dev_dbg(&ts->client->dev, "found magic code");
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (retry < retry_count) {
>> +             hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
>> +             hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
>> +             hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
>> +             hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
>> +             hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
>> +             hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
>> +             hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
>> +             hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
>> +
>> +             mdelay(1);
>> +     } else {
>> +             ret = -1;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_load_dwz(struct hideep_t *ts)
>> +{
>> +     int ret = 0;
>> +     struct pgm_packet packet_r;
>> +
>> +     ret = hideep_enter_pgm(ts);
>> +
>> +     mdelay(50);
>> +
>> +     ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
>> +             sizeof(struct dwz_info_t));
>> +
>> +     memcpy((unsigned char *)ts->dwz_info, packet_r.payload,
>> +             sizeof(struct dwz_info_t));
>> +     hideep_sw_reset(ts, 10);
>> +
>> +     if (ts->dwz_info->product_code & 0x40) {
>> +             /* Crimson IC */
>> +             ts->fw_size = 1024 * 48;
>> +     } else {
>> +             /* default fw size */
>> +             ts->fw_size = 1024 * 64;
>> +     }
>> +
>> +     dev_dbg(&ts->client->dev, "firmware release version : %04x",
>> +             ts->dwz_info->release_ver);
>> +
>> +     mdelay(50);
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_nvm_unlock(struct hideep_t *ts)
>> +{
>> +     int ret = 0;
>> +     unsigned int unmask_code = 0;
>> +
>> +     ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
>> +
>> +     ret = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
>> +
>> +     ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
>> +
>> +     /* make it unprotected code */
>> +     unmask_code &= (~_PROT_MODE);
>> +
>> +     /* compare unmask code */
>> +     if (unmask_code != NVM_MASK)
>> +             dev_dbg(&ts->client->dev, "read mask code different 0x%x",
>> +                     unmask_code);
>> +
>> +     ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
>> +     SET_FLASH_PIO(0);
>> +
>> +     NVM_W_SFR(NVM_MASK_OFS, NVM_MASK);
>> +     SET_FLASH_HWCONTROL();
>> +     ret = hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_program_page(struct hideep_t *ts,
>> +     unsigned int addr, struct pgm_packet *packet_w)
>> +{
>> +     int ret = 0;
>> +     unsigned int pio_cmd = WRONLY;
>> +     unsigned int pio_cmd_page_erase = PERASE;
>> +     unsigned int status;
>> +     int time_out = 0;
>> +     int inf_en = 0;
>> +     unsigned int end_flag = 124;
>> +#ifndef PGM_BURST_WR
>> +     unsigned int i;
>> +#endif
>> +
>> +     hideep_pgm_r_reg(ts, FLASH_STA, &status);
>> +     ret = (status == 0) ? -1:0;
>> +
>> +     addr = addr & ~(NVM_PAGE_SIZE - 1);
>> +
>> +     if (addr > INF_SECTION) {
>> +             /* added INF flag set in pio_cmd */
>> +             addr -= INF_SECTION;
>> +             pio_cmd |= INF;
>> +             pio_cmd_page_erase |= INF;
>> +             inf_en = 1;
>> +     }
>> +
>> +     SET_FLASH_PIO(0);
>> +     SET_FLASH_PIO(1);
>> +
>> +     /* first erase */
>> +     SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
>> +
>> +     SET_FLASH_PIO(0);
>> +     time_out = 0;
>> +
>> +     while (1) {
>> +             mdelay(1);
>> +             hideep_pgm_r_reg(ts, FLASH_STA, &status);
>> +             if ((status) != 0)
>> +                     break;
>> +             if (time_out++ > 100)
>> +                     break;
>> +     }
>> +     SET_FLASH_PIO(1);
>> +     /* first erase end*/
>> +
>> +     SET_PIO_SIG(pio_cmd + addr, htonl(packet_w->payload[0]));
>> +
>> +#ifdef PGM_BURST_WR
>> +     hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
>> +             packet_w, NVM_PAGE_SIZE);
>> +#else
>> +     for (i = 0; i < NVM_PAGE_SIZE / 4; i++)
>> +             SET_PIO_SIG(pio_cmd + (i<<2), packet_w->payload[i]);
>> +#endif
>> +     if (inf_en == 0)
>> +             SET_PIO_SIG(end_flag, htonl(packet_w->payload[31]));
>> +     else
>> +             SET_PIO_SIG(end_flag | INF, htonl(packet_w->payload[31]));
>> +
>> +     SET_FLASH_PIO(0);
>> +
>> +     mdelay(1);
>> +
>> +     while (1) {
>> +             hideep_pgm_r_reg(ts, FLASH_STA, &status);
>> +             if ((status) != 0)
>> +                     break;
>> +     }
>> +     /* write routine end */
>> +
>> +     SET_FLASH_HWCONTROL();
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_program_nvm(struct hideep_t *ts, const unsigned char *ucode,
>> +     int len, int offset, unsigned char *old_fw)
>> +{
>> +     int i;
>> +     int ret = 0;
>> +     int len_r;
>> +     int len_w;
>> +     int addr = 0;
>> +     unsigned int pages;
>> +
>> +     ret = hideep_enter_pgm(ts);
>> +
>> +     hideep_nvm_unlock(ts);
>> +
>> +     pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
>> +     addr = offset;
>> +     len_r = len;
>> +     len_w = len_r;
>> +
>> +     dev_dbg(&ts->client->dev, "pages : %d", pages);
>> +     for (i = 0; i < pages; i++) {
>> +             if (len_r >= NVM_PAGE_SIZE)
>> +                     len_w = NVM_PAGE_SIZE;
>> +
>> +             /* compare */
>> +             if (old_fw != NULL)
>> +                     ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
>> +
>> +             if (ret != 0 || old_fw == NULL) {
>> +                     /* write page */
>> +                     memcpy(packet_w.payload, &(ucode[addr]), len_w);
>> +
>> +                     ret = hideep_program_page(ts, addr, &packet_w);
>> +                     mdelay(1);
>> +                     if (ret < 0)
>> +                             dev_err(&ts->client->dev,
>> +                                     "hideep_program_nvm : error(%08x):",
>> +                                     addr);
>> +             }
>> +
>> +             addr += NVM_PAGE_SIZE;
>> +             len_r -= NVM_PAGE_SIZE;
>> +             len_w = len_r;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_verify_nvm(struct hideep_t *ts, const unsigned char *ucode,
>> +     int len, int offset)
>> +{
>> +     int i, j;
>> +     int ret = 0;
>> +     unsigned char page_chk = 0;
>> +     unsigned int addr = offset;
>> +     unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
>> +     int len_r = len;
>> +     int len_v = len_r;
>> +
>> +     for (i = 0; i < pages; i++) {
>> +             if (len_r >= NVM_PAGE_SIZE)
>> +                     len_v = NVM_PAGE_SIZE;
>> +
>> +#ifdef PGM_BURST_WR
>> +             hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
>> +                     NVM_PAGE_SIZE);
>> +#else
>> +             for (j = 0; j < (NVM_PAGE_SIZE >> 2); j++)
>> +                     hideep_pgm_r_reg(ts, addr + (j << 2),
>> +                             &(packet_r.payload[j]));
>> +#endif
>> +             page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
>> +
>> +             if (page_chk != 0) {
>> +                     u8 *read = (u8 *)packet_r.payload;
>> +
>> +                     for (j = 0; j < NVM_PAGE_SIZE; j++)
>> +                             dev_err(&ts->client->dev, "%02x : %02x",
>> +                                             ucode[addr+j], read[j]);
>> +
>> +                     dev_err(&ts->client->dev, "verify : error(addr : %d)",
>> +                             addr);
>> +
>> +                     ret = -1;
>> +             }
>> +
>> +             addr += NVM_PAGE_SIZE;
>> +             len_r -= NVM_PAGE_SIZE;
>> +             len_v = len_r;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static void hideep_read_nvm(struct hideep_t *ts, unsigned char *data, int len,
>> +     int offset)
>> +{
>> +     int ret = 0;
>> +     int pages, i;
>> +     int len_r, len_v;
>> +     int addr = offset;
>> +#ifndef PGM_BURST_WR
>> +     int j;
>> +#endif
>> +
>> +     pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
>> +     len_r = len;
>> +     len_v = len_r;
>> +
>> +     ts->hideep_api->reset_ic(ts);
>> +
>> +     ret = hideep_enter_pgm(ts);
>> +
>> +     hideep_nvm_unlock(ts);
>> +
>> +     for (i = 0; i < pages; i++) {
>> +             if (len_r >= NVM_PAGE_SIZE)
>> +                     len_v = NVM_PAGE_SIZE;
>> +
>> +#ifdef PGM_BURST_WR
>> +             hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
>> +                     NVM_PAGE_SIZE);
>> +#else
>> +             for (j = 0; j < NVM_PAGE_SIZE / 4; j++)
>> +                     hideep_pgm_r_reg(ts, addr + (j << 2),
>> +                             &(packet_r.payload[j]));
>> +#endif
>> +             memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
>> +
>> +             addr += NVM_PAGE_SIZE;
>> +             len_r -= NVM_PAGE_SIZE;
>> +             len_v = len_r;
>> +     }
>> +
>> +     hideep_sw_reset(ts, 1000);
>> +}
>> +
>> +static int hideep_fw_verify_run(struct hideep_t *ts, unsigned char *fw,
>> +     size_t len, int offset)
>> +{
>> +     int ret = 0;
>> +     int retry = 3;
>> +
>> +     while (retry--) {
>> +             ret = hideep_verify_nvm(ts, fw, len, offset);
>> +             if (ret == 0) {
>> +                     dev_dbg(&ts->client->dev, "update success");
>> +                     break;
>> +             }
>> +             dev_err(&ts->client->dev, "download fw failed(%d)", retry);
>> +     }
>> +
>> +     ret = (retry == 0) ? -1:0;
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_wr_firmware(struct hideep_t *ts, unsigned char *code,
>> +     int len, int offset, bool mode)
>> +{
>> +     int ret = 0;
>> +     int firm_len;
>> +     unsigned char *ic_fw;
>> +     unsigned char *dwz_info;
>> +
>> +     ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
>> +     dwz_info = kmalloc(sizeof(struct dwz_info_t) + 64, GFP_KERNEL);
>> +
>> +     memset(dwz_info, 0x0, sizeof(struct dwz_info_t) + 64);
>> +
>> +     firm_len = len;
>> +     dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
>> +             firm_len, ts->fw_size);
>> +
>> +     if (firm_len > ts->fw_size)
>> +             firm_len = ts->fw_size;
>> +
>> +     dev_dbg(&ts->client->dev, "enter");
>> +     /* memory dump of target IC */
>> +     hideep_read_nvm(ts, ic_fw, firm_len, offset);
>> +     /* comparing & programming each page, if the memory of specified
>> +      * page is exactly same, no need to update.
>> +      */
>> +     ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
>> +
>> +#ifdef PGM_VERIFY
>> +     hideep_fw_verify_run(ts, code, firm_len, offset);
>> +     if (ret < 0) {
>> +             if (mode == true) {
>> +                     /* clear dwz version, it will be store once again
>> +                      * after update success
>> +                      */
>> +                     ts->dwz_info->release_ver = 0;
>> +                     memcpy(&dwz_info[64], ts->dwz_info,
>> +                             sizeof(struct dwz_info_t));
>> +                     ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
>> +                             0x280, NULL);
>> +             }
>> +     }
>> +#endif
>> +     get_dwz_from_binary(code, ts->dwz_info);
>> +
>> +     hideep_sw_reset(ts, 1000);
>> +
>> +     kfree(ic_fw);
>> +     kfree(dwz_info);
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_update_all_firmware(struct hideep_t *ts, const char *fn)
>> +{
>> +     int ret = 0;
>> +     const struct firmware *fw_entry;
>> +     unsigned char *fw_buf;
>> +     unsigned int fw_length;
>> +
>> +     dev_dbg(&ts->client->dev, "enter");
>> +     ret = request_firmware(&fw_entry, fn, &ts->client->dev);
>> +
>> +     if (ret != 0) {
>> +             dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
>> +             return ret;
>> +     }
>> +
>> +     fw_buf = (unsigned char *)fw_entry->data;
>> +     fw_length = (unsigned int)fw_entry->size;
>> +
>> +     /* chip specific code for flash fuse */
>> +     mutex_lock(&ts->dev_mutex);
>> +
>> +     ts->dev_state = state_updating;
>> +
>> +     ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
>> +
>> +     ts->dev_state = state_normal;
>> +
>> +     mutex_unlock(&ts->dev_mutex);
>> +
>> +     release_firmware(fw_entry);
>> +
>> +     return ret;
>> +}
>> +
>> +static int hideep_update_part_firmware(struct hideep_t *ts,
>> +     unsigned char *code, int len, int offset, bool mode)
>> +{
>> +     int ret = 0;
>> +
>> +     mutex_lock(&ts->dev_mutex);
>> +
>> +     ret = hideep_wr_firmware(ts, code, len, offset, false);
>> +
>> +     mutex_unlock(&ts->dev_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +void hideep_isp_init(struct hideep_t *ts)
>> +{
>> +     ts->hideep_api->update_all = hideep_update_all_firmware;
>> +     ts->hideep_api->update_part = hideep_update_part_firmware;
>> +     ts->hideep_api->get_dwz_info = hideep_load_dwz;
>> +     ts->hideep_api->sp_func = hideep_read_nvm;
>> +}
>> diff --git a/drivers/input/touchscreen/hideep_isp.h b/drivers/input/touchscreen/hideep_isp.h
>> new file mode 100644
>> index 0000000..648f271
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep_isp.h
>> @@ -0,0 +1,96 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#ifndef _LINUX_HIDEEP_ISP_H
>> +#define _LINUX_HIDEEP_ISP_H
>> +
>> +#define PGM_BURST_WR
>> +#define PGM_VERIFY
>> +
>> +#define NVM_DEFAULT_PAGE             0
>> +#define NVM_SFR_WPAGE                        1
>> +#define NVM_SFR_RPAGE                        2
>> +
>> +#define PIO_SIG                                      0x00400000
>> +#define _PROT_MODE                           0x03400000
>> +
>> +#define NVM_PAGE_SIZE                        128
>> +
>> +#define HIDEEP_NVM_DOWNLOAD          0x10000000
>> +
>> +/*************************************************************************
>> + * register map
>> + *************************************************************************/
>> +#define YRAM_BASE                            0x40000000
>> +#define PERIPHERAL_BASE                      0x50000000
>> +#define ESI_BASE                             (PERIPHERAL_BASE + 0x00000000)
>> +#define FLASH_BASE                           (PERIPHERAL_BASE + 0x01000000)
>> +#define SYSCON_BASE                          (PERIPHERAL_BASE + 0x02000000)
>> +
>> +#define SYSCON_MOD_CON                       (SYSCON_BASE + 0x0000)
>> +#define SYSCON_SPC_CON                       (SYSCON_BASE + 0x0004)
>> +#define SYSCON_CLK_CON                       (SYSCON_BASE + 0x0008)
>> +#define SYSCON_CLK_ENA                       (SYSCON_BASE + 0x000C)
>> +#define SYSCON_RST_CON                       (SYSCON_BASE + 0x0010)
>> +#define SYSCON_WDT_CON                       (SYSCON_BASE + 0x0014)
>> +#define SYSCON_WDT_CNT                       (SYSCON_BASE + 0x0018)
>> +#define SYSCON_PWR_CON                       (SYSCON_BASE + 0x0020)
>> +#define SYSCON_PGM_ID                        (SYSCON_BASE + 0x00F4)
>> +
>> +#define FLASH_CON                            (FLASH_BASE + 0x0000)
>> +#define FLASH_STA                            (FLASH_BASE + 0x0004)
>> +#define FLASH_CFG                            (FLASH_BASE + 0x0008)
>> +#define FLASH_TIM                            (FLASH_BASE + 0x000C)
>> +#define FLASH_CACHE_CFG                      (FLASH_BASE + 0x0010)
>> +
>> +#define ESI_TX_INVALID                       (ESI_BASE + 0x0008)
>> +
>> +/*************************************************************************
>> + * flash commands
>> + *************************************************************************/
>> +#define MERASE                                       0x00010000
>> +#define SERASE                                       0x00020000
>> +#define PERASE                                       0x00040000
>> +#define PROG                                 0x00080000
>> +#define WRONLY                                       0x00100000
>> +#define INF                                          0x00200000
>> +
>> +/*************************************************************************
>> + * NVM Mask
>> + *************************************************************************/
>> +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_CRIMSON
>> +#define NVM_MASK_OFS                 0x0000000C
>> +#define NVM_MASK                             0x00310000
>> +#define INF_SECTION                          0x0000C000
>> +#endif
>> +#ifdef CONFIG_TOUCHSCREEN_HIDEEP_LIME
>> +#define NVM_MASK_OFS                 0x0000000C
>> +#define NVM_MASK                             0x0030027B
>> +#define INF_SECTION                          0x00010000
>> +#endif
>> +
>> +/*************************************************************************
>> + * DWZ info
>> + *************************************************************************/
>> +#define HIDEEP_BOOT_SECTION          0x00000400
>> +#define HIDEEP_BOOT_LEN                      0x00000400
>> +#define HIDEEP_DWZ_SECTION           0x00000280
>> +#define HIDEEP_DWZ_INFO_OFS          0x000002C0
>> +#define HIDEEP_DWZ_LEN                       (HIDEEP_BOOT_SECTION \
>> +                                                     - HIDEEP_DWZ_SECTION)
>> +
>> +struct pgm_packet {
>> +     union {
>> +             unsigned char b[8];
>> +             unsigned int w[2];
>> +     } header;
>> +
>> +     unsigned int payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
>> +};
>> +
>> +#endif /* _LINUX_HIDEEP_ISP_H */
>> diff --git a/drivers/input/touchscreen/hideep_sysfs.c b/drivers/input/touchscreen/hideep_sysfs.c
>> new file mode 100644
>> index 0000000..7f28fb4
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/hideep_sysfs.c
>> @@ -0,0 +1,245 @@
>> +/*
>> + * Copyright (C) 2012-2017 Hideep, 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
>> + * as published by the Free Software Foudation.
>> + */
>> +
>> +#include "hideep.h"
>> +
>> +static ssize_t update_fw(struct device *dev, struct device_attribute *attr,
>> +     const char *buf, size_t count)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int mode, ret;
>> +     char *fw_name;
>> +
>> +     ret = kstrtoint(buf, 8, &mode);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (mode == 1) {
>> +             disable_irq(ts->client->irq);
>> +
>> +             ts->dev_state = state_updating;
>> +             fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
>> +                     ts->dwz_info->product_id);
>> +             ret = ts->hideep_api->update_all(ts, fw_name);
>> +
>> +             kfree(fw_name);
>> +
>> +             enable_irq(ts->client->irq);
>> +
>> +             ts->dev_state = state_normal;
>> +             if (ret != 0)
>> +                     dev_err(dev, "The firmware update failed(%d)", ret);
>> +     }
>> +
>> +     return count;
>> +}
>> +
>> +static ssize_t fw_version_show(struct device *dev,
>> +     struct device_attribute *attr, char *buf)
>> +{
>> +     int len = 0;
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +
>> +     dev_info(dev, "release version : %04x",
>> +             ts->dwz_info->release_ver);
>> +
>> +     mutex_lock(&ts->dev_mutex);
>> +     len = scnprintf(buf, PAGE_SIZE,
>> +             "%04x\n", ts->dwz_info->release_ver);
>> +     mutex_unlock(&ts->dev_mutex);
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t product_id_show(struct device *dev,
>> +     struct device_attribute *attr, char *buf)
>> +{
>> +     int len = 0;
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +
>> +     dev_info(dev, "product id : %04x",
>> +             ts->dwz_info->product_id);
>> +
>> +     mutex_lock(&ts->dev_mutex);
>> +     len = scnprintf(buf, PAGE_SIZE,
>> +             "%04x\n", ts->dwz_info->product_id);
>> +     mutex_unlock(&ts->dev_mutex);
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t power_status(struct device *dev, struct device_attribute *attr,
>> +     char *buf)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int len;
>> +
>> +     len = scnprintf(buf, PAGE_SIZE, "power status : %s\n",
>> +             (ts->dev_state == state_init) ? "off" : "on");
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t power_control(struct device *dev, struct device_attribute *attr,
>> +     const char *buf, size_t count)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int on, ret;
>> +
>> +     ret = kstrtoint(buf, 8, &on);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (on) {
>> +             ts->hideep_api->power(ts, on);
>> +             ts->dev_state = state_normal;
>> +             ts->hideep_api->reset_ic(ts);
>> +     } else {
>> +             ts->hideep_api->power(ts, on);
>> +             ts->dev_state = state_init;
>> +     }
>> +
>> +     return count;
>> +}
>> +
>> +#ifdef HIDEEP_SUPPORT_STYLUS
>> +static ssize_t stylus_status(struct device *dev, struct device_attribute *attr,
>> +     char *buf)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int len;
>> +     unsigned char status[1];
>> +
>> +     ts->hideep_api->i2c_read(ts, 0xB00C, 1, status);
>> +
>> +     len = scnprintf(buf, PAGE_SIZE, "stylus mode enable : %s\n",
>> +             (status[0] == 0x00) ? "off" : "on");
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t stylus_enable(struct device *dev, struct device_attribute *attr,
>> +     const char *buf, size_t count)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int on, ret;
>> +     unsigned char data[2];
>> +
>> +     ret = kstrtoint(buf, 8, &on);
>> +     if (ret)
>> +             return ret;
>> +
>> +     data[0] = 0x04;
>> +
>> +     if (on) {
>> +             data[1] = 0x01;
>> +             ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
>> +     } else {
>> +             data[1] = 0x00;
>> +             ts->hideep_api->i2c_write(ts, HIDEEP_SWTICH_CMD, 2, data);
>> +     }
>> +
>> +     return count;
>> +}
>> +#endif
>> +
>> +static ssize_t debug_status(struct device *dev, struct device_attribute *attr,
>> +     char *buf)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int len;
>> +
>> +     len = scnprintf(buf, PAGE_SIZE, "debug mode : %s\n",
>> +             (ts->debug_dev.debug_enable == 0) ? "off" : "on");
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t debug_mode(struct device *dev, struct device_attribute *attr,
>> +     const char *buf, size_t count)
>> +{
>> +     struct hideep_t *ts = dev_get_drvdata(dev);
>> +     int on, ret;
>> +     unsigned char data[2];
>> +
>> +     ret = kstrtoint(buf, 8, &on);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (on) {
>> +             ts->debug_dev.debug_enable = 1;
>> +             ts->dev_state = state_debugging;
>> +     } else {
>> +             ts->debug_dev.debug_enable = 0;
>> +             ts->dev_state = state_normal;
>> +             /* set touch mode */
>> +             data[0] = 0x00;
>> +             data[1] = 0x00;
>> +             ts->hideep_api->i2c_write(ts, HIDEEP_OPMODE_CMD, 2, data);
>> +             if (ts->interrupt_state == 0) {
>> +                     data[0] = 0x5A;
>> +                     ts->hideep_api->i2c_write(ts, HIDEEP_INTCLR_CMD, 1,
>> +                             data);
>> +                     enable_irq(ts->client->irq);
>> +                     ts->interrupt_state = 1;
>> +             }
>> +     }
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(update_fw, 0664, NULL, update_fw);
>> +static DEVICE_ATTR(version, 0664, fw_version_show, NULL);
>> +static DEVICE_ATTR(product_id, 0664, product_id_show, NULL);
>> +static DEVICE_ATTR(power_en, 0664, power_status, power_control);
>
> Why does this need to be exported?

Sorry, it is just for debug, I will remove it.

>
>> +static DEVICE_ATTR(debug_en, 0664, debug_status, debug_mode);
>
> I do not think we want it in mainline.
>
>> +#ifdef HIDEEP_SUPPORT_STYLUS
>> +static DEVICE_ATTR(stylus_en, 0664, stylus_status, stylus_enable);
>
> Why do we need to disable stylus?

Some customers want to enable/disable for stylus function at user space.
So I added it. If don't want to add mainline, I will remove it.

>
>> +#endif
>> +
>> +static struct attribute *hideep_ts_sysfs_entries[] = {
>> +     &dev_attr_update_fw.attr,
>> +     &dev_attr_version.attr,
>> +     &dev_attr_product_id.attr,
>> +     &dev_attr_power_en.attr,
>> +     &dev_attr_debug_en.attr,
>> +#ifdef HIDEEP_SUPPORT_STYLUS
>> +     &dev_attr_stylus_en.attr,
>> +#endif
>> +     NULL
>> +};
>> +
>> +static struct attribute_group hideep_ts_attr_group = {
>> +     .attrs = hideep_ts_sysfs_entries,
>> +};
>> +
>> +int hideep_sysfs_init(struct hideep_t *ts)
>> +{
>> +     int ret;
>> +     struct i2c_client *client = ts->client;
>> +
>> +     /* Create the files associated with this kobject */
>> +     ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
>> +
>> +     dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
>> +
>> +     if (ret)
>> +             dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
>> +                      __func__, ret);
>> +
>> +     return ret;
>> +}
>> +
>> +int hideep_sysfs_exit(struct hideep_t *ts)
>> +{
>> +     struct i2c_client *client = ts->client;
>> +
>> +     sysfs_remove_group(&client->dev.kobj, &hideep_ts_attr_group);
>> +
>> +     return 0;
>> +}
>> --
>> 2.7.4
>>
>
> Thanks.
>
> --
> Dmitry

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
       [not found]         ` <1500965607-2446-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
@ 2017-08-22  9:03           ` Anthony Kim
  2017-08-09 23:49           ` Dmitry Torokhov
  1 sibling, 0 replies; 29+ messages in thread
From: Anthony Kim @ 2017-08-22  9:03 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, mark.rutland, rydberg
  Cc: linux-kernel, linux-input, devicetree, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |   39 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/input/touchscreen/Kconfig                  |   11 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/hideep_core.c            | 1294 ++++++++++++++++++++
 drivers/input/touchscreen/hideep_core.h            |  221 ++++
 6 files changed, 1567 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep_core.c
 create mode 100644 drivers/input/touchscreen/hideep_core.h

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..7a5b9a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,39 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-crimson"
+					or "hideep,hideep-lime".
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- touchscreen-size-x	: X axis size of touchscreen
+- touchscreen-size-y	: Y axis size of touchscreen
+- touchkey-use	: Using touchkey in system.
+
+Example:
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-lime";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		touchscreen-size-x = 1079;
+		touchscreen-size-y = 1919;
+		touchkey-use;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..13e11c7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..03ec3bb 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -103,3 +103,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_core.o
diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
new file mode 100644
index 0000000..1d5887d
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.c
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <asm/unaligned.h>
+
+#include "hideep_core.h"
+
+static void hideep_reset_ic(struct hideep_ts *ts);
+
+static int hideep_pgm_w_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	put_unaligned_be32((0x80 | (len / 4 - 1)), &packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	for (i = 0; i < len / sizeof(unsigned int); i++)
+		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, &packet->header.b[3],
+		(len + 5));
+
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+	unsigned char buff[len];
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	put_unaligned_be32((0x00 | (len / 4 - 1)), &packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	ret = i2c_master_send(ts->client, &packet->header.b[3], 5);
+
+	if (ret < 0)
+		goto err;
+
+	ret = i2c_master_recv(ts->client, buff,	len);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < len / 4; i++)
+		packet->payload[i] = get_unaligned_be32(&buff[i * 4]);
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x00, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x80, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void get_dwz_from_binary(unsigned char *pres,
+	struct dwz_info *dwz_info)
+{
+	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
+		sizeof(struct dwz_info));
+}
+
+static void hideep_sw_reset(struct hideep_ts *ts, unsigned int food)
+{
+	SW_RESET_IN_PGM(food);
+}
+
+static int hideep_enter_pgm(struct hideep_ts *ts)
+{
+	int ret = 0;
+	int retry_count = 10;
+	int retry = 0;
+	unsigned int status;
+	unsigned int pattern = 0xDF9DAF39;
+
+	while (retry < retry_count) {
+		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
+		mdelay(1);
+
+		/* flush invalid Tx load register */
+		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+		if (pattern != get_unaligned_be32(&status)) {
+			retry++;
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				get_unaligned_be32(&status));
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry < retry_count) {
+		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
+
+		mdelay(1);
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+static void hideep_nvm_unlock(struct hideep_ts *ts)
+{
+	unsigned int unmask_code = 0;
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != ts->nvm_mask)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, ts->nvm_mask);
+	SET_FLASH_HWCONTROL();
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+}
+
+static int hideep_program_page(struct hideep_ts *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret = 0;
+	unsigned int pio_cmd = WRONLY;
+	unsigned int pio_cmd_page_erase = PERASE;
+	unsigned int status;
+	int time_out = 0;
+	unsigned int end_flag = 124;
+
+	hideep_pgm_r_reg(ts, FLASH_STA, &status);
+	ret = (status == 0) ? -1:0;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* first erase */
+	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+	time_out = 0;
+
+	while (1) {
+		mdelay(1);
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+		if (time_out++ > 100)
+			break;
+	}
+	SET_FLASH_PIO(1);
+	/* first erase end*/
+
+	SET_PIO_SIG(pio_cmd + addr, get_unaligned_be32(&packet_w->payload[0]));
+
+	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
+		packet_w, NVM_PAGE_SIZE);
+
+	SET_PIO_SIG(end_flag, get_unaligned_be32(&packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	while (1) {
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+	}
+	/* write routine end */
+
+	SET_FLASH_HWCONTROL();
+
+	return ret;
+}
+
+static int hideep_program_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len, int offset, unsigned char *old_fw)
+{
+	int i;
+	int ret = 0;
+	int len_r;
+	int len_w;
+	int addr = 0;
+	unsigned int pages;
+
+	struct pgm_packet packet_w;
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	addr = offset;
+	len_r = len;
+	len_w = len_r;
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_w = NVM_PAGE_SIZE;
+
+		/* compare */
+		if (old_fw != NULL)
+			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
+
+		if (ret != 0 || old_fw == NULL) {
+			/* write page */
+			memcpy(packet_w.payload, &(ucode[addr]), len_w);
+
+			ret = hideep_program_page(ts, addr, &packet_w);
+			mdelay(1);
+			if (ret < 0)
+				dev_err(&ts->client->dev,
+					"hideep_program_nvm : error(%08x):",
+					addr);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_w = len_r;
+	}
+
+	return ret;
+}
+
+static int hideep_verify_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len, int offset)
+{
+	int i, j;
+	int ret = 0;
+	unsigned char page_chk = 0;
+	unsigned int addr = offset;
+	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	int len_r = len;
+	int len_v = len_r;
+
+	struct pgm_packet packet_r;
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+
+		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
+
+		if (page_chk != 0) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++)
+				dev_err(&ts->client->dev, "%02x : %02x",
+						ucode[addr+j], read[j]);
+
+			dev_err(&ts->client->dev, "verify : error(addr : %d)",
+				addr);
+
+			ret = -1;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	return ret;
+}
+
+static void hideep_read_nvm(struct hideep_ts *ts, unsigned char *data, int len,
+	int offset)
+{
+	int ret = 0;
+	int pages, i;
+	int len_r, len_v;
+	int addr = offset;
+
+	struct pgm_packet packet_r;
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	len_r = len;
+	len_v = len_r;
+
+	hideep_reset_ic(ts);
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+
+		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	hideep_sw_reset(ts, 1000);
+}
+
+static int hideep_fw_verify_run(struct hideep_ts *ts, unsigned char *fw,
+	size_t len, int offset)
+{
+	int ret = 0;
+	int retry = 3;
+
+	while (retry--) {
+		ret = hideep_verify_nvm(ts, fw, len, offset);
+		if (ret == 0) {
+			dev_dbg(&ts->client->dev, "update success");
+			break;
+		}
+		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
+	}
+
+	ret = (retry == 0) ? -1:0;
+
+	return ret;
+}
+
+static int hideep_wr_firmware(struct hideep_ts *ts, unsigned char *code,
+	int len, int offset, bool mode)
+{
+	int ret = 0;
+	int firm_len;
+	unsigned char *ic_fw;
+	unsigned char *dwz_info;
+
+	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
+	dwz_info = kmalloc(sizeof(struct dwz_info) + 64, GFP_KERNEL);
+
+	memset(dwz_info, 0x0, sizeof(struct dwz_info) + 64);
+
+	firm_len = len;
+	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
+		firm_len, ts->fw_size);
+
+	if (firm_len > ts->fw_size)
+		firm_len = ts->fw_size;
+
+	dev_dbg(&ts->client->dev, "enter");
+	/* memory dump of target IC */
+	hideep_read_nvm(ts, ic_fw, firm_len, offset);
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
+
+	hideep_fw_verify_run(ts, code, firm_len, offset);
+	if (ret < 0) {
+		if (mode == true) {
+			/* clear dwz version, it will be store once again
+			 * after update success
+			 */
+			ts->dwz_info.release_ver = 0;
+			memcpy(&dwz_info[64], &ts->dwz_info,
+				sizeof(struct dwz_info));
+			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
+				0x280, NULL);
+		}
+	}
+
+	get_dwz_from_binary(code, &ts->dwz_info);
+
+	hideep_sw_reset(ts, 1000);
+
+	kfree(ic_fw);
+	kfree(dwz_info);
+
+	return ret;
+}
+
+static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
+{
+	int ret = 0;
+	const struct firmware *fw_entry;
+	unsigned char *fw_buf;
+	unsigned int fw_length;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	fw_buf = (unsigned char *)fw_entry->data;
+	fw_length = (unsigned int)fw_entry->size;
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_ts *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+	if (ret < 0)
+		return ret;
+
+	mdelay(50);
+
+	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
+		sizeof(struct dwz_info));
+
+	memcpy(&ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info));
+	hideep_sw_reset(ts, 10);
+
+	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
+		/* Crimson IC */
+		ts->fw_size = 1024 * 48;
+		ts->nvm_mask = 0x00310000;
+	} else {
+		/* Lime fw size */
+		ts->fw_size = 1024 * 64;
+		ts->nvm_mask = 0x0030027B;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mdelay(50);
+
+	return ret;
+}
+
+static int hideep_i2c_read(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned short r_len, raddr;
+	int length;
+
+	length = len;
+	raddr = addr;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	do {
+		if (length > MAX_I2C_BUFFER_SIZE)
+			r_len = MAX_I2C_BUFFER_SIZE;
+		else
+			r_len = length;
+
+		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
+			raddr, r_len);
+
+		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
+
+		if (ret < 0)
+			goto i2c_err;
+
+		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
+		length -= MAX_I2C_BUFFER_SIZE;
+		buf += MAX_I2C_BUFFER_SIZE;
+		raddr += MAX_I2C_BUFFER_SIZE;
+
+		if (ret < 0)
+			goto i2c_err;
+	} while (length > 0);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	return  0;
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static int hideep_i2c_write(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned char data[len + 2];
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	// data mangling..
+	data[0] = (addr >> 0) & 0xFF;
+	data[1] = (addr >> 8) & 0xFF;
+	memcpy(&data[2], buf, len);
+
+	ret = i2c_master_send(ts->client, data, len + 2);
+
+	if (ret < 0)
+		goto i2c_err;
+
+	mutex_unlock(&ts->i2c_mutex);
+	return  0;
+
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static void hideep_reset_ic(struct hideep_ts *ts)
+{
+	unsigned char cmd = 0x01;
+
+	if (!IS_ERR(ts->reset_gpio)) {
+		dev_dbg(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpiod_set_value(ts->reset_gpio, 0);
+		mdelay(20);
+		gpiod_set_value(ts->reset_gpio, 1);
+	} else {
+		hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+}
+
+static int hideep_pwr_on(struct hideep_ts *ts)
+{
+	int ret = 0;
+
+	if (!IS_ERR(ts->vcc_vdd)) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(ts->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+	}
+	usleep_range(999, 1000);
+
+	if (!IS_ERR(ts->vcc_vid)) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(ts->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+	usleep_range(2999, 3000);
+
+	return ret;
+}
+
+static void hideep_pwr_off(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	if (!IS_ERR(ts->reset_gpio))
+		gpiod_set_value(ts->reset_gpio, 0);
+
+	if (!IS_ERR(ts->vcc_vid))
+		regulator_disable(ts->vcc_vid);
+
+	if (!IS_ERR(ts->vcc_vdd))
+		regulator_disable(ts->vcc_vdd);
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void pops_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = (event->index >> 0) & 0x0F;
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type), false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+}
+
+static void push_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	bool btn_dn = 0;
+	bool btn_mv = 0;
+	int evt = 0;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = (event->index >> 0) & 0x0F;
+		btn_up = (event->flag >> HIDEEP_MT_RELEASED) & 0x01;
+		btn_dn = (event->flag >> HIDEEP_MT_FIRST_CONTACT)
+			& 0x01;
+		btn_mv = (event->flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
+
+		if (btn_up)
+			clear_bit(id, &ts->tch_bit);
+		else
+			__set_bit(id, &ts->tch_bit);
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			event->type, event->index, i,
+			get_unaligned_le16(&event->x),
+			get_unaligned_le16(&event->y),
+			get_unaligned_le16(&event->z));
+
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				get_unaligned_le16(&event->x));
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&event->y));
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				get_unaligned_le16(&event->z));
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				event->w);
+			evt++;
+		}
+	}
+
+	if (ts->tch_bit == 0)
+		evt = 0;
+
+	input_report_key(ts->input_dev, BTN_TOUCH, evt);
+	input_mt_sync_frame(ts->input_dev);
+}
+
+static void pops_ky(struct hideep_ts *ts)
+{
+	input_report_key(ts->input_dev, KEY_HOME, false);
+	input_report_key(ts->input_dev, KEY_MENU, false);
+	input_report_key(ts->input_dev, KEY_BACK, false);
+}
+
+static void push_ky(struct hideep_ts *ts)
+{
+	int i;
+	int pressed;
+	int key;
+	int status;
+	int code;
+
+	for (i = 0; i < ts->key_count; i++) {
+		key = ts->key_event[i + i * 2] & 0x0F;
+		status = ts->key_event[i + i * 2] & 0xF0;
+		code = 0;
+		pressed = false;
+
+		if (status & HIDEEP_KEY_PRESSED_MASK)
+			pressed = true;
+		else
+			pressed = false;
+
+		switch (key) {
+		case 0:
+			code = KEY_HOME;
+			break;
+		case 1:
+			code = KEY_MENU;
+			break;
+		case 2:
+			code = KEY_BACK;
+			break;
+		}
+		input_report_key(ts->input_dev, code, pressed);
+	}
+}
+
+static void hideep_put_event(struct hideep_ts *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+	if (ts->key_count > 0)
+		push_ky(ts);
+
+	input_sync(ts->input_dev);
+}
+
+static int hideep_get_event(struct hideep_ts *ts)
+{
+	int ret;
+	int touch_count;
+	int event_size;
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		event_size = ts->tch_count *
+			sizeof(struct hideep_event);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			event_size, ts->touch_event);
+
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+	if (ts->key_count > 0) {
+		event_size = ts->key_count * 2;
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			event_size, ts->key_event);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+	return touch_count;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char buff[2];
+	int ret;
+
+	struct hideep_ts *ts = (struct hideep_ts *) handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x", ts->dev_state);
+
+	if (ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, buff);
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			ts->interrupt_state = 0;
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = buff[0];
+		ts->key_count = buff[1] & 0x0f;
+		ts->lpm_count = buff[1] & 0xf0;
+
+		ret = hideep_get_event(ts);
+
+		if (ret >= 0)
+			hideep_put_event(ts);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_ts *ts)
+{
+	int ret;
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+
+	if (ts->key_use) {
+		input_set_capability(ts->input_dev, EV_KEY, KEY_HOME);
+		input_set_capability(ts->input_dev, EV_KEY, KEY_MENU);
+		input_set_capability(ts->input_dev, EV_KEY, KEY_BACK);
+	}
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
+		65535, 0, 0);
+
+	ret = input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+
+	if (ret)
+		return ret;
+
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, 65535, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	return 0;
+}
+
+static void hideep_get_info(struct hideep_ts *ts)
+{
+	unsigned char val[4];
+
+	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
+		hideep_i2c_read(ts, 0x28, 4, val);
+
+		ts->prop.max_x = get_unaligned_le16(&val[2]);
+		ts->prop.max_y = get_unaligned_le16(&val[0]);
+
+		dev_info(&ts->client->dev, "X : %d, Y : %d",
+			ts->prop.max_x, ts->prop.max_y);
+	}
+}
+
+static ssize_t hideep_update_fw(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	if (mode == 1) {
+		disable_irq(ts->client->irq);
+
+		ts->dev_state = state_updating;
+		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+			get_unaligned_le16(&ts->dwz_info.product_id));
+		ret = hideep_update_firmware(ts, fw_name);
+
+		kfree(fw_name);
+
+		enable_irq(ts->client->irq);
+
+		ts->dev_state = state_normal;
+		if (ret != 0)
+			dev_err(dev, "The firmware update failed(%d)", ret);
+	}
+
+	return count;
+}
+
+static ssize_t hideep_fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t hideep_product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+static int hideep_sysfs_init(struct hideep_ts *ts)
+{
+	int ret;
+	struct i2c_client *client = ts->client;
+
+	/* Create the files associated with this kobject */
+	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
+
+	if (ret)
+		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
+			 __func__, ret);
+
+	return ret;
+}
+
+static void hideep_sysfs_exit(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &hideep_ts_attr_group);
+}
+
+static int __maybe_unused hideep_resume(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state == state_normal)
+		goto hideep_resume_exit;
+
+	dev_dbg(dev, "not waiting.");
+	ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+hideep_resume_exit:
+	mdelay(10);
+	hideep_reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+static int __maybe_unused hideep_suspend(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	mutex_lock(&ts->dev_mutex);
+	if (ts->dev_state == state_sleep)
+		goto hideep_suspend_exit;
+
+	dev_dbg(dev, "not waiting.");
+	ts->dev_state = state_sleep;
+
+	/* send sleep command.. */
+	pops_mt(ts);
+	if (ts->key_use)
+		pops_ky(ts);
+	input_sync(ts->input_dev);
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+hideep_suspend_exit:
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+
+#ifdef CONFIG_OF
+static int hideep_parse_dts(struct hideep_ts *ts)
+{
+	/* device tree information get */
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(pdata->reset_gpio))
+		return -EINVAL;
+
+	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(ts->vcc_vdd))
+		return -EINVAL;
+
+	ts->vcc_vid = devm_regulator_get(dev, "vid");
+	if (IS_ERR(ts->vcc_vid))
+		return -EINVAL;
+
+	ts->key_use = device_property_read_bool(&ts->client->dev,
+		"touchkey-use");
+
+	return 0;
+}
+#else
+static int hideep_parse_dts(struct hideep_ts *ts)
+{
+	return -EINVAL;
+}
+#endif
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_ts *ts;
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		return -ENODEV;
+	}
+
+	/* init hideep_ts */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+
+	if (client->dev.of_node) {
+		ret = hideep_parse_dts(ts);
+		if (ret)
+			return ret;
+	}
+
+	ts->client = client;
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	/* power on */
+	ret = hideep_pwr_on(ts);
+	if (ret) {
+		dev_err(&ts->client->dev, "power on failed");
+		return ret;
+	}
+
+	ret = devm_add_action(&ts->client->dev, hideep_pwr_off, ts);
+	if (ret) {
+		hideep_pwr_off(ts);
+		return ret;
+	}
+
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	hideep_reset_ic(ts);
+
+	/* read info */
+	ret = hideep_load_dwz(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		return ret;
+	}
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		return -ENOMEM;
+	}
+
+	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
+	hideep_get_info(ts);
+
+	ret = hideep_capability(ts);
+	if (ret) {
+		dev_err(&client->dev, "can't init input properties");
+		return ret;
+	}
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		return ret;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (IS_ERR(&ts->client->irq)) {
+		dev_err(&client->dev, "can't be assigned irq");
+		return -ENOMEM;
+	}
+
+	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
+		NULL, hideep_irq_task, IRQF_ONESHOT,
+		ts->client->name, ts);
+
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+			ret);
+		return ret;
+	}
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+	ret = hideep_sysfs_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		return ret;
+	}
+
+	ret = devm_add_action(&ts->client->dev, hideep_sysfs_exit, ts);
+	if (ret) {
+		hideep_sysfs_exit(ts);
+		return ret;
+	}
+
+	dev_info(&client->dev, "probe is ok!");
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
+#endif
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-lime" },
+	{ .compatible = "hideep,hideep-crimson" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/hideep_core.h b/drivers/input/touchscreen/hideep_core.h
new file mode 100644
index 0000000..be6f3da
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_CORE_H
+#define _LINUX_HIDEEP_CORE_H
+/*************************************************************************
+ * Buffer size
+ *************************************************************************/
+#define FRAME_HEADER_SIZE				8
+/* if size of system i2c buffer is smaller than this value, need to modify */
+#define MAX_I2C_BUFFER_SIZE				512
+
+/*************************************************************************
+ * board porting config
+ *************************************************************************/
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+/*************************************************************************
+ * register addr
+ *************************************************************************/
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/*************************************************************************
+ * multi-touch & key definitions.
+ *************************************************************************/
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/*************************************************************************
+ * HiDeep Event
+ *************************************************************************/
+struct hideep_event {
+	__le16 x;
+	__le16 y;
+	__le16 z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+} __packed;
+
+
+/*************************************************************************
+ * IC State
+ *************************************************************************/
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+	state_debugging,
+};
+
+/*************************************************************************
+ * Firmware info
+ *************************************************************************/
+struct dwz_info {
+	__le32	code_start;
+	unsigned char code_crc[12];
+
+	__le32 c_code_start;
+	__le16 c_code_len;
+	__le16 gen_ver;
+
+	__le32 vr_start;
+	__le16 vr_len;
+	__le16 rsv0;
+
+	__le32 ft_start;
+	__le16 ft_len;
+	__le16 vr_version;
+
+	__le16 boot_ver;
+	__le16 core_ver;
+	__le16 custom_ver;
+	__le16 release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	__le16 product_code;
+	__le16 extra_option;
+
+	__le16 product_id;
+	__le16 vendor_id;
+} __packed;
+
+struct hideep_ts {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+
+	struct touchscreen_properties prop;
+
+	struct gpio_desc *reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	bool suspended;
+	enum e_dev_state dev_state;
+
+	long tch_bit;
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	unsigned char touch_event[HIDEEP_MT_MAX * 10];
+	unsigned char key_event[HIDEEP_KEY_MAX * 2];
+	bool key_use;
+
+	struct dwz_info dwz_info;
+
+	int interrupt_state;
+	int fw_size;
+	int nvm_mask;
+} __packed;
+
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_NVM_DOWNLOAD		0x10000000
+
+/*************************************************************************
+ * register map
+ *************************************************************************/
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+/*************************************************************************
+ * flash commands
+ *************************************************************************/
+#define MERASE					0x00010000
+#define SERASE					0x00020000
+#define PERASE					0x00040000
+#define PROG					0x00080000
+#define WRONLY					0x00100000
+
+#define NVM_MASK_OFS			0x0000000C
+
+/*************************************************************************
+ * DWZ info
+ *************************************************************************/
+#define HIDEEP_BOOT_SECTION		0x00000400
+#define HIDEEP_BOOT_LEN			0x00000400
+#define HIDEEP_DWZ_SECTION		0x00000280
+#define HIDEEP_DWZ_INFO_OFS		0x000002C0
+#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
+							- HIDEEP_DWZ_SECTION)
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		__be32 w[2];
+	} header;
+
+	__be32 payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+#endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
@ 2017-08-22  9:03           ` Anthony Kim
  0 siblings, 0 replies; 29+ messages in thread
From: Anthony Kim @ 2017-08-22  9:03 UTC (permalink / raw)
  To: dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	rydberg-FFUHeuDi6mxAfugRpC6u6w
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
---
 .../bindings/input/touchscreen/hideep.txt          |   39 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/input/touchscreen/Kconfig                  |   11 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/hideep_core.c            | 1294 ++++++++++++++++++++
 drivers/input/touchscreen/hideep_core.h            |  221 ++++
 6 files changed, 1567 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep_core.c
 create mode 100644 drivers/input/touchscreen/hideep_core.h

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..7a5b9a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,39 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-crimson"
+					or "hideep,hideep-lime".
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- touchscreen-size-x	: X axis size of touchscreen
+- touchscreen-size-y	: Y axis size of touchscreen
+- touchkey-use	: Using touchkey in system.
+
+Example:
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-lime";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		touchscreen-size-x = 1079;
+		touchscreen-size-y = 1919;
+		touchkey-use;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..13e11c7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
 	  To compile this driver as a module, choose M here: the
 	  module will be called bu21023_ts.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..03ec3bb 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -103,3 +103,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
 obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_core.o
diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
new file mode 100644
index 0000000..1d5887d
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.c
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <asm/unaligned.h>
+
+#include "hideep_core.h"
+
+static void hideep_reset_ic(struct hideep_ts *ts);
+
+static int hideep_pgm_w_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	put_unaligned_be32((0x80 | (len / 4 - 1)), &packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	for (i = 0; i < len / sizeof(unsigned int); i++)
+		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, &packet->header.b[3],
+		(len + 5));
+
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret = 0;
+	int i;
+	unsigned char buff[len];
+
+	if ((len % 4) != 0)
+		return -1;
+
+	mutex_lock(&ts->i2c_mutex);
+
+	put_unaligned_be32((0x00 | (len / 4 - 1)), &packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	ret = i2c_master_send(ts->client, &packet->header.b[3], 5);
+
+	if (ret < 0)
+		goto err;
+
+	ret = i2c_master_recv(ts->client, buff,	len);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < len / 4; i++)
+		packet->payload[i] = get_unaligned_be32(&buff[i * 4]);
+
+err:
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x00, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret = 0;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x80, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void get_dwz_from_binary(unsigned char *pres,
+	struct dwz_info *dwz_info)
+{
+	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
+		sizeof(struct dwz_info));
+}
+
+static void hideep_sw_reset(struct hideep_ts *ts, unsigned int food)
+{
+	SW_RESET_IN_PGM(food);
+}
+
+static int hideep_enter_pgm(struct hideep_ts *ts)
+{
+	int ret = 0;
+	int retry_count = 10;
+	int retry = 0;
+	unsigned int status;
+	unsigned int pattern = 0xDF9DAF39;
+
+	while (retry < retry_count) {
+		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);
+		mdelay(1);
+
+		/* flush invalid Tx load register */
+		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+		if (pattern != get_unaligned_be32(&status)) {
+			retry++;
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				get_unaligned_be32(&status));
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry < retry_count) {
+		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
+
+		mdelay(1);
+	} else {
+		ret = -1;
+	}
+
+	return ret;
+}
+static void hideep_nvm_unlock(struct hideep_ts *ts)
+{
+	unsigned int unmask_code = 0;
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != ts->nvm_mask)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, ts->nvm_mask);
+	SET_FLASH_HWCONTROL();
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+}
+
+static int hideep_program_page(struct hideep_ts *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret = 0;
+	unsigned int pio_cmd = WRONLY;
+	unsigned int pio_cmd_page_erase = PERASE;
+	unsigned int status;
+	int time_out = 0;
+	unsigned int end_flag = 124;
+
+	hideep_pgm_r_reg(ts, FLASH_STA, &status);
+	ret = (status == 0) ? -1:0;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* first erase */
+	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+	time_out = 0;
+
+	while (1) {
+		mdelay(1);
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+		if (time_out++ > 100)
+			break;
+	}
+	SET_FLASH_PIO(1);
+	/* first erase end*/
+
+	SET_PIO_SIG(pio_cmd + addr, get_unaligned_be32(&packet_w->payload[0]));
+
+	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
+		packet_w, NVM_PAGE_SIZE);
+
+	SET_PIO_SIG(end_flag, get_unaligned_be32(&packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	while (1) {
+		hideep_pgm_r_reg(ts, FLASH_STA, &status);
+		if ((status) != 0)
+			break;
+	}
+	/* write routine end */
+
+	SET_FLASH_HWCONTROL();
+
+	return ret;
+}
+
+static int hideep_program_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len, int offset, unsigned char *old_fw)
+{
+	int i;
+	int ret = 0;
+	int len_r;
+	int len_w;
+	int addr = 0;
+	unsigned int pages;
+
+	struct pgm_packet packet_w;
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	addr = offset;
+	len_r = len;
+	len_w = len_r;
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_w = NVM_PAGE_SIZE;
+
+		/* compare */
+		if (old_fw != NULL)
+			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
+
+		if (ret != 0 || old_fw == NULL) {
+			/* write page */
+			memcpy(packet_w.payload, &(ucode[addr]), len_w);
+
+			ret = hideep_program_page(ts, addr, &packet_w);
+			mdelay(1);
+			if (ret < 0)
+				dev_err(&ts->client->dev,
+					"hideep_program_nvm : error(%08x):",
+					addr);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_w = len_r;
+	}
+
+	return ret;
+}
+
+static int hideep_verify_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len, int offset)
+{
+	int i, j;
+	int ret = 0;
+	unsigned char page_chk = 0;
+	unsigned int addr = offset;
+	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	int len_r = len;
+	int len_v = len_r;
+
+	struct pgm_packet packet_r;
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+
+		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
+
+		if (page_chk != 0) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++)
+				dev_err(&ts->client->dev, "%02x : %02x",
+						ucode[addr+j], read[j]);
+
+			dev_err(&ts->client->dev, "verify : error(addr : %d)",
+				addr);
+
+			ret = -1;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	return ret;
+}
+
+static void hideep_read_nvm(struct hideep_ts *ts, unsigned char *data, int len,
+	int offset)
+{
+	int ret = 0;
+	int pages, i;
+	int len_r, len_v;
+	int addr = offset;
+
+	struct pgm_packet packet_r;
+
+	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
+	len_r = len;
+	len_v = len_r;
+
+	hideep_reset_ic(ts);
+
+	ret = hideep_enter_pgm(ts);
+
+	hideep_nvm_unlock(ts);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r >= NVM_PAGE_SIZE)
+			len_v = NVM_PAGE_SIZE;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+
+		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		len_v = len_r;
+	}
+
+	hideep_sw_reset(ts, 1000);
+}
+
+static int hideep_fw_verify_run(struct hideep_ts *ts, unsigned char *fw,
+	size_t len, int offset)
+{
+	int ret = 0;
+	int retry = 3;
+
+	while (retry--) {
+		ret = hideep_verify_nvm(ts, fw, len, offset);
+		if (ret == 0) {
+			dev_dbg(&ts->client->dev, "update success");
+			break;
+		}
+		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
+	}
+
+	ret = (retry == 0) ? -1:0;
+
+	return ret;
+}
+
+static int hideep_wr_firmware(struct hideep_ts *ts, unsigned char *code,
+	int len, int offset, bool mode)
+{
+	int ret = 0;
+	int firm_len;
+	unsigned char *ic_fw;
+	unsigned char *dwz_info;
+
+	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
+	dwz_info = kmalloc(sizeof(struct dwz_info) + 64, GFP_KERNEL);
+
+	memset(dwz_info, 0x0, sizeof(struct dwz_info) + 64);
+
+	firm_len = len;
+	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
+		firm_len, ts->fw_size);
+
+	if (firm_len > ts->fw_size)
+		firm_len = ts->fw_size;
+
+	dev_dbg(&ts->client->dev, "enter");
+	/* memory dump of target IC */
+	hideep_read_nvm(ts, ic_fw, firm_len, offset);
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
+
+	hideep_fw_verify_run(ts, code, firm_len, offset);
+	if (ret < 0) {
+		if (mode == true) {
+			/* clear dwz version, it will be store once again
+			 * after update success
+			 */
+			ts->dwz_info.release_ver = 0;
+			memcpy(&dwz_info[64], &ts->dwz_info,
+				sizeof(struct dwz_info));
+			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
+				0x280, NULL);
+		}
+	}
+
+	get_dwz_from_binary(code, &ts->dwz_info);
+
+	hideep_sw_reset(ts, 1000);
+
+	kfree(ic_fw);
+	kfree(dwz_info);
+
+	return ret;
+}
+
+static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
+{
+	int ret = 0;
+	const struct firmware *fw_entry;
+	unsigned char *fw_buf;
+	unsigned int fw_length;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	fw_buf = (unsigned char *)fw_entry->data;
+	fw_length = (unsigned int)fw_entry->size;
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_ts *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+	if (ret < 0)
+		return ret;
+
+	mdelay(50);
+
+	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
+		sizeof(struct dwz_info));
+
+	memcpy(&ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info));
+	hideep_sw_reset(ts, 10);
+
+	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
+		/* Crimson IC */
+		ts->fw_size = 1024 * 48;
+		ts->nvm_mask = 0x00310000;
+	} else {
+		/* Lime fw size */
+		ts->fw_size = 1024 * 64;
+		ts->nvm_mask = 0x0030027B;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mdelay(50);
+
+	return ret;
+}
+
+static int hideep_i2c_read(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned short r_len, raddr;
+	int length;
+
+	length = len;
+	raddr = addr;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	do {
+		if (length > MAX_I2C_BUFFER_SIZE)
+			r_len = MAX_I2C_BUFFER_SIZE;
+		else
+			r_len = length;
+
+		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
+			raddr, r_len);
+
+		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
+
+		if (ret < 0)
+			goto i2c_err;
+
+		ret = i2c_master_recv(ts->client, (char *)buf, r_len);
+		length -= MAX_I2C_BUFFER_SIZE;
+		buf += MAX_I2C_BUFFER_SIZE;
+		raddr += MAX_I2C_BUFFER_SIZE;
+
+		if (ret < 0)
+			goto i2c_err;
+	} while (length > 0);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	return  0;
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static int hideep_i2c_write(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret = -1;
+	unsigned char data[len + 2];
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	// data mangling..
+	data[0] = (addr >> 0) & 0xFF;
+	data[1] = (addr >> 8) & 0xFF;
+	memcpy(&data[2], buf, len);
+
+	ret = i2c_master_send(ts->client, data, len + 2);
+
+	if (ret < 0)
+		goto i2c_err;
+
+	mutex_unlock(&ts->i2c_mutex);
+	return  0;
+
+i2c_err:
+	mutex_unlock(&ts->i2c_mutex);
+	return -1;
+}
+
+static void hideep_reset_ic(struct hideep_ts *ts)
+{
+	unsigned char cmd = 0x01;
+
+	if (!IS_ERR(ts->reset_gpio)) {
+		dev_dbg(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpiod_set_value(ts->reset_gpio, 0);
+		mdelay(20);
+		gpiod_set_value(ts->reset_gpio, 1);
+	} else {
+		hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+}
+
+static int hideep_pwr_on(struct hideep_ts *ts)
+{
+	int ret = 0;
+
+	if (!IS_ERR(ts->vcc_vdd)) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(ts->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+	}
+	usleep_range(999, 1000);
+
+	if (!IS_ERR(ts->vcc_vid)) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(ts->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+	}
+	usleep_range(2999, 3000);
+
+	return ret;
+}
+
+static void hideep_pwr_off(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	if (!IS_ERR(ts->reset_gpio))
+		gpiod_set_value(ts->reset_gpio, 0);
+
+	if (!IS_ERR(ts->vcc_vid))
+		regulator_disable(ts->vcc_vid);
+
+	if (!IS_ERR(ts->vcc_vdd))
+		regulator_disable(ts->vcc_vdd);
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void pops_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = (event->index >> 0) & 0x0F;
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type), false);
+		input_report_key(ts->input_dev, BTN_TOUCH, false);
+	}
+}
+
+static void push_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	bool btn_dn = 0;
+	bool btn_mv = 0;
+	int evt = 0;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = (event->index >> 0) & 0x0F;
+		btn_up = (event->flag >> HIDEEP_MT_RELEASED) & 0x01;
+		btn_dn = (event->flag >> HIDEEP_MT_FIRST_CONTACT)
+			& 0x01;
+		btn_mv = (event->flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
+
+		if (btn_up)
+			clear_bit(id, &ts->tch_bit);
+		else
+			__set_bit(id, &ts->tch_bit);
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			event->type, event->index, i,
+			get_unaligned_le16(&event->x),
+			get_unaligned_le16(&event->y),
+			get_unaligned_le16(&event->z));
+
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				get_unaligned_le16(&event->x));
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&event->y));
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				get_unaligned_le16(&event->z));
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				event->w);
+			evt++;
+		}
+	}
+
+	if (ts->tch_bit == 0)
+		evt = 0;
+
+	input_report_key(ts->input_dev, BTN_TOUCH, evt);
+	input_mt_sync_frame(ts->input_dev);
+}
+
+static void pops_ky(struct hideep_ts *ts)
+{
+	input_report_key(ts->input_dev, KEY_HOME, false);
+	input_report_key(ts->input_dev, KEY_MENU, false);
+	input_report_key(ts->input_dev, KEY_BACK, false);
+}
+
+static void push_ky(struct hideep_ts *ts)
+{
+	int i;
+	int pressed;
+	int key;
+	int status;
+	int code;
+
+	for (i = 0; i < ts->key_count; i++) {
+		key = ts->key_event[i + i * 2] & 0x0F;
+		status = ts->key_event[i + i * 2] & 0xF0;
+		code = 0;
+		pressed = false;
+
+		if (status & HIDEEP_KEY_PRESSED_MASK)
+			pressed = true;
+		else
+			pressed = false;
+
+		switch (key) {
+		case 0:
+			code = KEY_HOME;
+			break;
+		case 1:
+			code = KEY_MENU;
+			break;
+		case 2:
+			code = KEY_BACK;
+			break;
+		}
+		input_report_key(ts->input_dev, code, pressed);
+	}
+}
+
+static void hideep_put_event(struct hideep_ts *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+	if (ts->key_count > 0)
+		push_ky(ts);
+
+	input_sync(ts->input_dev);
+}
+
+static int hideep_get_event(struct hideep_ts *ts)
+{
+	int ret;
+	int touch_count;
+	int event_size;
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		event_size = ts->tch_count *
+			sizeof(struct hideep_event);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			event_size, ts->touch_event);
+
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+	if (ts->key_count > 0) {
+		event_size = ts->key_count * 2;
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			event_size, ts->key_event);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return -1;
+		}
+	}
+
+	return touch_count;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char buff[2];
+	int ret;
+
+	struct hideep_ts *ts = (struct hideep_ts *) handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x", ts->dev_state);
+
+	if (ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, buff);
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			ts->interrupt_state = 0;
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = buff[0];
+		ts->key_count = buff[1] & 0x0f;
+		ts->lpm_count = buff[1] & 0xf0;
+
+		ret = hideep_get_event(ts);
+
+		if (ret >= 0)
+			hideep_put_event(ts);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_ts *ts)
+{
+	int ret;
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+
+	if (ts->key_use) {
+		input_set_capability(ts->input_dev, EV_KEY, KEY_HOME);
+		input_set_capability(ts->input_dev, EV_KEY, KEY_MENU);
+		input_set_capability(ts->input_dev, EV_KEY, KEY_BACK);
+	}
+
+	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
+		65535, 0, 0);
+
+	ret = input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+
+	if (ret)
+		return ret;
+
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, 65535, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	return 0;
+}
+
+static void hideep_get_info(struct hideep_ts *ts)
+{
+	unsigned char val[4];
+
+	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
+		hideep_i2c_read(ts, 0x28, 4, val);
+
+		ts->prop.max_x = get_unaligned_le16(&val[2]);
+		ts->prop.max_y = get_unaligned_le16(&val[0]);
+
+		dev_info(&ts->client->dev, "X : %d, Y : %d",
+			ts->prop.max_x, ts->prop.max_y);
+	}
+}
+
+static ssize_t hideep_update_fw(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	if (mode == 1) {
+		disable_irq(ts->client->irq);
+
+		ts->dev_state = state_updating;
+		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+			get_unaligned_le16(&ts->dwz_info.product_id));
+		ret = hideep_update_firmware(ts, fw_name);
+
+		kfree(fw_name);
+
+		enable_irq(ts->client->irq);
+
+		ts->dev_state = state_normal;
+		if (ret != 0)
+			dev_err(dev, "The firmware update failed(%d)", ret);
+	}
+
+	return count;
+}
+
+static ssize_t hideep_fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t hideep_product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+static int hideep_sysfs_init(struct hideep_ts *ts)
+{
+	int ret;
+	struct i2c_client *client = ts->client;
+
+	/* Create the files associated with this kobject */
+	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
+
+	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
+
+	if (ret)
+		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
+			 __func__, ret);
+
+	return ret;
+}
+
+static void hideep_sysfs_exit(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	sysfs_remove_group(&ts->client->dev.kobj, &hideep_ts_attr_group);
+}
+
+static int __maybe_unused hideep_resume(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state == state_normal)
+		goto hideep_resume_exit;
+
+	dev_dbg(dev, "not waiting.");
+	ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+hideep_resume_exit:
+	mdelay(10);
+	hideep_reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+static int __maybe_unused hideep_suspend(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	mutex_lock(&ts->dev_mutex);
+	if (ts->dev_state == state_sleep)
+		goto hideep_suspend_exit;
+
+	dev_dbg(dev, "not waiting.");
+	ts->dev_state = state_sleep;
+
+	/* send sleep command.. */
+	pops_mt(ts);
+	if (ts->key_use)
+		pops_ky(ts);
+	input_sync(ts->input_dev);
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+hideep_suspend_exit:
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+
+#ifdef CONFIG_OF
+static int hideep_parse_dts(struct hideep_ts *ts)
+{
+	/* device tree information get */
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(pdata->reset_gpio))
+		return -EINVAL;
+
+	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(ts->vcc_vdd))
+		return -EINVAL;
+
+	ts->vcc_vid = devm_regulator_get(dev, "vid");
+	if (IS_ERR(ts->vcc_vid))
+		return -EINVAL;
+
+	ts->key_use = device_property_read_bool(&ts->client->dev,
+		"touchkey-use");
+
+	return 0;
+}
+#else
+static int hideep_parse_dts(struct hideep_ts *ts)
+{
+	return -EINVAL;
+}
+#endif
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_ts *ts;
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		return -ENODEV;
+	}
+
+	/* init hideep_ts */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(struct hideep_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+
+	if (client->dev.of_node) {
+		ret = hideep_parse_dts(ts);
+		if (ret)
+			return ret;
+	}
+
+	ts->client = client;
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	/* power on */
+	ret = hideep_pwr_on(ts);
+	if (ret) {
+		dev_err(&ts->client->dev, "power on failed");
+		return ret;
+	}
+
+	ret = devm_add_action(&ts->client->dev, hideep_pwr_off, ts);
+	if (ret) {
+		hideep_pwr_off(ts);
+		return ret;
+	}
+
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	hideep_reset_ic(ts);
+
+	/* read info */
+	ret = hideep_load_dwz(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		return ret;
+	}
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		return -ENOMEM;
+	}
+
+	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
+	hideep_get_info(ts);
+
+	ret = hideep_capability(ts);
+	if (ret) {
+		dev_err(&client->dev, "can't init input properties");
+		return ret;
+	}
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		return ret;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (IS_ERR(&ts->client->irq)) {
+		dev_err(&client->dev, "can't be assigned irq");
+		return -ENOMEM;
+	}
+
+	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
+		NULL, hideep_irq_task, IRQF_ONESHOT,
+		ts->client->name, ts);
+
+	disable_irq(ts->client->irq);
+	ts->interrupt_state = 0;
+
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+			ret);
+		return ret;
+	}
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+	ts->interrupt_state = 1;
+
+	ret = hideep_sysfs_init(ts);
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		return ret;
+	}
+
+	ret = devm_add_action(&ts->client->dev, hideep_sysfs_exit, ts);
+	if (ret) {
+		hideep_sysfs_exit(ts);
+		return ret;
+	}
+
+	dev_info(&client->dev, "probe is ok!");
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
+#endif
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-lime" },
+	{ .compatible = "hideep,hideep-crimson" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/hideep_core.h b/drivers/input/touchscreen/hideep_core.h
new file mode 100644
index 0000000..be6f3da
--- /dev/null
+++ b/drivers/input/touchscreen/hideep_core.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#ifndef _LINUX_HIDEEP_CORE_H
+#define _LINUX_HIDEEP_CORE_H
+/*************************************************************************
+ * Buffer size
+ *************************************************************************/
+#define FRAME_HEADER_SIZE				8
+/* if size of system i2c buffer is smaller than this value, need to modify */
+#define MAX_I2C_BUFFER_SIZE				512
+
+/*************************************************************************
+ * board porting config
+ *************************************************************************/
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+/*************************************************************************
+ * register addr
+ *************************************************************************/
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/*************************************************************************
+ * multi-touch & key definitions.
+ *************************************************************************/
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/*************************************************************************
+ * HiDeep Event
+ *************************************************************************/
+struct hideep_event {
+	__le16 x;
+	__le16 y;
+	__le16 z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+} __packed;
+
+
+/*************************************************************************
+ * IC State
+ *************************************************************************/
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+	state_debugging,
+};
+
+/*************************************************************************
+ * Firmware info
+ *************************************************************************/
+struct dwz_info {
+	__le32	code_start;
+	unsigned char code_crc[12];
+
+	__le32 c_code_start;
+	__le16 c_code_len;
+	__le16 gen_ver;
+
+	__le32 vr_start;
+	__le16 vr_len;
+	__le16 rsv0;
+
+	__le32 ft_start;
+	__le16 ft_len;
+	__le16 vr_version;
+
+	__le16 boot_ver;
+	__le16 core_ver;
+	__le16 custom_ver;
+	__le16 release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	__le16 product_code;
+	__le16 extra_option;
+
+	__le16 product_id;
+	__le16 vendor_id;
+} __packed;
+
+struct hideep_ts {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+
+	struct touchscreen_properties prop;
+
+	struct gpio_desc *reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	bool suspended;
+	enum e_dev_state dev_state;
+
+	long tch_bit;
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	unsigned char touch_event[HIDEEP_MT_MAX * 10];
+	unsigned char key_event[HIDEEP_KEY_MAX * 2];
+	bool key_use;
+
+	struct dwz_info dwz_info;
+
+	int interrupt_state;
+	int fw_size;
+	int nvm_mask;
+} __packed;
+
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_NVM_DOWNLOAD		0x10000000
+
+/*************************************************************************
+ * register map
+ *************************************************************************/
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+/*************************************************************************
+ * flash commands
+ *************************************************************************/
+#define MERASE					0x00010000
+#define SERASE					0x00020000
+#define PERASE					0x00040000
+#define PROG					0x00080000
+#define WRONLY					0x00100000
+
+#define NVM_MASK_OFS			0x0000000C
+
+/*************************************************************************
+ * DWZ info
+ *************************************************************************/
+#define HIDEEP_BOOT_SECTION		0x00000400
+#define HIDEEP_BOOT_LEN			0x00000400
+#define HIDEEP_DWZ_SECTION		0x00000280
+#define HIDEEP_DWZ_INFO_OFS		0x000002C0
+#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
+							- HIDEEP_DWZ_SECTION)
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		__be32 w[2];
+	} header;
+
+	__be32 payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+#endif
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-08-22  9:03           ` Anthony Kim
  (?)
@ 2017-08-23  0:07           ` Rob Herring
  -1 siblings, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-08-23  0:07 UTC (permalink / raw)
  To: Anthony Kim
  Cc: dmitry.torokhov, mark.rutland, rydberg, linux-kernel,
	linux-input, devicetree

On Tue, Aug 22, 2017 at 06:03:38PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |   39 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +

Acked-by: Rob Herring <robh@kernel.org>

>  drivers/input/touchscreen/Kconfig                  |   11 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  drivers/input/touchscreen/hideep_core.c            | 1294 ++++++++++++++++++++
>  drivers/input/touchscreen/hideep_core.h            |  221 ++++
>  6 files changed, 1567 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_core.h

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-08-22  9:03           ` Anthony Kim
  (?)
  (?)
@ 2017-08-31  6:51           ` Dmitry Torokhov
  -1 siblings, 0 replies; 29+ messages in thread
From: Dmitry Torokhov @ 2017-08-31  6:51 UTC (permalink / raw)
  To: Anthony Kim
  Cc: robh+dt, mark.rutland, rydberg, linux-kernel, linux-input, devicetree

Hi Anthony,

On Tue, Aug 22, 2017 at 06:03:38PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |   39 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>  drivers/input/touchscreen/Kconfig                  |   11 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  drivers/input/touchscreen/hideep_core.c            | 1294 ++++++++++++++++++++
>  drivers/input/touchscreen/hideep_core.h            |  221 ++++

You have only one .c file now. Maybe call it hideep.c and fold
hideep_core.h into it?

>  6 files changed, 1567 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep_core.c
>  create mode 100644 drivers/input/touchscreen/hideep_core.h
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..7a5b9a6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,39 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible		: must be "hideep,hideep-crimson"
> +					or "hideep,hideep-lime".
> +- reg			: I2C slave address, (e.g. 0x6C).
> +- interrupt-parent : Interrupt controller to which the chip is connected.
> +- interrupts : Interrupt to which the chip is connected.
> +
> +Optional properties:
> +- vdd-supply	: It is the controller supply for controlling
> +					 main voltage(3.3V) through the regulator.
> +- vid-supply	: It is the controller supply for controlling
> +					IO voltage(1.8V) through the regulator.
> +- reset-gpios	: Define for reset gpio pin.
> +						It is to use for reset IC.
> +- touchscreen-size-x	: X axis size of touchscreen
> +- touchscreen-size-y	: Y axis size of touchscreen
> +- touchkey-use	: Using touchkey in system.

Instead of a flah and a hard-coded keycodes, I'd prefer we used
optional "linux,keycodes" property for that.  See
drivers/input/keyboard/mpr121_touchkey.c in my 'next' branch for
example of parsing.

> +
> +Example:
> +
> +i2c@00000000 {
> +
> +	/* ... */
> +
> +	touchscreen@6c {
> +		compatible = "hideep,hideep-lime";
> +		reg = <0x6c>;
> +		interrupt-parent = <&gpx1>;
> +		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
> +		vdd-supply = <&ldo15_reg>";
> +		vid-supply = <&ldo18_reg>;
> +		reset-gpios = <&gpx1 5 0>;
> +		touchscreen-size-x = 1079;
> +		touchscreen-size-y = 1919;
> +		touchkey-use;
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index c03d201..aa2a301 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -131,6 +131,7 @@ gw	Gateworks Corporation
>  hannstar	HannStar Display Corporation
>  haoyu	Haoyu Microelectronic Co. Ltd.
>  hardkernel	Hardkernel Co., Ltd
> +hideep	HiDeep Inc.
>  himax	Himax Technologies, Inc.
>  hisilicon	Hisilicon Limited.
>  hit	Hitachi Ltd.
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 64b30fe..13e11c7 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -1246,4 +1246,15 @@ config TOUCHSCREEN_ROHM_BU21023
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called bu21023_ts.
>  
> +config TOUCHSCREEN_HIDEEP
> +	tristate "HiDeep Touch IC"
> +	depends on I2C
> +	help
> +	  Say Y here if you have a touchscreen using HiDeep.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a moudle, choose M here : the
> +	  module will be called hideep_ts.
> +
>  endif
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 6badce8..03ec3bb 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -103,3 +103,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223)	+= zet6223.o
>  obj-$(CONFIG_TOUCHSCREEN_ZFORCE)	+= zforce_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)	+= colibri-vf50-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)	+= rohm_bu21023.o
> +obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep_core.o

Please try inserting in [loosely] alphabetical order.

> diff --git a/drivers/input/touchscreen/hideep_core.c b/drivers/input/touchscreen/hideep_core.c
> new file mode 100644
> index 0000000..1d5887d
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_core.c
> @@ -0,0 +1,1294 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/firmware.h>
> +#include <linux/delay.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/acpi.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysfs.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/input/touchscreen.h>
> +#include <asm/unaligned.h>
> +
> +#include "hideep_core.h"
> +
> +static void hideep_reset_ic(struct hideep_ts *ts);
> +
> +static int hideep_pgm_w_mem(struct hideep_ts *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret = 0;

No need to initialize here. If anything it simply masks use of
uninitialized variable. This applies to entire driver.

> +	int i;
> +
> +	if ((len % 4) != 0)
> +		return -1;

-EINVAL

> +
> +	mutex_lock(&ts->i2c_mutex);

Could you explain why you need this mute? I would expect you'd have a
higher-level mutexes, for example one protecting entire device during
firmware update, but need to protect individual i2c operations is not
clear to me.

> +
> +	put_unaligned_be32((0x80 | (len / 4 - 1)), &packet->header.w[0]);
> +	put_unaligned_be32(addr, &packet->header.w[1]);
> +
> +	for (i = 0; i < len / sizeof(unsigned int); i++)

sizeof(u32)

> +		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
> +
> +	ret = i2c_master_send(ts->client, &packet->header.b[3],
> +		(len + 5));
> +
> +	if (ret < 0)
> +		goto err;
> +
> +err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_mem(struct hideep_ts *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret = 0;
> +	int i;
> +	unsigned char buff[len];
> +
> +	if ((len % 4) != 0)
> +		return -1;
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	put_unaligned_be32((0x00 | (len / 4 - 1)), &packet->header.w[0]);
> +	put_unaligned_be32(addr, &packet->header.w[1]);
> +
> +	ret = i2c_master_send(ts->client, &packet->header.b[3], 5);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = i2c_master_recv(ts->client, buff,	len);
> +
> +	if (ret < 0)
> +		goto err;

Does this have to be 2 separate transactions?

> +
> +	for (i = 0; i < len / 4; i++)

len / sizeof(u32)

> +		packet->payload[i] = get_unaligned_be32(&buff[i * 4]);

i * sizeof(u32)

> +
> +err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_reg(struct hideep_ts *ts, unsigned int addr,
> +	unsigned int *val)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet;
> +
> +	put_unaligned_be32(0x00, &packet.header.w[0]);
> +	put_unaligned_be32(addr, &packet.header.w[1]);
> +
> +	ret = hideep_pgm_r_mem(ts, addr, &packet, 4);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	*val = packet.payload[0];
> +
> +err:
> +	return ret;
> +}
> +
> +static int hideep_pgm_w_reg(struct hideep_ts *ts, unsigned int addr,
> +	unsigned int data)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet;
> +
> +	put_unaligned_be32(0x80, &packet.header.w[0]);
> +	put_unaligned_be32(addr, &packet.header.w[1]);
> +	packet.payload[0] = data;
> +
> +	ret = hideep_pgm_w_mem(ts, addr, &packet, 4);
> +
> +	return ret;
> +}
> +
> +#define SW_RESET_IN_PGM(CLK) \
> +{ \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
> +}
> +
> +#define SET_FLASH_PIO(CE) \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | ((CE) << 1))
> +#define SET_PIO_SIG(X, Y) \
> +	hideep_pgm_w_reg(ts, FLASH_BASE + PIO_SIG + (X), Y)
> +#define SET_FLASH_HWCONTROL() \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x00000000)
> +
> +#define NVM_W_SFR(x, y) \
> +{ \
> +	SET_FLASH_PIO(1); \
> +	SET_PIO_SIG(x, y); \
> +	SET_FLASH_PIO(0); \
> +}
> +
> +static void get_dwz_from_binary(unsigned char *pres,
> +	struct dwz_info *dwz_info)
> +{
> +	memcpy(dwz_info, pres + HIDEEP_DWZ_INFO_OFS,
> +		sizeof(struct dwz_info));
> +}
> +
> +static void hideep_sw_reset(struct hideep_ts *ts, unsigned int food)
> +{
> +	SW_RESET_IN_PGM(food);
> +}
> +
> +static int hideep_enter_pgm(struct hideep_ts *ts)
> +{
> +	int ret = 0;
> +	int retry_count = 10;
> +	int retry = 0;
> +	unsigned int status;
> +	unsigned int pattern = 0xDF9DAF39;
> +
> +	while (retry < retry_count) {
> +		i2c_master_send(ts->client, (unsigned char *)&pattern, 4);

Error handling?


> +		mdelay(1);
> +
> +		/* flush invalid Tx load register */
> +		hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);

And here?

> +
> +		hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);

And here...

> +
> +		if (pattern != get_unaligned_be32(&status)) {
> +			retry++;
> +			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
> +				get_unaligned_be32(&status));
> +		} else {
> +			dev_dbg(&ts->client->dev, "found magic code");
> +			break;
> +		}
> +	}
> +
> +	if (retry < retry_count) {
> +		hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
> +		hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
> +		hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
> +		hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
> +		hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
> +		hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
> +		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
> +		hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x02);
> +
> +		mdelay(1);
> +	} else {
> +		ret = -1;
> +	}
> +
> +	return ret;
> +}
> +static void hideep_nvm_unlock(struct hideep_ts *ts)
> +{
> +	unsigned int unmask_code = 0;
> +
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
> +
> +	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
> +
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +
> +	/* make it unprotected code */
> +	unmask_code &= (~_PROT_MODE);
> +
> +	/* compare unmask code */
> +	if (unmask_code != ts->nvm_mask)
> +		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
> +			unmask_code);
> +
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
> +	SET_FLASH_PIO(0);
> +
> +	NVM_W_SFR(NVM_MASK_OFS, ts->nvm_mask);
> +	SET_FLASH_HWCONTROL();
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +}
> +
> +static int hideep_program_page(struct hideep_ts *ts,
> +	unsigned int addr, struct pgm_packet *packet_w)
> +{
> +	int ret = 0;
> +	unsigned int pio_cmd = WRONLY;
> +	unsigned int pio_cmd_page_erase = PERASE;
> +	unsigned int status;
> +	int time_out = 0;
> +	unsigned int end_flag = 124;
> +
> +	hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +	ret = (status == 0) ? -1:0;
> +
> +	addr = addr & ~(NVM_PAGE_SIZE - 1);
> +
> +	SET_FLASH_PIO(0);
> +	SET_FLASH_PIO(1);
> +
> +	/* first erase */
> +	SET_PIO_SIG(pio_cmd_page_erase  + addr, 0xFFFFFFFF);
> +
> +	SET_FLASH_PIO(0);
> +	time_out = 0;
> +
> +	while (1) {
> +		mdelay(1);
> +		hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +		if ((status) != 0)
> +			break;
> +		if (time_out++ > 100)
> +			break;
> +	}
> +	SET_FLASH_PIO(1);
> +	/* first erase end*/
> +
> +	SET_PIO_SIG(pio_cmd + addr, get_unaligned_be32(&packet_w->payload[0]));
> +
> +	hideep_pgm_w_mem(ts, (FLASH_BASE + 0x400000) + pio_cmd,
> +		packet_w, NVM_PAGE_SIZE);
> +
> +	SET_PIO_SIG(end_flag, get_unaligned_be32(&packet_w->payload[31]));
> +
> +	SET_FLASH_PIO(0);
> +
> +	mdelay(1);
> +
> +	while (1) {
> +		hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +		if ((status) != 0)
> +			break;
> +	}
> +	/* write routine end */
> +
> +	SET_FLASH_HWCONTROL();
> +
> +	return ret;
> +}
> +
> +static int hideep_program_nvm(struct hideep_ts *ts, const unsigned char *ucode,
> +	int len, int offset, unsigned char *old_fw)
> +{
> +	int i;
> +	int ret = 0;
> +	int len_r;
> +	int len_w;
> +	int addr = 0;
> +	unsigned int pages;
> +
> +	struct pgm_packet packet_w;
> +
> +	ret = hideep_enter_pgm(ts);

What if the above returns error?

> +
> +	hideep_nvm_unlock(ts);
> +
> +	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;

DIV_ROUND_UP()

> +	addr = offset;
> +	len_r = len;
> +	len_w = len_r;
> +
> +	dev_dbg(&ts->client->dev, "pages : %d", pages);
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_w = NVM_PAGE_SIZE;
> +
> +		/* compare */
> +		if (old_fw != NULL)

		if (!old_fw)

> +			ret = memcmp(&ucode[addr], &old_fw[addr], len_w);
> +
> +		if (ret != 0 || old_fw == NULL) {
> +			/* write page */
> +			memcpy(packet_w.payload, &(ucode[addr]), len_w);

No need for parentheses around ucode[addr].

> +
> +			ret = hideep_program_page(ts, addr, &packet_w);
> +			mdelay(1);
> +			if (ret < 0)
> +				dev_err(&ts->client->dev,
> +					"hideep_program_nvm : error(%08x):",
> +					addr);
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_w = len_r;
> +	}
> +
> +	return ret;
> +}
> +
> +static int hideep_verify_nvm(struct hideep_ts *ts, const unsigned char *ucode,
> +	int len, int offset)
> +{
> +	int i, j;
> +	int ret = 0;
> +	unsigned char page_chk = 0;
> +	unsigned int addr = offset;
> +	unsigned int pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	int len_r = len;
> +	int len_v = len_r;
> +
> +	struct pgm_packet packet_r;
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_v = NVM_PAGE_SIZE;
> +
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +
> +		page_chk = memcmp(&(ucode[addr]), packet_r.payload, len_v);
> +
> +		if (page_chk != 0) {
> +			u8 *read = (u8 *)packet_r.payload;
> +
> +			for (j = 0; j < NVM_PAGE_SIZE; j++)
> +				dev_err(&ts->client->dev, "%02x : %02x",
> +						ucode[addr+j], read[j]);
> +
> +			dev_err(&ts->client->dev, "verify : error(addr : %d)",
> +				addr);
> +
> +			ret = -1;
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_v = len_r;
> +	}
> +
> +	return ret;
> +}
> +
> +static void hideep_read_nvm(struct hideep_ts *ts, unsigned char *data, int len,
> +	int offset)
> +{
> +	int ret = 0;
> +	int pages, i;
> +	int len_r, len_v;
> +	int addr = offset;
> +
> +	struct pgm_packet packet_r;
> +
> +	pages = (len + NVM_PAGE_SIZE - 1) / NVM_PAGE_SIZE;
> +	len_r = len;
> +	len_v = len_r;
> +
> +	hideep_reset_ic(ts);
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	hideep_nvm_unlock(ts);
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r >= NVM_PAGE_SIZE)
> +			len_v = NVM_PAGE_SIZE;
> +
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +
> +		memcpy(&(data[i * NVM_PAGE_SIZE]), &packet_r.payload[0], len_v);
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		len_v = len_r;
> +	}
> +
> +	hideep_sw_reset(ts, 1000);
> +}
> +
> +static int hideep_fw_verify_run(struct hideep_ts *ts, unsigned char *fw,
> +	size_t len, int offset)
> +{
> +	int ret = 0;
> +	int retry = 3;
> +
> +	while (retry--) {
> +		ret = hideep_verify_nvm(ts, fw, len, offset);
> +		if (ret == 0) {
> +			dev_dbg(&ts->client->dev, "update success");
> +			break;
> +		}
> +		dev_err(&ts->client->dev, "download fw failed(%d)", retry);
> +	}
> +
> +	ret = (retry == 0) ? -1:0;
> +
> +	return ret;
> +}
> +
> +static int hideep_wr_firmware(struct hideep_ts *ts, unsigned char *code,
> +	int len, int offset, bool mode)
> +{
> +	int ret = 0;
> +	int firm_len;
> +	unsigned char *ic_fw;
> +	unsigned char *dwz_info;
> +
> +	ic_fw = kmalloc(ts->fw_size, GFP_KERNEL);
> +	dwz_info = kmalloc(sizeof(struct dwz_info) + 64, GFP_KERNEL);

Error handing.

> +
> +	memset(dwz_info, 0x0, sizeof(struct dwz_info) + 64);
> +
> +	firm_len = len;
> +	dev_dbg(&ts->client->dev, "fw len : %d, define size : %d",
> +		firm_len, ts->fw_size);
> +
> +	if (firm_len > ts->fw_size)
> +		firm_len = ts->fw_size;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	/* memory dump of target IC */
> +	hideep_read_nvm(ts, ic_fw, firm_len, offset);
> +	/* comparing & programming each page, if the memory of specified
> +	 * page is exactly same, no need to update.
> +	 */
> +	ret = hideep_program_nvm(ts, code, firm_len, offset, ic_fw);
> +
> +	hideep_fw_verify_run(ts, code, firm_len, offset);
> +	if (ret < 0) {
> +		if (mode == true) {
> +			/* clear dwz version, it will be store once again
> +			 * after update success
> +			 */
> +			ts->dwz_info.release_ver = 0;
> +			memcpy(&dwz_info[64], &ts->dwz_info,
> +				sizeof(struct dwz_info));
> +			ret = hideep_program_nvm(ts, dwz_info, HIDEEP_DWZ_LEN,
> +				0x280, NULL);
> +		}
> +	}
> +
> +	get_dwz_from_binary(code, &ts->dwz_info);
> +
> +	hideep_sw_reset(ts, 1000);
> +
> +	kfree(ic_fw);
> +	kfree(dwz_info);
> +
> +	return ret;
> +}
> +
> +static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
> +{
> +	int ret = 0;
> +	const struct firmware *fw_entry;
> +	unsigned char *fw_buf;
> +	unsigned int fw_length;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
> +
> +	if (ret != 0) {
> +		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
> +		return ret;
> +	}
> +
> +	fw_buf = (unsigned char *)fw_entry->data;
> +	fw_length = (unsigned int)fw_entry->size;
> +
> +	/* chip specific code for flash fuse */
> +	mutex_lock(&ts->dev_mutex);
> +
> +	ts->dev_state = state_updating;
> +
> +	ret = hideep_wr_firmware(ts, fw_buf, fw_length, 0, true);
> +
> +	ts->dev_state = state_normal;
> +
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	release_firmware(fw_entry);
> +
> +	return ret;
> +}
> +
> +static int hideep_load_dwz(struct hideep_ts *ts)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet_r;
> +
> +	ret = hideep_enter_pgm(ts);
> +	if (ret < 0)
> +		return ret;
> +
> +	mdelay(50);
> +
> +	ret = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO_OFS, &packet_r,
> +		sizeof(struct dwz_info));
> +
> +	memcpy(&ts->dwz_info, packet_r.payload,
> +		sizeof(struct dwz_info));
> +	hideep_sw_reset(ts, 10);
> +
> +	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
> +		/* Crimson IC */
> +		ts->fw_size = 1024 * 48;
> +		ts->nvm_mask = 0x00310000;
> +	} else {
> +		/* Lime fw size */
> +		ts->fw_size = 1024 * 64;
> +		ts->nvm_mask = 0x0030027B;
> +	}
> +
> +	dev_dbg(&ts->client->dev, "firmware release version : %04x",
> +		get_unaligned_le16(&ts->dwz_info.release_ver));
> +
> +	mdelay(50);
> +
> +	return ret;
> +}
> +
> +static int hideep_i2c_read(struct hideep_ts *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret = -1;
> +	unsigned short r_len, raddr;
> +	int length;
> +
> +	length = len;
> +	raddr = addr;
> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	do {
> +		if (length > MAX_I2C_BUFFER_SIZE)
> +			r_len = MAX_I2C_BUFFER_SIZE;
> +		else
> +			r_len = length;
> +
> +		dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d",
> +			raddr, r_len);
> +
> +		ret = i2c_master_send(ts->client, (char *)&raddr, 2);
> +
> +		if (ret < 0)
> +			goto i2c_err;
> +
> +		ret = i2c_master_recv(ts->client, (char *)buf, r_len);

Can it be a single i2c_transfer() with 2 messages?

> +		length -= MAX_I2C_BUFFER_SIZE;
> +		buf += MAX_I2C_BUFFER_SIZE;
> +		raddr += MAX_I2C_BUFFER_SIZE;
> +
> +		if (ret < 0)
> +			goto i2c_err;
> +	} while (length > 0);
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +
> +	return  0;
> +i2c_err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return -1;
> +}
> +
> +static int hideep_i2c_write(struct hideep_ts *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret = -1;
> +	unsigned char data[len + 2];

How big is len? It is not a good style to have potentially unbound
variables on stack. Could we allocate the buffer or use scratch buffer?

> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	// data mangling..
> +	data[0] = (addr >> 0) & 0xFF;
> +	data[1] = (addr >> 8) & 0xFF;

	put_unaligned_le16().

> +	memcpy(&data[2], buf, len);
> +
> +	ret = i2c_master_send(ts->client, data, len + 2);
> +
> +	if (ret < 0)
> +		goto i2c_err;
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +	return  0;
> +
> +i2c_err:
> +	mutex_unlock(&ts->i2c_mutex);
> +	return -1;

Why do you clobber return value of i2c_master_send() with -1?

> +}
> +
> +static void hideep_reset_ic(struct hideep_ts *ts)
> +{
> +	unsigned char cmd = 0x01;
> +
> +	if (!IS_ERR(ts->reset_gpio)) {

You should check for NULL for optional GPIO, not error. Error would
result in probe() aborting.

> +		dev_dbg(&ts->client->dev, "hideep:enable the reset_gpio");
> +		gpiod_set_value(ts->reset_gpio, 0);
> +		mdelay(20);
> +		gpiod_set_value(ts->reset_gpio, 1);

This seems inverted. You want to activate GPIO, wait, then release it.
The actual polarity is handled by GPIOLIB, here we are dealing with
logical state of GPIO.

> +	} else {
> +		hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
> +	}
> +	mdelay(50);
> +}
> +
> +static int hideep_pwr_on(struct hideep_ts *ts)
> +{
> +	int ret = 0;
> +
> +	if (!IS_ERR(ts->vcc_vdd)) {

You do not need this check. If there was an error getting regulator
probe() would have aborted.

> +		dev_dbg(&ts->client->dev, "hideep:vcc_vdd is enable");
> +		ret = regulator_enable(ts->vcc_vdd);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vdd enable failed ret=%d", ret);
> +	}
> +	usleep_range(999, 1000);
> +
> +	if (!IS_ERR(ts->vcc_vid)) {

Same here.

> +		dev_dbg(&ts->client->dev, "hideep:vcc_vid is enable");
> +		ret = regulator_enable(ts->vcc_vid);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vcc_vid enable failed ret=%d", ret);
> +	}
> +	usleep_range(2999, 3000);
> +
> +	return ret;
> +}
> +
> +static void hideep_pwr_off(void *data)
> +{
> +	struct hideep_ts *ts = data;
> +
> +	if (!IS_ERR(ts->reset_gpio))
> +		gpiod_set_value(ts->reset_gpio, 0);
> +
> +	if (!IS_ERR(ts->vcc_vid))
> +		regulator_disable(ts->vcc_vid);
> +
> +	if (!IS_ERR(ts->vcc_vdd))
> +		regulator_disable(ts->vcc_vdd);
> +}
> +
> +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
> +
> +static void pops_mt(struct hideep_ts *ts)
> +{
> +	int id;
> +	int i;
> +	int offset = sizeof(struct hideep_event);
> +	struct hideep_event *event;
> +
> +	for (i = 0; i < ts->tch_count; i++) {
> +		event = (struct hideep_event *)&ts->touch_event[i * offset];
> +		id = (event->index >> 0) & 0x0F;
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(event->type), false);
> +		input_report_key(ts->input_dev, BTN_TOUCH, false);

I do not think you need to report release of BTN_TOUCH explicitly. In
fact, I believe it is wrong as one of contacts can still be active.

> +	}
> +}
> +
> +static void push_mt(struct hideep_ts *ts)
> +{
> +	int id;
> +	int i;
> +	bool btn_up = 0;
> +	bool btn_dn = 0;
> +	bool btn_mv = 0;
> +	int evt = 0;
> +	int offset = sizeof(struct hideep_event);
> +	struct hideep_event *event;
> +
> +	/* load multi-touch event to input system */
> +	for (i = 0; i < ts->tch_count; i++) {
> +		event = (struct hideep_event *)&ts->touch_event[i * offset];
> +		id = (event->index >> 0) & 0x0F;
> +		btn_up = (event->flag >> HIDEEP_MT_RELEASED) & 0x01;
> +		btn_dn = (event->flag >> HIDEEP_MT_FIRST_CONTACT)
> +			& 0x01;
> +		btn_mv = (event->flag >> HIDEEP_MT_DRAG_MOVE) & 0x01;
> +
> +		if (btn_up)
> +			clear_bit(id, &ts->tch_bit);
> +		else
> +			__set_bit(id, &ts->tch_bit);
> +
> +		dev_dbg(&ts->client->dev,
> +			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
> +			event->type, event->index, i,
> +			get_unaligned_le16(&event->x),
> +			get_unaligned_le16(&event->y),
> +			get_unaligned_le16(&event->z));
> +
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(event->type),
> +			(btn_up == 0));
> +
> +		if (btn_up == 0) {
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +				get_unaligned_le16(&event->x));
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +				get_unaligned_le16(&event->y));
> +			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +				get_unaligned_le16(&event->z));
> +			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +				event->w);
> +			evt++;
> +		}
> +	}
> +
> +	if (ts->tch_bit == 0)
> +		evt = 0;
> +
> +	input_report_key(ts->input_dev, BTN_TOUCH, evt);

You do not need to emit BTN_TOUCH yourself, input_mt_sync_frame() calls
input_mt_report_pointer_emulation(), which will do that for you.

> +	input_mt_sync_frame(ts->input_dev);
> +}
> +
> +static void pops_ky(struct hideep_ts *ts)
> +{
> +	input_report_key(ts->input_dev, KEY_HOME, false);
> +	input_report_key(ts->input_dev, KEY_MENU, false);
> +	input_report_key(ts->input_dev, KEY_BACK, false);
> +}
> +
> +static void push_ky(struct hideep_ts *ts)
> +{
> +	int i;
> +	int pressed;
> +	int key;
> +	int status;
> +	int code;
> +
> +	for (i = 0; i < ts->key_count; i++) {
> +		key = ts->key_event[i + i * 2] & 0x0F;
> +		status = ts->key_event[i + i * 2] & 0xF0;
> +		code = 0;
> +		pressed = false;
> +
> +		if (status & HIDEEP_KEY_PRESSED_MASK)
> +			pressed = true;
> +		else
> +			pressed = false;
> +
> +		switch (key) {
> +		case 0:
> +			code = KEY_HOME;
> +			break;
> +		case 1:
> +			code = KEY_MENU;
> +			break;
> +		case 2:
> +			code = KEY_BACK;
> +			break;
> +		}
> +		input_report_key(ts->input_dev, code, pressed);

Just use
		input_report_key(ts->input_dev, code,
				 status & HIDEEP_KEY_PRESSED_MASK);

as it will normalize the value (to 0/1) for you.

> +	}
> +}
> +
> +static void hideep_put_event(struct hideep_ts *ts)
> +{
> +	/* mangling touch information */
> +	if (ts->tch_count > 0)
> +		push_mt(ts);
> +
> +	if (ts->key_count > 0)
> +		push_ky(ts);
> +
> +	input_sync(ts->input_dev);
> +}
> +
> +static int hideep_get_event(struct hideep_ts *ts)
> +{
> +	int ret;
> +	int touch_count;
> +	int event_size;
> +
> +	/* get touch event count */
> +	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
> +		ts->tch_count, ts->key_count, ts->lpm_count);
> +
> +	/* get touch event information */
> +	if (ts->tch_count > HIDEEP_MT_MAX)
> +		ts->tch_count = 0;
> +
> +	if (ts->key_count > HIDEEP_KEY_MAX)
> +		ts->key_count = 0;
> +
> +	touch_count = ts->tch_count + ts->key_count;
> +
> +	if (ts->tch_count > 0) {
> +		event_size = ts->tch_count *
> +			sizeof(struct hideep_event);
> +		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
> +			event_size, ts->touch_event);
> +
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return -1;

Do not clobber return values, pass them on to the higher layers.

> +		}
> +	}
> +
> +	if (ts->key_count > 0) {
> +		event_size = ts->key_count * 2;
> +		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
> +			event_size, ts->key_event);
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return -1;
> +		}
> +	}
> +
> +	return touch_count;
> +}
> +
> +static irqreturn_t hideep_irq_task(int irq, void *handle)
> +{
> +	unsigned char buff[2];
> +	int ret;
> +
> +	struct hideep_ts *ts = (struct hideep_ts *) handle;

No need to cast void pointers.

> +
> +	dev_dbg(&ts->client->dev, "state = 0x%x", ts->dev_state);
> +
> +	if (ts->dev_state == state_normal) {
> +		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
> +			2, buff);
> +		if (ret < 0) {
> +			disable_irq(ts->client->irq);
> +			ts->interrupt_state = 0;
> +			return IRQ_HANDLED;
> +		}
> +
> +		ts->tch_count = buff[0];
> +		ts->key_count = buff[1] & 0x0f;
> +		ts->lpm_count = buff[1] & 0xf0;
> +
> +		ret = hideep_get_event(ts);
> +
> +		if (ret >= 0)
> +			hideep_put_event(ts);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int hideep_capability(struct hideep_ts *ts)
> +{
> +	int ret;
> +
> +	ts->input_dev->name = HIDEEP_TS_NAME;
> +	ts->input_dev->id.bustype = BUS_I2C;
> +
> +	input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);

Does not need to be set explicitly, since you are using
input_mt_init_slots() with INPUT_MT_DIRECT.

> +
> +	if (ts->key_use) {
> +		input_set_capability(ts->input_dev, EV_KEY, KEY_HOME);
> +		input_set_capability(ts->input_dev, EV_KEY, KEY_MENU);
> +		input_set_capability(ts->input_dev, EV_KEY, KEY_BACK);
> +	}
> +
> +	input_set_abs_params(ts->input_dev, ABS_X, 0, ts->prop.max_x, 0, 0);
> +	input_set_abs_params(ts->input_dev, ABS_Y, 0, ts->prop.max_y, 0, 0);
> +	input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0,
> +		65535, 0, 0);
> +
> +	ret = input_mt_init_slots(ts->input_dev,
> +		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
> +
> +	if (ret)
> +		return ret;
> +
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_PRESSURE, 0, 65535, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);


Please set the MT parameters first and then call input_mt_init_slots().
Do not set ST parameters explicitly as input_mt_init_slots() will do
that for you.

> +
> +	return 0;
> +}
> +
> +static void hideep_get_info(struct hideep_ts *ts)
> +{
> +	unsigned char val[4];
> +
> +	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
> +		hideep_i2c_read(ts, 0x28, 4, val);
> +
> +		ts->prop.max_x = get_unaligned_le16(&val[2]);
> +		ts->prop.max_y = get_unaligned_le16(&val[0]);
> +
> +		dev_info(&ts->client->dev, "X : %d, Y : %d",
> +			ts->prop.max_x, ts->prop.max_y);
> +	}
> +}
> +
> +static ssize_t hideep_update_fw(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +	int mode, ret;
> +	char *fw_name;
> +
> +	ret = kstrtoint(buf, 8, &mode);
> +	if (ret)
> +		return ret;
> +
> +	if (mode == 1) {

Why do we check the value? Any write should do.

> +		disable_irq(ts->client->irq);
> +
> +		ts->dev_state = state_updating;
> +		fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
> +			get_unaligned_le16(&ts->dwz_info.product_id));
> +		ret = hideep_update_firmware(ts, fw_name);
> +
> +		kfree(fw_name);
> +
> +		enable_irq(ts->client->irq);
> +
> +		ts->dev_state = state_normal;
> +		if (ret != 0)
> +			dev_err(dev, "The firmware update failed(%d)", ret);
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t hideep_fw_version_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "release version : %04x",
> +		get_unaligned_le16(&ts->dwz_info.release_ver));
> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static ssize_t hideep_product_id_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "product id : %04x",
> +		get_unaligned_le16(&ts->dwz_info.product_id));
> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
> +static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
> +static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
> +
> +static struct attribute *hideep_ts_sysfs_entries[] = {
> +	&dev_attr_version.attr,
> +	&dev_attr_product_id.attr,
> +	&dev_attr_update_fw.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group hideep_ts_attr_group = {
> +	.attrs = hideep_ts_sysfs_entries,
> +};
> +
> +static int hideep_sysfs_init(struct hideep_ts *ts)
> +{
> +	int ret;
> +	struct i2c_client *client = ts->client;
> +
> +	/* Create the files associated with this kobject */
> +	ret = sysfs_create_group(&client->dev.kobj, &hideep_ts_attr_group);
> +
> +	dev_info(&ts->client->dev, "device : %s ", client->dev.kobj.name);
> +
> +	if (ret)
> +		dev_err(&ts->client->dev, "%s: Fail create link error = %d\n",
> +			 __func__, ret);
> +
> +	return ret;
> +}
> +
> +static void hideep_sysfs_exit(void *data)
> +{
> +	struct hideep_ts *ts = data;
> +
> +	sysfs_remove_group(&ts->client->dev.kobj, &hideep_ts_attr_group);
> +}
> +
> +static int __maybe_unused hideep_resume(struct device *dev)
> +{
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x00;
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	if (ts->dev_state == state_normal)
> +		goto hideep_resume_exit;
> +
> +	dev_dbg(dev, "not waiting.");
> +	ts->dev_state = state_normal;
> +
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	enable_irq(ts->client->irq);
> +	ts->interrupt_state = 1;
> +
> +hideep_resume_exit:
> +	mdelay(10);
> +	hideep_reset_ic(ts);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +	return 0;
> +}
> +
> +static int __maybe_unused hideep_suspend(struct device *dev)
> +{
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x01;
> +
> +	mutex_lock(&ts->dev_mutex);
> +	if (ts->dev_state == state_sleep)

How can this be?

> +		goto hideep_suspend_exit;
> +
> +	dev_dbg(dev, "not waiting.");
> +	ts->dev_state = state_sleep;
> +
> +	/* send sleep command.. */
> +	pops_mt(ts);
> +	if (ts->key_use)
> +		pops_ky(ts);

I am pretty sure input core releases keys/contacts for you.

> +	input_sync(ts->input_dev);
> +
> +	/* default deep sleep */
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	disable_irq(ts->client->irq);
> +	ts->interrupt_state = 0;
> +
> +hideep_suspend_exit:
> +	mutex_unlock(&ts->dev_mutex);
> +	return 0;
> +}
> +
> +
> +#ifdef CONFIG_OF

Do not guard it for OF systems only - it is all applicable to ACPI
systems and can even be used on legacy boards.

> +static int hideep_parse_dts(struct hideep_ts *ts)
> +{
> +	/* device tree information get */
> +	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> +							GPIOD_OUT_HIGH);
> +	if (IS_ERR(pdata->reset_gpio))
> +		return -EINVAL;

No, do not clobber return values. Your driver will fail to handle device
if you get a deferral here. Use


		return PTR_ERR(pdata->reset_gpio);


> +
> +	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
> +	if (IS_ERR(ts->vcc_vdd))
> +		return -EINVAL;

Same here.

> +
> +	ts->vcc_vid = devm_regulator_get(dev, "vid");
> +	if (IS_ERR(ts->vcc_vid))
> +		return -EINVAL;

And here.

> +
> +	ts->key_use = device_property_read_bool(&ts->client->dev,
> +		"touchkey-use");
> +
> +	return 0;
> +}
> +#else
> +static int hideep_parse_dts(struct hideep_ts *ts)
> +{
> +	return -EINVAL;
> +}
> +#endif
> +
> +static int hideep_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
> +{
> +	int ret = 0;
> +	struct hideep_ts *ts;
> +
> +	/* check i2c bus */
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_I2C)) {
> +		dev_err(&client->dev, "check i2c device error");
> +		return -ENODEV;
> +	}
> +
> +	/* init hideep_ts */
> +	ts = devm_kzalloc(&client->dev,
> +		sizeof(struct hideep_ts), GFP_KERNEL);

sizeof(*ts) is preferred.

> +	if (!ts)
> +		return -ENOMEM;
> +
> +
> +	if (client->dev.of_node) {

Call it unconditionally.

> +		ret = hideep_parse_dts(ts);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ts->client = client;
> +
> +	i2c_set_clientdata(client, ts);
> +
> +	mutex_init(&ts->i2c_mutex);
> +	mutex_init(&ts->dev_mutex);
> +
> +	/* power on */
> +	ret = hideep_pwr_on(ts);
> +	if (ret) {
> +		dev_err(&ts->client->dev, "power on failed");
> +		return ret;
> +	}
> +
> +	ret = devm_add_action(&ts->client->dev, hideep_pwr_off, ts);
> +	if (ret) {
> +		hideep_pwr_off(ts);
> +		return ret;
> +	}
> +
> +	ts->dev_state = state_init;
> +	mdelay(30);
> +
> +	/* ic reset */
> +	hideep_reset_ic(ts);
> +
> +	/* read info */
> +	ret = hideep_load_dwz(ts);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
> +		return ret;
> +	}
> +
> +	/* init input device */
> +	ts->input_dev = devm_input_allocate_device(&client->dev);
> +	if (!ts->input_dev) {
> +		dev_err(&client->dev, "can't allocate memory for input_dev");
> +		return -ENOMEM;
> +	}
> +
> +	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
> +	hideep_get_info(ts);
> +
> +	ret = hideep_capability(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "can't init input properties");
> +		return ret;
> +	}
> +
> +	ret = input_register_device(ts->input_dev);
> +	if (ret) {
> +		dev_err(&client->dev, "can't register input_dev");
> +		return ret;
> +	}
> +
> +	input_set_drvdata(ts->input_dev, ts);
> +
> +	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
> +	if (IS_ERR(&ts->client->irq)) {
> +		dev_err(&client->dev, "can't be assigned irq");
> +		return -ENOMEM;
> +	}
> +
> +	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
> +		NULL, hideep_irq_task, IRQF_ONESHOT,
> +		ts->client->name, ts);
> +
> +	disable_irq(ts->client->irq);
> +	ts->interrupt_state = 0;
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
> +			ret);
> +		return ret;
> +	}
> +
> +	ts->dev_state = state_normal;
> +	enable_irq(ts->client->irq);
> +	ts->interrupt_state = 1;
> +
> +	ret = hideep_sysfs_init(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
> +		return ret;
> +	}
> +
> +	ret = devm_add_action(&ts->client->dev, hideep_sysfs_exit, ts);

We have devm_device_add_group() now.

> +	if (ret) {
> +		hideep_sysfs_exit(ts);
> +		return ret;
> +	}
> +
> +	dev_info(&client->dev, "probe is ok!");

Not needed.

> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM

Drop this guard.

> +static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
> +#endif
> +
> +static const struct i2c_device_id hideep_dev_idtable[] = {
> +	{ HIDEEP_I2C_NAME, 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id hideep_acpi_id[] = {
> +	{ "HIDP0001", 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id hideep_match_table[] = {
> +	{ .compatible = "hideep,hideep-lime" },
> +	{ .compatible = "hideep,hideep-crimson" },

Why do we need 2 compatibles when we can detect the kind of device we
are dealing with by querying it?

> +	{},

This is  entinel, no need for the trailing comma as nothing can be added
past it.

> +};
> +MODULE_DEVICE_TABLE(of, hideep_match_table);
> +#endif
> +
> +static struct i2c_driver hideep_driver = {
> +	.probe = hideep_probe,
> +	.id_table = hideep_dev_idtable,
> +	.driver = {
> +		.name = HIDEEP_I2C_NAME,
> +		.of_match_table = of_match_ptr(hideep_match_table),
> +		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
> +		.pm = &hideep_pm_ops,
> +	},
> +};
> +
> +module_i2c_driver(hideep_driver);
> +
> +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
> +MODULE_AUTHOR("anthony.kim@hideep.com");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/input/touchscreen/hideep_core.h b/drivers/input/touchscreen/hideep_core.h
> new file mode 100644
> index 0000000..be6f3da
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep_core.h
> @@ -0,0 +1,221 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#ifndef _LINUX_HIDEEP_CORE_H
> +#define _LINUX_HIDEEP_CORE_H
> +/*************************************************************************
> + * Buffer size
> + *************************************************************************/
> +#define FRAME_HEADER_SIZE				8
> +/* if size of system i2c buffer is smaller than this value, need to modify */
> +#define MAX_I2C_BUFFER_SIZE				512
> +
> +/*************************************************************************
> + * board porting config
> + *************************************************************************/
> +#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
> +#define HIDEEP_I2C_NAME					"hideep_ts"
> +
> +/*************************************************************************
> + * register addr
> + *************************************************************************/
> +/* Touch & key event */
> +#define HIDEEP_EVENT_COUNT_ADDR			0x240
> +#define HIDEEP_TOUCH_DATA_ADDR			0x242
> +#define HIDEEP_KEY_DATA_ADDR			0x2A6
> +#define HIDEEP_RAW_DATA_ADDR			0x1000
> +
> +/* command list */
> +#define HIDEEP_RESET_CMD				0x9800
> +#define HIDEEP_INTCLR_CMD				0x9802
> +#define HIDEEP_OPMODE_CMD				0x9804
> +#define HIDEEP_SWTICH_CMD				0x9805
> +#define HIDEEP_SLEEP_CMD				0x980D
> +
> +/*************************************************************************
> + * multi-touch & key definitions.
> + *************************************************************************/
> +#define HIDEEP_MT_MAX					10
> +#define HIDEEP_KEY_MAX					3
> +
> +/* multi touch event bit */
> +#define HIDEEP_MT_ALWAYS_REPORT			0
> +#define HIDEEP_MT_TOUCHED				1
> +#define HIDEEP_MT_FIRST_CONTACT			2
> +#define HIDEEP_MT_DRAG_MOVE				3
> +#define HIDEEP_MT_RELEASED				4
> +#define HIDEEP_MT_PINCH					5
> +#define HIDEEP_MT_PRESSURE				6
> +
> +/* key event bit */
> +#define HIDEEP_KEY_RELEASED				0x20
> +#define HIDEEP_KEY_PRESSED				0x40
> +#define HIDEEP_KEY_FIRST_PRESSED		0x80
> +#define HIDEEP_KEY_PRESSED_MASK			0xC0
> +
> +/*************************************************************************
> + * HiDeep Event
> + *************************************************************************/
> +struct hideep_event {
> +	__le16 x;
> +	__le16 y;
> +	__le16 z;
> +	unsigned char w;
> +	unsigned char flag;
> +	unsigned char type;
> +	unsigned char index;
> +} __packed;
> +
> +
> +/*************************************************************************
> + * IC State
> + *************************************************************************/
> +enum e_dev_state {
> +	state_init = 1,
> +	state_normal,
> +	state_sleep,
> +	state_updating,
> +	state_debugging,
> +};
> +
> +/*************************************************************************
> + * Firmware info
> + *************************************************************************/
> +struct dwz_info {
> +	__le32	code_start;
> +	unsigned char code_crc[12];
> +
> +	__le32 c_code_start;
> +	__le16 c_code_len;
> +	__le16 gen_ver;
> +
> +	__le32 vr_start;
> +	__le16 vr_len;
> +	__le16 rsv0;
> +
> +	__le32 ft_start;
> +	__le16 ft_len;
> +	__le16 vr_version;
> +
> +	__le16 boot_ver;
> +	__le16 core_ver;
> +	__le16 custom_ver;
> +	__le16 release_ver;
> +
> +	unsigned char factory_id;
> +	unsigned char panel_type;
> +	unsigned char model_name[6];
> +	__le16 product_code;
> +	__le16 extra_option;
> +
> +	__le16 product_id;
> +	__le16 vendor_id;
> +} __packed;
> +
> +struct hideep_ts {
> +	struct i2c_client *client;
> +	struct input_dev *input_dev;
> +
> +	struct touchscreen_properties prop;
> +
> +	struct gpio_desc *reset_gpio;
> +
> +	struct regulator *vcc_vdd;
> +	struct regulator *vcc_vid;
> +
> +	struct mutex dev_mutex;
> +	struct mutex i2c_mutex;
> +
> +	bool suspended;
> +	enum e_dev_state dev_state;
> +
> +	long tch_bit;
> +	unsigned int tch_count;
> +	unsigned int key_count;
> +	unsigned int lpm_count;
> +
> +	unsigned char touch_event[HIDEEP_MT_MAX * 10];
> +	unsigned char key_event[HIDEEP_KEY_MAX * 2];
> +	bool key_use;
> +
> +	struct dwz_info dwz_info;
> +
> +	int interrupt_state;
> +	int fw_size;
> +	int nvm_mask;
> +} __packed;
> +
> +#define NVM_DEFAULT_PAGE		0
> +#define NVM_SFR_WPAGE			1
> +#define NVM_SFR_RPAGE			2
> +
> +#define PIO_SIG					0x00400000
> +#define _PROT_MODE				0x03400000
> +
> +#define NVM_PAGE_SIZE			128
> +
> +#define HIDEEP_NVM_DOWNLOAD		0x10000000
> +
> +/*************************************************************************
> + * register map
> + *************************************************************************/
> +#define YRAM_BASE				0x40000000
> +#define PERIPHERAL_BASE			0x50000000
> +#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
> +#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
> +#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
> +
> +#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
> +#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
> +#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
> +#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
> +#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
> +#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
> +#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
> +#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
> +#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
> +
> +#define FLASH_CON				(FLASH_BASE + 0x0000)
> +#define FLASH_STA				(FLASH_BASE + 0x0004)
> +#define FLASH_CFG				(FLASH_BASE + 0x0008)
> +#define FLASH_TIM				(FLASH_BASE + 0x000C)
> +#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
> +
> +#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
> +
> +/*************************************************************************
> + * flash commands
> + *************************************************************************/
> +#define MERASE					0x00010000
> +#define SERASE					0x00020000
> +#define PERASE					0x00040000
> +#define PROG					0x00080000
> +#define WRONLY					0x00100000
> +
> +#define NVM_MASK_OFS			0x0000000C
> +
> +/*************************************************************************
> + * DWZ info
> + *************************************************************************/
> +#define HIDEEP_BOOT_SECTION		0x00000400
> +#define HIDEEP_BOOT_LEN			0x00000400
> +#define HIDEEP_DWZ_SECTION		0x00000280
> +#define HIDEEP_DWZ_INFO_OFS		0x000002C0
> +#define HIDEEP_DWZ_LEN			(HIDEEP_BOOT_SECTION \
> +							- HIDEEP_DWZ_SECTION)
> +
> +struct pgm_packet {
> +	union {
> +		unsigned char b[8];
> +		__be32 w[2];
> +	} header;
> +
> +	__be32 payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
> +};
> +
> +#endif
> -- 
> 2.7.4
> 

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
  2017-08-22  9:03           ` Anthony Kim
                             ` (2 preceding siblings ...)
  (?)
@ 2017-09-11  7:27           ` Anthony Kim
  2017-09-12 14:17             ` Rob Herring
                               ` (3 more replies)
  -1 siblings, 4 replies; 29+ messages in thread
From: Anthony Kim @ 2017-09-11  7:27 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt
  Cc: linux-input, devicetree, linux-kernel, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |   42 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/input/touchscreen/Kconfig                  |   11 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/hideep.c                 | 1274 ++++++++++++++++++++
 5 files changed, 1329 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..86b3a97
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,42 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-ts"
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- touchscreen-size-x	: X axis size of touchscreen
+- touchscreen-size-y	: Y axis size of touchscreen
+- linux,keycodes	: Specifies an array of numeric keycode values to
+			be used for reporting button presses. The array can
+			contain up to 3 entries.
+
+Example:
+
+#include "dt-bindings/input/input.h"
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-ts";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		touchscreen-size-x = 1079;
+		touchscreen-size-y = 1919;
+		linux,keycodes = <KEY_HOME>, <KEY_MENU>, <KEY_BACK>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..d0c8dafc 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -344,6 +344,17 @@ config TOUCHSCREEN_GOODIX
 	  To compile this driver as a module, choose M here: the
 	  module will be called goodix.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 config TOUCHSCREEN_ILI210X
 	tristate "Ilitek ILI210X based touchscreen"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..873b67e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)	+= egalax_ts_serial.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP)	+= hideep.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c
new file mode 100644
index 0000000..388e41e
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.c
@@ -0,0 +1,1274 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <asm/unaligned.h>
+
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+#define FRAME_HEADER_SIZE				8
+
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/* For NVM */
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+#define FLASH_PIO_SIG			(FLASH_BASE + 0x400000)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+#define PERASE					0x00040000
+#define WRONLY					0x00100000
+
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_DWZ_INFO			0x000002C0
+
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+};
+
+struct hideep_event {
+	__le16 x;
+	__le16 y;
+	__le16 z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+} __packed;
+
+struct dwz_info {
+	__le32	code_start;
+	unsigned char code_crc[12];
+
+	__le32 c_code_start;
+	__le16 c_code_len;
+	__le16 gen_ver;
+
+	__le32 vr_start;
+	__le16 vr_len;
+	__le16 rsv0;
+
+	__le32 ft_start;
+	__le16 ft_len;
+	__le16 vr_version;
+
+	__le16 boot_ver;
+	__le16 core_ver;
+	__le16 custom_ver;
+	__le16 release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	__le16 product_code;
+	__le16 extra_option;
+
+	__le16 product_id;
+	__le16 vendor_id;
+} __packed;
+
+struct hideep_ts {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+
+	struct touchscreen_properties prop;
+
+	struct gpio_desc *reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	enum e_dev_state dev_state;
+
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	unsigned char touch_event[HIDEEP_MT_MAX * 10];
+	unsigned char key_event[HIDEEP_KEY_MAX * 2];
+
+	int key_num;
+	int key_codes[HIDEEP_KEY_MAX];
+
+	struct dwz_info dwz_info;
+
+	int fw_size;
+	int nvm_mask;
+} __packed;
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		__be32 w[2];
+	} header;
+
+	__be32 payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+static void hideep_reset_ic(struct hideep_ts *ts);
+
+static int hideep_pgm_w_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret;
+	int i;
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	put_unaligned_be32((0x80 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, &packet->header.b[3],
+		(len + 5));
+
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret;
+	int i;
+	unsigned char buff[len];	// need to modify
+	struct i2c_msg msg[2];
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	put_unaligned_be32((0x00 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = &packet->header.b[3];
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buff;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		packet->payload[i] = get_unaligned_be32(&buff[i * sizeof(u32)]);
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x00, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, sizeof(u32));
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x80, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, sizeof(u32));
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | (CE << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_PIO_SIG + X, Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void hideep_pgm_set(struct hideep_ts *ts)
+{
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+	hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+	hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+	hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+	hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+	hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+	hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+}
+
+static int hideep_pgm_get_pattern(struct hideep_ts *ts)
+{
+	int ret;
+	unsigned int status;
+	const unsigned char pattern[] = { 0x39, 0xAF, 0x9D, 0xDF };
+
+	ret = i2c_master_send(ts->client, pattern, sizeof(pattern));
+
+	if (ret < 0) {
+		dev_err(&ts->client->dev, "%d, %08X", __LINE__, ret);
+		return ret;
+	}
+
+	mdelay(1);
+
+	/* flush invalid Tx load register */
+	ret = hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+	if (ret < 0)
+		return ret;
+
+	ret = hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+	if (ret < 0)
+		return ret;
+
+	dev_info(&ts->client->dev, "%s, %08X", __func__, status);
+	return status;
+}
+
+static int hideep_enter_pgm(struct hideep_ts *ts)
+{
+	int retry_count = 10;
+	int val;
+	unsigned int pgm_pattern = 0xDF9DAF39;
+
+	while (retry_count--) {
+		val = hideep_pgm_get_pattern(ts);
+
+		if (pgm_pattern != get_unaligned_be32(&val)) {
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				get_unaligned_be32(&val));
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+
+	if (retry_count < 0) {
+		dev_err(&ts->client->dev, "couldn't enter pgm mode!!!");
+		SW_RESET_IN_PGM(1000);
+		return -EBADMSG;
+	}
+
+	hideep_pgm_set(ts);
+	mdelay(1);
+
+	return 0;
+}
+
+static void hideep_nvm_unlock(struct hideep_ts *ts)
+{
+	unsigned int unmask_code = 0;
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != ts->nvm_mask)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, ts->nvm_mask);
+	SET_FLASH_HWCONTROL();
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+}
+
+static int hideep_check_status(struct hideep_ts *ts)
+{
+	int ret, status;
+	int time_out = 100;
+
+	while (time_out--) {
+		mdelay(1);
+		ret = hideep_pgm_r_reg(ts, FLASH_STA, &status);
+
+		if (ret < 0)
+			continue;
+
+		if (status)
+			return status;
+	}
+
+	return time_out;
+}
+
+static int hideep_program_page(struct hideep_ts *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret;
+
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* erase page */
+	SET_PIO_SIG((PERASE | addr), 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	/* write page */
+	SET_FLASH_PIO(1);
+
+	SET_PIO_SIG((WRONLY | addr), get_unaligned_be32(&packet_w->payload[0]));
+
+	hideep_pgm_w_mem(ts, (FLASH_PIO_SIG | WRONLY),
+		packet_w, NVM_PAGE_SIZE);
+
+	SET_PIO_SIG(124, get_unaligned_be32(&packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	SET_FLASH_HWCONTROL();
+
+	return 0;
+}
+
+static void hideep_program_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len)
+{
+	struct pgm_packet packet_w;
+	struct pgm_packet packet_r;
+	int i;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_w = NVM_PAGE_SIZE;
+	unsigned int pages = DIV_ROUND_UP(len, NVM_PAGE_SIZE);
+
+
+	hideep_nvm_unlock(ts);
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < NVM_PAGE_SIZE)
+			len_w = len_r;
+
+		/* compare */
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+		ret = memcmp(&ucode[addr], packet_r.payload, len_w);
+
+		if (ret) {
+			/* write page */
+			memcpy(packet_w.payload, &ucode[addr], len_w);
+			ret = hideep_program_page(ts, addr, &packet_w);
+			if (ret)
+				dev_err(&ts->client->dev,
+					"%s : error(%08x):",
+					__func__, addr);
+			mdelay(1);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+}
+
+static int hideep_verify_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len)
+{
+	struct pgm_packet packet_r;
+	int i, j;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_v = NVM_PAGE_SIZE;
+	unsigned int pages = DIV_ROUND_UP(len, NVM_PAGE_SIZE);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < NVM_PAGE_SIZE)
+			len_v = len_r;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+
+		ret = memcmp(&ucode[addr], packet_r.payload, len_v);
+
+		if (ret) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++) {
+				if (ucode[addr + j] != read[j])
+					dev_err(&ts->client->dev,
+						"verify : error([%d] %02x : %02x)",
+						addr + j, ucode[addr + j],
+						read[j]);
+			}
+			return ret;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+
+	return 0;
+}
+
+static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
+{
+	int ret;
+	int retry, retry_cnt = 3;
+	const struct firmware *fw_entry;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	if (fw_entry->size > ts->fw_size) {
+		dev_err(&ts->client->dev,
+			"file size(%ld) is big more than fw memory size(%d)",
+			fw_entry->size, ts->fw_size);
+		release_firmware(fw_entry);
+		return -EFBIG;
+	}
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	/* enter program mode */
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	for (retry = 0; retry < retry_cnt; retry++) {
+		hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
+
+		ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
+		if (!ret)
+			break;
+	}
+
+	if (retry < retry_cnt)
+		dev_dbg(&ts->client->dev, "update success!!!");
+	else
+		dev_err(&ts->client->dev, "update failed!!!");
+
+	SW_RESET_IN_PGM(1000);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_ts *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	mdelay(50);
+
+	hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, &packet_r,
+		sizeof(struct dwz_info));
+
+	memcpy(&ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info));
+
+	SW_RESET_IN_PGM(10);
+
+	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x60) {
+		/* Lime fw size */
+		dev_dbg(&ts->client->dev, "used lime IC");
+		ts->fw_size = 1024 * 64;
+		ts->nvm_mask = 0x0030027B;
+	} else if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
+		/* Crimson IC */
+		dev_dbg(&ts->client->dev, "used crimson IC");
+		ts->fw_size = 1024 * 48;
+		ts->nvm_mask = 0x00310000;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mdelay(50);
+
+	return 0;
+}
+
+static int hideep_i2c_read(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret;
+	struct i2c_msg msg[2];
+
+	mutex_lock(&ts->i2c_mutex);
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 2;
+	msg[0].buf = (u8 *)&addr;
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buf;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_i2c_write(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret;
+	unsigned char *wbuf;
+	struct i2c_msg msg;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	wbuf = kmalloc(len + 2, GFP_KERNEL);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	put_unaligned_le16(addr, &wbuf[0]);
+	memcpy(&wbuf[2], buf, len);
+
+	msg.addr = ts->client->addr;
+	msg.flags = 0;
+	msg.len = len + 2;
+	msg.buf = wbuf;
+
+	ret = i2c_transfer(ts->client->adapter, &msg, 1);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	kfree(wbuf);
+
+	return  ret;
+}
+
+static void hideep_reset_ic(struct hideep_ts *ts)
+{
+	unsigned char cmd = 0x01;
+
+	if (!ts->reset_gpio) {
+		dev_dbg(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_LOW);
+		mdelay(20);
+		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_HIGH);
+	} else {
+		hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+}
+
+static int hideep_pwr_on(struct hideep_ts *ts)
+{
+	int ret = 0;
+
+	if (!ts->vcc_vdd) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(ts->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+		usleep_range(999, 1000);
+	}
+
+
+	if (!ts->vcc_vid) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(ts->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+		usleep_range(2999, 3000);
+	}
+
+	return ret;
+}
+
+static void hideep_pwr_off(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	if (!ts->reset_gpio)
+		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_LOW);
+
+	if (!ts->vcc_vid)
+		regulator_disable(ts->vcc_vid);
+
+	if (!ts->vcc_vdd)
+		regulator_disable(ts->vcc_vdd);
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void push_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	int evt = 0;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = (event->index >> 0) & 0x0F;
+		btn_up = (event->flag >> HIDEEP_MT_RELEASED) & 0x01;
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			event->type, event->index, i,
+			get_unaligned_le16(&event->x),
+			get_unaligned_le16(&event->y),
+			get_unaligned_le16(&event->z));
+
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				get_unaligned_le16(&event->x));
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&event->y));
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				get_unaligned_le16(&event->z));
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				event->w);
+			evt++;
+		}
+	}
+
+	input_mt_sync_frame(ts->input_dev);
+}
+
+static void push_ky(struct hideep_ts *ts)
+{
+	int i;
+	int status;
+	int code;
+
+	for (i = 0; i < ts->key_count; i++) {
+		code = ts->key_event[i + i * 2] & 0x0F;
+		status = ts->key_event[i + i * 2] & 0xF0;
+
+		input_report_key(ts->input_dev, ts->key_codes[code],
+			status & HIDEEP_KEY_PRESSED_MASK);
+	}
+}
+
+static void hideep_put_event(struct hideep_ts *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+	if (ts->key_count > 0)
+		push_ky(ts);
+
+	input_sync(ts->input_dev);
+}
+
+static int hideep_get_event(struct hideep_ts *ts)
+{
+	int ret;
+	int touch_count;
+	int event_size;
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		event_size = ts->tch_count *
+			sizeof(struct hideep_event);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			event_size, ts->touch_event);
+
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return ret;
+		}
+	}
+
+	if (ts->key_count > 0) {
+		event_size = ts->key_count * 2;
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			event_size, ts->key_event);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return ret;
+		}
+	}
+
+	return touch_count;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char buff[2];
+	int ret;
+
+	struct hideep_ts *ts = handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x", ts->dev_state);
+
+	if (ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, buff);
+
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = buff[0];
+		ts->key_count = buff[1] & 0x0f;
+		ts->lpm_count = buff[1] & 0xf0;
+
+		ret = hideep_get_event(ts);
+
+		if (ret >= 0)
+			hideep_put_event(ts);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_ts *ts)
+{
+	int ret, i;
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	if (ts->key_num) {
+		ts->input_dev->keycode = ts->key_codes;
+		ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
+		ts->input_dev->keycodemax = ts->key_num;
+		for (i = 0; i < ts->key_num; i++)
+			input_set_capability(ts->input_dev, EV_KEY,
+				ts->key_codes[i]);
+	}
+
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, 65535, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	ret = input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+
+	return ret;
+}
+
+static void hideep_get_info(struct hideep_ts *ts)
+{
+	unsigned char val[4];
+
+	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
+		hideep_i2c_read(ts, 0x28, 4, val);
+
+		ts->prop.max_x = get_unaligned_le16(&val[2]);
+		ts->prop.max_y = get_unaligned_le16(&val[0]);
+
+		dev_info(&ts->client->dev, "X : %d, Y : %d",
+			ts->prop.max_x, ts->prop.max_y);
+	}
+}
+
+static ssize_t hideep_update_fw(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	disable_irq(ts->client->irq);
+
+	ts->dev_state = state_updating;
+	fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+	ret = hideep_update_firmware(ts, fw_name);
+
+	if (ret != 0)
+		dev_err(dev, "The firmware update failed(%d)", ret);
+
+	kfree(fw_name);
+
+	ret = hideep_load_dwz(ts);
+
+	if (ret < 0)
+		dev_err(&ts->client->dev, "fail to load dwz, ret = 0x%x", ret);
+
+	enable_irq(ts->client->irq);
+
+	ts->dev_state = state_normal;
+
+	return count;
+}
+
+static ssize_t hideep_fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t hideep_product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+static int __maybe_unused hideep_resume(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state != state_normal)
+		ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+
+	mdelay(10);
+	hideep_reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+static int __maybe_unused hideep_suspend(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state != state_sleep)
+		ts->dev_state = state_sleep;
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+static int  hideep_parse_dts(struct device *dev, struct hideep_ts *ts)
+{
+	int ret;
+
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->reset_gpio))
+		return PTR_ERR(ts->reset_gpio);
+
+	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(ts->vcc_vdd))
+		return PTR_ERR(ts->vcc_vdd);
+
+	ts->vcc_vid = devm_regulator_get(dev, "vid");
+	if (IS_ERR(ts->vcc_vid))
+		return PTR_ERR(ts->vcc_vid);
+
+	ts->key_num = device_property_read_u32_array(dev, "linux,keycodes",
+						NULL, 0);
+
+	if (ts->key_num > HIDEEP_KEY_MAX) {
+		dev_err(dev, "too many support key defined(%d)!!!",
+			ts->key_num);
+		return -EINVAL;
+	}
+
+	ret = device_property_read_u32_array(dev, "linux,keycodes",
+				ts->key_codes, ts->key_num);
+	if (ret) {
+		dev_dbg(dev, "don't support touch key");
+		ts->key_num = 0;
+	}
+
+	return 0;
+}
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_ts *ts;
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		return -ENODEV;
+	}
+
+	/* init hideep_ts */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ret = hideep_parse_dts(&client->dev, ts);
+
+	if (ret)
+		return ret;
+
+	ts->client = client;
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	/* power on */
+	ret = hideep_pwr_on(ts);
+	if (ret) {
+		dev_err(&ts->client->dev, "power on failed");
+		return ret;
+	}
+
+	ret = devm_add_action(&ts->client->dev, hideep_pwr_off, ts);
+	if (ret) {
+		hideep_pwr_off(ts);
+		return ret;
+	}
+
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	hideep_reset_ic(ts);
+
+	/* read info */
+	ret = hideep_load_dwz(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		return ret;
+	}
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		return -ENOMEM;
+	}
+
+	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
+	hideep_get_info(ts);
+
+	ret = hideep_capability(ts);
+	if (ret) {
+		dev_err(&client->dev, "can't init input properties");
+		return ret;
+	}
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		return ret;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (IS_ERR(&ts->client->irq)) {
+		dev_err(&client->dev, "can't be assigned irq");
+		return -ENOMEM;
+	}
+
+	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
+		NULL, hideep_irq_task, IRQF_ONESHOT,
+		ts->client->name, ts);
+
+	disable_irq(ts->client->irq);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+			ret);
+		return ret;
+	}
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+
+	ret = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
+
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-ts" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-09-11  7:27           ` Anthony Kim
@ 2017-09-12 14:17             ` Rob Herring
  2017-09-13  7:50               ` kbuild test robot
                               ` (2 subsequent siblings)
  3 siblings, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-09-12 14:17 UTC (permalink / raw)
  To: Anthony Kim; +Cc: dmitry.torokhov, linux-input, devicetree, linux-kernel

On Mon, Sep 11, 2017 at 04:27:42PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |   42 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +

Acked-by: Rob Herring <robh@kernel.org>

>  drivers/input/touchscreen/Kconfig                  |   11 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  drivers/input/touchscreen/hideep.c                 | 1274 ++++++++++++++++++++
>  5 files changed, 1329 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.c

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-09-11  7:27           ` Anthony Kim
@ 2017-09-13  7:50               ` kbuild test robot
  2017-09-13  7:50               ` kbuild test robot
                                 ` (2 subsequent siblings)
  3 siblings, 0 replies; 29+ messages in thread
From: kbuild test robot @ 2017-09-13  7:50 UTC (permalink / raw)
  To: Anthony Kim
  Cc: kbuild-all, dmitry.torokhov, robh+dt, linux-input, devicetree,
	linux-kernel, Anthony Kim

[-- Attachment #1: Type: text/plain, Size: 2905 bytes --]

Hi Anthony,

[auto build test WARNING on input/next]
[also build test WARNING on v4.13 next-20170912]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Anthony-Kim/Input-add-support-for-HiDeep-touchscreen/20170913-140147
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   drivers/input/touchscreen/hideep.c: In function 'hideep_update_firmware':
>> drivers/input/touchscreen/hideep.c:575:17: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'size_t {aka const unsigned int}' [-Wformat=]
       "file size(%ld) is big more than fw memory size(%d)",
                    ^

vim +575 drivers/input/touchscreen/hideep.c

   558	
   559	static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
   560	{
   561		int ret;
   562		int retry, retry_cnt = 3;
   563		const struct firmware *fw_entry;
   564	
   565		dev_dbg(&ts->client->dev, "enter");
   566		ret = request_firmware(&fw_entry, fn, &ts->client->dev);
   567	
   568		if (ret != 0) {
   569			dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
   570			return ret;
   571		}
   572	
   573		if (fw_entry->size > ts->fw_size) {
   574			dev_err(&ts->client->dev,
 > 575				"file size(%ld) is big more than fw memory size(%d)",
   576				fw_entry->size, ts->fw_size);
   577			release_firmware(fw_entry);
   578			return -EFBIG;
   579		}
   580	
   581		/* chip specific code for flash fuse */
   582		mutex_lock(&ts->dev_mutex);
   583	
   584		ts->dev_state = state_updating;
   585	
   586		/* enter program mode */
   587		ret = hideep_enter_pgm(ts);
   588	
   589		if (ret)
   590			return ret;
   591	
   592		/* comparing & programming each page, if the memory of specified
   593		 * page is exactly same, no need to update.
   594		 */
   595		for (retry = 0; retry < retry_cnt; retry++) {
   596			hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
   597	
   598			ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
   599			if (!ret)
   600				break;
   601		}
   602	
   603		if (retry < retry_cnt)
   604			dev_dbg(&ts->client->dev, "update success!!!");
   605		else
   606			dev_err(&ts->client->dev, "update failed!!!");
   607	
   608		SW_RESET_IN_PGM(1000);
   609	
   610		ts->dev_state = state_normal;
   611	
   612		mutex_unlock(&ts->dev_mutex);
   613	
   614		release_firmware(fw_entry);
   615	
   616		return ret;
   617	}
   618	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 59595 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
@ 2017-09-13  7:50               ` kbuild test robot
  0 siblings, 0 replies; 29+ messages in thread
From: kbuild test robot @ 2017-09-13  7:50 UTC (permalink / raw)
  Cc: kbuild-all, dmitry.torokhov, robh+dt, linux-input, devicetree,
	linux-kernel, Anthony Kim

[-- Attachment #1: Type: text/plain, Size: 2905 bytes --]

Hi Anthony,

[auto build test WARNING on input/next]
[also build test WARNING on v4.13 next-20170912]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Anthony-Kim/Input-add-support-for-HiDeep-touchscreen/20170913-140147
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   drivers/input/touchscreen/hideep.c: In function 'hideep_update_firmware':
>> drivers/input/touchscreen/hideep.c:575:17: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'size_t {aka const unsigned int}' [-Wformat=]
       "file size(%ld) is big more than fw memory size(%d)",
                    ^

vim +575 drivers/input/touchscreen/hideep.c

   558	
   559	static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
   560	{
   561		int ret;
   562		int retry, retry_cnt = 3;
   563		const struct firmware *fw_entry;
   564	
   565		dev_dbg(&ts->client->dev, "enter");
   566		ret = request_firmware(&fw_entry, fn, &ts->client->dev);
   567	
   568		if (ret != 0) {
   569			dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
   570			return ret;
   571		}
   572	
   573		if (fw_entry->size > ts->fw_size) {
   574			dev_err(&ts->client->dev,
 > 575				"file size(%ld) is big more than fw memory size(%d)",
   576				fw_entry->size, ts->fw_size);
   577			release_firmware(fw_entry);
   578			return -EFBIG;
   579		}
   580	
   581		/* chip specific code for flash fuse */
   582		mutex_lock(&ts->dev_mutex);
   583	
   584		ts->dev_state = state_updating;
   585	
   586		/* enter program mode */
   587		ret = hideep_enter_pgm(ts);
   588	
   589		if (ret)
   590			return ret;
   591	
   592		/* comparing & programming each page, if the memory of specified
   593		 * page is exactly same, no need to update.
   594		 */
   595		for (retry = 0; retry < retry_cnt; retry++) {
   596			hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
   597	
   598			ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
   599			if (!ret)
   600				break;
   601		}
   602	
   603		if (retry < retry_cnt)
   604			dev_dbg(&ts->client->dev, "update success!!!");
   605		else
   606			dev_err(&ts->client->dev, "update failed!!!");
   607	
   608		SW_RESET_IN_PGM(1000);
   609	
   610		ts->dev_state = state_normal;
   611	
   612		mutex_unlock(&ts->dev_mutex);
   613	
   614		release_firmware(fw_entry);
   615	
   616		return ret;
   617	}
   618	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 59595 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-09-11  7:27           ` Anthony Kim
@ 2017-09-13  8:51               ` kbuild test robot
  2017-09-13  7:50               ` kbuild test robot
                                 ` (2 subsequent siblings)
  3 siblings, 0 replies; 29+ messages in thread
From: kbuild test robot @ 2017-09-13  8:51 UTC (permalink / raw)
  To: Anthony Kim
  Cc: kbuild-all, dmitry.torokhov, robh+dt, linux-input, devicetree,
	linux-kernel, Anthony Kim

[-- Attachment #1: Type: text/plain, Size: 2977 bytes --]

Hi Anthony,

[auto build test WARNING on input/next]
[also build test WARNING on v4.13 next-20170912]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Anthony-Kim/Input-add-support-for-HiDeep-touchscreen/20170913-140147
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m68k 

All warnings (new ones prefixed by >>):

   drivers/input/touchscreen/hideep.c: In function 'hideep_update_firmware':
>> drivers/input/touchscreen/hideep.c:576:4: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'size_t' [-Wformat=]
       fw_entry->size, ts->fw_size);
       ^

vim +576 drivers/input/touchscreen/hideep.c

   558	
   559	static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
   560	{
   561		int ret;
   562		int retry, retry_cnt = 3;
   563		const struct firmware *fw_entry;
   564	
   565		dev_dbg(&ts->client->dev, "enter");
   566		ret = request_firmware(&fw_entry, fn, &ts->client->dev);
   567	
   568		if (ret != 0) {
   569			dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
   570			return ret;
   571		}
   572	
   573		if (fw_entry->size > ts->fw_size) {
   574			dev_err(&ts->client->dev,
   575				"file size(%ld) is big more than fw memory size(%d)",
 > 576				fw_entry->size, ts->fw_size);
   577			release_firmware(fw_entry);
   578			return -EFBIG;
   579		}
   580	
   581		/* chip specific code for flash fuse */
   582		mutex_lock(&ts->dev_mutex);
   583	
   584		ts->dev_state = state_updating;
   585	
   586		/* enter program mode */
   587		ret = hideep_enter_pgm(ts);
   588	
   589		if (ret)
   590			return ret;
   591	
   592		/* comparing & programming each page, if the memory of specified
   593		 * page is exactly same, no need to update.
   594		 */
   595		for (retry = 0; retry < retry_cnt; retry++) {
   596			hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
   597	
   598			ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
   599			if (!ret)
   600				break;
   601		}
   602	
   603		if (retry < retry_cnt)
   604			dev_dbg(&ts->client->dev, "update success!!!");
   605		else
   606			dev_err(&ts->client->dev, "update failed!!!");
   607	
   608		SW_RESET_IN_PGM(1000);
   609	
   610		ts->dev_state = state_normal;
   611	
   612		mutex_unlock(&ts->dev_mutex);
   613	
   614		release_firmware(fw_entry);
   615	
   616		return ret;
   617	}
   618	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 40413 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
@ 2017-09-13  8:51               ` kbuild test robot
  0 siblings, 0 replies; 29+ messages in thread
From: kbuild test robot @ 2017-09-13  8:51 UTC (permalink / raw)
  Cc: kbuild-all, dmitry.torokhov, robh+dt, linux-input, devicetree,
	linux-kernel, Anthony Kim

[-- Attachment #1: Type: text/plain, Size: 2977 bytes --]

Hi Anthony,

[auto build test WARNING on input/next]
[also build test WARNING on v4.13 next-20170912]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Anthony-Kim/Input-add-support-for-HiDeep-touchscreen/20170913-140147
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m68k 

All warnings (new ones prefixed by >>):

   drivers/input/touchscreen/hideep.c: In function 'hideep_update_firmware':
>> drivers/input/touchscreen/hideep.c:576:4: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'size_t' [-Wformat=]
       fw_entry->size, ts->fw_size);
       ^

vim +576 drivers/input/touchscreen/hideep.c

   558	
   559	static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
   560	{
   561		int ret;
   562		int retry, retry_cnt = 3;
   563		const struct firmware *fw_entry;
   564	
   565		dev_dbg(&ts->client->dev, "enter");
   566		ret = request_firmware(&fw_entry, fn, &ts->client->dev);
   567	
   568		if (ret != 0) {
   569			dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
   570			return ret;
   571		}
   572	
   573		if (fw_entry->size > ts->fw_size) {
   574			dev_err(&ts->client->dev,
   575				"file size(%ld) is big more than fw memory size(%d)",
 > 576				fw_entry->size, ts->fw_size);
   577			release_firmware(fw_entry);
   578			return -EFBIG;
   579		}
   580	
   581		/* chip specific code for flash fuse */
   582		mutex_lock(&ts->dev_mutex);
   583	
   584		ts->dev_state = state_updating;
   585	
   586		/* enter program mode */
   587		ret = hideep_enter_pgm(ts);
   588	
   589		if (ret)
   590			return ret;
   591	
   592		/* comparing & programming each page, if the memory of specified
   593		 * page is exactly same, no need to update.
   594		 */
   595		for (retry = 0; retry < retry_cnt; retry++) {
   596			hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
   597	
   598			ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
   599			if (!ret)
   600				break;
   601		}
   602	
   603		if (retry < retry_cnt)
   604			dev_dbg(&ts->client->dev, "update success!!!");
   605		else
   606			dev_err(&ts->client->dev, "update failed!!!");
   607	
   608		SW_RESET_IN_PGM(1000);
   609	
   610		ts->dev_state = state_normal;
   611	
   612		mutex_unlock(&ts->dev_mutex);
   613	
   614		release_firmware(fw_entry);
   615	
   616		return ret;
   617	}
   618	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 40413 bytes --]

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
  2017-09-11  7:27           ` Anthony Kim
                               ` (2 preceding siblings ...)
  2017-09-13  8:51               ` kbuild test robot
@ 2017-09-14  4:36             ` Anthony Kim
  2017-09-14 18:51               ` Dmitry Torokhov
                                 ` (3 more replies)
  3 siblings, 4 replies; 29+ messages in thread
From: Anthony Kim @ 2017-09-14  4:36 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt, lkp
  Cc: kbuild-all, linux-input, devicetree, linux-kernel, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |   42 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/input/touchscreen/Kconfig                  |   11 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/hideep.c                 | 1274 ++++++++++++++++++++
 5 files changed, 1329 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..86b3a97
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,42 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-ts"
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- touchscreen-size-x	: X axis size of touchscreen
+- touchscreen-size-y	: Y axis size of touchscreen
+- linux,keycodes	: Specifies an array of numeric keycode values to
+			be used for reporting button presses. The array can
+			contain up to 3 entries.
+
+Example:
+
+#include "dt-bindings/input/input.h"
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-ts";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		touchscreen-size-x = 1079;
+		touchscreen-size-y = 1919;
+		linux,keycodes = <KEY_HOME>, <KEY_MENU>, <KEY_BACK>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d201..aa2a301 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -131,6 +131,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..d0c8dafc 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -344,6 +344,17 @@ config TOUCHSCREEN_GOODIX
 	  To compile this driver as a module, choose M here: the
 	  module will be called goodix.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 config TOUCHSCREEN_ILI210X
 	tristate "Ilitek ILI210X based touchscreen"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..873b67e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)	+= egalax_ts_serial.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP)	+= hideep.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c
new file mode 100644
index 0000000..f1b021b
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.c
@@ -0,0 +1,1274 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <asm/unaligned.h>
+
+#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME					"hideep_ts"
+
+#define HIDEEP_MT_MAX					10
+#define HIDEEP_KEY_MAX					3
+#define FRAME_HEADER_SIZE				8
+
+/* Touch & key event */
+#define HIDEEP_EVENT_COUNT_ADDR			0x240
+#define HIDEEP_TOUCH_DATA_ADDR			0x242
+#define HIDEEP_KEY_DATA_ADDR			0x2A6
+#define HIDEEP_RAW_DATA_ADDR			0x1000
+
+/* command list */
+#define HIDEEP_RESET_CMD				0x9800
+#define HIDEEP_INTCLR_CMD				0x9802
+#define HIDEEP_OPMODE_CMD				0x9804
+#define HIDEEP_SWTICH_CMD				0x9805
+#define HIDEEP_SLEEP_CMD				0x980D
+
+/* multi touch event bit */
+#define HIDEEP_MT_ALWAYS_REPORT			0
+#define HIDEEP_MT_TOUCHED				1
+#define HIDEEP_MT_FIRST_CONTACT			2
+#define HIDEEP_MT_DRAG_MOVE				3
+#define HIDEEP_MT_RELEASED				4
+#define HIDEEP_MT_PINCH					5
+#define HIDEEP_MT_PRESSURE				6
+
+/* key event bit */
+#define HIDEEP_KEY_RELEASED				0x20
+#define HIDEEP_KEY_PRESSED				0x40
+#define HIDEEP_KEY_FIRST_PRESSED		0x80
+#define HIDEEP_KEY_PRESSED_MASK			0xC0
+
+/* For NVM */
+#define YRAM_BASE				0x40000000
+#define PERIPHERAL_BASE			0x50000000
+#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
+#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
+#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
+
+#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
+#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
+#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
+#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
+#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
+#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
+#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
+#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
+#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
+
+#define FLASH_CON				(FLASH_BASE + 0x0000)
+#define FLASH_STA				(FLASH_BASE + 0x0004)
+#define FLASH_CFG				(FLASH_BASE + 0x0008)
+#define FLASH_TIM				(FLASH_BASE + 0x000C)
+#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
+#define FLASH_PIO_SIG			(FLASH_BASE + 0x400000)
+
+#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
+
+#define PERASE					0x00040000
+#define WRONLY					0x00100000
+
+#define NVM_MASK_OFS			0x0000000C
+#define NVM_DEFAULT_PAGE		0
+#define NVM_SFR_WPAGE			1
+#define NVM_SFR_RPAGE			2
+
+#define PIO_SIG					0x00400000
+#define _PROT_MODE				0x03400000
+
+#define NVM_PAGE_SIZE			128
+
+#define HIDEEP_DWZ_INFO			0x000002C0
+
+enum e_dev_state {
+	state_init = 1,
+	state_normal,
+	state_sleep,
+	state_updating,
+};
+
+struct hideep_event {
+	__le16 x;
+	__le16 y;
+	__le16 z;
+	unsigned char w;
+	unsigned char flag;
+	unsigned char type;
+	unsigned char index;
+} __packed;
+
+struct dwz_info {
+	__le32	code_start;
+	unsigned char code_crc[12];
+
+	__le32 c_code_start;
+	__le16 c_code_len;
+	__le16 gen_ver;
+
+	__le32 vr_start;
+	__le16 vr_len;
+	__le16 rsv0;
+
+	__le32 ft_start;
+	__le16 ft_len;
+	__le16 vr_version;
+
+	__le16 boot_ver;
+	__le16 core_ver;
+	__le16 custom_ver;
+	__le16 release_ver;
+
+	unsigned char factory_id;
+	unsigned char panel_type;
+	unsigned char model_name[6];
+	__le16 product_code;
+	__le16 extra_option;
+
+	__le16 product_id;
+	__le16 vendor_id;
+} __packed;
+
+struct hideep_ts {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+
+	struct touchscreen_properties prop;
+
+	struct gpio_desc *reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct mutex dev_mutex;
+	struct mutex i2c_mutex;
+
+	enum e_dev_state dev_state;
+
+	unsigned int tch_count;
+	unsigned int key_count;
+	unsigned int lpm_count;
+
+	unsigned char touch_event[HIDEEP_MT_MAX * 10];
+	unsigned char key_event[HIDEEP_KEY_MAX * 2];
+
+	int key_num;
+	int key_codes[HIDEEP_KEY_MAX];
+
+	struct dwz_info dwz_info;
+
+	int fw_size;
+	int nvm_mask;
+} __packed;
+
+struct pgm_packet {
+	union {
+		unsigned char b[8];
+		__be32 w[2];
+	} header;
+
+	__be32 payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
+};
+
+static void hideep_reset_ic(struct hideep_ts *ts);
+
+static int hideep_pgm_w_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret;
+	int i;
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	put_unaligned_be32((0x80 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
+
+	ret = i2c_master_send(ts->client, &packet->header.b[3],
+		(len + 5));
+
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_ts *ts, unsigned int addr,
+	struct pgm_packet *packet, unsigned int len)
+{
+	int ret;
+	int i;
+	unsigned char buff[len];	// need to modify
+	struct i2c_msg msg[2];
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	put_unaligned_be32((0x00 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = &packet->header.b[3];
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buff;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+
+	if (ret < 0)
+		goto err;
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		packet->payload[i] = get_unaligned_be32(&buff[i * sizeof(u32)]);
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int *val)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x00, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, sizeof(u32));
+
+	if (ret < 0)
+		goto err;
+
+	*val = packet.payload[0];
+
+err:
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_ts *ts, unsigned int addr,
+	unsigned int data)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x80, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, sizeof(u32));
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | (CE << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, FLASH_PIO_SIG + X, Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, FLASH_CON, 0x00)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void hideep_pgm_set(struct hideep_ts *ts)
+{
+	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
+	hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
+	hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
+	hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
+	hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
+	hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
+	hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
+}
+
+static int hideep_pgm_get_pattern(struct hideep_ts *ts)
+{
+	int ret;
+	unsigned int status;
+	const unsigned char pattern[] = { 0x39, 0xAF, 0x9D, 0xDF };
+
+	ret = i2c_master_send(ts->client, pattern, sizeof(pattern));
+
+	if (ret < 0) {
+		dev_err(&ts->client->dev, "%d, %08X", __LINE__, ret);
+		return ret;
+	}
+
+	mdelay(1);
+
+	/* flush invalid Tx load register */
+	ret = hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
+
+	if (ret < 0)
+		return ret;
+
+	ret = hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
+
+	if (ret < 0)
+		return ret;
+
+	dev_info(&ts->client->dev, "%s, %08X", __func__, status);
+	return status;
+}
+
+static int hideep_enter_pgm(struct hideep_ts *ts)
+{
+	int retry_count = 10;
+	int val;
+	unsigned int pgm_pattern = 0xDF9DAF39;
+
+	while (retry_count--) {
+		val = hideep_pgm_get_pattern(ts);
+
+		if (pgm_pattern != get_unaligned_be32(&val)) {
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				get_unaligned_be32(&val));
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+
+	if (retry_count < 0) {
+		dev_err(&ts->client->dev, "couldn't enter pgm mode!!!");
+		SW_RESET_IN_PGM(1000);
+		return -EBADMSG;
+	}
+
+	hideep_pgm_set(ts);
+	mdelay(1);
+
+	return 0;
+}
+
+static void hideep_nvm_unlock(struct hideep_ts *ts)
+{
+	unsigned int unmask_code = 0;
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
+
+	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != ts->nvm_mask)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(NVM_MASK_OFS, ts->nvm_mask);
+	SET_FLASH_HWCONTROL();
+	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
+}
+
+static int hideep_check_status(struct hideep_ts *ts)
+{
+	int ret, status;
+	int time_out = 100;
+
+	while (time_out--) {
+		mdelay(1);
+		ret = hideep_pgm_r_reg(ts, FLASH_STA, &status);
+
+		if (ret < 0)
+			continue;
+
+		if (status)
+			return status;
+	}
+
+	return time_out;
+}
+
+static int hideep_program_page(struct hideep_ts *ts,
+	unsigned int addr, struct pgm_packet *packet_w)
+{
+	int ret;
+
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	addr = addr & ~(NVM_PAGE_SIZE - 1);
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* erase page */
+	SET_PIO_SIG((PERASE | addr), 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	/* write page */
+	SET_FLASH_PIO(1);
+
+	SET_PIO_SIG((WRONLY | addr), get_unaligned_be32(&packet_w->payload[0]));
+
+	hideep_pgm_w_mem(ts, (FLASH_PIO_SIG | WRONLY),
+		packet_w, NVM_PAGE_SIZE);
+
+	SET_PIO_SIG(124, get_unaligned_be32(&packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	SET_FLASH_HWCONTROL();
+
+	return 0;
+}
+
+static void hideep_program_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len)
+{
+	struct pgm_packet packet_w;
+	struct pgm_packet packet_r;
+	int i;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_w = NVM_PAGE_SIZE;
+	unsigned int pages = DIV_ROUND_UP(len, NVM_PAGE_SIZE);
+
+
+	hideep_nvm_unlock(ts);
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < NVM_PAGE_SIZE)
+			len_w = len_r;
+
+		/* compare */
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+		ret = memcmp(&ucode[addr], packet_r.payload, len_w);
+
+		if (ret) {
+			/* write page */
+			memcpy(packet_w.payload, &ucode[addr], len_w);
+			ret = hideep_program_page(ts, addr, &packet_w);
+			if (ret)
+				dev_err(&ts->client->dev,
+					"%s : error(%08x):",
+					__func__, addr);
+			mdelay(1);
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+}
+
+static int hideep_verify_nvm(struct hideep_ts *ts, const unsigned char *ucode,
+	int len)
+{
+	struct pgm_packet packet_r;
+	int i, j;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_v = NVM_PAGE_SIZE;
+	unsigned int pages = DIV_ROUND_UP(len, NVM_PAGE_SIZE);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < NVM_PAGE_SIZE)
+			len_v = len_r;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			NVM_PAGE_SIZE);
+
+		ret = memcmp(&ucode[addr], packet_r.payload, len_v);
+
+		if (ret) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < NVM_PAGE_SIZE; j++) {
+				if (ucode[addr + j] != read[j])
+					dev_err(&ts->client->dev,
+						"verify : error([%d] %02x : %02x)",
+						addr + j, ucode[addr + j],
+						read[j]);
+			}
+			return ret;
+		}
+
+		addr += NVM_PAGE_SIZE;
+		len_r -= NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+
+	return 0;
+}
+
+static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
+{
+	int ret;
+	int retry, retry_cnt = 3;
+	const struct firmware *fw_entry;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	if (fw_entry->size > ts->fw_size) {
+		dev_err(&ts->client->dev,
+			"file size(%zu) is big more than fw memory size(%d)",
+			fw_entry->size, ts->fw_size);
+		release_firmware(fw_entry);
+		return -EFBIG;
+	}
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	ts->dev_state = state_updating;
+
+	/* enter program mode */
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	for (retry = 0; retry < retry_cnt; retry++) {
+		hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
+
+		ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
+		if (!ret)
+			break;
+	}
+
+	if (retry < retry_cnt)
+		dev_dbg(&ts->client->dev, "update success!!!");
+	else
+		dev_err(&ts->client->dev, "update failed!!!");
+
+	SW_RESET_IN_PGM(1000);
+
+	ts->dev_state = state_normal;
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_ts *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	mdelay(50);
+
+	hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, &packet_r,
+		sizeof(struct dwz_info));
+
+	memcpy(&ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info));
+
+	SW_RESET_IN_PGM(10);
+
+	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x60) {
+		/* Lime fw size */
+		dev_dbg(&ts->client->dev, "used lime IC");
+		ts->fw_size = 1024 * 64;
+		ts->nvm_mask = 0x0030027B;
+	} else if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
+		/* Crimson IC */
+		dev_dbg(&ts->client->dev, "used crimson IC");
+		ts->fw_size = 1024 * 48;
+		ts->nvm_mask = 0x00310000;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mdelay(50);
+
+	return 0;
+}
+
+static int hideep_i2c_read(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret;
+	struct i2c_msg msg[2];
+
+	mutex_lock(&ts->i2c_mutex);
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 2;
+	msg[0].buf = (u8 *)&addr;
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buf;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+
+	mutex_unlock(&ts->i2c_mutex);
+	return ret;
+}
+
+static int hideep_i2c_write(struct hideep_ts *ts, unsigned short addr,
+	unsigned short len, unsigned char *buf)
+{
+	int ret;
+	unsigned char *wbuf;
+	struct i2c_msg msg;
+
+	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
+
+	wbuf = kmalloc(len + 2, GFP_KERNEL);
+
+	mutex_lock(&ts->i2c_mutex);
+
+	put_unaligned_le16(addr, &wbuf[0]);
+	memcpy(&wbuf[2], buf, len);
+
+	msg.addr = ts->client->addr;
+	msg.flags = 0;
+	msg.len = len + 2;
+	msg.buf = wbuf;
+
+	ret = i2c_transfer(ts->client->adapter, &msg, 1);
+
+	mutex_unlock(&ts->i2c_mutex);
+
+	kfree(wbuf);
+
+	return  ret;
+}
+
+static void hideep_reset_ic(struct hideep_ts *ts)
+{
+	unsigned char cmd = 0x01;
+
+	if (!ts->reset_gpio) {
+		dev_dbg(&ts->client->dev, "hideep:enable the reset_gpio");
+		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_LOW);
+		mdelay(20);
+		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_HIGH);
+	} else {
+		hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
+	}
+	mdelay(50);
+}
+
+static int hideep_pwr_on(struct hideep_ts *ts)
+{
+	int ret = 0;
+
+	if (!ts->vcc_vdd) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vdd is enable");
+		ret = regulator_enable(ts->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+		usleep_range(999, 1000);
+	}
+
+
+	if (!ts->vcc_vid) {
+		dev_dbg(&ts->client->dev, "hideep:vcc_vid is enable");
+		ret = regulator_enable(ts->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+		usleep_range(2999, 3000);
+	}
+
+	return ret;
+}
+
+static void hideep_pwr_off(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	if (!ts->reset_gpio)
+		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_LOW);
+
+	if (!ts->vcc_vid)
+		regulator_disable(ts->vcc_vid);
+
+	if (!ts->vcc_vdd)
+		regulator_disable(ts->vcc_vdd);
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void push_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	bool btn_up = 0;
+	int evt = 0;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = (event->index >> 0) & 0x0F;
+		btn_up = (event->flag >> HIDEEP_MT_RELEASED) & 0x01;
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			event->type, event->index, i,
+			get_unaligned_le16(&event->x),
+			get_unaligned_le16(&event->y),
+			get_unaligned_le16(&event->z));
+
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				get_unaligned_le16(&event->x));
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&event->y));
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				get_unaligned_le16(&event->z));
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				event->w);
+			evt++;
+		}
+	}
+
+	input_mt_sync_frame(ts->input_dev);
+}
+
+static void push_ky(struct hideep_ts *ts)
+{
+	int i;
+	int status;
+	int code;
+
+	for (i = 0; i < ts->key_count; i++) {
+		code = ts->key_event[i + i * 2] & 0x0F;
+		status = ts->key_event[i + i * 2] & 0xF0;
+
+		input_report_key(ts->input_dev, ts->key_codes[code],
+			status & HIDEEP_KEY_PRESSED_MASK);
+	}
+}
+
+static void hideep_put_event(struct hideep_ts *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+	if (ts->key_count > 0)
+		push_ky(ts);
+
+	input_sync(ts->input_dev);
+}
+
+static int hideep_get_event(struct hideep_ts *ts)
+{
+	int ret;
+	int touch_count;
+	int event_size;
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count > HIDEEP_MT_MAX)
+		ts->tch_count = 0;
+
+	if (ts->key_count > HIDEEP_KEY_MAX)
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	if (ts->tch_count > 0) {
+		event_size = ts->tch_count *
+			sizeof(struct hideep_event);
+		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
+			event_size, ts->touch_event);
+
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return ret;
+		}
+	}
+
+	if (ts->key_count > 0) {
+		event_size = ts->key_count * 2;
+		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
+			event_size, ts->key_event);
+		if (ret < 0) {
+			dev_err(&ts->client->dev, "read I2C error.");
+			return ret;
+		}
+	}
+
+	return touch_count;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	unsigned char buff[2];
+	int ret;
+
+	struct hideep_ts *ts = handle;
+
+	dev_dbg(&ts->client->dev, "state = 0x%x", ts->dev_state);
+
+	if (ts->dev_state == state_normal) {
+		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
+			2, buff);
+
+		if (ret < 0) {
+			disable_irq(ts->client->irq);
+			return IRQ_HANDLED;
+		}
+
+		ts->tch_count = buff[0];
+		ts->key_count = buff[1] & 0x0f;
+		ts->lpm_count = buff[1] & 0xf0;
+
+		ret = hideep_get_event(ts);
+
+		if (ret >= 0)
+			hideep_put_event(ts);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int hideep_capability(struct hideep_ts *ts)
+{
+	int ret, i;
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	if (ts->key_num) {
+		ts->input_dev->keycode = ts->key_codes;
+		ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
+		ts->input_dev->keycodemax = ts->key_num;
+		for (i = 0; i < ts->key_num; i++)
+			input_set_capability(ts->input_dev, EV_KEY,
+				ts->key_codes[i]);
+	}
+
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, 65535, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	ret = input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+
+	return ret;
+}
+
+static void hideep_get_info(struct hideep_ts *ts)
+{
+	unsigned char val[4];
+
+	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
+		hideep_i2c_read(ts, 0x28, 4, val);
+
+		ts->prop.max_x = get_unaligned_le16(&val[2]);
+		ts->prop.max_y = get_unaligned_le16(&val[0]);
+
+		dev_info(&ts->client->dev, "X : %d, Y : %d",
+			ts->prop.max_x, ts->prop.max_y);
+	}
+}
+
+static ssize_t hideep_update_fw(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	disable_irq(ts->client->irq);
+
+	ts->dev_state = state_updating;
+	fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+	ret = hideep_update_firmware(ts, fw_name);
+
+	if (ret != 0)
+		dev_err(dev, "The firmware update failed(%d)", ret);
+
+	kfree(fw_name);
+
+	ret = hideep_load_dwz(ts);
+
+	if (ret < 0)
+		dev_err(&ts->client->dev, "fail to load dwz, ret = 0x%x", ret);
+
+	enable_irq(ts->client->irq);
+
+	ts->dev_state = state_normal;
+
+	return count;
+}
+
+static ssize_t hideep_fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t hideep_product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	dev_info(dev, "product id : %04x",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+static int __maybe_unused hideep_resume(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x00;
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state != state_normal)
+		ts->dev_state = state_normal;
+
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	enable_irq(ts->client->irq);
+
+	mdelay(10);
+	hideep_reset_ic(ts);
+
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+static int __maybe_unused hideep_suspend(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	unsigned char sleep_cmd = 0x01;
+
+	mutex_lock(&ts->dev_mutex);
+
+	if (ts->dev_state != state_sleep)
+		ts->dev_state = state_sleep;
+
+	/* default deep sleep */
+	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
+	disable_irq(ts->client->irq);
+
+	mutex_unlock(&ts->dev_mutex);
+	return 0;
+}
+
+static int  hideep_parse_dts(struct device *dev, struct hideep_ts *ts)
+{
+	int ret;
+
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->reset_gpio))
+		return PTR_ERR(ts->reset_gpio);
+
+	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(ts->vcc_vdd))
+		return PTR_ERR(ts->vcc_vdd);
+
+	ts->vcc_vid = devm_regulator_get(dev, "vid");
+	if (IS_ERR(ts->vcc_vid))
+		return PTR_ERR(ts->vcc_vid);
+
+	ts->key_num = device_property_read_u32_array(dev, "linux,keycodes",
+						NULL, 0);
+
+	if (ts->key_num > HIDEEP_KEY_MAX) {
+		dev_err(dev, "too many support key defined(%d)!!!",
+			ts->key_num);
+		return -EINVAL;
+	}
+
+	ret = device_property_read_u32_array(dev, "linux,keycodes",
+				ts->key_codes, ts->key_num);
+	if (ret) {
+		dev_dbg(dev, "don't support touch key");
+		ts->key_num = 0;
+	}
+
+	return 0;
+}
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct hideep_ts *ts;
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		return -ENODEV;
+	}
+
+	/* init hideep_ts */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ret = hideep_parse_dts(&client->dev, ts);
+
+	if (ret)
+		return ret;
+
+	ts->client = client;
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->i2c_mutex);
+	mutex_init(&ts->dev_mutex);
+
+	/* power on */
+	ret = hideep_pwr_on(ts);
+	if (ret) {
+		dev_err(&ts->client->dev, "power on failed");
+		return ret;
+	}
+
+	ret = devm_add_action(&ts->client->dev, hideep_pwr_off, ts);
+	if (ret) {
+		hideep_pwr_off(ts);
+		return ret;
+	}
+
+	ts->dev_state = state_init;
+	mdelay(30);
+
+	/* ic reset */
+	hideep_reset_ic(ts);
+
+	/* read info */
+	ret = hideep_load_dwz(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		return ret;
+	}
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		return -ENOMEM;
+	}
+
+	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
+	hideep_get_info(ts);
+
+	ret = hideep_capability(ts);
+	if (ret) {
+		dev_err(&client->dev, "can't init input properties");
+		return ret;
+	}
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		return ret;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (IS_ERR(&ts->client->irq)) {
+		dev_err(&client->dev, "can't be assigned irq");
+		return -ENOMEM;
+	}
+
+	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
+		NULL, hideep_irq_task, IRQF_ONESHOT,
+		ts->client->name, ts);
+
+	disable_irq(ts->client->irq);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+			ret);
+		return ret;
+	}
+
+	ts->dev_state = state_normal;
+	enable_irq(ts->client->irq);
+
+	ret = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
+
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-ts" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-09-14  4:36             ` Anthony Kim
@ 2017-09-14 18:51               ` Dmitry Torokhov
  2017-09-18 19:03               ` Rob Herring
                                 ` (2 subsequent siblings)
  3 siblings, 0 replies; 29+ messages in thread
From: Dmitry Torokhov @ 2017-09-14 18:51 UTC (permalink / raw)
  To: Anthony Kim
  Cc: robh+dt, lkp, kbuild-all, linux-input, devicetree, linux-kernel

Hi Anthony,

On Thu, Sep 14, 2017 at 01:36:44PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>

Thank you for making the changes, The driver is shaping up, however
there are still a few issues (below). Also:

- please install and run tool "sparse" on the driver (you can invoke it
  via "make C=2 drivers/input/touchscreen/hideep.o") - there are a few
  endianness issues that it will show.

- try using regmap for accessing the device, I think it will work well
  here.

- there is error handling is missing in quite a few places.

> ---
>  .../bindings/input/touchscreen/hideep.txt          |   42 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>  drivers/input/touchscreen/Kconfig                  |   11 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  drivers/input/touchscreen/hideep.c                 | 1274 ++++++++++++++++++++
>  5 files changed, 1329 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.c
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..86b3a97
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,42 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible		: must be "hideep,hideep-ts"
> +- reg			: I2C slave address, (e.g. 0x6C).
> +- interrupt-parent : Interrupt controller to which the chip is connected.
> +- interrupts : Interrupt to which the chip is connected.
> +
> +Optional properties:
> +- vdd-supply	: It is the controller supply for controlling
> +					 main voltage(3.3V) through the regulator.
> +- vid-supply	: It is the controller supply for controlling
> +					IO voltage(1.8V) through the regulator.
> +- reset-gpios	: Define for reset gpio pin.
> +						It is to use for reset IC.
> +- touchscreen-size-x	: X axis size of touchscreen
> +- touchscreen-size-y	: Y axis size of touchscreen
> +- linux,keycodes	: Specifies an array of numeric keycode values to
> +			be used for reporting button presses. The array can
> +			contain up to 3 entries.
> +
> +Example:
> +
> +#include "dt-bindings/input/input.h"
> +
> +i2c@00000000 {
> +
> +	/* ... */
> +
> +	touchscreen@6c {
> +		compatible = "hideep,hideep-ts";
> +		reg = <0x6c>;
> +		interrupt-parent = <&gpx1>;
> +		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
> +		vdd-supply = <&ldo15_reg>";
> +		vid-supply = <&ldo18_reg>;
> +		reset-gpios = <&gpx1 5 0>;
> +		touchscreen-size-x = 1079;
> +		touchscreen-size-y = 1919;
> +		linux,keycodes = <KEY_HOME>, <KEY_MENU>, <KEY_BACK>;
> +	};
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index c03d201..aa2a301 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -131,6 +131,7 @@ gw	Gateworks Corporation
>  hannstar	HannStar Display Corporation
>  haoyu	Haoyu Microelectronic Co. Ltd.
>  hardkernel	Hardkernel Co., Ltd
> +hideep	HiDeep Inc.
>  himax	Himax Technologies, Inc.
>  hisilicon	Hisilicon Limited.
>  hit	Hitachi Ltd.
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 64b30fe..d0c8dafc 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -344,6 +344,17 @@ config TOUCHSCREEN_GOODIX
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called goodix.
>  
> +config TOUCHSCREEN_HIDEEP
> +	tristate "HiDeep Touch IC"
> +	depends on I2C
> +	help
> +	  Say Y here if you have a touchscreen using HiDeep.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a moudle, choose M here : the
> +	  module will be called hideep_ts.
> +
>  config TOUCHSCREEN_ILI210X
>  	tristate "Ilitek ILI210X based touchscreen"
>  	depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 6badce8..873b67e 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)	+= egalax_ts_serial.o
>  obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
> +obj-$(CONFIG_TOUCHSCREEN_HIDEEP)	+= hideep.o
>  obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
>  obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
> diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c
> new file mode 100644
> index 0000000..f1b021b
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep.c
> @@ -0,0 +1,1274 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/firmware.h>
> +#include <linux/delay.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/acpi.h>
> +#include <linux/interrupt.h>
> +#include <linux/sysfs.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/input/touchscreen.h>
> +#include <asm/unaligned.h>
> +
> +#define HIDEEP_TS_NAME					"HiDeep Touchscreen"
> +#define HIDEEP_I2C_NAME					"hideep_ts"
> +
> +#define HIDEEP_MT_MAX					10
> +#define HIDEEP_KEY_MAX					3
> +#define FRAME_HEADER_SIZE				8
> +
> +/* Touch & key event */
> +#define HIDEEP_EVENT_COUNT_ADDR			0x240
> +#define HIDEEP_TOUCH_DATA_ADDR			0x242
> +#define HIDEEP_KEY_DATA_ADDR			0x2A6
> +#define HIDEEP_RAW_DATA_ADDR			0x1000

Can we try aligning defines consistently? When using "standard" 8
poisitons tabs the values are not quite lining up.

> +
> +/* command list */
> +#define HIDEEP_RESET_CMD				0x9800
> +#define HIDEEP_INTCLR_CMD				0x9802
> +#define HIDEEP_OPMODE_CMD				0x9804
> +#define HIDEEP_SWTICH_CMD				0x9805
> +#define HIDEEP_SLEEP_CMD				0x980D
> +
> +/* multi touch event bit */
> +#define HIDEEP_MT_ALWAYS_REPORT			0
> +#define HIDEEP_MT_TOUCHED				1
> +#define HIDEEP_MT_FIRST_CONTACT			2
> +#define HIDEEP_MT_DRAG_MOVE				3
> +#define HIDEEP_MT_RELEASED				4
> +#define HIDEEP_MT_PINCH					5
> +#define HIDEEP_MT_PRESSURE				6

Please use BIT() for these.

> +
> +/* key event bit */
> +#define HIDEEP_KEY_RELEASED				0x20
> +#define HIDEEP_KEY_PRESSED				0x40
> +#define HIDEEP_KEY_FIRST_PRESSED		0x80

These seem to be BIT()s as well.

> +#define HIDEEP_KEY_PRESSED_MASK			0xC0

Is this (KEY_PRESSED | KEY_FIRST_PRESSED) ?

> +
> +/* For NVM */
> +#define YRAM_BASE				0x40000000
> +#define PERIPHERAL_BASE			0x50000000
> +#define ESI_BASE				(PERIPHERAL_BASE + 0x00000000)
> +#define FLASH_BASE				(PERIPHERAL_BASE + 0x01000000)
> +#define SYSCON_BASE				(PERIPHERAL_BASE + 0x02000000)
> +
> +#define SYSCON_MOD_CON			(SYSCON_BASE + 0x0000)
> +#define SYSCON_SPC_CON			(SYSCON_BASE + 0x0004)
> +#define SYSCON_CLK_CON			(SYSCON_BASE + 0x0008)
> +#define SYSCON_CLK_ENA			(SYSCON_BASE + 0x000C)
> +#define SYSCON_RST_CON			(SYSCON_BASE + 0x0010)
> +#define SYSCON_WDT_CON			(SYSCON_BASE + 0x0014)
> +#define SYSCON_WDT_CNT			(SYSCON_BASE + 0x0018)
> +#define SYSCON_PWR_CON			(SYSCON_BASE + 0x0020)
> +#define SYSCON_PGM_ID			(SYSCON_BASE + 0x00F4)
> +
> +#define FLASH_CON				(FLASH_BASE + 0x0000)
> +#define FLASH_STA				(FLASH_BASE + 0x0004)
> +#define FLASH_CFG				(FLASH_BASE + 0x0008)
> +#define FLASH_TIM				(FLASH_BASE + 0x000C)
> +#define FLASH_CACHE_CFG			(FLASH_BASE + 0x0010)
> +#define FLASH_PIO_SIG			(FLASH_BASE + 0x400000)
> +
> +#define ESI_TX_INVALID			(ESI_BASE + 0x0008)
> +
> +#define PERASE					0x00040000
> +#define WRONLY					0x00100000
> +
> +#define NVM_MASK_OFS			0x0000000C
> +#define NVM_DEFAULT_PAGE		0
> +#define NVM_SFR_WPAGE			1
> +#define NVM_SFR_RPAGE			2
> +
> +#define PIO_SIG					0x00400000
> +#define _PROT_MODE				0x03400000
> +
> +#define NVM_PAGE_SIZE			128

Maybe use HIDEEP_ prefix for all of the above defines too?

> +
> +#define HIDEEP_DWZ_INFO			0x000002C0
> +
> +enum e_dev_state {
> +	state_init = 1,
> +	state_normal,
> +	state_sleep,
> +	state_updating,
> +};

I do not understand why this enum is needed. You only check it in IRQ to
see if you are in normal mode, but usually you disable interrupt before
carrying out various actions, so IRQ will not even fire when the device
is not on normal mode...

> +
> +struct hideep_event {
> +	__le16 x;
> +	__le16 y;
> +	__le16 z;
> +	unsigned char w;
> +	unsigned char flag;
> +	unsigned char type;
> +	unsigned char index;

Use u8 type for byte data.

> +} __packed;
> +
> +struct dwz_info {
> +	__le32	code_start;
> +	unsigned char code_crc[12];

	u8

> +
> +	__le32 c_code_start;
> +	__le16 c_code_len;
> +	__le16 gen_ver;
> +
> +	__le32 vr_start;
> +	__le16 vr_len;
> +	__le16 rsv0;
> +
> +	__le32 ft_start;
> +	__le16 ft_len;
> +	__le16 vr_version;
> +
> +	__le16 boot_ver;
> +	__le16 core_ver;
> +	__le16 custom_ver;
> +	__le16 release_ver;
> +
> +	unsigned char factory_id;
> +	unsigned char panel_type;
> +	unsigned char model_name[6];

u8 for the above 3.

> +	__le16 product_code;
> +	__le16 extra_option;
> +
> +	__le16 product_id;
> +	__le16 vendor_id;
> +} __packed;
> +
> +struct hideep_ts {
> +	struct i2c_client *client;
> +	struct input_dev *input_dev;
> +
> +	struct touchscreen_properties prop;
> +
> +	struct gpio_desc *reset_gpio;
> +
> +	struct regulator *vcc_vdd;
> +	struct regulator *vcc_vid;
> +
> +	struct mutex dev_mutex;
> +	struct mutex i2c_mutex;
> +
> +	enum e_dev_state dev_state;
> +
> +	unsigned int tch_count;
> +	unsigned int key_count;
> +	unsigned int lpm_count;
> +
> +	unsigned char touch_event[HIDEEP_MT_MAX * 10];
> +	unsigned char key_event[HIDEEP_KEY_MAX * 2];
> +
> +	int key_num;
> +	int key_codes[HIDEEP_KEY_MAX];
> +
> +	struct dwz_info dwz_info;
> +
> +	int fw_size;
> +	int nvm_mask;
> +} __packed;

THis structure does not need to be backed.

> +
> +struct pgm_packet {
> +	union {
> +		unsigned char b[8];
> +		__be32 w[2];
> +	} header;
> +
> +	__be32 payload[NVM_PAGE_SIZE / sizeof(unsigned int)];
> +};
> +
> +static void hideep_reset_ic(struct hideep_ts *ts);
> +
> +static int hideep_pgm_w_mem(struct hideep_ts *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret;
> +	int i;
> +
> +	if ((len % sizeof(u32)) != 0)
> +		return -EINVAL;
> +
> +	put_unaligned_be32((0x80 | (len / sizeof(u32) - 1)),
> +		&packet->header.w[0]);
> +	put_unaligned_be32(addr, &packet->header.w[1]);
> +
> +	for (i = 0; i < len / sizeof(u32); i++)
> +		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
> +
> +	ret = i2c_master_send(ts->client, &packet->header.b[3],
> +		(len + 5));
> +
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_mem(struct hideep_ts *ts, unsigned int addr,
> +	struct pgm_packet *packet, unsigned int len)
> +{
> +	int ret;
> +	int i;
> +	unsigned char buff[len];	// need to modify
> +	struct i2c_msg msg[2];
> +
> +	if ((len % sizeof(u32)) != 0)
> +		return -EINVAL;
> +
> +	put_unaligned_be32((0x00 | (len / sizeof(u32) - 1)),
> +		&packet->header.w[0]);
> +	put_unaligned_be32(addr, &packet->header.w[1]);
> +
> +	msg[0].addr = ts->client->addr;
> +	msg[0].flags = 0;
> +	msg[0].len = 5;
> +	msg[0].buf = &packet->header.b[3];
> +
> +	msg[1].addr = ts->client->addr;
> +	msg[1].flags = I2C_M_RD;
> +	msg[1].len = len;
> +	msg[1].buf = buff;
> +
> +	ret = i2c_transfer(ts->client->adapter, msg, 2);
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	for (i = 0; i < len / sizeof(u32); i++)
> +		packet->payload[i] = get_unaligned_be32(&buff[i * sizeof(u32)]);
> +
> +err:
> +	return ret;
> +}
> +
> +static int hideep_pgm_r_reg(struct hideep_ts *ts, unsigned int addr,
> +	unsigned int *val)
> +{
> +	int ret;
> +	struct pgm_packet packet;
> +
> +	put_unaligned_be32(0x00, &packet.header.w[0]);
> +	put_unaligned_be32(addr, &packet.header.w[1]);
> +
> +	ret = hideep_pgm_r_mem(ts, addr, &packet, sizeof(u32));
> +
> +	if (ret < 0)
> +		goto err;
> +
> +	*val = packet.payload[0];
> +
> +err:
> +	return ret;
> +}
> +
> +static int hideep_pgm_w_reg(struct hideep_ts *ts, unsigned int addr,
> +	unsigned int data)
> +{
> +	int ret;
> +	struct pgm_packet packet;
> +
> +	put_unaligned_be32(0x80, &packet.header.w[0]);
> +	put_unaligned_be32(addr, &packet.header.w[1]);
> +	packet.payload[0] = data;
> +
> +	ret = hideep_pgm_w_mem(ts, addr, &packet, sizeof(u32));
> +
> +	return ret;
> +}
> +
> +#define SW_RESET_IN_PGM(CLK) \
> +{ \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CNT, CLK); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x03); \
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x01); \
> +}
> +
> +#define SET_FLASH_PIO(CE) \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x01 | (CE << 1))
> +#define SET_PIO_SIG(X, Y) \
> +	hideep_pgm_w_reg(ts, FLASH_PIO_SIG + X, Y)
> +#define SET_FLASH_HWCONTROL() \
> +	hideep_pgm_w_reg(ts, FLASH_CON, 0x00)
> +
> +#define NVM_W_SFR(x, y) \
> +{ \
> +	SET_FLASH_PIO(1); \
> +	SET_PIO_SIG(x, y); \
> +	SET_FLASH_PIO(0); \
> +}
> +
> +static void hideep_pgm_set(struct hideep_ts *ts)
> +{
> +	hideep_pgm_w_reg(ts, SYSCON_WDT_CON, 0x00);
> +	hideep_pgm_w_reg(ts, SYSCON_SPC_CON, 0x00);
> +	hideep_pgm_w_reg(ts, SYSCON_CLK_ENA, 0xFF);
> +	hideep_pgm_w_reg(ts, SYSCON_CLK_CON, 0x01);
> +	hideep_pgm_w_reg(ts, SYSCON_PWR_CON, 0x01);
> +	hideep_pgm_w_reg(ts, FLASH_TIM, 0x03);
> +	hideep_pgm_w_reg(ts, FLASH_CACHE_CFG, 0x00);
> +}
> +
> +static int hideep_pgm_get_pattern(struct hideep_ts *ts)
> +{
> +	int ret;
> +	unsigned int status;
> +	const unsigned char pattern[] = { 0x39, 0xAF, 0x9D, 0xDF };
> +
> +	ret = i2c_master_send(ts->client, pattern, sizeof(pattern));
> +
> +	if (ret < 0) {
> +		dev_err(&ts->client->dev, "%d, %08X", __LINE__, ret);
> +		return ret;
> +	}
> +
> +	mdelay(1);
> +
> +	/* flush invalid Tx load register */
> +	ret = hideep_pgm_w_reg(ts, ESI_TX_INVALID, 0x01);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = hideep_pgm_r_reg(ts, SYSCON_PGM_ID, &status);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	dev_info(&ts->client->dev, "%s, %08X", __func__, status);
> +	return status;
> +}
> +
> +static int hideep_enter_pgm(struct hideep_ts *ts)
> +{
> +	int retry_count = 10;
> +	int val;
> +	unsigned int pgm_pattern = 0xDF9DAF39;
> +
> +	while (retry_count--) {
> +		val = hideep_pgm_get_pattern(ts);
> +
> +		if (pgm_pattern != get_unaligned_be32(&val)) {
> +			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
> +				get_unaligned_be32(&val));
> +		} else {
> +			dev_dbg(&ts->client->dev, "found magic code");
> +			break;
> +		}
> +	}
> +
> +
> +	if (retry_count < 0) {
> +		dev_err(&ts->client->dev, "couldn't enter pgm mode!!!");
> +		SW_RESET_IN_PGM(1000);
> +		return -EBADMSG;
> +	}
> +
> +	hideep_pgm_set(ts);
> +	mdelay(1);
> +
> +	return 0;
> +}
> +
> +static void hideep_nvm_unlock(struct hideep_ts *ts)
> +{
> +	unsigned int unmask_code = 0;
> +
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_RPAGE);
> +
> +	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
> +
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +
> +	/* make it unprotected code */
> +	unmask_code &= (~_PROT_MODE);
> +
> +	/* compare unmask code */
> +	if (unmask_code != ts->nvm_mask)
> +		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
> +			unmask_code);
> +
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_SFR_WPAGE);
> +	SET_FLASH_PIO(0);
> +
> +	NVM_W_SFR(NVM_MASK_OFS, ts->nvm_mask);
> +	SET_FLASH_HWCONTROL();
> +	hideep_pgm_w_reg(ts, FLASH_CFG, NVM_DEFAULT_PAGE);
> +}
> +
> +static int hideep_check_status(struct hideep_ts *ts)
> +{
> +	int ret, status;
> +	int time_out = 100;
> +
> +	while (time_out--) {
> +		mdelay(1);
> +		ret = hideep_pgm_r_reg(ts, FLASH_STA, &status);
> +
> +		if (ret < 0)
> +			continue;
> +
> +		if (status)
> +			return status;
> +	}
> +
> +	return time_out;
> +}
> +
> +static int hideep_program_page(struct hideep_ts *ts,
> +	unsigned int addr, struct pgm_packet *packet_w)
> +{
> +	int ret;
> +
> +
> +	ret = hideep_check_status(ts);
> +
> +	if (ret < 0)
> +		return -EBUSY;
> +
> +	addr = addr & ~(NVM_PAGE_SIZE - 1);
> +
> +	SET_FLASH_PIO(0);
> +	SET_FLASH_PIO(1);
> +
> +	/* erase page */
> +	SET_PIO_SIG((PERASE | addr), 0xFFFFFFFF);
> +
> +	SET_FLASH_PIO(0);
> +
> +	ret = hideep_check_status(ts);
> +
> +	if (ret < 0)
> +		return -EBUSY;
> +
> +	/* write page */
> +	SET_FLASH_PIO(1);
> +
> +	SET_PIO_SIG((WRONLY | addr), get_unaligned_be32(&packet_w->payload[0]));
> +
> +	hideep_pgm_w_mem(ts, (FLASH_PIO_SIG | WRONLY),
> +		packet_w, NVM_PAGE_SIZE);
> +
> +	SET_PIO_SIG(124, get_unaligned_be32(&packet_w->payload[31]));
> +
> +	SET_FLASH_PIO(0);
> +
> +	mdelay(1);
> +
> +	ret = hideep_check_status(ts);
> +
> +	if (ret < 0)
> +		return -EBUSY;
> +
> +	SET_FLASH_HWCONTROL();
> +
> +	return 0;
> +}
> +
> +static void hideep_program_nvm(struct hideep_ts *ts, const unsigned char *ucode,
> +	int len)
> +{
> +	struct pgm_packet packet_w;
> +	struct pgm_packet packet_r;
> +	int i;
> +	int ret;
> +	int addr = 0;
> +	int len_r = len;
> +	int len_w = NVM_PAGE_SIZE;
> +	unsigned int pages = DIV_ROUND_UP(len, NVM_PAGE_SIZE);
> +
> +
> +	hideep_nvm_unlock(ts);
> +
> +	dev_dbg(&ts->client->dev, "pages : %d", pages);
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r < NVM_PAGE_SIZE)
> +			len_w = len_r;
> +
> +		/* compare */
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +		ret = memcmp(&ucode[addr], packet_r.payload, len_w);
> +
> +		if (ret) {
> +			/* write page */
> +			memcpy(packet_w.payload, &ucode[addr], len_w);
> +			ret = hideep_program_page(ts, addr, &packet_w);
> +			if (ret)
> +				dev_err(&ts->client->dev,
> +					"%s : error(%08x):",
> +					__func__, addr);
> +			mdelay(1);
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		if (len_r < 0)
> +			break;
> +	}
> +}
> +
> +static int hideep_verify_nvm(struct hideep_ts *ts, const unsigned char *ucode,
> +	int len)
> +{
> +	struct pgm_packet packet_r;
> +	int i, j;
> +	int ret;
> +	int addr = 0;
> +	int len_r = len;
> +	int len_v = NVM_PAGE_SIZE;
> +	unsigned int pages = DIV_ROUND_UP(len, NVM_PAGE_SIZE);
> +
> +	for (i = 0; i < pages; i++) {
> +		if (len_r < NVM_PAGE_SIZE)
> +			len_v = len_r;
> +
> +		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +			NVM_PAGE_SIZE);
> +
> +		ret = memcmp(&ucode[addr], packet_r.payload, len_v);
> +
> +		if (ret) {
> +			u8 *read = (u8 *)packet_r.payload;
> +
> +			for (j = 0; j < NVM_PAGE_SIZE; j++) {
> +				if (ucode[addr + j] != read[j])
> +					dev_err(&ts->client->dev,
> +						"verify : error([%d] %02x : %02x)",
> +						addr + j, ucode[addr + j],
> +						read[j]);
> +			}
> +			return ret;
> +		}
> +
> +		addr += NVM_PAGE_SIZE;
> +		len_r -= NVM_PAGE_SIZE;
> +		if (len_r < 0)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
> +{
> +	int ret;
> +	int retry, retry_cnt = 3;
> +	const struct firmware *fw_entry;
> +
> +	dev_dbg(&ts->client->dev, "enter");
> +	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
> +
> +	if (ret != 0) {
> +		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
> +		return ret;
> +	}
> +
> +	if (fw_entry->size > ts->fw_size) {
> +		dev_err(&ts->client->dev,
> +			"file size(%zu) is big more than fw memory size(%d)",
> +			fw_entry->size, ts->fw_size);
> +		release_firmware(fw_entry);
> +		return -EFBIG;
> +	}
> +
> +	/* chip specific code for flash fuse */
> +	mutex_lock(&ts->dev_mutex);
> +
> +	ts->dev_state = state_updating;
> +
> +	/* enter program mode */
> +	ret = hideep_enter_pgm(ts);
> +
> +	if (ret)
> +		return ret;
> +
> +	/* comparing & programming each page, if the memory of specified
> +	 * page is exactly same, no need to update.
> +	 */
> +	for (retry = 0; retry < retry_cnt; retry++) {
> +		hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
> +
> +		ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
> +		if (!ret)
> +			break;
> +	}
> +
> +	if (retry < retry_cnt)
> +		dev_dbg(&ts->client->dev, "update success!!!");
> +	else
> +		dev_err(&ts->client->dev, "update failed!!!");
> +
> +	SW_RESET_IN_PGM(1000);
> +
> +	ts->dev_state = state_normal;
> +
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	release_firmware(fw_entry);
> +
> +	return ret;
> +}
> +
> +static int hideep_load_dwz(struct hideep_ts *ts)
> +{
> +	int ret = 0;
> +	struct pgm_packet packet_r;
> +
> +	ret = hideep_enter_pgm(ts);
> +
> +	if (ret)
> +		return ret;
> +
> +	mdelay(50);
> +
> +	hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, &packet_r,
> +		sizeof(struct dwz_info));
> +
> +	memcpy(&ts->dwz_info, packet_r.payload,
> +		sizeof(struct dwz_info));
> +
> +	SW_RESET_IN_PGM(10);
> +
> +	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x60) {
> +		/* Lime fw size */
> +		dev_dbg(&ts->client->dev, "used lime IC");
> +		ts->fw_size = 1024 * 64;
> +		ts->nvm_mask = 0x0030027B;
> +	} else if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
> +		/* Crimson IC */
> +		dev_dbg(&ts->client->dev, "used crimson IC");
> +		ts->fw_size = 1024 * 48;
> +		ts->nvm_mask = 0x00310000;
> +	}
> +
> +	dev_dbg(&ts->client->dev, "firmware release version : %04x",
> +		get_unaligned_le16(&ts->dwz_info.release_ver));
> +
> +	mdelay(50);
> +
> +	return 0;
> +}
> +
> +static int hideep_i2c_read(struct hideep_ts *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret;
> +	struct i2c_msg msg[2];
> +
> +	mutex_lock(&ts->i2c_mutex);

I still have the same question as in my other email: why do we need to
protect individual I2C transfers?

> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	msg[0].addr = ts->client->addr;
> +	msg[0].flags = 0;
> +	msg[0].len = 2;
> +	msg[0].buf = (u8 *)&addr;
> +
> +	msg[1].addr = ts->client->addr;
> +	msg[1].flags = I2C_M_RD;
> +	msg[1].len = len;
> +	msg[1].buf = buf;
> +
> +	ret = i2c_transfer(ts->client->adapter, msg, 2);
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +	return ret;
> +}
> +
> +static int hideep_i2c_write(struct hideep_ts *ts, unsigned short addr,
> +	unsigned short len, unsigned char *buf)
> +{
> +	int ret;
> +	unsigned char *wbuf;
> +	struct i2c_msg msg;
> +
> +	dev_dbg(&ts->client->dev, "addr = 0x%02x, len = %d", addr, len);
> +
> +	wbuf = kmalloc(len + 2, GFP_KERNEL);

Error handling.

> +
> +	mutex_lock(&ts->i2c_mutex);
> +
> +	put_unaligned_le16(addr, &wbuf[0]);
> +	memcpy(&wbuf[2], buf, len);
> +
> +	msg.addr = ts->client->addr;
> +	msg.flags = 0;
> +	msg.len = len + 2;
> +	msg.buf = wbuf;
> +
> +	ret = i2c_transfer(ts->client->adapter, &msg, 1);
> +
> +	mutex_unlock(&ts->i2c_mutex);
> +
> +	kfree(wbuf);
> +
> +	return  ret;
> +}
> +
> +static void hideep_reset_ic(struct hideep_ts *ts)
> +{
> +	unsigned char cmd = 0x01;
> +
> +	if (!ts->reset_gpio) {
> +		dev_dbg(&ts->client->dev, "hideep:enable the reset_gpio");
> +		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_LOW);

I believe you mean GPIOD_OUT_HIGH here: you want to activate reset line.
Remember, here you are dealing with logical state, gpiod API will
convert to the right value based on declared GPIO polarity (active low
or high).

> +		mdelay(20);
> +		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_HIGH);
> +	} else {
> +		hideep_i2c_write(ts, HIDEEP_RESET_CMD, 1, &cmd);
> +	}
> +	mdelay(50);

I would prefer if you integrated the reset into power on/power off
sequence and also shut off the power in suspend (if you have GPIO/reset
line and the device is not a wakeup source).

> +}
> +
> +static int hideep_pwr_on(struct hideep_ts *ts)
> +{
> +	int ret = 0;
> +
> +	if (!ts->vcc_vdd) {
> +		dev_dbg(&ts->client->dev, "hideep:vcc_vdd is enable");
> +		ret = regulator_enable(ts->vcc_vdd);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vdd enable failed ret=%d", ret);
> +		usleep_range(999, 1000);
> +	}
> +
> +
> +	if (!ts->vcc_vid) {
> +		dev_dbg(&ts->client->dev, "hideep:vcc_vid is enable");
> +		ret = regulator_enable(ts->vcc_vid);
> +		if (ret)
> +			dev_err(&ts->client->dev,
> +				"Regulator vcc_vid enable failed ret=%d", ret);
> +		usleep_range(2999, 3000);
> +	}
> +
> +	return ret;
> +}
> +
> +static void hideep_pwr_off(void *data)
> +{
> +	struct hideep_ts *ts = data;
> +
> +	if (!ts->reset_gpio)
> +		gpiod_set_value(ts->reset_gpio, GPIOD_OUT_LOW);
> +
> +	if (!ts->vcc_vid)
> +		regulator_disable(ts->vcc_vid);
> +
> +	if (!ts->vcc_vdd)
> +		regulator_disable(ts->vcc_vdd);
> +}
> +
> +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
> +
> +static void push_mt(struct hideep_ts *ts)
> +{
> +	int id;
> +	int i;
> +	bool btn_up = 0;
> +	int evt = 0;
> +	int offset = sizeof(struct hideep_event);
> +	struct hideep_event *event;
> +
> +	/* load multi-touch event to input system */
> +	for (i = 0; i < ts->tch_count; i++) {
> +		event = (struct hideep_event *)&ts->touch_event[i * offset];
> +		id = (event->index >> 0) & 0x0F;
> +		btn_up = (event->flag >> HIDEEP_MT_RELEASED) & 0x01;

There is no need to convert. You just say

	int release;

	...

		release = event->flag & HIDEEP_MT_RELEASED_BIT;

> +
> +		dev_dbg(&ts->client->dev,
> +			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
> +			event->type, event->index, i,
> +			get_unaligned_le16(&event->x),
> +			get_unaligned_le16(&event->y),
> +			get_unaligned_le16(&event->z));
> +
> +		input_mt_slot(ts->input_dev, id);
> +		input_mt_report_slot_state(ts->input_dev,
> +			__GET_MT_TOOL_TYPE(event->type),
> +			(btn_up == 0));
> +
> +		if (btn_up == 0) {
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +				get_unaligned_le16(&event->x));
> +			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +				get_unaligned_le16(&event->y));
> +			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +				get_unaligned_le16(&event->z));
> +			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +				event->w);
> +			evt++;
> +		}
> +	}
> +
> +	input_mt_sync_frame(ts->input_dev);
> +}
> +
> +static void push_ky(struct hideep_ts *ts)
> +{
> +	int i;
> +	int status;
> +	int code;
> +
> +	for (i = 0; i < ts->key_count; i++) {
> +		code = ts->key_event[i + i * 2] & 0x0F;
> +		status = ts->key_event[i + i * 2] & 0xF0;

This does not seem right. Are you sure you want index [i*3] and not
[i*2 + 1]?

> +
> +		input_report_key(ts->input_dev, ts->key_codes[code],
> +			status & HIDEEP_KEY_PRESSED_MASK);
> +	}
> +}
> +
> +static void hideep_put_event(struct hideep_ts *ts)
> +{
> +	/* mangling touch information */
> +	if (ts->tch_count > 0)
> +		push_mt(ts);
> +
> +	if (ts->key_count > 0)
> +		push_ky(ts);
> +
> +	input_sync(ts->input_dev);
> +}
> +
> +static int hideep_get_event(struct hideep_ts *ts)
> +{
> +	int ret;
> +	int touch_count;
> +	int event_size;
> +
> +	/* get touch event count */
> +	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
> +		ts->tch_count, ts->key_count, ts->lpm_count);
> +
> +	/* get touch event information */
> +	if (ts->tch_count > HIDEEP_MT_MAX)
> +		ts->tch_count = 0;
> +
> +	if (ts->key_count > HIDEEP_KEY_MAX)
> +		ts->key_count = 0;
> +
> +	touch_count = ts->tch_count + ts->key_count;
> +
> +	if (ts->tch_count > 0) {
> +		event_size = ts->tch_count *
> +			sizeof(struct hideep_event);
> +		ret = hideep_i2c_read(ts, HIDEEP_TOUCH_DATA_ADDR,
> +			event_size, ts->touch_event);
> +
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return ret;
> +		}
> +	}
> +
> +	if (ts->key_count > 0) {
> +		event_size = ts->key_count * 2;
> +		ret = hideep_i2c_read(ts, HIDEEP_KEY_DATA_ADDR,
> +			event_size, ts->key_event);
> +		if (ret < 0) {
> +			dev_err(&ts->client->dev, "read I2C error.");
> +			return ret;
> +		}
> +	}
> +
> +	return touch_count;
> +}
> +
> +static irqreturn_t hideep_irq_task(int irq, void *handle)
> +{
> +	unsigned char buff[2];
> +	int ret;
> +
> +	struct hideep_ts *ts = handle;
> +
> +	dev_dbg(&ts->client->dev, "state = 0x%x", ts->dev_state);
> +
> +	if (ts->dev_state == state_normal) {
> +		ret = hideep_i2c_read(ts, HIDEEP_EVENT_COUNT_ADDR,
> +			2, buff);
> +
> +		if (ret < 0) {
> +			disable_irq(ts->client->irq);

You can not disable IRQ from IRQ handler. Also, why do you want to do
that.

> +			return IRQ_HANDLED;
> +		}
> +
> +		ts->tch_count = buff[0];
> +		ts->key_count = buff[1] & 0x0f;
> +		ts->lpm_count = buff[1] & 0xf0;
> +
> +		ret = hideep_get_event(ts);

The way your code is laid out you usually have 2 I2C transactions per
interrupt. Can you combine reading from HIDEEP_EVENT_COUNT_ADDR and
continue to reading the touch and key data in one transaction (the
addresses are consecutive so I think it should work), and then figure
out the parts of the buffer that contain valid data? 

> +
> +		if (ret >= 0)
> +			hideep_put_event(ts);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int hideep_capability(struct hideep_ts *ts)
> +{
> +	int ret, i;
> +
> +	ts->input_dev->name = HIDEEP_TS_NAME;
> +	ts->input_dev->id.bustype = BUS_I2C;
> +
> +	if (ts->key_num) {
> +		ts->input_dev->keycode = ts->key_codes;
> +		ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
> +		ts->input_dev->keycodemax = ts->key_num;
> +		for (i = 0; i < ts->key_num; i++)
> +			input_set_capability(ts->input_dev, EV_KEY,
> +				ts->key_codes[i]);
> +	}
> +
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_PRESSURE, 0, 65535, 0, 0);
> +	input_set_abs_params(ts->input_dev,
> +		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +
> +	ret = input_mt_init_slots(ts->input_dev,
> +		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
> +
> +	return ret;
> +}
> +
> +static void hideep_get_info(struct hideep_ts *ts)
> +{
> +	unsigned char val[4];
> +
> +	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
> +		hideep_i2c_read(ts, 0x28, 4, val);

Error handling?

> +
> +		ts->prop.max_x = get_unaligned_le16(&val[2]);
> +		ts->prop.max_y = get_unaligned_le16(&val[0]);
> +
> +		dev_info(&ts->client->dev, "X : %d, Y : %d",
> +			ts->prop.max_x, ts->prop.max_y);

dev_dbg.

> +	}
> +}
> +
> +static ssize_t hideep_update_fw(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t count)
> +{
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +	int mode, ret;
> +	char *fw_name;
> +
> +	ret = kstrtoint(buf, 8, &mode);
> +	if (ret)
> +		return ret;
> +
> +	disable_irq(ts->client->irq);
> +
> +	ts->dev_state = state_updating;
> +	fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
> +		get_unaligned_le16(&ts->dwz_info.product_id));
> +	ret = hideep_update_firmware(ts, fw_name);
> +
> +	if (ret != 0)
> +		dev_err(dev, "The firmware update failed(%d)", ret);
> +
> +	kfree(fw_name);
> +
> +	ret = hideep_load_dwz(ts);
> +
> +	if (ret < 0)
> +		dev_err(&ts->client->dev, "fail to load dwz, ret = 0x%x", ret);
> +
> +	enable_irq(ts->client->irq);
> +
> +	ts->dev_state = state_normal;
> +
> +	return count;
> +}
> +
> +static ssize_t hideep_fw_version_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "release version : %04x",
> +		get_unaligned_le16(&ts->dwz_info.release_ver));

Why do you log the value here? You will get it in the buffer.

> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static ssize_t hideep_product_id_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	int len = 0;
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "product id : %04x",
> +		get_unaligned_le16(&ts->dwz_info.product_id));

Same here, why the log?

> +
> +	mutex_lock(&ts->dev_mutex);
> +	len = scnprintf(buf, PAGE_SIZE,
> +		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
> +	mutex_unlock(&ts->dev_mutex);
> +
> +	return len;
> +}
> +
> +static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
> +static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
> +static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
> +
> +static struct attribute *hideep_ts_sysfs_entries[] = {
> +	&dev_attr_version.attr,
> +	&dev_attr_product_id.attr,
> +	&dev_attr_update_fw.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group hideep_ts_attr_group = {
> +	.attrs = hideep_ts_sysfs_entries,
> +};
> +
> +static int __maybe_unused hideep_resume(struct device *dev)
> +{
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x00;
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	if (ts->dev_state != state_normal)

We know that the state should be sleep, why do we need to check it?

> +		ts->dev_state = state_normal;
> +
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	enable_irq(ts->client->irq);
> +
> +	mdelay(10);
> +	hideep_reset_ic(ts);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +	return 0;
> +}
> +
> +static int __maybe_unused hideep_suspend(struct device *dev)
> +{
> +	struct hideep_ts *ts = dev_get_drvdata(dev);
> +	unsigned char sleep_cmd = 0x01;
> +
> +	mutex_lock(&ts->dev_mutex);
> +
> +	if (ts->dev_state != state_sleep)
> +		ts->dev_state = state_sleep;
> +
> +	/* default deep sleep */
> +	hideep_i2c_write(ts, HIDEEP_SLEEP_CMD, 1, &sleep_cmd);
> +	disable_irq(ts->client->irq);
> +
> +	mutex_unlock(&ts->dev_mutex);
> +	return 0;
> +}
> +
> +static int  hideep_parse_dts(struct device *dev, struct hideep_ts *ts)
> +{
> +	int ret;
> +
> +	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> +							GPIOD_OUT_HIGH);
> +	if (IS_ERR(ts->reset_gpio))
> +		return PTR_ERR(ts->reset_gpio);
> +
> +	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
> +	if (IS_ERR(ts->vcc_vdd))
> +		return PTR_ERR(ts->vcc_vdd);
> +
> +	ts->vcc_vid = devm_regulator_get(dev, "vid");
> +	if (IS_ERR(ts->vcc_vid))
> +		return PTR_ERR(ts->vcc_vid);
> +
> +	ts->key_num = device_property_read_u32_array(dev, "linux,keycodes",
> +						NULL, 0);
> +
> +	if (ts->key_num > HIDEEP_KEY_MAX) {
> +		dev_err(dev, "too many support key defined(%d)!!!",
> +			ts->key_num);
> +		return -EINVAL;
> +	}
> +
> +	ret = device_property_read_u32_array(dev, "linux,keycodes",
> +				ts->key_codes, ts->key_num);
> +	if (ret) {
> +		dev_dbg(dev, "don't support touch key");
> +		ts->key_num = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hideep_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
> +{
> +	int ret = 0;
> +	struct hideep_ts *ts;
> +
> +	/* check i2c bus */
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_I2C)) {
> +		dev_err(&client->dev, "check i2c device error");
> +		return -ENODEV;
> +	}
> +
> +	/* init hideep_ts */
> +	ts = devm_kzalloc(&client->dev,
> +		sizeof(*ts), GFP_KERNEL);
> +	if (!ts)
> +		return -ENOMEM;
> +
> +	ret = hideep_parse_dts(&client->dev, ts);
> +
> +	if (ret)
> +		return ret;
> +
> +	ts->client = client;
> +
> +	i2c_set_clientdata(client, ts);
> +
> +	mutex_init(&ts->i2c_mutex);
> +	mutex_init(&ts->dev_mutex);
> +
> +	/* power on */
> +	ret = hideep_pwr_on(ts);
> +	if (ret) {
> +		dev_err(&ts->client->dev, "power on failed");
> +		return ret;
> +	}
> +
> +	ret = devm_add_action(&ts->client->dev, hideep_pwr_off, ts);

devm_add_action_or_reset()

> +	if (ret) {
> +		hideep_pwr_off(ts);
> +		return ret;
> +	}
> +
> +	ts->dev_state = state_init;
> +	mdelay(30);
> +
> +	/* ic reset */
> +	hideep_reset_ic(ts);
> +
> +	/* read info */
> +	ret = hideep_load_dwz(ts);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
> +		return ret;
> +	}
> +
> +	/* init input device */
> +	ts->input_dev = devm_input_allocate_device(&client->dev);
> +	if (!ts->input_dev) {
> +		dev_err(&client->dev, "can't allocate memory for input_dev");
> +		return -ENOMEM;
> +	}
> +
> +	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
> +	hideep_get_info(ts);
> +
> +	ret = hideep_capability(ts);
> +	if (ret) {
> +		dev_err(&client->dev, "can't init input properties");
> +		return ret;
> +	}
> +
> +	ret = input_register_device(ts->input_dev);
> +	if (ret) {
> +		dev_err(&client->dev, "can't register input_dev");
> +		return ret;
> +	}
> +
> +	input_set_drvdata(ts->input_dev, ts);
> +
> +	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
> +	if (IS_ERR(&ts->client->irq)) {

client->irq is never PTR_ERR encoded. You need to do

	if (client->irq <= 0)
		...

> +		dev_err(&client->dev, "can't be assigned irq");
> +		return -ENOMEM;

Why ENOMEM? I'd say -EINVAL.

> +	}
> +
> +	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
> +		NULL, hideep_irq_task, IRQF_ONESHOT,
> +		ts->client->name, ts);
> +
> +	disable_irq(ts->client->irq);
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
> +			ret);
> +		return ret;
> +	}
> +
> +	ts->dev_state = state_normal;
> +	enable_irq(ts->client->irq);

Why do we need this disable/enable IRQ?

> +
> +	ret = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
> +
> +	if (ret) {
> +		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
> +
> +static const struct i2c_device_id hideep_dev_idtable[] = {
> +	{ HIDEEP_I2C_NAME, 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id hideep_acpi_id[] = {
> +	{ "HIDP0001", 0 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id hideep_match_table[] = {
> +	{ .compatible = "hideep,hideep-ts" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, hideep_match_table);
> +#endif
> +
> +static struct i2c_driver hideep_driver = {
> +	.probe = hideep_probe,
> +	.id_table = hideep_dev_idtable,
> +	.driver = {
> +		.name = HIDEEP_I2C_NAME,
> +		.of_match_table = of_match_ptr(hideep_match_table),
> +		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
> +		.pm = &hideep_pm_ops,
> +	},
> +};
> +
> +module_i2c_driver(hideep_driver);
> +
> +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
> +MODULE_AUTHOR("anthony.kim@hideep.com");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.7.4
> 

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-09-14  4:36             ` Anthony Kim
  2017-09-14 18:51               ` Dmitry Torokhov
@ 2017-09-18 19:03               ` Rob Herring
  2017-09-26  1:42               ` Anthony Kim
  2017-09-26  3:28               ` Anthony Kim
  3 siblings, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-09-18 19:03 UTC (permalink / raw)
  To: Anthony Kim
  Cc: dmitry.torokhov, lkp, kbuild-all, linux-input, devicetree, linux-kernel

On Thu, Sep 14, 2017 at 01:36:44PM +0900, Anthony Kim wrote:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
> 
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
> 
> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |   42 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +

Please add acks when posting new versions.

>  drivers/input/touchscreen/Kconfig                  |   11 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  drivers/input/touchscreen/hideep.c                 | 1274 ++++++++++++++++++++
>  5 files changed, 1329 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.c

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
  2017-09-14  4:36             ` Anthony Kim
  2017-09-14 18:51               ` Dmitry Torokhov
  2017-09-18 19:03               ` Rob Herring
@ 2017-09-26  1:42               ` Anthony Kim
  2017-09-26  3:28               ` Anthony Kim
  3 siblings, 0 replies; 29+ messages in thread
From: Anthony Kim @ 2017-09-26  1:42 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt
  Cc: linux-input, devictree, linux-kernel, ktyrun, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |   42 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +

Acked-by: Rob Herring <robh+dt@kernel.org>

 drivers/input/touchscreen/Kconfig                  |   11 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/hideep.c                 | 1176 ++++++++++++++++++++
 5 files changed, 1231 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..121d9b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,42 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-ts"
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- touchscreen-size-x	: X axis size of touchscreen
+- touchscreen-size-y	: Y axis size of touchscreen
+- linux,keycodes	: Specifies an array of numeric keycode values to
+			be used for reporting button presses. The array can
+			contain up to 3 entries.
+
+Example:
+
+#include "dt-bindings/input/input.h"
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-ts";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		touchscreen-size-x = <1080>;
+		touchscreen-size-y = <1920>;
+		linux,keycodes = <KEY_HOME>, <KEY_MENU>, <KEY_BACK>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index daf465be..9913a03 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -134,6 +134,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..d0c8dafc 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -344,6 +344,17 @@ config TOUCHSCREEN_GOODIX
 	  To compile this driver as a module, choose M here: the
 	  module will be called goodix.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 config TOUCHSCREEN_ILI210X
 	tristate "Ilitek ILI210X based touchscreen"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..873b67e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)	+= egalax_ts_serial.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP)	+= hideep.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c
new file mode 100644
index 0000000..9866f4d
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.c
@@ -0,0 +1,1176 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#define HIDEEP_TS_NAME				"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME				"hideep_ts"
+
+#define HIDEEP_MT_MAX				10
+#define HIDEEP_KEY_MAX				3
+/* count(2) + touch data(100) + key data(6) */
+#define HIDEEP_MAX_EVENT			108
+#define HIDEEP_TOUCH_EVENT_INDEX		2
+#define HIDEEP_KEY_EVENT_INDEX			102
+
+/* Touch & key event */
+#define HIDEEP_EVENT_ADDR			0x240
+
+/* command list */
+#define HIDEEP_RESET_CMD			0x9800
+
+/* event bit */
+#define HIDEEP_MT_RELEASED			BIT(4)
+#define HIDEEP_KEY_PRESSED			BIT(7)
+#define HIDEEP_KEY_FIRST_PRESSED		BIT(8)
+#define HIDEEP_KEY_PRESSED_MASK \
+	(HIDEEP_KEY_PRESSED | HIDEEP_KEY_FIRST_PRESSED)
+
+/* For NVM */
+#define HIDEEP_YRAM_BASE			0x40000000
+#define HIDEEP_PERIPHERAL_BASE			0x50000000
+#define HIDEEP_ESI_BASE \
+	(HIDEEP_PERIPHERAL_BASE + 0x00000000)
+#define HIDEEP_FLASH_BASE \
+	(HIDEEP_PERIPHERAL_BASE + 0x01000000)
+#define HIDEEP_SYSCON_BASE \
+	(HIDEEP_PERIPHERAL_BASE + 0x02000000)
+
+#define HIDEEP_SYSCON_MOD_CON			(HIDEEP_SYSCON_BASE + 0x0000)
+#define HIDEEP_SYSCON_SPC_CON			(HIDEEP_SYSCON_BASE + 0x0004)
+#define HIDEEP_SYSCON_CLK_CON			(HIDEEP_SYSCON_BASE + 0x0008)
+#define HIDEEP_SYSCON_CLK_ENA			(HIDEEP_SYSCON_BASE + 0x000C)
+#define HIDEEP_SYSCON_RST_CON			(HIDEEP_SYSCON_BASE + 0x0010)
+#define HIDEEP_SYSCON_WDT_CON			(HIDEEP_SYSCON_BASE + 0x0014)
+#define HIDEEP_SYSCON_WDT_CNT			(HIDEEP_SYSCON_BASE + 0x0018)
+#define HIDEEP_SYSCON_PWR_CON			(HIDEEP_SYSCON_BASE + 0x0020)
+#define HIDEEP_SYSCON_PGM_ID			(HIDEEP_SYSCON_BASE + 0x00F4)
+
+#define HIDEEP_FLASH_CON			(HIDEEP_FLASH_BASE + 0x0000)
+#define HIDEEP_FLASH_STA			(HIDEEP_FLASH_BASE + 0x0004)
+#define HIDEEP_FLASH_CFG			(HIDEEP_FLASH_BASE + 0x0008)
+#define HIDEEP_FLASH_TIM			(HIDEEP_FLASH_BASE + 0x000C)
+#define HIDEEP_FLASH_CACHE_CFG			(HIDEEP_FLASH_BASE + 0x0010)
+#define HIDEEP_FLASH_PIO_SIG			(HIDEEP_FLASH_BASE + 0x400000)
+
+#define HIDEEP_ESI_TX_INVALID			(HIDEEP_ESI_BASE + 0x0008)
+
+#define HIDEEP_PERASE				0x00040000
+#define HIDEEP_WRONLY				0x00100000
+
+#define HIDEEP_NVM_MASK_OFS			0x0000000C
+#define HIDEEP_NVM_DEFAULT_PAGE			0
+#define HIDEEP_NVM_SFR_WPAGE			1
+#define HIDEEP_NVM_SFR_RPAGE			2
+
+#define HIDEEP_PIO_SIG				0x00400000
+#define HIDEEP_PROT_MODE			0x03400000
+
+#define HIDEEP_NVM_PAGE_SIZE			128
+
+#define HIDEEP_DWZ_INFO				0x000002C0
+
+struct hideep_event {
+	__le16 x;
+	__le16 y;
+	__le16 z;
+	u8 w;
+	u8 flag;
+	u8 type;
+	u8 index;
+} __packed;
+
+struct dwz_info {
+	__le32	code_start;
+	u8 code_crc[12];
+
+	__le32 c_code_start;
+	__le16 c_code_len;
+	__le16 gen_ver;
+
+	__le32 vr_start;
+	__le16 vr_len;
+	__le16 rsv0;
+
+	__le32 ft_start;
+	__le16 ft_len;
+	__le16 vr_version;
+
+	__le16 boot_ver;
+	__le16 core_ver;
+	__le16 custom_ver;
+	__le16 release_ver;
+
+	u8 factory_id;
+	u8 panel_type;
+	u8 model_name[6];
+	__le16 product_code;
+	__le16 extra_option;
+
+	__le16 product_id;
+	__le16 vendor_id;
+} __packed;
+
+struct hideep_ts {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct regmap *reg;
+
+	struct touchscreen_properties prop;
+
+	struct gpio_desc *reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct mutex dev_mutex;
+
+	u32 tch_count;
+	u32 key_count;
+	u32 lpm_count;
+
+	u8 touch_event[HIDEEP_MT_MAX * 10];
+	u8 key_event[HIDEEP_KEY_MAX * 2];
+
+	int key_num;
+	int key_codes[HIDEEP_KEY_MAX];
+
+	struct dwz_info dwz_info;
+
+	int fw_size;
+	int nvm_mask;
+};
+
+struct pgm_packet {
+	union {
+		u8 b[8];
+		u32 w[2];
+	} header;
+
+	u32 payload[HIDEEP_NVM_PAGE_SIZE / sizeof(u32)];
+};
+
+static int hideep_pgm_w_mem(struct hideep_ts *ts, u32 addr,
+	struct pgm_packet *packet, u32 len)
+{
+	int ret;
+	int i;
+	struct i2c_msg msg;
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	put_unaligned_be32((0x80 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
+
+	msg.addr = ts->client->addr;
+	msg.flags = 0;
+	msg.len = len + 5;
+	msg.buf = &packet->header.b[3];
+	
+	ret = i2c_transfer(ts->client->adapter, &msg, 1);
+
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_ts *ts, u32 addr,
+	struct pgm_packet *packet, u32 len)
+{
+	int ret;
+	int i;
+	u8 *buff;
+	struct i2c_msg msg[2];
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	buff = kmalloc(len, GFP_KERNEL);
+
+	if (!buff)
+		return -ENOMEM;
+
+	put_unaligned_be32((0x00 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = &packet->header.b[3];
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buff;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		packet->payload[i] = get_unaligned_be32(&buff[i * sizeof(u32)]);
+
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_ts *ts, u32 addr,
+	u32 *val)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x00, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, sizeof(u32));
+
+	if (ret < 0)
+		return ret;
+
+	*val = packet.payload[0];
+
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_ts *ts, u32 addr,
+	u32 data)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x80, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, sizeof(u32));
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x01 | (CE << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_PIO_SIG + X, Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x00)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void hideep_pgm_set(struct hideep_ts *ts)
+{
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x00);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_SPC_CON, 0x00);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_ENA, 0xFF);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_CON, 0x01);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_PWR_CON, 0x01);
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_TIM, 0x03);
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CACHE_CFG, 0x00);
+}
+
+static int hideep_pgm_get_pattern(struct hideep_ts *ts)
+{
+	int ret;
+	u32 status;
+	u16 p1 = 0xAF39;
+	u16 p2 = 0xDF9D;
+
+	ret = regmap_bulk_write(ts->reg, p1, (void *)&p2, 1);
+
+	if (ret < 0) {
+		dev_err(&ts->client->dev, "%d, %08X", __LINE__, ret);
+		return ret;
+	}
+
+	mdelay(1);
+
+	/* flush invalid Tx load register */
+	ret = hideep_pgm_w_reg(ts, HIDEEP_ESI_TX_INVALID, 0x01);
+
+	if (ret < 0)
+		return ret;
+
+	ret = hideep_pgm_r_reg(ts, HIDEEP_SYSCON_PGM_ID, &status);
+
+	if (ret < 0)
+		return ret;
+
+	return status;
+}
+
+static int hideep_enter_pgm(struct hideep_ts *ts)
+{
+	int retry_count = 10;
+	int val;
+	u32 pgm_pattern = 0xDF9DAF39;
+
+	while (retry_count--) {
+		val = hideep_pgm_get_pattern(ts);
+
+		if (pgm_pattern != get_unaligned_be32(&val)) {
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				get_unaligned_be32(&val));
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry_count < 0) {
+		dev_err(&ts->client->dev, "couldn't enter pgm mode!!!");
+		SW_RESET_IN_PGM(1000);
+		return -EBADMSG;
+	}
+
+	hideep_pgm_set(ts);
+	mdelay(1);
+
+	return 0;
+}
+
+static void hideep_nvm_unlock(struct hideep_ts *ts)
+{
+	u32 unmask_code = 0;
+
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_SFR_RPAGE);
+
+	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~HIDEEP_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != ts->nvm_mask)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(HIDEEP_NVM_MASK_OFS, ts->nvm_mask);
+	SET_FLASH_HWCONTROL();
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_DEFAULT_PAGE);
+}
+
+static int hideep_check_status(struct hideep_ts *ts)
+{
+	int ret, status;
+	int time_out = 100;
+
+	while (time_out--) {
+		mdelay(1);
+		ret = hideep_pgm_r_reg(ts, HIDEEP_FLASH_STA,
+			&status);
+
+		if (ret < 0)
+			continue;
+
+		if (status)
+			return status;
+	}
+
+	return time_out;
+}
+
+static int hideep_program_page(struct hideep_ts *ts,
+	u32 addr, struct pgm_packet *packet_w)
+{
+	int ret;
+
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	addr = addr & ~(HIDEEP_NVM_PAGE_SIZE - 1);
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* erase page */
+	SET_PIO_SIG((HIDEEP_PERASE | addr), 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	/* write page */
+	SET_FLASH_PIO(1);
+
+	SET_PIO_SIG((HIDEEP_WRONLY | addr),
+		get_unaligned_be32(&packet_w->payload[0]));
+
+	hideep_pgm_w_mem(ts, (HIDEEP_FLASH_PIO_SIG | HIDEEP_WRONLY),
+		packet_w, HIDEEP_NVM_PAGE_SIZE);
+
+	SET_PIO_SIG(124, get_unaligned_be32(&packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	SET_FLASH_HWCONTROL();
+
+	return 0;
+}
+
+static void hideep_program_nvm(struct hideep_ts *ts, const u8 *ucode,
+	int len)
+{
+	struct pgm_packet packet_w;
+	struct pgm_packet packet_r;
+	int i;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_w = HIDEEP_NVM_PAGE_SIZE;
+	u32 pages = DIV_ROUND_UP(len, HIDEEP_NVM_PAGE_SIZE);
+
+
+	hideep_nvm_unlock(ts);
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < HIDEEP_NVM_PAGE_SIZE)
+			len_w = len_r;
+
+		/* compare */
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			HIDEEP_NVM_PAGE_SIZE);
+		ret = memcmp(&ucode[addr], packet_r.payload, len_w);
+
+		if (ret) {
+			/* write page */
+			memcpy(packet_w.payload, &ucode[addr], len_w);
+			ret = hideep_program_page(ts, addr, &packet_w);
+			if (ret)
+				dev_err(&ts->client->dev,
+					"%s : error(%08x):",
+					__func__, addr);
+			mdelay(1);
+		}
+
+		addr += HIDEEP_NVM_PAGE_SIZE;
+		len_r -= HIDEEP_NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+}
+
+static int hideep_verify_nvm(struct hideep_ts *ts, const u8 *ucode,
+	int len)
+{
+	struct pgm_packet packet_r;
+	int i, j;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_v = HIDEEP_NVM_PAGE_SIZE;
+	u32 pages = DIV_ROUND_UP(len, HIDEEP_NVM_PAGE_SIZE);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < HIDEEP_NVM_PAGE_SIZE)
+			len_v = len_r;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			HIDEEP_NVM_PAGE_SIZE);
+
+		ret = memcmp(&ucode[addr], packet_r.payload, len_v);
+
+		if (ret) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < HIDEEP_NVM_PAGE_SIZE; j++) {
+				if (ucode[addr + j] != read[j])
+					dev_err(&ts->client->dev,
+						"verify : error([%d] %02x : %02x)",
+						addr + j, ucode[addr + j],
+						read[j]);
+			}
+			return ret;
+		}
+
+		addr += HIDEEP_NVM_PAGE_SIZE;
+		len_r -= HIDEEP_NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+
+	return 0;
+}
+
+static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
+{
+	int ret;
+	int retry, retry_cnt = 3;
+	const struct firmware *fw_entry;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	if (fw_entry->size > ts->fw_size) {
+		dev_err(&ts->client->dev,
+			"file size(%zu) is big more than fw memory size(%d)",
+			fw_entry->size, ts->fw_size);
+		release_firmware(fw_entry);
+		return -EFBIG;
+	}
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	/* enter program mode */
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	for (retry = 0; retry < retry_cnt; retry++) {
+		hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
+
+		ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
+		if (!ret)
+			break;
+	}
+
+	if (retry < retry_cnt)
+		dev_dbg(&ts->client->dev, "update success!!!");
+	else
+		dev_err(&ts->client->dev, "update failed!!!");
+
+	SW_RESET_IN_PGM(1000);
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_ts *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	mdelay(50);
+
+	hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, &packet_r,
+		sizeof(struct dwz_info));
+
+	memcpy(&ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info));
+
+	SW_RESET_IN_PGM(10);
+
+	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x60) {
+		/* Lime fw size */
+		dev_dbg(&ts->client->dev, "used lime IC");
+		ts->fw_size = 1024 * 64;
+		ts->nvm_mask = 0x0030027B;
+	} else if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
+		/* Crimson IC */
+		dev_dbg(&ts->client->dev, "used crimson IC");
+		ts->fw_size = 1024 * 48;
+		ts->nvm_mask = 0x00310000;
+	} else {
+		dev_dbg(&ts->client->dev, "product code is wrong!!!");
+		return -EINVAL;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mdelay(50);
+
+	return 0;
+}
+
+static int hideep_pwr_on(struct hideep_ts *ts)
+{
+	int ret = 0;
+	u8 cmd = 0x01;
+
+	if (ts->vcc_vdd) {
+		ret = regulator_enable(ts->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+		usleep_range(999, 1000);
+	}
+
+	if (ts->vcc_vid) {
+		ret = regulator_enable(ts->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+		usleep_range(2999, 3000);
+	}
+
+	mdelay(30);
+
+	if (ts->reset_gpio)
+		gpiod_set_raw_value(ts->reset_gpio, 1);
+	else
+		regmap_write(ts->reg, HIDEEP_RESET_CMD, cmd);
+
+	mdelay(50);
+
+	return ret;
+}
+
+static void hideep_pwr_off(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	if (ts->reset_gpio)
+		gpiod_set_value(ts->reset_gpio, 0);
+
+	if (ts->vcc_vid)
+		regulator_disable(ts->vcc_vid);
+
+	if (ts->vcc_vdd)
+		regulator_disable(ts->vcc_vdd);
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void push_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	int btn_up = 0;
+	int evt = 0;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = event->index & 0x0F;
+		btn_up = event->flag & HIDEEP_MT_RELEASED;
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			event->type, event->index, i,
+			get_unaligned_le16(&event->x),
+			get_unaligned_le16(&event->y),
+			get_unaligned_le16(&event->z));
+
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				get_unaligned_le16(&event->x));
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&event->y));
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				get_unaligned_le16(&event->z));
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				event->w);
+			evt++;
+		}
+	}
+
+	input_mt_sync_frame(ts->input_dev);
+}
+
+static void push_ky(struct hideep_ts *ts)
+{
+	int i;
+	int status;
+	int code;
+
+	for (i = 0; i < ts->key_count; i++) {
+		code = ts->key_event[i * 2] & 0x0F;
+		status = ts->key_event[i * 2] & 0xF0;
+
+		input_report_key(ts->input_dev, ts->key_codes[code],
+			status & HIDEEP_KEY_PRESSED_MASK);
+	}
+}
+
+static void hideep_put_event(struct hideep_ts *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+	if (ts->key_count > 0)
+		push_ky(ts);
+
+	input_sync(ts->input_dev);
+}
+
+static int hideep_parse_event(struct hideep_ts *ts, u8 *data)
+{
+	int touch_count;
+
+	ts->tch_count = data[0];
+	ts->key_count = data[1] & 0x0f;
+	ts->lpm_count = data[1] & 0xf0;
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count < HIDEEP_MT_MAX)
+		memcpy(ts->touch_event, &data[HIDEEP_TOUCH_EVENT_INDEX],
+			HIDEEP_MT_MAX * sizeof(struct hideep_event));
+	else
+		ts->tch_count = 0;
+
+	if (ts->key_count < HIDEEP_KEY_MAX)
+		memcpy(ts->key_event, &data[HIDEEP_KEY_EVENT_INDEX],
+			HIDEEP_KEY_MAX * 2);
+	else
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	return touch_count;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	u8 buff[HIDEEP_MAX_EVENT];
+	int ret;
+
+	struct hideep_ts *ts = handle;
+
+	ret = regmap_bulk_read(ts->reg, HIDEEP_EVENT_ADDR,
+		buff, HIDEEP_MAX_EVENT / 2);
+
+	if (ret < 0)
+		return IRQ_HANDLED;
+
+	ret = hideep_parse_event(ts, buff);
+
+	if (ret > 0)
+		hideep_put_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static void hideep_get_axis_info(struct hideep_ts *ts)
+{
+	int ret;
+	u8 val[4];
+
+	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
+		ret = regmap_bulk_read(ts->reg, 0x28, val, 2);
+
+		if (ret < 0) {
+			ts->prop.max_x = -1;
+			ts->prop.max_y = -1;
+		} else {
+			ts->prop.max_x =
+				get_unaligned_le16(&val[0]);
+			ts->prop.max_y =
+				get_unaligned_le16(&val[2]);
+		}
+	}
+
+	dev_dbg(&ts->client->dev, "X : %d, Y : %d",
+		ts->prop.max_x, ts->prop.max_y);
+}
+
+static int hideep_capability(struct hideep_ts *ts)
+{
+	int ret, i;
+
+	hideep_get_axis_info(ts);
+
+	if (ts->prop.max_x < 0 || ts->prop.max_y < 0)
+		return -EINVAL;
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	if (ts->key_num) {
+		ts->input_dev->keycode = ts->key_codes;
+		ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
+		ts->input_dev->keycodemax = ts->key_num;
+		for (i = 0; i < ts->key_num; i++)
+			input_set_capability(ts->input_dev, EV_KEY,
+				ts->key_codes[i]);
+	}
+
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, 65535, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	ret = input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+
+	return ret;
+}
+
+static ssize_t hideep_update_fw(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	disable_irq(ts->client->irq);
+
+	fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+	ret = hideep_update_firmware(ts, fw_name);
+
+	if (ret != 0)
+		dev_err(dev, "The firmware update failed(%d)", ret);
+
+	kfree(fw_name);
+
+	ret = hideep_load_dwz(ts);
+
+	if (ret < 0)
+		dev_err(&ts->client->dev, "fail to load dwz, ret = 0x%x", ret);
+
+	enable_irq(ts->client->irq);
+
+	return count;
+}
+
+static ssize_t hideep_fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t hideep_product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+static int __maybe_unused hideep_resume(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int ret;
+
+	ret = hideep_pwr_on(ts);
+	if (ret < 0)
+		dev_err(&ts->client->dev, "power on failed");
+	else
+		enable_irq(ts->client->irq);
+
+	return ret;
+}
+
+static int __maybe_unused hideep_suspend(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	disable_irq(ts->client->irq);
+	hideep_pwr_off(ts);
+
+	return 0;
+}
+
+static int  hideep_parse_dts(struct device *dev, struct hideep_ts *ts)
+{
+	int ret;
+
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->reset_gpio))
+		return PTR_ERR(ts->reset_gpio);
+
+	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(ts->vcc_vdd))
+		return PTR_ERR(ts->vcc_vdd);
+
+	ts->vcc_vid = devm_regulator_get(dev, "vid");
+	if (IS_ERR(ts->vcc_vid))
+		return PTR_ERR(ts->vcc_vid);
+
+	ts->key_num = device_property_read_u32_array(dev, "linux,keycodes",
+						NULL, 0);
+
+	if (ts->key_num > HIDEEP_KEY_MAX) {
+		dev_err(dev, "too many support key defined(%d)!!!",
+			ts->key_num);
+		return -EINVAL;
+	}
+
+	ret = device_property_read_u32_array(dev, "linux,keycodes",
+				ts->key_codes, ts->key_num);
+	if (ret) {
+		dev_dbg(dev, "don't support touch key");
+		ts->key_num = 0;
+	}
+
+	return 0;
+}
+
+const struct regmap_config hideep_regmap_config = {
+	.reg_bits = 16,
+	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
+	.val_bits = 16,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+	.max_register = 0xffff,
+};
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret;
+	struct regmap *regmap;
+	struct hideep_ts *ts;
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		return -ENODEV;
+	}
+
+	regmap = devm_regmap_init_i2c(client, &hideep_regmap_config);
+
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "don't init regmap");
+		return PTR_ERR(regmap);
+	}
+
+	/* init hideep_ts */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ret = hideep_parse_dts(&client->dev, ts);
+
+	if (ret)
+		return ret;
+
+	ts->client = client;
+	ts->reg = regmap;
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->dev_mutex);
+
+	/* power on */
+	ret = hideep_pwr_on(ts);
+	if (ret) {
+		dev_err(&ts->client->dev, "power on failed");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(&ts->client->dev, hideep_pwr_off, ts);
+	if (ret) {
+		hideep_pwr_off(ts);
+		return ret;
+	}
+
+	mdelay(30);
+
+	/* read info */
+	ret = hideep_load_dwz(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		return ret;
+	}
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		return -ENOMEM;
+	}
+
+	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
+
+	ret = hideep_capability(ts);
+	if (ret) {
+		dev_err(&client->dev, "can't init input properties");
+		return ret;
+	}
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		return ret;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (client->irq <= 0) {
+		dev_err(&client->dev, "can't be assigned irq");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
+		NULL, hideep_irq_task, IRQF_ONESHOT,
+		ts->client->name, ts);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+			ret);
+		return ret;
+	}
+
+	ret = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
+
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-ts" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH] Input: add support for HiDeep touchscreen
  2017-09-14  4:36             ` Anthony Kim
                                 ` (2 preceding siblings ...)
  2017-09-26  1:42               ` Anthony Kim
@ 2017-09-26  3:28               ` Anthony Kim
  2017-09-26  3:30                 ` Anthony Kim
  3 siblings, 1 reply; 29+ messages in thread
From: Anthony Kim @ 2017-09-26  3:28 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt
  Cc: linux-input, devicetree, linux-kernel, ktyrun, Anthony Kim

The HiDeep touchscreen device is a capacitive multi-touch controller
mainly for multi-touch supported devices use. It use I2C interface for
communication to IC and provide axis X, Y, Z locations for ten finger
touch through input event interface to userspace.

It support the Crimson and the Lime two type IC. They are different
the number of channel supported and FW size. But the working protocol
is same.

Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
---
 .../bindings/input/touchscreen/hideep.txt          |   42 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +

Acked-by: Rob Herring <robh+dt@kernel.org>

 drivers/input/touchscreen/Kconfig                  |   11 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/hideep.c                 | 1176 ++++++++++++++++++++
 5 files changed, 1231 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
 create mode 100644 drivers/input/touchscreen/hideep.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
new file mode 100644
index 0000000..121d9b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
@@ -0,0 +1,42 @@
+* HiDeep Finger and Stylus touchscreen controller
+
+Required properties:
+- compatible		: must be "hideep,hideep-ts"
+- reg			: I2C slave address, (e.g. 0x6C).
+- interrupt-parent : Interrupt controller to which the chip is connected.
+- interrupts : Interrupt to which the chip is connected.
+
+Optional properties:
+- vdd-supply	: It is the controller supply for controlling
+					 main voltage(3.3V) through the regulator.
+- vid-supply	: It is the controller supply for controlling
+					IO voltage(1.8V) through the regulator.
+- reset-gpios	: Define for reset gpio pin.
+						It is to use for reset IC.
+- touchscreen-size-x	: X axis size of touchscreen
+- touchscreen-size-y	: Y axis size of touchscreen
+- linux,keycodes	: Specifies an array of numeric keycode values to
+			be used for reporting button presses. The array can
+			contain up to 3 entries.
+
+Example:
+
+#include "dt-bindings/input/input.h"
+
+i2c@00000000 {
+
+	/* ... */
+
+	touchscreen@6c {
+		compatible = "hideep,hideep-ts";
+		reg = <0x6c>;
+		interrupt-parent = <&gpx1>;
+		interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+		vdd-supply = <&ldo15_reg>";
+		vid-supply = <&ldo18_reg>;
+		reset-gpios = <&gpx1 5 0>;
+		touchscreen-size-x = <1080>;
+		touchscreen-size-y = <1920>;
+		linux,keycodes = <KEY_HOME>, <KEY_MENU>, <KEY_BACK>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index daf465be..9913a03 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -134,6 +134,7 @@ gw	Gateworks Corporation
 hannstar	HannStar Display Corporation
 haoyu	Haoyu Microelectronic Co. Ltd.
 hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
 himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 64b30fe..d0c8dafc 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -344,6 +344,17 @@ config TOUCHSCREEN_GOODIX
 	  To compile this driver as a module, choose M here: the
 	  module will be called goodix.
 
+config TOUCHSCREEN_HIDEEP
+	tristate "HiDeep Touch IC"
+	depends on I2C
+	help
+	  Say Y here if you have a touchscreen using HiDeep.
+
+	  If unsure, say N.
+
+	  To compile this driver as a moudle, choose M here : the
+	  module will be called hideep_ts.
+
 config TOUCHSCREEN_ILI210X
 	tristate "Ilitek ILI210X based touchscreen"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6badce8..873b67e 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)	+= egalax_ts_serial.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
+obj-$(CONFIG_TOUCHSCREEN_HIDEEP)	+= hideep.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)	+= imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c
new file mode 100644
index 0000000..186cc30
--- /dev/null
+++ b/drivers/input/touchscreen/hideep.c
@@ -0,0 +1,1176 @@
+/*
+ * Copyright (C) 2012-2017 Hideep, 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
+ * as published by the Free Software Foudation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#define HIDEEP_TS_NAME				"HiDeep Touchscreen"
+#define HIDEEP_I2C_NAME				"hideep_ts"
+
+#define HIDEEP_MT_MAX				10
+#define HIDEEP_KEY_MAX				3
+/* count(2) + touch data(100) + key data(6) */
+#define HIDEEP_MAX_EVENT			108
+#define HIDEEP_TOUCH_EVENT_INDEX		2
+#define HIDEEP_KEY_EVENT_INDEX			102
+
+/* Touch & key event */
+#define HIDEEP_EVENT_ADDR			0x240
+
+/* command list */
+#define HIDEEP_RESET_CMD			0x9800
+
+/* event bit */
+#define HIDEEP_MT_RELEASED			BIT(4)
+#define HIDEEP_KEY_PRESSED			BIT(7)
+#define HIDEEP_KEY_FIRST_PRESSED		BIT(8)
+#define HIDEEP_KEY_PRESSED_MASK \
+	(HIDEEP_KEY_PRESSED | HIDEEP_KEY_FIRST_PRESSED)
+
+/* For NVM */
+#define HIDEEP_YRAM_BASE			0x40000000
+#define HIDEEP_PERIPHERAL_BASE			0x50000000
+#define HIDEEP_ESI_BASE \
+	(HIDEEP_PERIPHERAL_BASE + 0x00000000)
+#define HIDEEP_FLASH_BASE \
+	(HIDEEP_PERIPHERAL_BASE + 0x01000000)
+#define HIDEEP_SYSCON_BASE \
+	(HIDEEP_PERIPHERAL_BASE + 0x02000000)
+
+#define HIDEEP_SYSCON_MOD_CON			(HIDEEP_SYSCON_BASE + 0x0000)
+#define HIDEEP_SYSCON_SPC_CON			(HIDEEP_SYSCON_BASE + 0x0004)
+#define HIDEEP_SYSCON_CLK_CON			(HIDEEP_SYSCON_BASE + 0x0008)
+#define HIDEEP_SYSCON_CLK_ENA			(HIDEEP_SYSCON_BASE + 0x000C)
+#define HIDEEP_SYSCON_RST_CON			(HIDEEP_SYSCON_BASE + 0x0010)
+#define HIDEEP_SYSCON_WDT_CON			(HIDEEP_SYSCON_BASE + 0x0014)
+#define HIDEEP_SYSCON_WDT_CNT			(HIDEEP_SYSCON_BASE + 0x0018)
+#define HIDEEP_SYSCON_PWR_CON			(HIDEEP_SYSCON_BASE + 0x0020)
+#define HIDEEP_SYSCON_PGM_ID			(HIDEEP_SYSCON_BASE + 0x00F4)
+
+#define HIDEEP_FLASH_CON			(HIDEEP_FLASH_BASE + 0x0000)
+#define HIDEEP_FLASH_STA			(HIDEEP_FLASH_BASE + 0x0004)
+#define HIDEEP_FLASH_CFG			(HIDEEP_FLASH_BASE + 0x0008)
+#define HIDEEP_FLASH_TIM			(HIDEEP_FLASH_BASE + 0x000C)
+#define HIDEEP_FLASH_CACHE_CFG			(HIDEEP_FLASH_BASE + 0x0010)
+#define HIDEEP_FLASH_PIO_SIG			(HIDEEP_FLASH_BASE + 0x400000)
+
+#define HIDEEP_ESI_TX_INVALID			(HIDEEP_ESI_BASE + 0x0008)
+
+#define HIDEEP_PERASE				0x00040000
+#define HIDEEP_WRONLY				0x00100000
+
+#define HIDEEP_NVM_MASK_OFS			0x0000000C
+#define HIDEEP_NVM_DEFAULT_PAGE			0
+#define HIDEEP_NVM_SFR_WPAGE			1
+#define HIDEEP_NVM_SFR_RPAGE			2
+
+#define HIDEEP_PIO_SIG				0x00400000
+#define HIDEEP_PROT_MODE			0x03400000
+
+#define HIDEEP_NVM_PAGE_SIZE			128
+
+#define HIDEEP_DWZ_INFO				0x000002C0
+
+struct hideep_event {
+	__le16 x;
+	__le16 y;
+	__le16 z;
+	u8 w;
+	u8 flag;
+	u8 type;
+	u8 index;
+} __packed;
+
+struct dwz_info {
+	__le32	code_start;
+	u8 code_crc[12];
+
+	__le32 c_code_start;
+	__le16 c_code_len;
+	__le16 gen_ver;
+
+	__le32 vr_start;
+	__le16 vr_len;
+	__le16 rsv0;
+
+	__le32 ft_start;
+	__le16 ft_len;
+	__le16 vr_version;
+
+	__le16 boot_ver;
+	__le16 core_ver;
+	__le16 custom_ver;
+	__le16 release_ver;
+
+	u8 factory_id;
+	u8 panel_type;
+	u8 model_name[6];
+	__le16 product_code;
+	__le16 extra_option;
+
+	__le16 product_id;
+	__le16 vendor_id;
+} __packed;
+
+struct hideep_ts {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct regmap *reg;
+
+	struct touchscreen_properties prop;
+
+	struct gpio_desc *reset_gpio;
+
+	struct regulator *vcc_vdd;
+	struct regulator *vcc_vid;
+
+	struct mutex dev_mutex;
+
+	u32 tch_count;
+	u32 key_count;
+	u32 lpm_count;
+
+	u8 touch_event[HIDEEP_MT_MAX * 10];
+	u8 key_event[HIDEEP_KEY_MAX * 2];
+
+	int key_num;
+	int key_codes[HIDEEP_KEY_MAX];
+
+	struct dwz_info dwz_info;
+
+	int fw_size;
+	int nvm_mask;
+};
+
+struct pgm_packet {
+	union {
+		u8 b[8];
+		u32 w[2];
+	} header;
+
+	u32 payload[HIDEEP_NVM_PAGE_SIZE / sizeof(u32)];
+};
+
+static int hideep_pgm_w_mem(struct hideep_ts *ts, u32 addr,
+	struct pgm_packet *packet, u32 len)
+{
+	int ret;
+	int i;
+	struct i2c_msg msg;
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	put_unaligned_be32((0x80 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		put_unaligned_be32(packet->payload[i], &packet->payload[i]);
+
+	msg.addr = ts->client->addr;
+	msg.flags = 0;
+	msg.len = len + 5;
+	msg.buf = &packet->header.b[3];
+
+	ret = i2c_transfer(ts->client->adapter, &msg, 1);
+
+	return ret;
+}
+
+static int hideep_pgm_r_mem(struct hideep_ts *ts, u32 addr,
+	struct pgm_packet *packet, u32 len)
+{
+	int ret;
+	int i;
+	u8 *buff;
+	struct i2c_msg msg[2];
+
+	if ((len % sizeof(u32)) != 0)
+		return -EINVAL;
+
+	buff = kmalloc(len, GFP_KERNEL);
+
+	if (!buff)
+		return -ENOMEM;
+
+	put_unaligned_be32((0x00 | (len / sizeof(u32) - 1)),
+		&packet->header.w[0]);
+	put_unaligned_be32(addr, &packet->header.w[1]);
+
+	msg[0].addr = ts->client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = &packet->header.b[3];
+
+	msg[1].addr = ts->client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = buff;
+
+	ret = i2c_transfer(ts->client->adapter, msg, 2);
+
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < len / sizeof(u32); i++)
+		packet->payload[i] = get_unaligned_be32(&buff[i * sizeof(u32)]);
+
+	return ret;
+}
+
+static int hideep_pgm_r_reg(struct hideep_ts *ts, u32 addr,
+	u32 *val)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x00, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+
+	ret = hideep_pgm_r_mem(ts, addr, &packet, sizeof(u32));
+
+	if (ret < 0)
+		return ret;
+
+	*val = packet.payload[0];
+
+	return ret;
+}
+
+static int hideep_pgm_w_reg(struct hideep_ts *ts, u32 addr,
+	u32 data)
+{
+	int ret;
+	struct pgm_packet packet;
+
+	put_unaligned_be32(0x80, &packet.header.w[0]);
+	put_unaligned_be32(addr, &packet.header.w[1]);
+	packet.payload[0] = data;
+
+	ret = hideep_pgm_w_mem(ts, addr, &packet, sizeof(u32));
+
+	return ret;
+}
+
+#define SW_RESET_IN_PGM(CLK) \
+{ \
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CNT, CLK); \
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x03); \
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x01); \
+}
+
+#define SET_FLASH_PIO(CE) \
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x01 | (CE << 1))
+#define SET_PIO_SIG(X, Y) \
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_PIO_SIG + X, Y)
+#define SET_FLASH_HWCONTROL() \
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x00)
+
+#define NVM_W_SFR(x, y) \
+{ \
+	SET_FLASH_PIO(1); \
+	SET_PIO_SIG(x, y); \
+	SET_FLASH_PIO(0); \
+}
+
+static void hideep_pgm_set(struct hideep_ts *ts)
+{
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x00);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_SPC_CON, 0x00);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_ENA, 0xFF);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_CON, 0x01);
+	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_PWR_CON, 0x01);
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_TIM, 0x03);
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CACHE_CFG, 0x00);
+}
+
+static int hideep_pgm_get_pattern(struct hideep_ts *ts)
+{
+	int ret;
+	u32 status;
+	u16 p1 = 0xAF39;
+	u16 p2 = 0xDF9D;
+
+	ret = regmap_bulk_write(ts->reg, p1, (void *)&p2, 1);
+
+	if (ret < 0) {
+		dev_err(&ts->client->dev, "%d, %08X", __LINE__, ret);
+		return ret;
+	}
+
+	mdelay(1);
+
+	/* flush invalid Tx load register */
+	ret = hideep_pgm_w_reg(ts, HIDEEP_ESI_TX_INVALID, 0x01);
+
+	if (ret < 0)
+		return ret;
+
+	ret = hideep_pgm_r_reg(ts, HIDEEP_SYSCON_PGM_ID, &status);
+
+	if (ret < 0)
+		return ret;
+
+	return status;
+}
+
+static int hideep_enter_pgm(struct hideep_ts *ts)
+{
+	int retry_count = 10;
+	int val;
+	u32 pgm_pattern = 0xDF9DAF39;
+
+	while (retry_count--) {
+		val = hideep_pgm_get_pattern(ts);
+
+		if (pgm_pattern != get_unaligned_be32(&val)) {
+			dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
+				get_unaligned_be32(&val));
+		} else {
+			dev_dbg(&ts->client->dev, "found magic code");
+			break;
+		}
+	}
+
+	if (retry_count < 0) {
+		dev_err(&ts->client->dev, "couldn't enter pgm mode!!!");
+		SW_RESET_IN_PGM(1000);
+		return -EBADMSG;
+	}
+
+	hideep_pgm_set(ts);
+	mdelay(1);
+
+	return 0;
+}
+
+static void hideep_nvm_unlock(struct hideep_ts *ts)
+{
+	u32 unmask_code = 0;
+
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_SFR_RPAGE);
+
+	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
+
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_DEFAULT_PAGE);
+
+	/* make it unprotected code */
+	unmask_code &= (~HIDEEP_PROT_MODE);
+
+	/* compare unmask code */
+	if (unmask_code != ts->nvm_mask)
+		dev_dbg(&ts->client->dev, "read mask code different 0x%x",
+			unmask_code);
+
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_SFR_WPAGE);
+	SET_FLASH_PIO(0);
+
+	NVM_W_SFR(HIDEEP_NVM_MASK_OFS, ts->nvm_mask);
+	SET_FLASH_HWCONTROL();
+	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
+		HIDEEP_NVM_DEFAULT_PAGE);
+}
+
+static int hideep_check_status(struct hideep_ts *ts)
+{
+	int ret, status;
+	int time_out = 100;
+
+	while (time_out--) {
+		mdelay(1);
+		ret = hideep_pgm_r_reg(ts, HIDEEP_FLASH_STA,
+			&status);
+
+		if (ret < 0)
+			continue;
+
+		if (status)
+			return status;
+	}
+
+	return time_out;
+}
+
+static int hideep_program_page(struct hideep_ts *ts,
+	u32 addr, struct pgm_packet *packet_w)
+{
+	int ret;
+
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	addr = addr & ~(HIDEEP_NVM_PAGE_SIZE - 1);
+
+	SET_FLASH_PIO(0);
+	SET_FLASH_PIO(1);
+
+	/* erase page */
+	SET_PIO_SIG((HIDEEP_PERASE | addr), 0xFFFFFFFF);
+
+	SET_FLASH_PIO(0);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	/* write page */
+	SET_FLASH_PIO(1);
+
+	SET_PIO_SIG((HIDEEP_WRONLY | addr),
+		get_unaligned_be32(&packet_w->payload[0]));
+
+	hideep_pgm_w_mem(ts, (HIDEEP_FLASH_PIO_SIG | HIDEEP_WRONLY),
+		packet_w, HIDEEP_NVM_PAGE_SIZE);
+
+	SET_PIO_SIG(124, get_unaligned_be32(&packet_w->payload[31]));
+
+	SET_FLASH_PIO(0);
+
+	mdelay(1);
+
+	ret = hideep_check_status(ts);
+
+	if (ret < 0)
+		return -EBUSY;
+
+	SET_FLASH_HWCONTROL();
+
+	return 0;
+}
+
+static void hideep_program_nvm(struct hideep_ts *ts, const u8 *ucode,
+	int len)
+{
+	struct pgm_packet packet_w;
+	struct pgm_packet packet_r;
+	int i;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_w = HIDEEP_NVM_PAGE_SIZE;
+	u32 pages = DIV_ROUND_UP(len, HIDEEP_NVM_PAGE_SIZE);
+
+
+	hideep_nvm_unlock(ts);
+
+	dev_dbg(&ts->client->dev, "pages : %d", pages);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < HIDEEP_NVM_PAGE_SIZE)
+			len_w = len_r;
+
+		/* compare */
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			HIDEEP_NVM_PAGE_SIZE);
+		ret = memcmp(&ucode[addr], packet_r.payload, len_w);
+
+		if (ret) {
+			/* write page */
+			memcpy(packet_w.payload, &ucode[addr], len_w);
+			ret = hideep_program_page(ts, addr, &packet_w);
+			if (ret)
+				dev_err(&ts->client->dev,
+					"%s : error(%08x):",
+					__func__, addr);
+			mdelay(1);
+		}
+
+		addr += HIDEEP_NVM_PAGE_SIZE;
+		len_r -= HIDEEP_NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+}
+
+static int hideep_verify_nvm(struct hideep_ts *ts, const u8 *ucode,
+	int len)
+{
+	struct pgm_packet packet_r;
+	int i, j;
+	int ret;
+	int addr = 0;
+	int len_r = len;
+	int len_v = HIDEEP_NVM_PAGE_SIZE;
+	u32 pages = DIV_ROUND_UP(len, HIDEEP_NVM_PAGE_SIZE);
+
+	for (i = 0; i < pages; i++) {
+		if (len_r < HIDEEP_NVM_PAGE_SIZE)
+			len_v = len_r;
+
+		hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
+			HIDEEP_NVM_PAGE_SIZE);
+
+		ret = memcmp(&ucode[addr], packet_r.payload, len_v);
+
+		if (ret) {
+			u8 *read = (u8 *)packet_r.payload;
+
+			for (j = 0; j < HIDEEP_NVM_PAGE_SIZE; j++) {
+				if (ucode[addr + j] != read[j])
+					dev_err(&ts->client->dev,
+						"verify : error([%d] %02x : %02x)",
+						addr + j, ucode[addr + j],
+						read[j]);
+			}
+			return ret;
+		}
+
+		addr += HIDEEP_NVM_PAGE_SIZE;
+		len_r -= HIDEEP_NVM_PAGE_SIZE;
+		if (len_r < 0)
+			break;
+	}
+
+	return 0;
+}
+
+static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
+{
+	int ret;
+	int retry, retry_cnt = 3;
+	const struct firmware *fw_entry;
+
+	dev_dbg(&ts->client->dev, "enter");
+	ret = request_firmware(&fw_entry, fn, &ts->client->dev);
+
+	if (ret != 0) {
+		dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
+		return ret;
+	}
+
+	if (fw_entry->size > ts->fw_size) {
+		dev_err(&ts->client->dev,
+			"file size(%zu) is big more than fw memory size(%d)",
+			fw_entry->size, ts->fw_size);
+		release_firmware(fw_entry);
+		return -EFBIG;
+	}
+
+	/* chip specific code for flash fuse */
+	mutex_lock(&ts->dev_mutex);
+
+	/* enter program mode */
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	/* comparing & programming each page, if the memory of specified
+	 * page is exactly same, no need to update.
+	 */
+	for (retry = 0; retry < retry_cnt; retry++) {
+		hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
+
+		ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
+		if (!ret)
+			break;
+	}
+
+	if (retry < retry_cnt)
+		dev_dbg(&ts->client->dev, "update success!!!");
+	else
+		dev_err(&ts->client->dev, "update failed!!!");
+
+	SW_RESET_IN_PGM(1000);
+
+	mutex_unlock(&ts->dev_mutex);
+
+	release_firmware(fw_entry);
+
+	return ret;
+}
+
+static int hideep_load_dwz(struct hideep_ts *ts)
+{
+	int ret = 0;
+	struct pgm_packet packet_r;
+
+	ret = hideep_enter_pgm(ts);
+
+	if (ret)
+		return ret;
+
+	mdelay(50);
+
+	hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, &packet_r,
+		sizeof(struct dwz_info));
+
+	memcpy(&ts->dwz_info, packet_r.payload,
+		sizeof(struct dwz_info));
+
+	SW_RESET_IN_PGM(10);
+
+	if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x60) {
+		/* Lime fw size */
+		dev_dbg(&ts->client->dev, "used lime IC");
+		ts->fw_size = 1024 * 64;
+		ts->nvm_mask = 0x0030027B;
+	} else if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
+		/* Crimson IC */
+		dev_dbg(&ts->client->dev, "used crimson IC");
+		ts->fw_size = 1024 * 48;
+		ts->nvm_mask = 0x00310000;
+	} else {
+		dev_dbg(&ts->client->dev, "product code is wrong!!!");
+		return -EINVAL;
+	}
+
+	dev_dbg(&ts->client->dev, "firmware release version : %04x",
+		get_unaligned_le16(&ts->dwz_info.release_ver));
+
+	mdelay(50);
+
+	return 0;
+}
+
+static int hideep_pwr_on(struct hideep_ts *ts)
+{
+	int ret = 0;
+	u8 cmd = 0x01;
+
+	if (ts->vcc_vdd) {
+		ret = regulator_enable(ts->vcc_vdd);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vdd enable failed ret=%d", ret);
+		usleep_range(999, 1000);
+	}
+
+	if (ts->vcc_vid) {
+		ret = regulator_enable(ts->vcc_vid);
+		if (ret)
+			dev_err(&ts->client->dev,
+				"Regulator vcc_vid enable failed ret=%d", ret);
+		usleep_range(2999, 3000);
+	}
+
+	mdelay(30);
+
+	if (ts->reset_gpio)
+		gpiod_set_raw_value(ts->reset_gpio, 1);
+	else
+		regmap_write(ts->reg, HIDEEP_RESET_CMD, cmd);
+
+	mdelay(50);
+
+	return ret;
+}
+
+static void hideep_pwr_off(void *data)
+{
+	struct hideep_ts *ts = data;
+
+	if (ts->reset_gpio)
+		gpiod_set_value(ts->reset_gpio, 0);
+
+	if (ts->vcc_vid)
+		regulator_disable(ts->vcc_vid);
+
+	if (ts->vcc_vdd)
+		regulator_disable(ts->vcc_vdd);
+}
+
+#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
+
+static void push_mt(struct hideep_ts *ts)
+{
+	int id;
+	int i;
+	int btn_up = 0;
+	int evt = 0;
+	int offset = sizeof(struct hideep_event);
+	struct hideep_event *event;
+
+	/* load multi-touch event to input system */
+	for (i = 0; i < ts->tch_count; i++) {
+		event = (struct hideep_event *)&ts->touch_event[i * offset];
+		id = event->index & 0x0F;
+		btn_up = event->flag & HIDEEP_MT_RELEASED;
+
+		dev_dbg(&ts->client->dev,
+			"type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
+			event->type, event->index, i,
+			get_unaligned_le16(&event->x),
+			get_unaligned_le16(&event->y),
+			get_unaligned_le16(&event->z));
+
+		input_mt_slot(ts->input_dev, id);
+		input_mt_report_slot_state(ts->input_dev,
+			__GET_MT_TOOL_TYPE(event->type),
+			(btn_up == 0));
+
+		if (btn_up == 0) {
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+				get_unaligned_le16(&event->x));
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+				get_unaligned_le16(&event->y));
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
+				get_unaligned_le16(&event->z));
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+				event->w);
+			evt++;
+		}
+	}
+
+	input_mt_sync_frame(ts->input_dev);
+}
+
+static void push_ky(struct hideep_ts *ts)
+{
+	int i;
+	int status;
+	int code;
+
+	for (i = 0; i < ts->key_count; i++) {
+		code = ts->key_event[i * 2] & 0x0F;
+		status = ts->key_event[i * 2] & 0xF0;
+
+		input_report_key(ts->input_dev, ts->key_codes[code],
+			status & HIDEEP_KEY_PRESSED_MASK);
+	}
+}
+
+static void hideep_put_event(struct hideep_ts *ts)
+{
+	/* mangling touch information */
+	if (ts->tch_count > 0)
+		push_mt(ts);
+
+	if (ts->key_count > 0)
+		push_ky(ts);
+
+	input_sync(ts->input_dev);
+}
+
+static int hideep_parse_event(struct hideep_ts *ts, u8 *data)
+{
+	int touch_count;
+
+	ts->tch_count = data[0];
+	ts->key_count = data[1] & 0x0f;
+	ts->lpm_count = data[1] & 0xf0;
+
+	/* get touch event count */
+	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
+		ts->tch_count, ts->key_count, ts->lpm_count);
+
+	/* get touch event information */
+	if (ts->tch_count < HIDEEP_MT_MAX)
+		memcpy(ts->touch_event, &data[HIDEEP_TOUCH_EVENT_INDEX],
+			HIDEEP_MT_MAX * sizeof(struct hideep_event));
+	else
+		ts->tch_count = 0;
+
+	if (ts->key_count < HIDEEP_KEY_MAX)
+		memcpy(ts->key_event, &data[HIDEEP_KEY_EVENT_INDEX],
+			HIDEEP_KEY_MAX * 2);
+	else
+		ts->key_count = 0;
+
+	touch_count = ts->tch_count + ts->key_count;
+
+	return touch_count;
+}
+
+static irqreturn_t hideep_irq_task(int irq, void *handle)
+{
+	u8 buff[HIDEEP_MAX_EVENT];
+	int ret;
+
+	struct hideep_ts *ts = handle;
+
+	ret = regmap_bulk_read(ts->reg, HIDEEP_EVENT_ADDR,
+		buff, HIDEEP_MAX_EVENT / 2);
+
+	if (ret < 0)
+		return IRQ_HANDLED;
+
+	ret = hideep_parse_event(ts, buff);
+
+	if (ret > 0)
+		hideep_put_event(ts);
+
+	return IRQ_HANDLED;
+}
+
+static void hideep_get_axis_info(struct hideep_ts *ts)
+{
+	int ret;
+	u8 val[4];
+
+	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
+		ret = regmap_bulk_read(ts->reg, 0x28, val, 2);
+
+		if (ret < 0) {
+			ts->prop.max_x = -1;
+			ts->prop.max_y = -1;
+		} else {
+			ts->prop.max_x =
+				get_unaligned_le16(&val[0]);
+			ts->prop.max_y =
+				get_unaligned_le16(&val[2]);
+		}
+	}
+
+	dev_dbg(&ts->client->dev, "X : %d, Y : %d",
+		ts->prop.max_x, ts->prop.max_y);
+}
+
+static int hideep_capability(struct hideep_ts *ts)
+{
+	int ret, i;
+
+	hideep_get_axis_info(ts);
+
+	if (ts->prop.max_x < 0 || ts->prop.max_y < 0)
+		return -EINVAL;
+
+	ts->input_dev->name = HIDEEP_TS_NAME;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	if (ts->key_num) {
+		ts->input_dev->keycode = ts->key_codes;
+		ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
+		ts->input_dev->keycodemax = ts->key_num;
+		for (i = 0; i < ts->key_num; i++)
+			input_set_capability(ts->input_dev, EV_KEY,
+				ts->key_codes[i]);
+	}
+
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_PRESSURE, 0, 65535, 0, 0);
+	input_set_abs_params(ts->input_dev,
+		ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	ret = input_mt_init_slots(ts->input_dev,
+		HIDEEP_MT_MAX, INPUT_MT_DIRECT);
+
+	return ret;
+}
+
+static ssize_t hideep_update_fw(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int mode, ret;
+	char *fw_name;
+
+	ret = kstrtoint(buf, 8, &mode);
+	if (ret)
+		return ret;
+
+	disable_irq(ts->client->irq);
+
+	fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
+		get_unaligned_le16(&ts->dwz_info.product_id));
+	ret = hideep_update_firmware(ts, fw_name);
+
+	if (ret != 0)
+		dev_err(dev, "The firmware update failed(%d)", ret);
+
+	kfree(fw_name);
+
+	ret = hideep_load_dwz(ts);
+
+	if (ret < 0)
+		dev_err(&ts->client->dev, "fail to load dwz, ret = 0x%x", ret);
+
+	enable_irq(ts->client->irq);
+
+	return count;
+}
+
+static ssize_t hideep_fw_version_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static ssize_t hideep_product_id_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	mutex_lock(&ts->dev_mutex);
+	len = scnprintf(buf, PAGE_SIZE,
+		"%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
+	mutex_unlock(&ts->dev_mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
+static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
+
+static struct attribute *hideep_ts_sysfs_entries[] = {
+	&dev_attr_version.attr,
+	&dev_attr_product_id.attr,
+	&dev_attr_update_fw.attr,
+	NULL,
+};
+
+static struct attribute_group hideep_ts_attr_group = {
+	.attrs = hideep_ts_sysfs_entries,
+};
+
+static int __maybe_unused hideep_resume(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+	int ret;
+
+	ret = hideep_pwr_on(ts);
+	if (ret < 0)
+		dev_err(&ts->client->dev, "power on failed");
+	else
+		enable_irq(ts->client->irq);
+
+	return ret;
+}
+
+static int __maybe_unused hideep_suspend(struct device *dev)
+{
+	struct hideep_ts *ts = dev_get_drvdata(dev);
+
+	disable_irq(ts->client->irq);
+	hideep_pwr_off(ts);
+
+	return 0;
+}
+
+static int  hideep_parse_dts(struct device *dev, struct hideep_ts *ts)
+{
+	int ret;
+
+	ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(ts->reset_gpio))
+		return PTR_ERR(ts->reset_gpio);
+
+	ts->vcc_vdd = devm_regulator_get(dev, "vdd");
+	if (IS_ERR(ts->vcc_vdd))
+		return PTR_ERR(ts->vcc_vdd);
+
+	ts->vcc_vid = devm_regulator_get(dev, "vid");
+	if (IS_ERR(ts->vcc_vid))
+		return PTR_ERR(ts->vcc_vid);
+
+	ts->key_num = device_property_read_u32_array(dev, "linux,keycodes",
+						NULL, 0);
+
+	if (ts->key_num > HIDEEP_KEY_MAX) {
+		dev_err(dev, "too many support key defined(%d)!!!",
+			ts->key_num);
+		return -EINVAL;
+	}
+
+	ret = device_property_read_u32_array(dev, "linux,keycodes",
+				ts->key_codes, ts->key_num);
+	if (ret) {
+		dev_dbg(dev, "don't support touch key");
+		ts->key_num = 0;
+	}
+
+	return 0;
+}
+
+const struct regmap_config hideep_regmap_config = {
+	.reg_bits = 16,
+	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
+	.val_bits = 16,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+	.max_register = 0xffff,
+};
+
+static int hideep_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int ret;
+	struct regmap *regmap;
+	struct hideep_ts *ts;
+
+	/* check i2c bus */
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "check i2c device error");
+		return -ENODEV;
+	}
+
+	regmap = devm_regmap_init_i2c(client, &hideep_regmap_config);
+
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "don't init regmap");
+		return PTR_ERR(regmap);
+	}
+
+	/* init hideep_ts */
+	ts = devm_kzalloc(&client->dev,
+		sizeof(*ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ret = hideep_parse_dts(&client->dev, ts);
+
+	if (ret)
+		return ret;
+
+	ts->client = client;
+	ts->reg = regmap;
+
+	i2c_set_clientdata(client, ts);
+
+	mutex_init(&ts->dev_mutex);
+
+	/* power on */
+	ret = hideep_pwr_on(ts);
+	if (ret) {
+		dev_err(&ts->client->dev, "power on failed");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(&ts->client->dev, hideep_pwr_off, ts);
+	if (ret) {
+		hideep_pwr_off(ts);
+		return ret;
+	}
+
+	mdelay(30);
+
+	/* read info */
+	ret = hideep_load_dwz(ts);
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
+		return ret;
+	}
+
+	/* init input device */
+	ts->input_dev = devm_input_allocate_device(&client->dev);
+	if (!ts->input_dev) {
+		dev_err(&client->dev, "can't allocate memory for input_dev");
+		return -ENOMEM;
+	}
+
+	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
+
+	ret = hideep_capability(ts);
+	if (ret) {
+		dev_err(&client->dev, "can't init input properties");
+		return ret;
+	}
+
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		dev_err(&client->dev, "can't register input_dev");
+		return ret;
+	}
+
+	input_set_drvdata(ts->input_dev, ts);
+
+	dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
+	if (client->irq <= 0) {
+		dev_err(&client->dev, "can't be assigned irq");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
+		NULL, hideep_irq_task, IRQF_ONESHOT,
+		ts->client->name, ts);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
+			ret);
+		return ret;
+	}
+
+	ret = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
+
+	if (ret) {
+		dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
+
+static const struct i2c_device_id hideep_dev_idtable[] = {
+	{ HIDEEP_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id hideep_acpi_id[] = {
+	{ "HIDP0001", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id hideep_match_table[] = {
+	{ .compatible = "hideep,hideep-ts" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, hideep_match_table);
+#endif
+
+static struct i2c_driver hideep_driver = {
+	.probe = hideep_probe,
+	.id_table = hideep_dev_idtable,
+	.driver = {
+		.name = HIDEEP_I2C_NAME,
+		.of_match_table = of_match_ptr(hideep_match_table),
+		.acpi_match_table = ACPI_PTR(hideep_acpi_id),
+		.pm = &hideep_pm_ops,
+	},
+};
+
+module_i2c_driver(hideep_driver);
+
+MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
+MODULE_AUTHOR("anthony.kim@hideep.com");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
  2017-09-26  3:28               ` Anthony Kim
@ 2017-09-26  3:30                 ` Anthony Kim
  0 siblings, 0 replies; 29+ messages in thread
From: Anthony Kim @ 2017-09-26  3:30 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, devicetree, linux-kernel, 김태영,
	Anthony Kim, robh+dt

Hi, Dmitry

Always thank you for your comment.
I resent patch file modified about your comment. Please confirm it.
Incidentally, Our IC use 40bit register address I2C protocol format at
flash mode. 40 bit means just 5 byte buffers.
( address data address is as below )
------------------------------------------------------------
| 8bit                                   | 32bit                   |
------------------------------------------------------------
| R/W flag and data length | register address |
------------------------------------------------------------
But I wasn't find method for use 40 bit register address at regmap, so
I couldn't implement to use regmap at flash mode.
(I tried to use expanding bit, It was fail that couldn't set value in
expanding bit.)
So I only used regmap for normal mode I2C.
If I must used regmap at all of transaction, please give to advice how
to use 40 bit register address at regmap.

Thank you.
Anthony.

2017-09-26 12:28 GMT+09:00 Anthony Kim <anthony.kim@hideep.com>:
> The HiDeep touchscreen device is a capacitive multi-touch controller
> mainly for multi-touch supported devices use. It use I2C interface for
> communication to IC and provide axis X, Y, Z locations for ten finger
> touch through input event interface to userspace.
>
> It support the Crimson and the Lime two type IC. They are different
> the number of channel supported and FW size. But the working protocol
> is same.
>
> Signed-off-by: Anthony Kim <anthony.kim@hideep.com>
> ---
>  .../bindings/input/touchscreen/hideep.txt          |   42 +
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>
> Acked-by: Rob Herring <robh+dt@kernel.org>
>
>  drivers/input/touchscreen/Kconfig                  |   11 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  drivers/input/touchscreen/hideep.c                 | 1176 ++++++++++++++++++++
>  5 files changed, 1231 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
>  create mode 100644 drivers/input/touchscreen/hideep.c
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> new file mode 100644
> index 0000000..121d9b7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> @@ -0,0 +1,42 @@
> +* HiDeep Finger and Stylus touchscreen controller
> +
> +Required properties:
> +- compatible           : must be "hideep,hideep-ts"
> +- reg                  : I2C slave address, (e.g. 0x6C).
> +- interrupt-parent : Interrupt controller to which the chip is connected.
> +- interrupts : Interrupt to which the chip is connected.
> +
> +Optional properties:
> +- vdd-supply   : It is the controller supply for controlling
> +                                        main voltage(3.3V) through the regulator.
> +- vid-supply   : It is the controller supply for controlling
> +                                       IO voltage(1.8V) through the regulator.
> +- reset-gpios  : Define for reset gpio pin.
> +                                               It is to use for reset IC.
> +- touchscreen-size-x   : X axis size of touchscreen
> +- touchscreen-size-y   : Y axis size of touchscreen
> +- linux,keycodes       : Specifies an array of numeric keycode values to
> +                       be used for reporting button presses. The array can
> +                       contain up to 3 entries.
> +
> +Example:
> +
> +#include "dt-bindings/input/input.h"
> +
> +i2c@00000000 {
> +
> +       /* ... */
> +
> +       touchscreen@6c {
> +               compatible = "hideep,hideep-ts";
> +               reg = <0x6c>;
> +               interrupt-parent = <&gpx1>;
> +               interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
> +               vdd-supply = <&ldo15_reg>";
> +               vid-supply = <&ldo18_reg>;
> +               reset-gpios = <&gpx1 5 0>;
> +               touchscreen-size-x = <1080>;
> +               touchscreen-size-y = <1920>;
> +               linux,keycodes = <KEY_HOME>, <KEY_MENU>, <KEY_BACK>;
> +       };
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index daf465be..9913a03 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -134,6 +134,7 @@ gw  Gateworks Corporation
>  hannstar       HannStar Display Corporation
>  haoyu  Haoyu Microelectronic Co. Ltd.
>  hardkernel     Hardkernel Co., Ltd
> +hideep HiDeep Inc.
>  himax  Himax Technologies, Inc.
>  hisilicon      Hisilicon Limited.
>  hit    Hitachi Ltd.
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 64b30fe..d0c8dafc 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -344,6 +344,17 @@ config TOUCHSCREEN_GOODIX
>           To compile this driver as a module, choose M here: the
>           module will be called goodix.
>
> +config TOUCHSCREEN_HIDEEP
> +       tristate "HiDeep Touch IC"
> +       depends on I2C
> +       help
> +         Say Y here if you have a touchscreen using HiDeep.
> +
> +         If unsure, say N.
> +
> +         To compile this driver as a moudle, choose M here : the
> +         module will be called hideep_ts.
> +
>  config TOUCHSCREEN_ILI210X
>         tristate "Ilitek ILI210X based touchscreen"
>         depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 6badce8..873b67e 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)      += egalax_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)        += egalax_ts_serial.o
>  obj-$(CONFIG_TOUCHSCREEN_FUJITSU)      += fujitsu_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_GOODIX)       += goodix.o
> +obj-$(CONFIG_TOUCHSCREEN_HIDEEP)       += hideep.o
>  obj-$(CONFIG_TOUCHSCREEN_ILI210X)      += ili210x.o
>  obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)   += imx6ul_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
> diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c
> new file mode 100644
> index 0000000..186cc30
> --- /dev/null
> +++ b/drivers/input/touchscreen/hideep.c
> @@ -0,0 +1,1176 @@
> +/*
> + * Copyright (C) 2012-2017 Hideep, 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
> + * as published by the Free Software Foudation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/firmware.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/i2c.h>
> +#include <linux/acpi.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +#include <linux/sysfs.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/input/touchscreen.h>
> +#include <linux/regulator/consumer.h>
> +#include <asm/unaligned.h>
> +
> +#define HIDEEP_TS_NAME                         "HiDeep Touchscreen"
> +#define HIDEEP_I2C_NAME                                "hideep_ts"
> +
> +#define HIDEEP_MT_MAX                          10
> +#define HIDEEP_KEY_MAX                         3
> +/* count(2) + touch data(100) + key data(6) */
> +#define HIDEEP_MAX_EVENT                       108
> +#define HIDEEP_TOUCH_EVENT_INDEX               2
> +#define HIDEEP_KEY_EVENT_INDEX                 102
> +
> +/* Touch & key event */
> +#define HIDEEP_EVENT_ADDR                      0x240
> +
> +/* command list */
> +#define HIDEEP_RESET_CMD                       0x9800
> +
> +/* event bit */
> +#define HIDEEP_MT_RELEASED                     BIT(4)
> +#define HIDEEP_KEY_PRESSED                     BIT(7)
> +#define HIDEEP_KEY_FIRST_PRESSED               BIT(8)
> +#define HIDEEP_KEY_PRESSED_MASK \
> +       (HIDEEP_KEY_PRESSED | HIDEEP_KEY_FIRST_PRESSED)
> +
> +/* For NVM */
> +#define HIDEEP_YRAM_BASE                       0x40000000
> +#define HIDEEP_PERIPHERAL_BASE                 0x50000000
> +#define HIDEEP_ESI_BASE \
> +       (HIDEEP_PERIPHERAL_BASE + 0x00000000)
> +#define HIDEEP_FLASH_BASE \
> +       (HIDEEP_PERIPHERAL_BASE + 0x01000000)
> +#define HIDEEP_SYSCON_BASE \
> +       (HIDEEP_PERIPHERAL_BASE + 0x02000000)
> +
> +#define HIDEEP_SYSCON_MOD_CON                  (HIDEEP_SYSCON_BASE + 0x0000)
> +#define HIDEEP_SYSCON_SPC_CON                  (HIDEEP_SYSCON_BASE + 0x0004)
> +#define HIDEEP_SYSCON_CLK_CON                  (HIDEEP_SYSCON_BASE + 0x0008)
> +#define HIDEEP_SYSCON_CLK_ENA                  (HIDEEP_SYSCON_BASE + 0x000C)
> +#define HIDEEP_SYSCON_RST_CON                  (HIDEEP_SYSCON_BASE + 0x0010)
> +#define HIDEEP_SYSCON_WDT_CON                  (HIDEEP_SYSCON_BASE + 0x0014)
> +#define HIDEEP_SYSCON_WDT_CNT                  (HIDEEP_SYSCON_BASE + 0x0018)
> +#define HIDEEP_SYSCON_PWR_CON                  (HIDEEP_SYSCON_BASE + 0x0020)
> +#define HIDEEP_SYSCON_PGM_ID                   (HIDEEP_SYSCON_BASE + 0x00F4)
> +
> +#define HIDEEP_FLASH_CON                       (HIDEEP_FLASH_BASE + 0x0000)
> +#define HIDEEP_FLASH_STA                       (HIDEEP_FLASH_BASE + 0x0004)
> +#define HIDEEP_FLASH_CFG                       (HIDEEP_FLASH_BASE + 0x0008)
> +#define HIDEEP_FLASH_TIM                       (HIDEEP_FLASH_BASE + 0x000C)
> +#define HIDEEP_FLASH_CACHE_CFG                 (HIDEEP_FLASH_BASE + 0x0010)
> +#define HIDEEP_FLASH_PIO_SIG                   (HIDEEP_FLASH_BASE + 0x400000)
> +
> +#define HIDEEP_ESI_TX_INVALID                  (HIDEEP_ESI_BASE + 0x0008)
> +
> +#define HIDEEP_PERASE                          0x00040000
> +#define HIDEEP_WRONLY                          0x00100000
> +
> +#define HIDEEP_NVM_MASK_OFS                    0x0000000C
> +#define HIDEEP_NVM_DEFAULT_PAGE                        0
> +#define HIDEEP_NVM_SFR_WPAGE                   1
> +#define HIDEEP_NVM_SFR_RPAGE                   2
> +
> +#define HIDEEP_PIO_SIG                         0x00400000
> +#define HIDEEP_PROT_MODE                       0x03400000
> +
> +#define HIDEEP_NVM_PAGE_SIZE                   128
> +
> +#define HIDEEP_DWZ_INFO                                0x000002C0
> +
> +struct hideep_event {
> +       __le16 x;
> +       __le16 y;
> +       __le16 z;
> +       u8 w;
> +       u8 flag;
> +       u8 type;
> +       u8 index;
> +} __packed;
> +
> +struct dwz_info {
> +       __le32  code_start;
> +       u8 code_crc[12];
> +
> +       __le32 c_code_start;
> +       __le16 c_code_len;
> +       __le16 gen_ver;
> +
> +       __le32 vr_start;
> +       __le16 vr_len;
> +       __le16 rsv0;
> +
> +       __le32 ft_start;
> +       __le16 ft_len;
> +       __le16 vr_version;
> +
> +       __le16 boot_ver;
> +       __le16 core_ver;
> +       __le16 custom_ver;
> +       __le16 release_ver;
> +
> +       u8 factory_id;
> +       u8 panel_type;
> +       u8 model_name[6];
> +       __le16 product_code;
> +       __le16 extra_option;
> +
> +       __le16 product_id;
> +       __le16 vendor_id;
> +} __packed;
> +
> +struct hideep_ts {
> +       struct i2c_client *client;
> +       struct input_dev *input_dev;
> +       struct regmap *reg;
> +
> +       struct touchscreen_properties prop;
> +
> +       struct gpio_desc *reset_gpio;
> +
> +       struct regulator *vcc_vdd;
> +       struct regulator *vcc_vid;
> +
> +       struct mutex dev_mutex;
> +
> +       u32 tch_count;
> +       u32 key_count;
> +       u32 lpm_count;
> +
> +       u8 touch_event[HIDEEP_MT_MAX * 10];
> +       u8 key_event[HIDEEP_KEY_MAX * 2];
> +
> +       int key_num;
> +       int key_codes[HIDEEP_KEY_MAX];
> +
> +       struct dwz_info dwz_info;
> +
> +       int fw_size;
> +       int nvm_mask;
> +};
> +
> +struct pgm_packet {
> +       union {
> +               u8 b[8];
> +               u32 w[2];
> +       } header;
> +
> +       u32 payload[HIDEEP_NVM_PAGE_SIZE / sizeof(u32)];
> +};
> +
> +static int hideep_pgm_w_mem(struct hideep_ts *ts, u32 addr,
> +       struct pgm_packet *packet, u32 len)
> +{
> +       int ret;
> +       int i;
> +       struct i2c_msg msg;
> +
> +       if ((len % sizeof(u32)) != 0)
> +               return -EINVAL;
> +
> +       put_unaligned_be32((0x80 | (len / sizeof(u32) - 1)),
> +               &packet->header.w[0]);
> +       put_unaligned_be32(addr, &packet->header.w[1]);
> +
> +       for (i = 0; i < len / sizeof(u32); i++)
> +               put_unaligned_be32(packet->payload[i], &packet->payload[i]);
> +
> +       msg.addr = ts->client->addr;
> +       msg.flags = 0;
> +       msg.len = len + 5;
> +       msg.buf = &packet->header.b[3];
> +
> +       ret = i2c_transfer(ts->client->adapter, &msg, 1);
> +
> +       return ret;
> +}
> +
> +static int hideep_pgm_r_mem(struct hideep_ts *ts, u32 addr,
> +       struct pgm_packet *packet, u32 len)
> +{
> +       int ret;
> +       int i;
> +       u8 *buff;
> +       struct i2c_msg msg[2];
> +
> +       if ((len % sizeof(u32)) != 0)
> +               return -EINVAL;
> +
> +       buff = kmalloc(len, GFP_KERNEL);
> +
> +       if (!buff)
> +               return -ENOMEM;
> +
> +       put_unaligned_be32((0x00 | (len / sizeof(u32) - 1)),
> +               &packet->header.w[0]);
> +       put_unaligned_be32(addr, &packet->header.w[1]);
> +
> +       msg[0].addr = ts->client->addr;
> +       msg[0].flags = 0;
> +       msg[0].len = 5;
> +       msg[0].buf = &packet->header.b[3];
> +
> +       msg[1].addr = ts->client->addr;
> +       msg[1].flags = I2C_M_RD;
> +       msg[1].len = len;
> +       msg[1].buf = buff;
> +
> +       ret = i2c_transfer(ts->client->adapter, msg, 2);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       for (i = 0; i < len / sizeof(u32); i++)
> +               packet->payload[i] = get_unaligned_be32(&buff[i * sizeof(u32)]);
> +
> +       return ret;
> +}
> +
> +static int hideep_pgm_r_reg(struct hideep_ts *ts, u32 addr,
> +       u32 *val)
> +{
> +       int ret;
> +       struct pgm_packet packet;
> +
> +       put_unaligned_be32(0x00, &packet.header.w[0]);
> +       put_unaligned_be32(addr, &packet.header.w[1]);
> +
> +       ret = hideep_pgm_r_mem(ts, addr, &packet, sizeof(u32));
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       *val = packet.payload[0];
> +
> +       return ret;
> +}
> +
> +static int hideep_pgm_w_reg(struct hideep_ts *ts, u32 addr,
> +       u32 data)
> +{
> +       int ret;
> +       struct pgm_packet packet;
> +
> +       put_unaligned_be32(0x80, &packet.header.w[0]);
> +       put_unaligned_be32(addr, &packet.header.w[1]);
> +       packet.payload[0] = data;
> +
> +       ret = hideep_pgm_w_mem(ts, addr, &packet, sizeof(u32));
> +
> +       return ret;
> +}
> +
> +#define SW_RESET_IN_PGM(CLK) \
> +{ \
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CNT, CLK); \
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x03); \
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x01); \
> +}
> +
> +#define SET_FLASH_PIO(CE) \
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x01 | (CE << 1))
> +#define SET_PIO_SIG(X, Y) \
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_PIO_SIG + X, Y)
> +#define SET_FLASH_HWCONTROL() \
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x00)
> +
> +#define NVM_W_SFR(x, y) \
> +{ \
> +       SET_FLASH_PIO(1); \
> +       SET_PIO_SIG(x, y); \
> +       SET_FLASH_PIO(0); \
> +}
> +
> +static void hideep_pgm_set(struct hideep_ts *ts)
> +{
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x00);
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_SPC_CON, 0x00);
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_ENA, 0xFF);
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_CON, 0x01);
> +       hideep_pgm_w_reg(ts, HIDEEP_SYSCON_PWR_CON, 0x01);
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_TIM, 0x03);
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CACHE_CFG, 0x00);
> +}
> +
> +static int hideep_pgm_get_pattern(struct hideep_ts *ts)
> +{
> +       int ret;
> +       u32 status;
> +       u16 p1 = 0xAF39;
> +       u16 p2 = 0xDF9D;
> +
> +       ret = regmap_bulk_write(ts->reg, p1, (void *)&p2, 1);
> +
> +       if (ret < 0) {
> +               dev_err(&ts->client->dev, "%d, %08X", __LINE__, ret);
> +               return ret;
> +       }
> +
> +       mdelay(1);
> +
> +       /* flush invalid Tx load register */
> +       ret = hideep_pgm_w_reg(ts, HIDEEP_ESI_TX_INVALID, 0x01);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = hideep_pgm_r_reg(ts, HIDEEP_SYSCON_PGM_ID, &status);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       return status;
> +}
> +
> +static int hideep_enter_pgm(struct hideep_ts *ts)
> +{
> +       int retry_count = 10;
> +       int val;
> +       u32 pgm_pattern = 0xDF9DAF39;
> +
> +       while (retry_count--) {
> +               val = hideep_pgm_get_pattern(ts);
> +
> +               if (pgm_pattern != get_unaligned_be32(&val)) {
> +                       dev_err(&ts->client->dev, "enter_pgm : error(%08x):",
> +                               get_unaligned_be32(&val));
> +               } else {
> +                       dev_dbg(&ts->client->dev, "found magic code");
> +                       break;
> +               }
> +       }
> +
> +       if (retry_count < 0) {
> +               dev_err(&ts->client->dev, "couldn't enter pgm mode!!!");
> +               SW_RESET_IN_PGM(1000);
> +               return -EBADMSG;
> +       }
> +
> +       hideep_pgm_set(ts);
> +       mdelay(1);
> +
> +       return 0;
> +}
> +
> +static void hideep_nvm_unlock(struct hideep_ts *ts)
> +{
> +       u32 unmask_code = 0;
> +
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
> +               HIDEEP_NVM_SFR_RPAGE);
> +
> +       hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
> +
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
> +               HIDEEP_NVM_DEFAULT_PAGE);
> +
> +       /* make it unprotected code */
> +       unmask_code &= (~HIDEEP_PROT_MODE);
> +
> +       /* compare unmask code */
> +       if (unmask_code != ts->nvm_mask)
> +               dev_dbg(&ts->client->dev, "read mask code different 0x%x",
> +                       unmask_code);
> +
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
> +               HIDEEP_NVM_SFR_WPAGE);
> +       SET_FLASH_PIO(0);
> +
> +       NVM_W_SFR(HIDEEP_NVM_MASK_OFS, ts->nvm_mask);
> +       SET_FLASH_HWCONTROL();
> +       hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG,
> +               HIDEEP_NVM_DEFAULT_PAGE);
> +}
> +
> +static int hideep_check_status(struct hideep_ts *ts)
> +{
> +       int ret, status;
> +       int time_out = 100;
> +
> +       while (time_out--) {
> +               mdelay(1);
> +               ret = hideep_pgm_r_reg(ts, HIDEEP_FLASH_STA,
> +                       &status);
> +
> +               if (ret < 0)
> +                       continue;
> +
> +               if (status)
> +                       return status;
> +       }
> +
> +       return time_out;
> +}
> +
> +static int hideep_program_page(struct hideep_ts *ts,
> +       u32 addr, struct pgm_packet *packet_w)
> +{
> +       int ret;
> +
> +
> +       ret = hideep_check_status(ts);
> +
> +       if (ret < 0)
> +               return -EBUSY;
> +
> +       addr = addr & ~(HIDEEP_NVM_PAGE_SIZE - 1);
> +
> +       SET_FLASH_PIO(0);
> +       SET_FLASH_PIO(1);
> +
> +       /* erase page */
> +       SET_PIO_SIG((HIDEEP_PERASE | addr), 0xFFFFFFFF);
> +
> +       SET_FLASH_PIO(0);
> +
> +       ret = hideep_check_status(ts);
> +
> +       if (ret < 0)
> +               return -EBUSY;
> +
> +       /* write page */
> +       SET_FLASH_PIO(1);
> +
> +       SET_PIO_SIG((HIDEEP_WRONLY | addr),
> +               get_unaligned_be32(&packet_w->payload[0]));
> +
> +       hideep_pgm_w_mem(ts, (HIDEEP_FLASH_PIO_SIG | HIDEEP_WRONLY),
> +               packet_w, HIDEEP_NVM_PAGE_SIZE);
> +
> +       SET_PIO_SIG(124, get_unaligned_be32(&packet_w->payload[31]));
> +
> +       SET_FLASH_PIO(0);
> +
> +       mdelay(1);
> +
> +       ret = hideep_check_status(ts);
> +
> +       if (ret < 0)
> +               return -EBUSY;
> +
> +       SET_FLASH_HWCONTROL();
> +
> +       return 0;
> +}
> +
> +static void hideep_program_nvm(struct hideep_ts *ts, const u8 *ucode,
> +       int len)
> +{
> +       struct pgm_packet packet_w;
> +       struct pgm_packet packet_r;
> +       int i;
> +       int ret;
> +       int addr = 0;
> +       int len_r = len;
> +       int len_w = HIDEEP_NVM_PAGE_SIZE;
> +       u32 pages = DIV_ROUND_UP(len, HIDEEP_NVM_PAGE_SIZE);
> +
> +
> +       hideep_nvm_unlock(ts);
> +
> +       dev_dbg(&ts->client->dev, "pages : %d", pages);
> +
> +       for (i = 0; i < pages; i++) {
> +               if (len_r < HIDEEP_NVM_PAGE_SIZE)
> +                       len_w = len_r;
> +
> +               /* compare */
> +               hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +                       HIDEEP_NVM_PAGE_SIZE);
> +               ret = memcmp(&ucode[addr], packet_r.payload, len_w);
> +
> +               if (ret) {
> +                       /* write page */
> +                       memcpy(packet_w.payload, &ucode[addr], len_w);
> +                       ret = hideep_program_page(ts, addr, &packet_w);
> +                       if (ret)
> +                               dev_err(&ts->client->dev,
> +                                       "%s : error(%08x):",
> +                                       __func__, addr);
> +                       mdelay(1);
> +               }
> +
> +               addr += HIDEEP_NVM_PAGE_SIZE;
> +               len_r -= HIDEEP_NVM_PAGE_SIZE;
> +               if (len_r < 0)
> +                       break;
> +       }
> +}
> +
> +static int hideep_verify_nvm(struct hideep_ts *ts, const u8 *ucode,
> +       int len)
> +{
> +       struct pgm_packet packet_r;
> +       int i, j;
> +       int ret;
> +       int addr = 0;
> +       int len_r = len;
> +       int len_v = HIDEEP_NVM_PAGE_SIZE;
> +       u32 pages = DIV_ROUND_UP(len, HIDEEP_NVM_PAGE_SIZE);
> +
> +       for (i = 0; i < pages; i++) {
> +               if (len_r < HIDEEP_NVM_PAGE_SIZE)
> +                       len_v = len_r;
> +
> +               hideep_pgm_r_mem(ts, 0x00000000 + addr, &packet_r,
> +                       HIDEEP_NVM_PAGE_SIZE);
> +
> +               ret = memcmp(&ucode[addr], packet_r.payload, len_v);
> +
> +               if (ret) {
> +                       u8 *read = (u8 *)packet_r.payload;
> +
> +                       for (j = 0; j < HIDEEP_NVM_PAGE_SIZE; j++) {
> +                               if (ucode[addr + j] != read[j])
> +                                       dev_err(&ts->client->dev,
> +                                               "verify : error([%d] %02x : %02x)",
> +                                               addr + j, ucode[addr + j],
> +                                               read[j]);
> +                       }
> +                       return ret;
> +               }
> +
> +               addr += HIDEEP_NVM_PAGE_SIZE;
> +               len_r -= HIDEEP_NVM_PAGE_SIZE;
> +               if (len_r < 0)
> +                       break;
> +       }
> +
> +       return 0;
> +}
> +
> +static int hideep_update_firmware(struct hideep_ts *ts, const char *fn)
> +{
> +       int ret;
> +       int retry, retry_cnt = 3;
> +       const struct firmware *fw_entry;
> +
> +       dev_dbg(&ts->client->dev, "enter");
> +       ret = request_firmware(&fw_entry, fn, &ts->client->dev);
> +
> +       if (ret != 0) {
> +               dev_err(&ts->client->dev, "request_firmware : fail(%d)", ret);
> +               return ret;
> +       }
> +
> +       if (fw_entry->size > ts->fw_size) {
> +               dev_err(&ts->client->dev,
> +                       "file size(%zu) is big more than fw memory size(%d)",
> +                       fw_entry->size, ts->fw_size);
> +               release_firmware(fw_entry);
> +               return -EFBIG;
> +       }
> +
> +       /* chip specific code for flash fuse */
> +       mutex_lock(&ts->dev_mutex);
> +
> +       /* enter program mode */
> +       ret = hideep_enter_pgm(ts);
> +
> +       if (ret)
> +               return ret;
> +
> +       /* comparing & programming each page, if the memory of specified
> +        * page is exactly same, no need to update.
> +        */
> +       for (retry = 0; retry < retry_cnt; retry++) {
> +               hideep_program_nvm(ts, fw_entry->data, fw_entry->size);
> +
> +               ret = hideep_verify_nvm(ts, fw_entry->data, fw_entry->size);
> +               if (!ret)
> +                       break;
> +       }
> +
> +       if (retry < retry_cnt)
> +               dev_dbg(&ts->client->dev, "update success!!!");
> +       else
> +               dev_err(&ts->client->dev, "update failed!!!");
> +
> +       SW_RESET_IN_PGM(1000);
> +
> +       mutex_unlock(&ts->dev_mutex);
> +
> +       release_firmware(fw_entry);
> +
> +       return ret;
> +}
> +
> +static int hideep_load_dwz(struct hideep_ts *ts)
> +{
> +       int ret = 0;
> +       struct pgm_packet packet_r;
> +
> +       ret = hideep_enter_pgm(ts);
> +
> +       if (ret)
> +               return ret;
> +
> +       mdelay(50);
> +
> +       hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, &packet_r,
> +               sizeof(struct dwz_info));
> +
> +       memcpy(&ts->dwz_info, packet_r.payload,
> +               sizeof(struct dwz_info));
> +
> +       SW_RESET_IN_PGM(10);
> +
> +       if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x60) {
> +               /* Lime fw size */
> +               dev_dbg(&ts->client->dev, "used lime IC");
> +               ts->fw_size = 1024 * 64;
> +               ts->nvm_mask = 0x0030027B;
> +       } else if (get_unaligned_le16(&ts->dwz_info.product_code) & 0x40) {
> +               /* Crimson IC */
> +               dev_dbg(&ts->client->dev, "used crimson IC");
> +               ts->fw_size = 1024 * 48;
> +               ts->nvm_mask = 0x00310000;
> +       } else {
> +               dev_dbg(&ts->client->dev, "product code is wrong!!!");
> +               return -EINVAL;
> +       }
> +
> +       dev_dbg(&ts->client->dev, "firmware release version : %04x",
> +               get_unaligned_le16(&ts->dwz_info.release_ver));
> +
> +       mdelay(50);
> +
> +       return 0;
> +}
> +
> +static int hideep_pwr_on(struct hideep_ts *ts)
> +{
> +       int ret = 0;
> +       u8 cmd = 0x01;
> +
> +       if (ts->vcc_vdd) {
> +               ret = regulator_enable(ts->vcc_vdd);
> +               if (ret)
> +                       dev_err(&ts->client->dev,
> +                               "Regulator vdd enable failed ret=%d", ret);
> +               usleep_range(999, 1000);
> +       }
> +
> +       if (ts->vcc_vid) {
> +               ret = regulator_enable(ts->vcc_vid);
> +               if (ret)
> +                       dev_err(&ts->client->dev,
> +                               "Regulator vcc_vid enable failed ret=%d", ret);
> +               usleep_range(2999, 3000);
> +       }
> +
> +       mdelay(30);
> +
> +       if (ts->reset_gpio)
> +               gpiod_set_raw_value(ts->reset_gpio, 1);
> +       else
> +               regmap_write(ts->reg, HIDEEP_RESET_CMD, cmd);
> +
> +       mdelay(50);
> +
> +       return ret;
> +}
> +
> +static void hideep_pwr_off(void *data)
> +{
> +       struct hideep_ts *ts = data;
> +
> +       if (ts->reset_gpio)
> +               gpiod_set_value(ts->reset_gpio, 0);
> +
> +       if (ts->vcc_vid)
> +               regulator_disable(ts->vcc_vid);
> +
> +       if (ts->vcc_vdd)
> +               regulator_disable(ts->vcc_vdd);
> +}
> +
> +#define __GET_MT_TOOL_TYPE(X) ((X == 0x01) ? MT_TOOL_FINGER : MT_TOOL_PEN)
> +
> +static void push_mt(struct hideep_ts *ts)
> +{
> +       int id;
> +       int i;
> +       int btn_up = 0;
> +       int evt = 0;
> +       int offset = sizeof(struct hideep_event);
> +       struct hideep_event *event;
> +
> +       /* load multi-touch event to input system */
> +       for (i = 0; i < ts->tch_count; i++) {
> +               event = (struct hideep_event *)&ts->touch_event[i * offset];
> +               id = event->index & 0x0F;
> +               btn_up = event->flag & HIDEEP_MT_RELEASED;
> +
> +               dev_dbg(&ts->client->dev,
> +                       "type = %d, id = %d, i = %d, x = %d, y = %d, z = %d",
> +                       event->type, event->index, i,
> +                       get_unaligned_le16(&event->x),
> +                       get_unaligned_le16(&event->y),
> +                       get_unaligned_le16(&event->z));
> +
> +               input_mt_slot(ts->input_dev, id);
> +               input_mt_report_slot_state(ts->input_dev,
> +                       __GET_MT_TOOL_TYPE(event->type),
> +                       (btn_up == 0));
> +
> +               if (btn_up == 0) {
> +                       input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
> +                               get_unaligned_le16(&event->x));
> +                       input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
> +                               get_unaligned_le16(&event->y));
> +                       input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
> +                               get_unaligned_le16(&event->z));
> +                       input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
> +                               event->w);
> +                       evt++;
> +               }
> +       }
> +
> +       input_mt_sync_frame(ts->input_dev);
> +}
> +
> +static void push_ky(struct hideep_ts *ts)
> +{
> +       int i;
> +       int status;
> +       int code;
> +
> +       for (i = 0; i < ts->key_count; i++) {
> +               code = ts->key_event[i * 2] & 0x0F;
> +               status = ts->key_event[i * 2] & 0xF0;
> +
> +               input_report_key(ts->input_dev, ts->key_codes[code],
> +                       status & HIDEEP_KEY_PRESSED_MASK);
> +       }
> +}
> +
> +static void hideep_put_event(struct hideep_ts *ts)
> +{
> +       /* mangling touch information */
> +       if (ts->tch_count > 0)
> +               push_mt(ts);
> +
> +       if (ts->key_count > 0)
> +               push_ky(ts);
> +
> +       input_sync(ts->input_dev);
> +}
> +
> +static int hideep_parse_event(struct hideep_ts *ts, u8 *data)
> +{
> +       int touch_count;
> +
> +       ts->tch_count = data[0];
> +       ts->key_count = data[1] & 0x0f;
> +       ts->lpm_count = data[1] & 0xf0;
> +
> +       /* get touch event count */
> +       dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
> +               ts->tch_count, ts->key_count, ts->lpm_count);
> +
> +       /* get touch event information */
> +       if (ts->tch_count < HIDEEP_MT_MAX)
> +               memcpy(ts->touch_event, &data[HIDEEP_TOUCH_EVENT_INDEX],
> +                       HIDEEP_MT_MAX * sizeof(struct hideep_event));
> +       else
> +               ts->tch_count = 0;
> +
> +       if (ts->key_count < HIDEEP_KEY_MAX)
> +               memcpy(ts->key_event, &data[HIDEEP_KEY_EVENT_INDEX],
> +                       HIDEEP_KEY_MAX * 2);
> +       else
> +               ts->key_count = 0;
> +
> +       touch_count = ts->tch_count + ts->key_count;
> +
> +       return touch_count;
> +}
> +
> +static irqreturn_t hideep_irq_task(int irq, void *handle)
> +{
> +       u8 buff[HIDEEP_MAX_EVENT];
> +       int ret;
> +
> +       struct hideep_ts *ts = handle;
> +
> +       ret = regmap_bulk_read(ts->reg, HIDEEP_EVENT_ADDR,
> +               buff, HIDEEP_MAX_EVENT / 2);
> +
> +       if (ret < 0)
> +               return IRQ_HANDLED;
> +
> +       ret = hideep_parse_event(ts, buff);
> +
> +       if (ret > 0)
> +               hideep_put_event(ts);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void hideep_get_axis_info(struct hideep_ts *ts)
> +{
> +       int ret;
> +       u8 val[4];
> +
> +       if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
> +               ret = regmap_bulk_read(ts->reg, 0x28, val, 2);
> +
> +               if (ret < 0) {
> +                       ts->prop.max_x = -1;
> +                       ts->prop.max_y = -1;
> +               } else {
> +                       ts->prop.max_x =
> +                               get_unaligned_le16(&val[0]);
> +                       ts->prop.max_y =
> +                               get_unaligned_le16(&val[2]);
> +               }
> +       }
> +
> +       dev_dbg(&ts->client->dev, "X : %d, Y : %d",
> +               ts->prop.max_x, ts->prop.max_y);
> +}
> +
> +static int hideep_capability(struct hideep_ts *ts)
> +{
> +       int ret, i;
> +
> +       hideep_get_axis_info(ts);
> +
> +       if (ts->prop.max_x < 0 || ts->prop.max_y < 0)
> +               return -EINVAL;
> +
> +       ts->input_dev->name = HIDEEP_TS_NAME;
> +       ts->input_dev->id.bustype = BUS_I2C;
> +
> +       if (ts->key_num) {
> +               ts->input_dev->keycode = ts->key_codes;
> +               ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
> +               ts->input_dev->keycodemax = ts->key_num;
> +               for (i = 0; i < ts->key_num; i++)
> +                       input_set_capability(ts->input_dev, EV_KEY,
> +                               ts->key_codes[i]);
> +       }
> +
> +       input_set_abs_params(ts->input_dev,
> +               ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
> +       input_set_abs_params(ts->input_dev,
> +               ABS_MT_POSITION_X, 0, ts->prop.max_x, 0, 0);
> +       input_set_abs_params(ts->input_dev,
> +               ABS_MT_POSITION_Y, 0, ts->prop.max_y, 0, 0);
> +       input_set_abs_params(ts->input_dev,
> +               ABS_MT_PRESSURE, 0, 65535, 0, 0);
> +       input_set_abs_params(ts->input_dev,
> +               ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +
> +       ret = input_mt_init_slots(ts->input_dev,
> +               HIDEEP_MT_MAX, INPUT_MT_DIRECT);
> +
> +       return ret;
> +}
> +
> +static ssize_t hideep_update_fw(struct device *dev,
> +       struct device_attribute *attr, const char *buf, size_t count)
> +{
> +       struct hideep_ts *ts = dev_get_drvdata(dev);
> +       int mode, ret;
> +       char *fw_name;
> +
> +       ret = kstrtoint(buf, 8, &mode);
> +       if (ret)
> +               return ret;
> +
> +       disable_irq(ts->client->irq);
> +
> +       fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
> +               get_unaligned_le16(&ts->dwz_info.product_id));
> +       ret = hideep_update_firmware(ts, fw_name);
> +
> +       if (ret != 0)
> +               dev_err(dev, "The firmware update failed(%d)", ret);
> +
> +       kfree(fw_name);
> +
> +       ret = hideep_load_dwz(ts);
> +
> +       if (ret < 0)
> +               dev_err(&ts->client->dev, "fail to load dwz, ret = 0x%x", ret);
> +
> +       enable_irq(ts->client->irq);
> +
> +       return count;
> +}
> +
> +static ssize_t hideep_fw_version_show(struct device *dev,
> +       struct device_attribute *attr, char *buf)
> +{
> +       int len = 0;
> +       struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +       mutex_lock(&ts->dev_mutex);
> +       len = scnprintf(buf, PAGE_SIZE,
> +               "%04x\n", get_unaligned_le16(&ts->dwz_info.release_ver));
> +       mutex_unlock(&ts->dev_mutex);
> +
> +       return len;
> +}
> +
> +static ssize_t hideep_product_id_show(struct device *dev,
> +       struct device_attribute *attr, char *buf)
> +{
> +       int len = 0;
> +       struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +       mutex_lock(&ts->dev_mutex);
> +       len = scnprintf(buf, PAGE_SIZE,
> +               "%04x\n", get_unaligned_le16(&ts->dwz_info.product_id));
> +       mutex_unlock(&ts->dev_mutex);
> +
> +       return len;
> +}
> +
> +static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
> +static DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
> +static DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
> +
> +static struct attribute *hideep_ts_sysfs_entries[] = {
> +       &dev_attr_version.attr,
> +       &dev_attr_product_id.attr,
> +       &dev_attr_update_fw.attr,
> +       NULL,
> +};
> +
> +static struct attribute_group hideep_ts_attr_group = {
> +       .attrs = hideep_ts_sysfs_entries,
> +};
> +
> +static int __maybe_unused hideep_resume(struct device *dev)
> +{
> +       struct hideep_ts *ts = dev_get_drvdata(dev);
> +       int ret;
> +
> +       ret = hideep_pwr_on(ts);
> +       if (ret < 0)
> +               dev_err(&ts->client->dev, "power on failed");
> +       else
> +               enable_irq(ts->client->irq);
> +
> +       return ret;
> +}
> +
> +static int __maybe_unused hideep_suspend(struct device *dev)
> +{
> +       struct hideep_ts *ts = dev_get_drvdata(dev);
> +
> +       disable_irq(ts->client->irq);
> +       hideep_pwr_off(ts);
> +
> +       return 0;
> +}
> +
> +static int  hideep_parse_dts(struct device *dev, struct hideep_ts *ts)
> +{
> +       int ret;
> +
> +       ts->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> +                                                       GPIOD_OUT_HIGH);
> +       if (IS_ERR(ts->reset_gpio))
> +               return PTR_ERR(ts->reset_gpio);
> +
> +       ts->vcc_vdd = devm_regulator_get(dev, "vdd");
> +       if (IS_ERR(ts->vcc_vdd))
> +               return PTR_ERR(ts->vcc_vdd);
> +
> +       ts->vcc_vid = devm_regulator_get(dev, "vid");
> +       if (IS_ERR(ts->vcc_vid))
> +               return PTR_ERR(ts->vcc_vid);
> +
> +       ts->key_num = device_property_read_u32_array(dev, "linux,keycodes",
> +                                               NULL, 0);
> +
> +       if (ts->key_num > HIDEEP_KEY_MAX) {
> +               dev_err(dev, "too many support key defined(%d)!!!",
> +                       ts->key_num);
> +               return -EINVAL;
> +       }
> +
> +       ret = device_property_read_u32_array(dev, "linux,keycodes",
> +                               ts->key_codes, ts->key_num);
> +       if (ret) {
> +               dev_dbg(dev, "don't support touch key");
> +               ts->key_num = 0;
> +       }
> +
> +       return 0;
> +}
> +
> +const struct regmap_config hideep_regmap_config = {
> +       .reg_bits = 16,
> +       .reg_format_endian = REGMAP_ENDIAN_LITTLE,
> +       .val_bits = 16,
> +       .val_format_endian = REGMAP_ENDIAN_LITTLE,
> +       .max_register = 0xffff,
> +};
> +
> +static int hideep_probe(struct i2c_client *client,
> +       const struct i2c_device_id *id)
> +{
> +       int ret;
> +       struct regmap *regmap;
> +       struct hideep_ts *ts;
> +
> +       /* check i2c bus */
> +       if (!i2c_check_functionality(client->adapter,
> +               I2C_FUNC_I2C)) {
> +               dev_err(&client->dev, "check i2c device error");
> +               return -ENODEV;
> +       }
> +
> +       regmap = devm_regmap_init_i2c(client, &hideep_regmap_config);
> +
> +       if (IS_ERR(regmap)) {
> +               dev_err(&client->dev, "don't init regmap");
> +               return PTR_ERR(regmap);
> +       }
> +
> +       /* init hideep_ts */
> +       ts = devm_kzalloc(&client->dev,
> +               sizeof(*ts), GFP_KERNEL);
> +       if (!ts)
> +               return -ENOMEM;
> +
> +       ret = hideep_parse_dts(&client->dev, ts);
> +
> +       if (ret)
> +               return ret;
> +
> +       ts->client = client;
> +       ts->reg = regmap;
> +
> +       i2c_set_clientdata(client, ts);
> +
> +       mutex_init(&ts->dev_mutex);
> +
> +       /* power on */
> +       ret = hideep_pwr_on(ts);
> +       if (ret) {
> +               dev_err(&ts->client->dev, "power on failed");
> +               return ret;
> +       }
> +
> +       ret = devm_add_action_or_reset(&ts->client->dev, hideep_pwr_off, ts);
> +       if (ret) {
> +               hideep_pwr_off(ts);
> +               return ret;
> +       }
> +
> +       mdelay(30);
> +
> +       /* read info */
> +       ret = hideep_load_dwz(ts);
> +       if (ret < 0) {
> +               dev_err(&client->dev, "fail to load dwz, ret = 0x%x", ret);
> +               return ret;
> +       }
> +
> +       /* init input device */
> +       ts->input_dev = devm_input_allocate_device(&client->dev);
> +       if (!ts->input_dev) {
> +               dev_err(&client->dev, "can't allocate memory for input_dev");
> +               return -ENOMEM;
> +       }
> +
> +       touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
> +
> +       ret = hideep_capability(ts);
> +       if (ret) {
> +               dev_err(&client->dev, "can't init input properties");
> +               return ret;
> +       }
> +
> +       ret = input_register_device(ts->input_dev);
> +       if (ret) {
> +               dev_err(&client->dev, "can't register input_dev");
> +               return ret;
> +       }
> +
> +       input_set_drvdata(ts->input_dev, ts);
> +
> +       dev_info(&ts->client->dev, "ts irq: %d", ts->client->irq);
> +       if (client->irq <= 0) {
> +               dev_err(&client->dev, "can't be assigned irq");
> +               return -EINVAL;
> +       }
> +
> +       ret = devm_request_threaded_irq(&client->dev, ts->client->irq,
> +               NULL, hideep_irq_task, IRQF_ONESHOT,
> +               ts->client->name, ts);
> +
> +       if (ret < 0) {
> +               dev_err(&client->dev, "fail to get irq, ret = 0x%08x",
> +                       ret);
> +               return ret;
> +       }
> +
> +       ret = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
> +
> +       if (ret) {
> +               dev_err(&client->dev, "fail init sys, ret = 0x%x", ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
> +
> +static const struct i2c_device_id hideep_dev_idtable[] = {
> +       { HIDEEP_I2C_NAME, 0 },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(i2c, hideep_dev_idtable);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id hideep_acpi_id[] = {
> +       { "HIDP0001", 0 },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id hideep_match_table[] = {
> +       { .compatible = "hideep,hideep-ts" },
> +       {}
> +};
> +MODULE_DEVICE_TABLE(of, hideep_match_table);
> +#endif
> +
> +static struct i2c_driver hideep_driver = {
> +       .probe = hideep_probe,
> +       .id_table = hideep_dev_idtable,
> +       .driver = {
> +               .name = HIDEEP_I2C_NAME,
> +               .of_match_table = of_match_ptr(hideep_match_table),
> +               .acpi_match_table = ACPI_PTR(hideep_acpi_id),
> +               .pm = &hideep_pm_ops,
> +       },
> +};
> +
> +module_i2c_driver(hideep_driver);
> +
> +MODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
> +MODULE_AUTHOR("anthony.kim@hideep.com");
> +MODULE_LICENSE("GPL v2");
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
       [not found] ` <CA+d1ZXLAVrWHpgOzQMtWzPN9kz0=EdAxa7gKE=UxMbzVms3Npg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2017-07-17 17:27   ` Rob Herring
  0 siblings, 0 replies; 29+ messages in thread
From: Rob Herring @ 2017-07-17 17:27 UTC (permalink / raw)
  To: 김태영
  Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

On Tue, Jul 11, 2017 at 05:01:47PM +0900, 김태영 wrote:
> 2017-07-10 10:17 GMT+09:00 Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
> >
> > On Wed, Jul 05, 2017 at 03:19:48PM +0900, Anthony Kim wrote:
> > > The HiDeep touchscreen device is a capacitive multi-touch controller
> > > mainly for multi-touch supported devices use. It use I2C interface for
> > > communication to IC and provide axis X, Y, Z locations for ten finger
> > > touch through input event interface to userspace.
> > >
> > > It support the Crimson and the Lime two type IC. They are different
> > > the number of channel supported and FW size. But the working protocol
> > > is same.
> > >
> > > Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> > > ---
> > >  .../bindings/input/touchscreen/hideep.txt          |   40 +
> > >  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
> > >  drivers/input/touchscreen/Kconfig                  |   32 +
> > >  drivers/input/touchscreen/Makefile                 |    2 +
> > >  drivers/input/touchscreen/hideep.h                 |  338 +++++++
> > >  drivers/input/touchscreen/hideep_core.c            | 1029 ++++++++++++++++++++
> > >  drivers/input/touchscreen/hideep_dbg.c             |  405 ++++++++
> > >  drivers/input/touchscreen/hideep_dbg.h             |   24 +
> > >  drivers/input/touchscreen/hideep_isp.c             |  584 +++++++++++
> > >  drivers/input/touchscreen/hideep_isp.h             |   96 ++
> > >  drivers/input/touchscreen/hideep_sysfs.c           |  249 +++++
> > >  11 files changed, 2800 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> > >  create mode 100644 drivers/input/touchscreen/hideep.h
> > >  create mode 100644 drivers/input/touchscreen/hideep_core.c
> > >  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
> > >  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
> > >  create mode 100644 drivers/input/touchscreen/hideep_isp.c
> > >  create mode 100644 drivers/input/touchscreen/hideep_isp.h
> > >  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
> > >
> > > diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> > > new file mode 100644
> > > index 0000000..f5ab5e6
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> > > @@ -0,0 +1,40 @@
> > > +* HiDeep Finger and Stylus touchscreen controller
> > > +
> > > +Required properties:
> > > +- compatible         : must be "hideep,hideep_ts".
> >
> > s/_/-/
> >
> > Is there only 1 version or another way to determine the exact chip and
> > firmware? If not, then you need a more specific compatible.
> 
> Yes, we have to determine the chip and firmware through the firmware file name
>  and reading I2C about info.

How do you determine the firmware filename then?

> Also the chip can select by kernel config.

If the kernel knows the specific chip, then the DT certainly should have 
that.

Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Input: add support for HiDeep touchscreen
@ 2017-07-11  8:01 김태영
       [not found] ` <CA+d1ZXLAVrWHpgOzQMtWzPN9kz0=EdAxa7gKE=UxMbzVms3Npg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 29+ messages in thread
From: 김태영 @ 2017-07-11  8:01 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Anthony Kim

2017-07-10 10:17 GMT+09:00 Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
>
> On Wed, Jul 05, 2017 at 03:19:48PM +0900, Anthony Kim wrote:
> > The HiDeep touchscreen device is a capacitive multi-touch controller
> > mainly for multi-touch supported devices use. It use I2C interface for
> > communication to IC and provide axis X, Y, Z locations for ten finger
> > touch through input event interface to userspace.
> >
> > It support the Crimson and the Lime two type IC. They are different
> > the number of channel supported and FW size. But the working protocol
> > is same.
> >
> > Signed-off-by: Anthony Kim <anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
> > ---
> >  .../bindings/input/touchscreen/hideep.txt          |   40 +
> >  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
> >  drivers/input/touchscreen/Kconfig                  |   32 +
> >  drivers/input/touchscreen/Makefile                 |    2 +
> >  drivers/input/touchscreen/hideep.h                 |  338 +++++++
> >  drivers/input/touchscreen/hideep_core.c            | 1029 ++++++++++++++++++++
> >  drivers/input/touchscreen/hideep_dbg.c             |  405 ++++++++
> >  drivers/input/touchscreen/hideep_dbg.h             |   24 +
> >  drivers/input/touchscreen/hideep_isp.c             |  584 +++++++++++
> >  drivers/input/touchscreen/hideep_isp.h             |   96 ++
> >  drivers/input/touchscreen/hideep_sysfs.c           |  249 +++++
> >  11 files changed, 2800 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> >  create mode 100644 drivers/input/touchscreen/hideep.h
> >  create mode 100644 drivers/input/touchscreen/hideep_core.c
> >  create mode 100644 drivers/input/touchscreen/hideep_dbg.c
> >  create mode 100644 drivers/input/touchscreen/hideep_dbg.h
> >  create mode 100644 drivers/input/touchscreen/hideep_isp.c
> >  create mode 100644 drivers/input/touchscreen/hideep_isp.h
> >  create mode 100644 drivers/input/touchscreen/hideep_sysfs.c
> >
> > diff --git a/Documentation/devicetree/bindings/input/touchscreen/hideep.txt b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> > new file mode 100644
> > index 0000000..f5ab5e6
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/input/touchscreen/hideep.txt
> > @@ -0,0 +1,40 @@
> > +* HiDeep Finger and Stylus touchscreen controller
> > +
> > +Required properties:
> > +- compatible         : must be "hideep,hideep_ts".
>
> s/_/-/
>
> Is there only 1 version or another way to determine the exact chip and
> firmware? If not, then you need a more specific compatible.

Yes, we have to determine the chip and firmware through the firmware file name
 and reading I2C about info.
Also the chip can select by kernel config.

>
> > +- reg                        : I2C slave address, (e.g. 0x6C).
> > +- hideep,max_coords  : Max value for axis X, Y, W, Z.
> > +
> > +Optional properties:
> > +- pinctrl-names      : "reset_down", "reset-up", "intb-ctrl".
> > +                             They are gpio pinctrl names for should be search in driver code.
> > +- pinctrl-0  : Gpio control for "reset-down".
> > +- pinctrl-1  : Gpio control for "reset-up".
> > +- pinctrl-2  : Gpio control for "intb-ctrl".
> > +- hideep,regulator_vdd       : Main voltage(3.3V) name.
> > +- hideep,regulator_vid       : IO voltage(1.8V) name.
>
> Use standard regulator binding.
>
> > +- hideep,irq_gpio            : Define for interrupt gpio pin.
> > +                                             It is to use for set interrupt type.
>
> Use interrupt binding.
>
> > +- hideep,reset_gpio          : Define for reset gpio pin.
> > +                                             It is to use for reset IC.
>
> reset-gpios is the standard name.
>
> > +
> > +Example:
> > +
> > +i2c@00000000 {
> > +
> > +     /* ... */
> > +
> > +     hideep@6c {
>
> touchscreen@6c
>
> > +             compatible = "hideep,hideep_ts";
> > +             reg = <0x6c>;
> > +             pinctrl-names = "reset-down", "reset-up", "intb-ctrl";
> > +             pinctrl-0 = <&reset_gpio0>;
> > +             pinctrl-1 = <&reset_gpio1>;
> > +             pinctrl-2 = <&touch_int>;
> > +             hideep,regulator_vdd = "vdd_ldo33";     // need modify
> > +             hideep,regulator_vid = "vdd_ldo18";     // need modify
> > +             hideep,irq_gpio = <&gpx0 5 0x0>;        // <gpio-name gpio-num gpio-val> need modify
> > +             hideep,reset_gpio = <&gpa0 5 0x1>;      // <gpio-name gpio-num gpio-val> need modify
> > +             hideep,max_coords = <1080 1920 65535 65535>;    // x y w z, need modify
>
> Not documented. Probably should be standard touchscreen properties.
>
> > +     };
> > +};

Thank you for your comments.
I will resend patch to modified.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2017-09-26  3:36 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-05  6:19 [PATCH] Input: add support for HiDeep touchscreen Anthony Kim
     [not found] ` <1499235588-32219-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
2017-07-10  1:17   ` Rob Herring
2017-07-12  5:24 ` Anthony Kim
     [not found]   ` <1499837054-4659-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
2017-07-17 17:31     ` Rob Herring
2017-07-20  0:22     ` Anthony Kim
     [not found]       ` <1500510154-6661-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
2017-07-24 19:52         ` Rob Herring
2017-07-25  6:53       ` Anthony Kim
     [not found]         ` <1500965607-2446-1-git-send-email-anthony.kim-7TZDMcjUQMzQT0dZR+AlfA@public.gmane.org>
2017-08-03 17:50           ` Rob Herring
2017-08-09  7:54             ` Anthony Kim
2017-08-09 23:49           ` Dmitry Torokhov
2017-08-10  8:01             ` Anthony Kim
2017-08-22  9:03         ` Anthony Kim
2017-08-22  9:03           ` Anthony Kim
2017-08-23  0:07           ` Rob Herring
2017-08-31  6:51           ` Dmitry Torokhov
2017-09-11  7:27           ` Anthony Kim
2017-09-12 14:17             ` Rob Herring
2017-09-13  7:50             ` kbuild test robot
2017-09-13  7:50               ` kbuild test robot
2017-09-13  8:51             ` kbuild test robot
2017-09-13  8:51               ` kbuild test robot
2017-09-14  4:36             ` Anthony Kim
2017-09-14 18:51               ` Dmitry Torokhov
2017-09-18 19:03               ` Rob Herring
2017-09-26  1:42               ` Anthony Kim
2017-09-26  3:28               ` Anthony Kim
2017-09-26  3:30                 ` Anthony Kim
2017-07-11  8:01 김태영
     [not found] ` <CA+d1ZXLAVrWHpgOzQMtWzPN9kz0=EdAxa7gKE=UxMbzVms3Npg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-07-17 17:27   ` Rob Herring

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.