Add modified Startek panel driver with DT overlay

-Enable DSI driver with Startek panel. Note that issues with color are
still present.
-Enable touchscreen driver with overlay modifications needed to be
confirmed
-DT overlay still needs to be cleaned up a bit

Change-Id: I8b710bbd42b8f147be24a76ffbc34dd4219502b5
diff --git a/arch/arm64/boot/dts/mediatek/mt8167-coral-display-overlay.dts b/arch/arm64/boot/dts/mediatek/mt8167-coral-display-overlay.dts
index 0be0d31..4848e39 100644
--- a/arch/arm64/boot/dts/mediatek/mt8167-coral-display-overlay.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8167-coral-display-overlay.dts
@@ -3,12 +3,35 @@
 /plugin/;
 
 #include "mt8167-pinfunc.h"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
 
 / {
 	compatible = "mediatek,mt8167";
 	fragment@0 {
 		target = <&pio>;
 			__overlay__ {
+
+				panel_pins: panel_pins {
+					pins1 {
+						pinmux = <MT8167_PIN_66_LCM_RST__FUNC_GPIO66>;
+						output-high;
+					};
+				};
+
+				ts_pin_nint: ts_pin_nint {
+					pins1 {
+						pinmux = <MT8167_PIN_16_EINT16__FUNC_GPIO16>;
+					};
+				};
+
+				ts_pin_nrst: ts_pin_nrst {
+					pins1 {
+						pinmux = <MT8167_PIN_21_EINT21__FUNC_GPIO21>;
+						output-high;
+					};
+				};
+
 				pwm0_pin_default: pwm0_pin_default {
 					pins1 {
 						pinmux = <MT8167_PIN_25_EINT25__FUNC_PWM_B>;
@@ -23,6 +46,81 @@
 				status = "okay";
 				pinctrl-names = "default";
 				pinctrl-0 = <&pwm0_pin_default>;
+				#pwm-cells = <2>;
+			};
+	};
+
+	fragment@2 {
+		target = <&backlight>;
+			__overlay__ {
+				status = "okay";
+				pwms = <&pwm 0 1000000>;
+				enable-gpios = <&pio 25 0>;
+				brightness-levels = <0 4 8 16 32 64 128 255>;
+				default-brightness-level = <6>;
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c2>;
+			__overlay__ {
+				focaltech@38 {
+					status = "okay";
+					compatible = "focaltech,fts";
+					reg = <0x38>;
+					pinctrl-0 = <&ts_pin_nint>;
+					focaltech,panel-type = <FT6236>;
+					focaltech,reset-gpio = <&pio 21 GPIO_ACTIVE_HIGH>;
+					focaltech,irq-gpio = <&pio 16 GPIO_ACTIVE_HIGH>;
+					focaltech,max-touch-number = <5>;
+					focaltech,display-coords = <0 0 320 480>;
+				};
+			};
+	};
+
+	fragment@4 {
+		target = <&mipi_tx>;
+			__overlay__ {
+				status = "okay";
+		};
+	};
+
+
+	fragment@5 {
+		target = <&dsi>;
+			__overlay__ {
+				status = "okay";
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				ports {
+					port@0 {
+						reg = <0>;
+						dsi_out: endpoint {
+							remote-endpoint = <&panel_in>;
+						};
+					};
+				};
+
+				panel@0 {
+					compatible = "startek,ili9488";
+					reg = <0>;
+					pinctrl-names = "default";
+					pinctrl-0 = <&panel_pins>;
+					reset-gpio = <&pio 66 GPIO_ACTIVE_HIGH>;
+					dsi-lanes = <1>;
+					panel-width-mm = <49>;
+					panel-height-mm = <74>;
+					backlight = <&backlight>;
+					status = "okay";
+
+					port {
+						panel_in: endpoint {
+							remote-endpoint = <&dsi_out>;
+						};
+					};
+				};
 			};
 	};
 };
+
diff --git a/arch/arm64/boot/dts/mediatek/mt8167-coral.dts b/arch/arm64/boot/dts/mediatek/mt8167-coral.dts
index da6f034..aaba44d 100644
--- a/arch/arm64/boot/dts/mediatek/mt8167-coral.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8167-coral.dts
@@ -51,6 +51,13 @@
 		gpio = <&pio 17 GPIO_ACTIVE_LOW>;
 	};
 
+	backlight_vbus: backlight_vbus {
+		compatible = "regulator-fixed";
+		regulator-name = "backlight_vbus";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+	};
+
 	ga1600_pmic: ga1600_pmic {
 		compatible = "regulator-fixed";
 		regulator-name = "ga1600_pmic";
@@ -123,7 +130,13 @@
 		thermal_calibration: calib@180 {
 			reg = <0x180 0xc>;
 		};
-};
+	};
+
+	backlight: backlight {
+		compatible = "pwm-backlight";
+		status = "disabled";
+		power-supply = <&backlight_vbus>;
+	};
 
 	thermal-zones {
 		cpu_thermal: cpu_thermal {
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 429d697..3dc5dc4 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -125,6 +125,13 @@
 	  Pi 7" Touchscreen modified for use with the pumpkin boards.
 	  To compile this driver as a module, choose M here.
 
+config DRM_PANEL_STARTEK_ILI9488
+  tristate "Startek Panel"
+  depends on OF
+  depends on DRM_MIPI_DSI
+  help
+     Say Y here if you want to enable the Startek Panel.
+
 config DRM_PANEL_RAYDIUM_RM68200
 	tristate "Raydium RM68200 720x1280 DSI video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index e4cd72b6..6bf27b4 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -23,3 +23,4 @@
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_TPV_OTM1901A) += panel-tpv-otm1901a.o
 obj-$(CONFIG_DRM_PANEL_TRULY_R63350A) += panel-truly-r63350a.o
+obj-$(CONFIG_DRM_PANEL_STARTEK_ILI9488) += panel-startek-ili9488.o
diff --git a/drivers/gpu/drm/panel/panel-startek-ili9488.c b/drivers/gpu/drm/panel/panel-startek-ili9488.c
new file mode 100644
index 0000000..8304529
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-startek-ili9488.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2020 Google LLC
+ * Author: Michael Hoang
+ *
+ * Based on driver of same name originally meant for different platform.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <uapi/linux/media-bus-format.h>
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct startek_panel {
+	struct drm_panel panel;
+	struct mipi_dsi_device *dsi;
+	struct gpio_desc *reset;
+	struct backlight_device *backlight;
+};
+
+static const struct drm_display_mode startek_default_timing = {
+	.clock = 26000000 / 1000,
+	.hdisplay = 320,
+	.hsync_start = 320 + 170,
+	.hsync_end = 320 + 170 + 50,
+	.htotal = 320 + 170 + 50 + 50,
+	.vdisplay = 480,
+	.vsync_start = 480 + 2,
+	.vsync_end = 480 + 2 + 1,
+	.vtotal = 480 + 2 + 1 + 1,
+	.vrefresh = 60,
+};
+
+#define MAX_PARA_NUM 16
+struct dcs_command {
+	unsigned char cmd;
+	unsigned char para_size;
+	unsigned char para[MAX_PARA_NUM];
+	unsigned int sleep;
+};
+
+static const struct dcs_command g_startek_init[] = {
+		// PGAMCTRL
+		{.cmd = 0xe0,
+		 .para_size = 15,
+		 .para = {0x00, 0x04, 0x0e, 0x08, 0x17, 0x0a, 0x40, 0x79, 0x4d, 0x07, 0x0e,
+							0x0a, 0x1a, 0x1d, 0x0f}},
+
+		// NGAMCTRL
+		{.cmd = 0xe1,
+		 .para_size = 15,
+		 .para = {0x00, 0x1b, 0x1f, 0x02, 0x10, 0x05, 0x32, 0x34, 0x43, 0x02, 0x0a,
+							0x09, 0x33, 0x37, 0x0f}},
+
+		// Power Control 1
+		{.cmd = 0xc0, .para_size = 2, .para = {0x18, 0x16}},
+
+		// Power Control 2
+		{.cmd = 0xc1, .para_size = 1, .para = {0x41}},
+
+		// VCOM Control
+		{.cmd = 0xc5, .para_size = 3, .para = {0x00, 0x1e, 0x80}},
+
+		// Interface Mode Control
+		{.cmd = 0x3a, .para_size = 1, .para = {0x77}},
+
+		// Frame rate 70HZ
+		{.cmd = 0xb1, .para_size = 1, .para = {0xb0}},
+
+		// Display Inversion Control
+		{.cmd = 0xb4, .para_size = 1, .para = {0x02}},
+
+		// Blanking Porch Control
+		{.cmd = 0xb5, .para_size = 4, .para = {0x02, 0x02, 0xaa, 0x64}},
+
+		// Set Image Function
+		{.cmd = 0xe9, .para_size = 1, .para = {0x01}},
+
+		// Adjust Control 3
+		{.cmd = 0xf7, .para_size = 4, .para = {0xa9, 0x51, 0x2c, 0x82}},
+
+		// Display Function Control
+		{.cmd = 0xb6, .para_size = 3, .para = {0x02, 0x02, 0x3b}},
+
+		// Column Address Set
+		{.cmd = 0x2a, .para_size = 4, .para = {0x00, 0x00, 0x01, 0x3f}},
+
+		// Page Address Set
+		{.cmd = 0x2b, .para_size = 4, .para = {0x00, 0x00, 0x01, 0xdf}},
+
+		// Entry Mode Set
+		{.cmd = 0xb7, .para_size = 1, .para = {0xc6}},
+
+		// Interface Mode Control
+		{.cmd = 0xb0, .para_size = 1, .para = {0x00}},
+
+};
+
+// Seems 0x36 only takes effect after exit sleep.
+// And if init all registers after exit sleep, other issue.
+// So we seperate 2 init phases.
+
+static const struct dcs_command g_startek_later_init[] = {
+		// Memory Access Control
+		{.cmd = 0x36, .para_size = 1, .para = {0x48}},
+};
+
+static int startek_init(struct mipi_dsi_device *dsi,
+			             const struct dcs_command *init_command, int num)
+{
+	int i;
+	int ret;
+
+	dev_info(&dsi->dev, "%s(), command num %d\n", __func__, num);
+	for (i = 0; i < num; i++) {
+		struct dcs_command *cmd = &init_command[i];
+
+		ret = mipi_dsi_dcs_write(dsi, cmd->cmd, cmd->para, cmd->para_size);
+		if (ret < 0) {
+			dev_err(&dsi->dev, "%s(), command 0x%x, ret %d\n", __func__, cmd->cmd,
+							ret);
+			return ret;
+		}
+
+		if (cmd->sleep)
+			msleep(cmd->sleep);
+	}
+
+	return 0;
+}
+
+static int startek_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+	int ret;
+
+	mode = drm_mode_duplicate(panel->drm, &startek_default_timing);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+				startek_default_timing.hdisplay, startek_default_timing.vdisplay,
+				startek_default_timing.vrefresh);
+		return -ENOMEM;
+	}
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.bpc = 8;
+	panel->connector->display_info.height_mm = 49;
+	panel->connector->display_info.width_mm = 74;
+
+	panel->connector->display_info.bus_flags =
+			DRM_BUS_FLAG_PIXDATA_POSEDGE | DRM_BUS_FLAG_DE_HIGH;
+
+	ret = drm_display_info_set_bus_formats(&panel->connector->display_info,
+			                                &bus_format, 1);
+
+	if (ret) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int startek_panel_prepare(struct drm_panel *panel)
+{
+	int ret = 0;
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+	struct mipi_dsi_device *dsi = startek->dsi;
+	uint8_t ctrl = 0x2C;
+	uint8_t powersave = 0x0;
+	uint16_t min_bright = 0x80;
+
+	gpiod_set_value(startek->reset, 1);
+	msleep(1);
+	gpiod_set_value(startek->reset, 0);
+	msleep(10);
+	gpiod_set_value(startek->reset, 1);
+	msleep(120);
+
+	ret = startek_init(dsi, g_startek_init, ARRAY_SIZE(g_startek_init));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "%s(), startek_init failed, ret %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x0ff);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set brightness: %d", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, &ctrl,
+													 sizeof(ctrl));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set control: %d", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE, &powersave,
+													 sizeof(powersave));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set powersave: %d", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, &min_bright,
+													 sizeof(min_bright));
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to set min_bright: %d", ret);
+		return ret;
+	}
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to exit sleep mode: %d", ret);
+		return ret;
+	}
+	msleep(120);
+
+	startek_init(dsi, g_startek_later_init, ARRAY_SIZE(g_startek_later_init));
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to turn on display: %d", ret);
+		return ret;
+	}
+
+	dev_info(&dsi->dev, "prepare ok\n");
+
+	return 0;
+}
+
+static int startek_panel_unprepare(struct drm_panel *panel)
+{
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+	struct mipi_dsi_device *dsi = startek->dsi;
+	int ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Could not set display off: %d", ret);
+		return ret;
+	}
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Could not enter sleep mode: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int startek_panel_enable(struct drm_panel *panel)
+{
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+
+	backlight_enable(startek->backlight);
+
+	return 0;
+}
+
+static int startek_panel_disable(struct drm_panel *panel)
+{
+	struct startek_panel *startek =
+			container_of(panel, struct startek_panel, panel);
+
+	backlight_disable(startek->backlight);
+
+	return 0;
+}
+
+static const struct drm_panel_funcs startek_panel_funcs =
+{
+		.get_modes = startek_panel_get_modes,
+		.enable = startek_panel_enable,
+		.disable = startek_panel_disable,
+		.prepare = startek_panel_prepare,
+		.unprepare = startek_panel_unprepare,
+};
+
+static int startek_panel_probe(struct mipi_dsi_device *dsi) {
+	struct startek_panel *startek;
+	int ret;
+
+	startek = devm_kzalloc(&dsi->dev, sizeof(*startek), GFP_KERNEL);
+	if (!startek)
+		return -ENOMEM;
+
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =
+		MIPI_DSI_MODE_VIDEO_HSE |
+		MIPI_DSI_MODE_VIDEO |
+		MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ret = of_property_read_u32(dsi->dev.of_node, "dsi-lanes", &dsi->lanes);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to get dsi-lanes property: %d", ret);
+		return ret;
+	}
+
+	mipi_dsi_set_drvdata(dsi, startek);
+
+	startek->dsi = dsi;
+
+	startek->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(startek->reset)) {
+		ret = PTR_ERR(startek->reset);
+		dev_err(&dsi->dev, "Failed to get reset gpio: %d\n", ret);
+		return ret;
+	}
+
+	startek->backlight = devm_of_find_backlight(&startek->dsi->dev);
+	if (IS_ERR(startek->backlight)) {
+		ret = PTR_ERR(startek->backlight);
+		dev_err(&dsi->dev, "Failed to get backlight: %d\n", ret);
+		return ret;
+	}
+
+	drm_panel_init(&startek->panel);
+	startek->panel.funcs = &startek_panel_funcs;
+	startek->panel.dev = &startek->dsi->dev;
+
+	ret = drm_panel_add(&startek->panel);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Failed to add panel: %d\n", ret);
+		return ret;
+	}
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(&dsi->dev, "Unable to mipi_dsi_attach! %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int startek_panel_remove(struct mipi_dsi_device *dsi) {
+
+	struct startek_panel *startek = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	gpiod_set_value(startek->reset, 1);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "Failed to mipi_dsi_detach! %d\n", ret);
+
+	if (startek->panel.dev)
+		drm_panel_remove(&startek->panel);
+
+	return 0;
+}
+
+static const struct of_device_id startek_of_match[] = {
+		{
+				.compatible = "startek,ili9488",
+		},
+		{}};
+MODULE_DEVICE_TABLE(of, startek_of_match);
+
+static struct mipi_dsi_driver startek_panel_driver = {
+		.driver =
+				{
+						.name = "panel-startek-ili9488", .of_match_table = startek_of_match,
+				},
+		.probe = startek_panel_probe,
+		.remove = startek_panel_remove,
+};
+module_mipi_dsi_driver(startek_panel_driver);
+
+MODULE_AUTHOR("Coral Support <coral-support@google.com>");
+MODULE_DESCRIPTION("Startek Display panel");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/focaltech_touch/Kconfig b/drivers/input/touchscreen/focaltech_touch/Kconfig
new file mode 100644
index 0000000..fe8894d
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/Kconfig
@@ -0,0 +1,16 @@
+#
+# Focaltech Touchscreen driver configuration
+#
+
+config TOUCHSCREEN_FTS
+        bool "Focaltech Touchscreen"
+		depends on I2C
+		default y
+		help
+		  Say Y here if you have Focaltech touch panel.
+		  If unsure, say N.
+
+config TOUCHSCREEN_FTS_DIRECTORY
+    string "Focaltech ts directory name"
+    default "focaltech_touch"
+    depends on TOUCHSCREEN_FTS
diff --git a/drivers/input/touchscreen/focaltech_touch/Makefile b/drivers/input/touchscreen/focaltech_touch/Makefile
new file mode 100644
index 0000000..1b2b8ec
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the focaltech touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_FTS)	+=  focaltech_core.o
+obj-$(CONFIG_TOUCHSCREEN_FTS)	+=  focaltech_esdcheck.o
+obj-$(CONFIG_TOUCHSCREEN_FTS)	+=  focaltech_ex_mode.o
+obj-$(CONFIG_TOUCHSCREEN_FTS)	+=  focaltech_gesture.o
+obj-$(CONFIG_TOUCHSCREEN_FTS)   +=  focaltech_point_report_check.o
+obj-$(CONFIG_TOUCHSCREEN_FTS)   +=  focaltech_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_FTS)   +=  focaltech_sensor.o
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_common.h b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h
new file mode 100644
index 0000000..ae4807c
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h
@@ -0,0 +1,211 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_common.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-16
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_COMMON_H__
+#define __LINUX_FOCALTECH_COMMON_H__
+
+#include "focaltech_config.h"
+
+/*****************************************************************************
+* Macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_VERSION	"Focaltech V1.2 20161229"
+
+#define FLAGBIT(x)		(0x00000001 << (x))
+#define FLAGBITS(x, y)		((0xFFFFFFFF >> (32 - (y) - 1)) << (x))
+
+#define FLAG_ICSERIALS_LEN      5
+#define FLAG_IDC_BIT            11
+
+#define FTS_CHIP_TYPE_MAPPING { \
+	{0x01, 0x58, 0x22, 0x58, 0x22, 0x00, 0x00, 0x58, 0x2C}, \
+	{0x02, 0x54, 0x22, 0x54, 0x22, 0x00, 0x00, 0x54, 0x2C}, \
+	{0x03, 0x64, 0x26, 0x64, 0x26, 0x00, 0x00, 0x79, 0x1C}, \
+	{0x04, 0x33, 0x67, 0x64, 0x26, 0x00, 0x00, 0x79, 0x1C}, \
+	{0x05, 0x87, 0x16, 0x87, 0x16, 0x87, 0xA6, 0x00, 0x00}, \
+	{0x06, 0x87, 0x36, 0x87, 0x36, 0x87, 0xC6, 0x00, 0x00}, \
+	{0x07, 0x80, 0x06, 0x80, 0x06, 0x80, 0xC6, 0x80, 0xB6}, \
+	{0x08, 0x86, 0x06, 0x86, 0x06, 0x86, 0xA6, 0x00, 0x00}, \
+	{0x09, 0x86, 0x07, 0x86, 0x07, 0x86, 0xA7, 0x00, 0x00}, \
+	{0x0A, 0xE7, 0x16, 0x87, 0x16, 0xE7, 0xA6, 0x87, 0xB6}, \
+}
+
+#define I2C_BUFFER_LENGTH_MAXINUM           256
+#define FILE_NAME_LENGTH                    128
+#define ENABLE                              1
+#define DISABLE                             0
+/*register address*/
+#define FTS_REG_INT_CNT                     0x8F
+#define FTS_REG_FLOW_WORK_CNT               0x91
+#define FTS_REG_WORKMODE                    0x00
+#define FTS_REG_WORKMODE_FACTORY_VALUE      0x40
+#define FTS_REG_WORKMODE_WORK_VALUE         0x00
+#define FTS_REG_CHIP_ID                     0xA3
+#define FTS_REG_CHIP_ID2                    0x9F
+#define FTS_REG_POWER_MODE                  0xA5
+#define FTS_REG_POWER_MODE_SLEEP_VALUE      0x03
+#define FTS_REG_FW_VER                      0xA6
+#define FTS_REG_VENDOR_ID                   0xA8
+#define FTS_REG_LCD_BUSY_NUM                0xAB
+#define FTS_REG_FACE_DEC_MODE_EN            0xB0
+#define FTS_REG_GLOVE_MODE_EN               0xC0
+#define FTS_REG_COVER_MODE_EN               0xC1
+#define FTS_REG_CHARGER_MODE_EN             0x8B
+#define FTS_REG_GESTURE_EN                  0xD0
+#define FTS_REG_GESTURE_OUTPUT_ADDRESS      0xD3
+#define FTS_REG_ESD_SATURATE                0xED
+
+/*****************************************************************************
+*  Alternative mode (When something goes wrong, the modules may be able to solve the problem.)
+*****************************************************************************/
+/*
+ * point report check
+ * default: disable
+ */
+#define FTS_POINT_REPORT_CHECK_EN               0
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct ft_chip_t {
+	unsigned long type;
+	unsigned char chip_idh;
+	unsigned char chip_idl;
+	unsigned char rom_idh;
+	unsigned char rom_idl;
+	unsigned char pramboot_idh;
+	unsigned char pramboot_idl;
+	unsigned char bootloader_idh;
+	unsigned char bootloader_idl;
+};
+
+/* i2c communication*/
+int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue);
+int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue);
+int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen,
+		 char *readbuf, int readlen);
+int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen);
+int fts_i2c_init(void);
+int fts_i2c_exit(void);
+
+/* Gesture functions */
+#if FTS_GESTURE_EN
+int fts_gesture_init(struct input_dev *input_dev, struct i2c_client *client);
+int fts_gesture_exit(struct i2c_client *client);
+void fts_gesture_recovery(struct i2c_client *client);
+int fts_gesture_readdata(struct i2c_client *client);
+int fts_gesture_suspend(struct i2c_client *i2c_client);
+int fts_gesture_resume(struct i2c_client *client);
+#endif
+
+/* Apk and functions */
+#if FTS_APK_NODE_EN
+int fts_create_apk_debug_channel(struct i2c_client *client);
+void fts_release_apk_debug_channel(void);
+#endif
+
+/* ADB functions */
+#if FTS_SYSFS_NODE_EN
+int fts_create_sysfs(struct i2c_client *client);
+int fts_remove_sysfs(struct i2c_client *client);
+#endif
+
+/* ESD */
+#if FTS_ESDCHECK_EN
+int fts_esdcheck_init(void);
+int fts_esdcheck_exit(void);
+int fts_esdcheck_switch(bool enable);
+int fts_esdcheck_proc_busy(bool proc_debug);
+int fts_esdcheck_set_intr(bool intr);
+int fts_esdcheck_suspend(void);
+int fts_esdcheck_resume(void);
+int fts_esdcheck_get_status(void);
+#endif
+
+/* Production test */
+#if FTS_TEST_EN
+int fts_test_init(struct i2c_client *client);
+int fts_test_exit(struct i2c_client *client);
+#endif
+
+/* Point Report Check*/
+#if FTS_POINT_REPORT_CHECK_EN
+int fts_point_report_check_init(void);
+int fts_point_report_check_exit(void);
+void fts_point_report_check_queue_work(void);
+#endif
+
+/* Other */
+extern int g_show_log;
+int fts_reset_proc(int hdelayms);
+int fts_wait_tp_to_valid(struct i2c_client *client);
+void fts_tp_state_recovery(struct i2c_client *client);
+int fts_ex_mode_init(struct i2c_client *client);
+int fts_ex_mode_exit(struct i2c_client *client);
+int fts_ex_mode_recovery(struct i2c_client *client);
+
+void fts_irq_disable(void);
+void fts_irq_enable(void);
+
+/*****************************************************************************
+* DEBUG function define here
+*****************************************************************************/
+#if FTS_DEBUG_EN
+#define FTS_DEBUG_LEVEL     1
+
+#if (FTS_DEBUG_LEVEL == 2)
+#define FTS_DEBUG(fmt, args...) \
+	printk(KERN_ERR "[FTS][%s]"fmt"\n", __func__, ##args)
+#else
+#define FTS_DEBUG(fmt, args...) \
+	printk(KERN_ERR "[FTS]"fmt"\n", ##args)
+#endif
+
+#define FTS_FUNC_ENTER() \
+	printk(KERN_ERR "[FTS]%s: Enter\n", __func__)
+#define FTS_FUNC_EXIT() \
+	printk(KERN_ERR "[FTS]%s: Exit(%d)\n", __func__, __LINE__)
+#else
+#define FTS_DEBUG(fmt, args...)
+#define FTS_FUNC_ENTER()
+#define FTS_FUNC_EXIT()
+#endif
+
+#define FTS_INFO(fmt, args...) do {	\
+		if (g_show_log) \
+			printk(KERN_ERR "[FTS][Info]"fmt"\n", ##args); \
+	} while (0)
+
+#define FTS_ERROR(fmt, args...)  do {	\
+		if (g_show_log) \
+			printk(KERN_ERR "[FTS][Error]"fmt"\n", ##args); \
+	} while (0)
+
+#endif /* __LINUX_FOCALTECH_COMMON_H__ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_config.h b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h
new file mode 100644
index 0000000..87b622e
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h
@@ -0,0 +1,219 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/************************************************************************
+*
+* File Name: focaltech_config.h
+*
+*    Author: Focaltech Driver Team
+*
+*   Created: 2016-08-08
+*
+*  Abstract: global configurations
+*
+*   Version: v1.0
+*
+************************************************************************/
+#ifndef _LINUX_FOCLATECH_CONFIG_H_
+#define _LINUX_FOCLATECH_CONFIG_H_
+
+/**************************************************/
+/****** G: A, I: B, S: C, U: D  ******************/
+/****** chip type defines, do not modify *********/
+#define _FT8716     0x87160805
+#define _FT8736     0x87360806
+#define _FT8006     0x80060807
+#define _FT8606     0x86060808
+#define _FT8607     0x86070809
+#define _FTE716     0xE716080a
+
+#define _FT5416     0x54160002
+#define _FT5426     0x54260002
+#define _FT5435     0x54350002
+#define _FT5436     0x54360002
+#define _FT5526     0x55260002
+#define _FT5526I    0x5526B002
+#define _FT5446     0x54460002
+#define _FT5346     0x53460002
+#define _FT5446I    0x5446B002
+#define _FT5346I    0x5346B002
+#define _FT7661     0x76610002
+#define _FT7511     0x75110002
+#define _FT7421     0x74210002
+#define _FT7681     0x76810002
+#define _FT3C47U    0x3C47D002
+#define _FT3417     0x34170002
+#define _FT3517     0x35170002
+#define _FT3327     0x33270002
+#define _FT3427     0x34270002
+
+#define _FT5626     0x56260001
+#define _FT5726     0x57260001
+#define _FT5826B    0x5826B001
+#define _FT5826S    0x5826C001
+#define _FT7811     0x78110001
+#define _FT3D47     0x3D470001
+#define _FT3617     0x36170001
+#define _FT3717     0x37170001
+#define _FT3817B    0x3817B001
+
+#define _FT6236U    0x6236D003
+#define _FT6336G    0x6336A003
+#define _FT6336U    0x6336D003
+#define _FT6436U    0x6436D003
+
+#define _FT3267     0x32670004
+#define _FT3367     0x33670004
+
+/******************* Enables *********************/
+/*********** 1 to enable, 0 to disable ***********/
+
+/*
+ * show debug log info
+ * enable it for debug, disable it for release
+ */
+#define FTS_DEBUG_EN                            0
+
+/*
+ * Linux MultiTouch Protocol
+ * 1: Protocol B(default), 0: Protocol A
+ */
+#define FTS_MT_PROTOCOL_B_EN                    1
+
+/*
+ * Report Pressure in multitouch
+ * 1:enable(default),0:disable
+*/
+#define FTS_REPORT_PRESSURE_EN                  1
+
+/*
+ * Force touch support
+ * different pressure for multitouch
+ * 1: true pressure for force touch
+ * 0: constant pressure(default)
+ */
+#define FTS_FORCE_TOUCH_EN                      0
+
+/*
+ * Gesture function enable
+ * default: disable
+ */
+#define FTS_GESTURE_EN                          0
+
+/*
+ * ESD check & protection
+ * default: disable
+ */
+#define FTS_ESDCHECK_EN                         0
+
+/*
+ * Production test enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_TEST_EN                             0
+
+/*
+ * Glove mode enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_GLOVE_EN                            0
+/*
+ * cover enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_COVER_EN                            0
+/*
+ * Charger enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_CHARGER_EN                          0
+
+/*
+ * Proximity sensor
+ * default: disable
+ */
+#define FTS_PSENSOR_EN                          0
+
+/*
+ * Nodes for tools, please keep enable
+ */
+#define FTS_SYSFS_NODE_EN                       1
+#define FTS_APK_NODE_EN                         1
+
+/*
+ * Customer power enable
+ * enable it when customer need control TP power
+ * default: disable
+ */
+#define FTS_POWER_SOURCE_CUST_EN                0
+
+/****************************************************/
+
+/********************** Upgrade ****************************/
+/*
+ * auto upgrade, please keep enable
+ */
+#define FTS_AUTO_UPGRADE_EN                     1
+
+/*
+ * auto upgrade for lcd cfg
+ * default: 0
+ */
+#define FTS_AUTO_UPGRADE_FOR_LCD_CFG_EN         0
+
+/* auto cb check
+ * default: disable
+ */
+#define FTS_AUTO_CLB_EN                         0
+
+/*
+ * FW_APP.i file for upgrade
+ * define your own fw_app, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW_APP                      "include/firmware/FT8716_app_sample.i"
+
+/*
+ * lcd_cfg.i file for lcd cfg upgrade
+ * define your own lcd_cfg.i, the sample one is invalid
+ */
+#define FTS_UPGRADE_LCD_CFG                     "include/firmware/lcd_cfg.i"
+
+/* get vedor id from flash
+ * default: enable
+ */
+#define FTS_GET_VENDOR_ID                       0
+
+/*
+ * vendor_id(s) for the ic
+ * you need confirm vendor_id for upgrade
+ * if only one vendor, ignore vendor_2_id, otherwise
+ * you need define both of them
+ */
+#define FTS_VENDOR_1_ID                         0x8d
+#define FTS_VENDOR_2_ID                         0x8d
+
+/*
+ * upgrade stress test for debug
+ * enable it for upgrade debug if needed
+ * default: disable
+ */
+#define FTS_UPGRADE_STRESS_TEST					0
+/* stress test times, default: 1000 */
+#define FTS_UPGRADE_TEST_NUMBER                 1000
+
+/*********************************************************/
+
+#endif /* _LINUX_FOCLATECH_CONFIG_H_ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
new file mode 100644
index 0000000..77c70cc
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
@@ -0,0 +1,1448 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#define FTS_SUSPEND_LEVEL 1	/* Early-suspend level */
+#endif
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_NAME                     "fts_ts"
+#define INTERVAL_READ_REG                   20
+#define TIMEOUT_READ_REG                    300
+#if FTS_POWER_SOURCE_CUST_EN
+#define FTS_VTG_MIN_UV                      2600000
+#define FTS_VTG_MAX_UV                      3300000
+#define FTS_I2C_VTG_MIN_UV                  1800000
+#define FTS_I2C_VTG_MAX_UV                  1800000
+#endif
+#define FTS_READ_TOUCH_BUFFER_DIVIDED       0
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+******************************************************************************/
+struct i2c_client *fts_i2c_client;
+struct fts_ts_data *fts_wq_data;
+struct input_dev *fts_input_dev;
+
+#if FTS_DEBUG_EN
+int g_show_log = 1;
+#else
+int g_show_log;
+#endif
+
+#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
+char g_sz_debug[1024] = { 0 };
+#endif
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static void fts_release_all_finger(void);
+static int fts_ts_suspend(struct device *dev);
+static int fts_ts_resume(struct device *dev);
+
+static bool fts_chip_idc(struct fts_ts_data *data)
+{
+	return ((data->pdata->fts_chip_type & FLAGBIT(FLAG_IDC_BIT)) ==
+		FLAGBIT(FLAG_IDC_BIT));
+}
+
+/*****************************************************************************
+*  Name: fts_wait_tp_to_valid
+*  Brief:   Read chip id until TP FW become valid,
+*	    need call when reset/power on/resume...
+*           1. Read Chip ID per INTERVAL_READ_REG(20ms)
+*           2. Timeout: TIMEOUT_READ_REG(300ms)
+*  Input:
+*  Output:
+*  Return: 0 - Get correct Device ID
+*****************************************************************************/
+int fts_wait_tp_to_valid(struct i2c_client *client)
+{
+	int ret = 0;
+	int cnt = 0;
+	u8 reg_value = 0;
+
+	do {
+		ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &reg_value);
+		if (ret < 0)
+			FTS_INFO("TP Not Ready, ReadData = 0x%x", reg_value);
+		else {
+			FTS_INFO("TP Ready, Device ID = 0x%x", reg_value);
+			return 0;
+		}
+		cnt++;
+		msleep(INTERVAL_READ_REG);
+	} while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+	/* error: not get correct reg data */
+	return -1;
+}
+
+/*****************************************************************************
+*  Name: fts_recover_state
+*  Brief: Need execute this function when reset
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_tp_state_recovery(struct i2c_client *client)
+{
+	/* wait tp stable */
+	fts_wait_tp_to_valid(client);
+	/* recover TP charger state 0x8B */
+	/* recover TP glove state 0xC0 */
+	/* recover TP cover state 0xC1 */
+	fts_ex_mode_recovery(client);
+	/* recover TP gesture state 0xD0 */
+#if FTS_GESTURE_EN
+	fts_gesture_recovery(client);
+#endif
+
+	fts_release_all_finger();
+}
+
+/*****************************************************************************
+*  Name: fts_reset_proc
+*  Brief: Execute reset operation
+*  Input: hdelayms - delay time unit:ms
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_reset_proc(int hdelayms)
+{
+	gpio_direction_output(fts_wq_data->pdata->reset_gpio, 0);
+	msleep(20);
+	gpio_direction_output(fts_wq_data->pdata->reset_gpio, 1);
+	msleep(hdelayms);
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_irq_disable
+*  Brief: disable irq
+*  Input:
+*   sync:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_irq_disable(void)
+{
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&fts_wq_data->irq_lock, irqflags);
+
+	if (!fts_wq_data->irq_disable) {
+		disable_irq_nosync(fts_wq_data->client->irq);
+		fts_wq_data->irq_disable = 1;
+	}
+
+	spin_unlock_irqrestore(&fts_wq_data->irq_lock, irqflags);
+}
+
+/*****************************************************************************
+*  Name: fts_irq_enable
+*  Brief: enable irq
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_irq_enable(void)
+{
+	unsigned long irqflags = 0;
+
+	spin_lock_irqsave(&fts_wq_data->irq_lock, irqflags);
+
+	if (fts_wq_data->irq_disable) {
+		enable_irq(fts_wq_data->client->irq);
+		fts_wq_data->irq_disable = 0;
+	}
+
+	spin_unlock_irqrestore(&fts_wq_data->irq_lock, irqflags);
+}
+
+/*****************************************************************************
+*  Name: fts_input_dev_init
+*  Brief: input dev init
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_input_dev_init(struct i2c_client *client,
+			      struct fts_ts_data *data,
+			      struct input_dev *input_dev,
+			      struct fts_ts_platform_data *pdata)
+{
+	int err, len;
+
+	FTS_FUNC_ENTER();
+
+	/* Init and register Input device */
+	input_dev->name = FTS_DRIVER_NAME;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	input_set_drvdata(input_dev, data);
+	i2c_set_clientdata(client, data);
+
+	__set_bit(EV_KEY, input_dev->evbit);
+	if (data->pdata->have_key) {
+		FTS_DEBUG("set key capabilities");
+		for (len = 0; len < data->pdata->key_number; len++) {
+			input_set_capability(input_dev, EV_KEY,
+					     data->pdata->keys[len]);
+		}
+	}
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+#if FTS_MT_PROTOCOL_B_EN
+	input_mt_init_slots(input_dev, pdata->max_touch_number,
+			    INPUT_MT_DIRECT);
+#else
+	input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0f, 0, 0);
+#endif
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min,
+			     pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
+			     pdata->y_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
+#if FTS_REPORT_PRESSURE_EN
+	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
+#endif
+
+	err = input_register_device(input_dev);
+	if (err) {
+		FTS_ERROR("Input device registration failed");
+		goto free_inputdev;
+	}
+
+	FTS_FUNC_EXIT();
+
+	return 0;
+
+free_inputdev:
+	input_free_device(input_dev);
+	FTS_FUNC_EXIT();
+	return err;
+
+}
+
+/*****************************************************************************
+* Power Control
+*****************************************************************************/
+#if FTS_POWER_SOURCE_CUST_EN
+static int fts_power_source_init(struct fts_ts_data *data)
+{
+	int rc;
+
+	FTS_FUNC_ENTER();
+
+	data->vdd = regulator_get(&data->client->dev, "vdd");
+	if (IS_ERR(data->vdd)) {
+		rc = PTR_ERR(data->vdd);
+		FTS_ERROR("Regulator get failed vdd rc=%d", rc);
+	}
+
+	if (regulator_count_voltages(data->vdd) > 0) {
+		rc = regulator_set_voltage(data->vdd, FTS_VTG_MIN_UV,
+					   FTS_VTG_MAX_UV);
+		if (rc) {
+			FTS_ERROR("Regulator set_vtg failed vdd rc=%d", rc);
+			goto reg_vdd_put;
+		}
+	}
+
+	data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
+	if (IS_ERR(data->vcc_i2c)) {
+		rc = PTR_ERR(data->vcc_i2c);
+		FTS_ERROR("Regulator get failed vcc_i2c rc=%d", rc);
+		goto reg_vdd_set_vtg;
+	}
+
+	if (regulator_count_voltages(data->vcc_i2c) > 0) {
+		rc = regulator_set_voltage(data->vcc_i2c, FTS_I2C_VTG_MIN_UV,
+					   FTS_I2C_VTG_MAX_UV);
+		if (rc) {
+			FTS_ERROR("Regulator set_vtg failed vcc_i2c rc=%d", rc);
+			goto reg_vcc_i2c_put;
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return 0;
+
+reg_vcc_i2c_put:
+	regulator_put(data->vcc_i2c);
+reg_vdd_set_vtg:
+	if (regulator_count_voltages(data->vdd) > 0)
+		regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV);
+reg_vdd_put:
+	regulator_put(data->vdd);
+	FTS_FUNC_EXIT();
+	return rc;
+}
+
+static int fts_power_source_ctrl(struct fts_ts_data *data, int enable)
+{
+	int rc;
+
+	FTS_FUNC_ENTER();
+	if (enable) {
+		rc = regulator_enable(data->vdd);
+		if (rc)
+			FTS_ERROR("Regulator vdd enable failed rc=%d", rc);
+
+		rc = regulator_enable(data->vcc_i2c);
+		if (rc)
+			FTS_ERROR("Regulator vcc_i2c enable failed rc=%d", rc);
+	} else {
+		rc = regulator_disable(data->vdd);
+		if (rc)
+			FTS_ERROR("Regulator vdd disable failed rc=%d", rc);
+		rc = regulator_disable(data->vcc_i2c);
+		if (rc)
+			FTS_ERROR("Regulator vcc_i2c disable failed rc=%d", rc);
+	}
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+#endif
+
+/*****************************************************************************
+*  Reprot related
+*****************************************************************************/
+/*****************************************************************************
+*  Name: fts_release_all_finger
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_release_all_finger(void)
+{
+#if FTS_MT_PROTOCOL_B_EN
+	unsigned int finger_count = 0;
+
+	for (finger_count = 0;
+	     finger_count < fts_wq_data->pdata->max_touch_number;
+	     finger_count++) {
+		input_mt_slot(fts_input_dev, finger_count);
+		input_mt_report_slot_state(fts_input_dev, MT_TOOL_FINGER,
+					   false);
+	}
+#else
+	input_mt_sync(fts_input_dev);
+#endif
+	input_report_key(fts_input_dev, BTN_TOUCH, 0);
+	input_sync(fts_input_dev);
+}
+
+#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
+static void fts_show_touch_buffer(u8 *buf, int point_num)
+{
+	int len = point_num * FTS_ONE_TCH_LEN;
+	int count = 0;
+	int i;
+
+	memset(g_sz_debug, 0, 1024);
+	if (len > (POINT_READ_BUF - 3))
+		len = POINT_READ_BUF - 3;
+	else if (len == 0) {
+		len += FTS_ONE_TCH_LEN;
+
+	count += sprintf(g_sz_debug, "%02X,%02X,%02X", buf[0], buf[1], buf[2]);
+	for (i = 0; i < len; i++)
+		count += sprintf(g_sz_debug + count, ",%02X", buf[i + 3]);
+
+	FTS_DEBUG("buffer: %s", g_sz_debug);
+}
+#endif
+
+static int fts_input_dev_report_key_event(struct ts_event *event,
+					  struct fts_ts_data *data)
+{
+	int i;
+
+	if (data->pdata->have_key) {
+		if ((1 == event->touch_point || 1 == event->point_num)
+		    && (event->au16_y[0] == data->pdata->key_y_coord)) {
+
+			if (event->point_num == 0) {
+				FTS_DEBUG("Keys All Up!");
+				for (i = 0; i < data->pdata->key_number; i++) {
+					input_report_key(data->input_dev,
+							 data->pdata->keys[i],
+							 0);
+				}
+			} else {
+				for (i = 0; i < data->pdata->key_number; i++) {
+					if (event->au16_x[0] >
+					    (data->pdata->key_x_coords[i] -
+					     FTS_KEY_WIDTH)
+					    && event->au16_x[0] <
+					    (data->pdata->key_x_coords[i] +
+					     FTS_KEY_WIDTH)) {
+
+						if (event->au8_touch_event[i] ==
+						    0
+						    || event->au8_touch_event[i]
+						    == 2) {
+							input_report_key
+							    (data->input_dev,
+							     data->pdata->keys
+							     [i], 1);
+							FTS_DEBUG
+							    ("Key%d(%d, %d) DOWN!",
+							     i,
+							     event->au16_x[0],
+							     event->au16_y[0]);
+						} else {
+							input_report_key
+							    (data->input_dev,
+							     data->pdata->keys
+							     [i], 0);
+							FTS_DEBUG
+							    ("Key%d(%d, %d) Up!",
+							     i,
+							     event->au16_x[0],
+							     event->au16_y[0]);
+						}
+						break;
+					}
+				}
+			}
+			input_sync(data->input_dev);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+#if FTS_MT_PROTOCOL_B_EN
+static int fts_input_dev_report_b(struct ts_event *event,
+				  struct fts_ts_data *data)
+{
+	int i = 0;
+	int uppoint = 0;
+	int touchs = 0;
+
+	for (i = 0; i < event->touch_point; i++) {
+		input_mt_slot(data->input_dev, event->au8_finger_id[i]);
+
+		if (event->au8_touch_event[i] == FTS_TOUCH_DOWN
+		    || event->au8_touch_event[i] == FTS_TOUCH_CONTACT) {
+			input_mt_report_slot_state(data->input_dev,
+						   MT_TOOL_FINGER, true);
+
+#if FTS_REPORT_PRESSURE_EN
+#if FTS_FORCE_TOUCH_EN
+			if (event->pressure[i] <= 0) {
+				FTS_ERROR("[B]Illegal pressure: %d",
+					  event->pressure[i]);
+				event->pressure[i] = 1;
+			}
+#else
+			event->pressure[i] = 0x3f;
+#endif
+			input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+					 event->pressure[i]);
+#endif
+
+			if (event->area[i] <= 0) {
+				FTS_ERROR("[B]Illegal touch-major: %d",
+					  event->area[i]);
+				event->area[i] = 1;
+			}
+			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+					 event->area[i]);
+
+			input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+					 event->au16_x[i]);
+			input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+					 event->au16_y[i]);
+			touchs |= BIT(event->au8_finger_id[i]);
+			data->touchs |= BIT(event->au8_finger_id[i]);
+
+#if FTS_REPORT_PRESSURE_EN
+			FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+				  event->au8_finger_id[i], event->au16_x[i],
+				  event->au16_y[i], event->pressure[i],
+				  event->area[i]);
+#else
+			FTS_DEBUG("[B]P%d(%d, %d)[tm:%d] DOWN!",
+				  event->au8_finger_id[i], event->au16_x[i],
+				  event->au16_y[i], event->area[i]);
+#endif
+		} else {
+			uppoint++;
+			input_mt_report_slot_state(data->input_dev,
+						   MT_TOOL_FINGER, false);
+#if FTS_REPORT_PRESSURE_EN
+			input_report_abs(data->input_dev, ABS_MT_PRESSURE, 0);
+#endif
+			data->touchs &= ~BIT(event->au8_finger_id[i]);
+			FTS_DEBUG("[B]P%d UP!", event->au8_finger_id[i]);
+		}
+	}
+
+	if (unlikely(data->touchs ^ touchs)) {
+		for (i = 0; i < data->pdata->max_touch_number; i++) {
+			if (BIT(i) & (data->touchs ^ touchs)) {
+				FTS_DEBUG("[B]P%d UP!", i);
+				input_mt_slot(data->input_dev, i);
+				input_mt_report_slot_state(data->input_dev,
+							   MT_TOOL_FINGER,
+							   false);
+#if FTS_REPORT_PRESSURE_EN
+				input_report_abs(data->input_dev,
+						 ABS_MT_PRESSURE, 0);
+#endif
+			}
+		}
+	}
+
+	data->touchs = touchs;
+	if (event->touch_point == uppoint) {
+		FTS_DEBUG("Points All Up!");
+		input_report_key(data->input_dev, BTN_TOUCH, 0);
+	} else {
+		input_report_key(data->input_dev, BTN_TOUCH,
+				 event->touch_point > 0);
+	}
+
+	input_sync(data->input_dev);
+
+	return 0;
+
+}
+
+#else
+static int fts_input_dev_report_a(struct ts_event *event,
+				  struct fts_ts_data *data)
+{
+	int i = 0;
+	int uppoint = 0;
+	int touchs = 0;
+
+	for (i = 0; i < event->touch_point; i++) {
+
+		if (event->au8_touch_event[i] == FTS_TOUCH_DOWN
+		    || event->au8_touch_event[i] == FTS_TOUCH_CONTACT) {
+			input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+					 event->au8_finger_id[i]);
+#if FTS_REPORT_PRESSURE_EN
+#if FTS_FORCE_TOUCH_EN
+			if (event->pressure[i] <= 0) {
+				FTS_ERROR("[B]Illegal pressure: %d",
+					  event->pressure[i]);
+				event->pressure[i] = 1;
+			}
+#else
+			event->pressure[i] = 0x3f;
+#endif
+			input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+					 event->pressure[i]);
+#endif
+
+			if (event->area[i] <= 0) {
+				FTS_ERROR("[B]Illegal touch-major: %d",
+					  event->area[i]);
+				event->area[i] = 1;
+			}
+			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+					 event->area[i]);
+
+			input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+					 event->au16_x[i]);
+			input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+					 event->au16_y[i]);
+
+			input_mt_sync(data->input_dev);
+
+#if FTS_REPORT_PRESSURE_EN
+			FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+				  event->au8_finger_id[i], event->au16_x[i],
+				  event->au16_y[i], event->pressure[i],
+				  event->area[i]);
+#else
+			FTS_DEBUG("[B]P%d(%d, %d)[tm:%d] DOWN!",
+				  event->au8_finger_id[i], event->au16_x[i],
+				  event->au16_y[i], event->area[i]);
+#endif
+		} else {
+			uppoint++;
+		}
+	}
+
+	data->touchs = touchs;
+	if (event->touch_point == uppoint) {
+		FTS_DEBUG("Points All Up!");
+		input_report_key(data->input_dev, BTN_TOUCH, 0);
+		input_mt_sync(data->input_dev);
+	} else {
+		input_report_key(data->input_dev, BTN_TOUCH,
+				 event->touch_point > 0);
+	}
+
+	input_sync(data->input_dev);
+
+	return 0;
+}
+#endif
+
+/*****************************************************************************
+*  Name: fts_read_touchdata
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_read_touchdata(struct fts_ts_data *data)
+{
+	u8 buf[POINT_READ_BUF] = { 0 };
+	u8 pointid = FTS_MAX_ID;
+	int ret = -1;
+	int i;
+	struct ts_event *event = &(data->event);
+
+#if FTS_GESTURE_EN
+	{
+		u8 state;
+
+		if (data->suspended) {
+			fts_i2c_read_reg(data->client, FTS_REG_GESTURE_EN,
+					 &state);
+			if (state == 1) {
+				fts_gesture_readdata(data->client);
+				return 1;
+			}
+		}
+	}
+#endif
+
+#if FTS_PSENSOR_EN
+	if ((fts_sensor_read_data(data) != 0) && (data->suspended == 1))
+		return 1;
+#endif
+
+#if FTS_READ_TOUCH_BUFFER_DIVIDED
+	memset(buf, 0xFF, POINT_READ_BUF);
+	memset(event, 0, sizeof(struct ts_event));
+
+	buf[0] = 0x00;
+	ret = fts_i2c_read(data->client, buf, 1, buf, (3 + FTS_ONE_TCH_LEN));
+	if (ret < 0) {
+		FTS_ERROR("%s read touchdata failed.", __func__);
+		return ret;
+	}
+	event->touch_point = 0;
+	event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
+	if (event->point_num > data->pdata->max_touch_number)
+		event->point_num = data->pdata->max_touch_number;
+
+	if (event->point_num > 1) {
+		buf[9] = 0x09;
+		fts_i2c_read(data->client, buf + 9, 1, buf + 9,
+			     (event->point_num - 1) * FTS_ONE_TCH_LEN);
+	}
+#else
+	ret = fts_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
+	if (ret < 0) {
+		FTS_ERROR("[B]Read touchdata failed, ret: %d", ret);
+		return ret;
+	}
+#if FTS_POINT_REPORT_CHECK_EN
+	fts_point_report_check_queue_work();
+#endif
+
+	memset(event, 0, sizeof(struct ts_event));
+	event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
+	if (event->point_num > data->pdata->max_touch_number)
+		event->point_num = data->pdata->max_touch_number;
+	event->touch_point = 0;
+#endif
+
+#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
+	fts_show_touch_buffer(buf, event->point_num);
+#endif
+
+	for (i = 0; i < data->pdata->max_touch_number; i++) {
+		pointid = (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4;
+		if (pointid >= FTS_MAX_ID)
+			break;
+
+		event->touch_point++;
+		event->au16_x[i] =
+		    (s16) (buf[FTS_TOUCH_X_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F)
+		    << 8 | (s16) buf[FTS_TOUCH_X_L_POS + FTS_ONE_TCH_LEN * i];
+		event->au16_y[i] =
+		    (s16) (buf[FTS_TOUCH_Y_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F)
+		    << 8 | (s16) buf[FTS_TOUCH_Y_L_POS + FTS_ONE_TCH_LEN * i];
+		event->au8_touch_event[i] =
+		    buf[FTS_TOUCH_EVENT_POS + FTS_ONE_TCH_LEN * i] >> 6;
+
+		if (data->pdata->swap)
+			swap(event->au16_x[i], event->au16_y[i]);
+
+		if (data->pdata->scaling_down_half) {
+			event->au16_x[i] = event->au16_x[i] >> 1;
+			event->au16_y[i] = event->au16_y[i] >> 1;
+		}
+
+		event->au8_finger_id[i] =
+		    (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4;
+		event->area[i] =
+		    (buf[FTS_TOUCH_AREA_POS + FTS_ONE_TCH_LEN * i]) >> 4;
+		event->pressure[i] =
+		    (s16) buf[FTS_TOUCH_PRE_POS + FTS_ONE_TCH_LEN * i];
+
+		if (0 == event->area[i])
+			event->area[i] = 0x09;
+
+		if (0 == event->pressure[i])
+			event->pressure[i] = 0x3f;
+
+		if ((event->au8_touch_event[i] == 0
+		     || event->au8_touch_event[i] == 2)
+		    && (event->point_num == 0))
+			break;
+	}
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_report_value
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_report_value(struct fts_ts_data *data)
+{
+	struct ts_event *event = &data->event;
+
+	FTS_DEBUG("point number: %d, touch point: %d", event->point_num,
+		  event->touch_point);
+
+	if (0 == fts_input_dev_report_key_event(event, data))
+		return;
+#if FTS_MT_PROTOCOL_B_EN
+	fts_input_dev_report_b(event, data);
+#else
+	fts_input_dev_report_a(event, data);
+#endif
+
+	return;
+
+}
+
+/*****************************************************************************
+*  Name: fts_ts_interrupt
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static irqreturn_t fts_ts_interrupt(int irq, void *dev_id)
+{
+	struct fts_ts_data *fts_ts = dev_id;
+	int ret = -1;
+
+	if (!fts_ts) {
+		FTS_ERROR("[INTR]: Invalid fts_ts");
+		return IRQ_HANDLED;
+	}
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_set_intr(1);
+#endif
+
+	ret = fts_read_touchdata(fts_wq_data);
+
+	if (ret == 0)
+		fts_report_value(fts_wq_data);
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_set_intr(0);
+#endif
+
+	return IRQ_HANDLED;
+}
+
+/*****************************************************************************
+*  Name: fts_gpio_configure
+*  Brief: Configure IRQ&RESET GPIO
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_gpio_configure(struct fts_ts_data *data)
+{
+	int err = 0;
+
+	FTS_FUNC_ENTER();
+	/* request irq gpio */
+	if (gpio_is_valid(data->pdata->irq_gpio)) {
+		err = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio");
+		if (err) {
+			FTS_ERROR("[GPIO]irq gpio request failed");
+			goto err_irq_gpio_req;
+		}
+
+		err = gpio_direction_input(data->pdata->irq_gpio);
+		if (err) {
+			FTS_ERROR("[GPIO]set_direction for irq gpio failed");
+			goto err_irq_gpio_dir;
+		}
+	}
+	/* request reset gpio */
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		err = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio");
+		if (err) {
+			FTS_ERROR("[GPIO]reset gpio request failed");
+			goto err_irq_gpio_dir;
+		}
+
+		err = gpio_direction_output(data->pdata->reset_gpio, 1);
+		if (err) {
+			FTS_ERROR("[GPIO]set_direction for reset gpio failed");
+			goto err_reset_gpio_dir;
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return 0;
+
+err_reset_gpio_dir:
+	if (gpio_is_valid(data->pdata->reset_gpio))
+		gpio_free(data->pdata->reset_gpio);
+err_irq_gpio_dir:
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->irq_gpio);
+err_irq_gpio_req:
+	FTS_FUNC_EXIT();
+	return err;
+}
+
+/*****************************************************************************
+*  Name: fts_get_dt_coords
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_get_dt_coords(struct device *dev, char *name,
+			     struct fts_ts_platform_data *pdata)
+{
+	u32 coords[FTS_COORDS_ARR_SIZE];
+	struct property *prop;
+	struct device_node *np = dev->of_node;
+	int coords_size, rc;
+
+	prop = of_find_property(np, name, NULL);
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	coords_size = prop->length / sizeof(u32);
+	if (coords_size != FTS_COORDS_ARR_SIZE) {
+		FTS_ERROR("invalid %s", name);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_array(np, name, coords, coords_size);
+	if (rc && (rc != -EINVAL)) {
+		FTS_ERROR("Unable to read %s", name);
+		return rc;
+	}
+
+		pr_err("x_max: %d", pdata->x_max);
+		pr_err("y_max: %d", pdata->y_max);
+		pr_err("coords[2]: %d", coords[2]);
+		pr_err("coords[3]: %d", coords[3]);
+	if (!strcmp(name, "focaltech,display-coords")) {
+		pdata->x_min = coords[0];
+		pdata->y_min = coords[1];
+		pdata->x_max = coords[2];
+		pdata->y_max = coords[3];
+		pr_err("x_max: %d", pdata->x_max);
+		pr_err("y_max: %d", pdata->y_max);
+		pr_err("coords[2]: %d", coords[2]);
+		pr_err("coords[3]: %d", coords[3]);
+	} else {
+		FTS_ERROR("unsupported property %s", name);
+		pr_err("x_max: %d", pdata->x_max);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_parse_dt
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
+{
+	int rc;
+	struct device_node *np = dev->of_node;
+	u32 temp_val;
+
+	FTS_FUNC_ENTER();
+
+	pdata->fts_chip_type = _FT5416;
+	rc = of_property_read_u32(np, "focaltech,panel-type",
+					  &pdata->fts_chip_type);
+	if (rc)
+		FTS_ERROR("Panel type is undefined, use default panel FT5416");
+
+	rc = fts_get_dt_coords(dev, "focaltech,display-coords", pdata);
+	if (rc)
+		FTS_ERROR("Unable to get display-coords");
+
+	/* key */
+	pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
+	if (pdata->have_key) {
+		rc = of_property_read_u32(np, "focaltech,key-number",
+					  &pdata->key_number);
+		if (rc)
+			FTS_ERROR("Key number undefined!");
+		rc = of_property_read_u32_array(np, "focaltech,keys",
+						pdata->keys, pdata->key_number);
+		if (rc)
+			FTS_ERROR("Keys undefined!");
+		rc = of_property_read_u32(np, "focaltech,key-y-coord",
+					  &pdata->key_y_coord);
+		if (rc)
+			FTS_ERROR("Key Y Coord undefined!");
+		rc = of_property_read_u32_array(np, "focaltech,key-x-coords",
+						pdata->key_x_coords,
+						pdata->key_number);
+		if (rc)
+			FTS_ERROR("Key X Coords undefined!");
+
+		FTS_DEBUG("%d: (%d, %d, %d), [%d, %d, %d][%d]",
+			  pdata->key_number, pdata->keys[0], pdata->keys[1],
+			  pdata->keys[2], pdata->key_x_coords[0],
+			  pdata->key_x_coords[1], pdata->key_x_coords[2],
+			  pdata->key_y_coord);
+	}
+
+	/* reset, irq gpio info */
+	pdata->reset_gpio =
+	    of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0,
+				    &pdata->reset_gpio_flags);
+	if (pdata->reset_gpio < 0)
+		FTS_ERROR("Unable to get reset_gpio");
+
+	pdata->irq_gpio =
+	    of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0,
+				    &pdata->irq_gpio_flags);
+	if (pdata->irq_gpio < 0)
+		FTS_ERROR("Unable to get irq_gpio");
+
+	rc = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
+	if (!rc) {
+		pdata->max_touch_number = temp_val;
+		FTS_DEBUG("max_touch_number=%d", pdata->max_touch_number);
+	} else {
+		FTS_ERROR("Unable to get max-touch-number");
+		pdata->max_touch_number = FTS_MAX_POINTS;
+	}
+
+	pdata->swap = of_property_read_bool(np, "focaltech,swap-xy");
+	pdata->scaling_down_half = of_property_read_bool(np,
+					"focaltech,scaling-down-half");
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+#if defined(CONFIG_FB)
+/*****************************************************************************
+*  Name: fb_notifier_callback
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fb_notifier_callback(struct notifier_block *self,
+				unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+	struct fts_ts_data *fts_data =
+	    container_of(self, struct fts_ts_data, fb_notif);
+
+	if (evdata && evdata->data && event == FB_EVENT_BLANK && fts_data
+	    && fts_data->client) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK)
+			fts_ts_resume(&fts_data->client->dev);
+		else if (*blank == FB_BLANK_POWERDOWN)
+			fts_ts_suspend(&fts_data->client->dev);
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+/*****************************************************************************
+*  Name: fts_ts_early_suspend
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_ts_early_suspend(struct early_suspend *handler)
+{
+	struct fts_ts_data *data = container_of(handler,
+						struct fts_ts_data,
+						early_suspend);
+
+	fts_ts_suspend(&data->client->dev);
+}
+
+/*****************************************************************************
+*  Name: fts_ts_late_resume
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_ts_late_resume(struct early_suspend *handler)
+{
+	struct fts_ts_data *data = container_of(handler,
+						struct fts_ts_data,
+						early_suspend);
+
+	fts_ts_resume(&data->client->dev);
+}
+#endif
+
+/*****************************************************************************
+*  Name: fts_ts_probe
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_ts_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct fts_ts_platform_data *pdata;
+	struct fts_ts_data *data;
+	struct input_dev *input_dev;
+	int err;
+
+	FTS_FUNC_ENTER();
+	/* 1. Get Platform data */
+	if (client->dev.of_node) {
+		pdata =
+		    devm_kzalloc(&client->dev,
+				 sizeof(struct fts_ts_platform_data),
+				 GFP_KERNEL);
+		if (!pdata) {
+			FTS_ERROR("[MEMORY]Failed to allocate memory");
+			FTS_FUNC_EXIT();
+			return -ENOMEM;
+		}
+		err = fts_parse_dt(&client->dev, pdata);
+		if (err)
+			FTS_ERROR("[DTS]DT parsing failed");
+	} else
+		pdata = client->dev.platform_data;
+
+	if (!pdata) {
+		FTS_ERROR("Invalid pdata");
+		FTS_FUNC_EXIT();
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		FTS_ERROR("I2C not supported");
+		FTS_FUNC_EXIT();
+		return -ENODEV;
+	}
+
+	data =
+	    devm_kzalloc(&client->dev, sizeof(struct fts_ts_data), GFP_KERNEL);
+	if (!data) {
+		FTS_ERROR("[MEMORY]Failed to allocate memory");
+		FTS_FUNC_EXIT();
+		return -ENOMEM;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		FTS_ERROR("[INPUT]Failed to allocate input device");
+		FTS_FUNC_EXIT();
+		return -ENOMEM;
+	}
+
+	data->input_dev = input_dev;
+	data->client = client;
+	data->pdata = pdata;
+
+	fts_wq_data = data;
+	fts_i2c_client = client;
+	fts_input_dev = input_dev;
+
+	spin_lock_init(&fts_wq_data->irq_lock);
+
+	fts_input_dev_init(client, data, input_dev, pdata);
+
+#if FTS_POWER_SOURCE_CUST_EN
+	fts_power_source_init(data);
+	fts_power_source_ctrl(data, 1);
+#endif
+
+	err = fts_gpio_configure(data);
+	if (err < 0) {
+		FTS_ERROR("[GPIO]Failed to configure the gpios");
+		goto free_gpio;
+	}
+
+	fts_reset_proc(200);
+	fts_wait_tp_to_valid(client);
+
+	err =
+	    request_threaded_irq(client->irq, NULL, fts_ts_interrupt,
+				 pdata->irq_gpio_flags | IRQF_ONESHOT |
+				 IRQF_TRIGGER_FALLING, client->dev.driver->name,
+				 data);
+	if (err) {
+		FTS_ERROR("Request irq failed!");
+		goto free_gpio;
+	}
+
+	fts_irq_disable();
+
+#if FTS_PSENSOR_EN
+	if (fts_sensor_init(data) != 0) {
+		FTS_ERROR("fts_sensor_init failed!");
+		FTS_FUNC_EXIT();
+		return 0;
+	}
+#endif
+
+#if FTS_POINT_REPORT_CHECK_EN
+	fts_point_report_check_init();
+#endif
+
+	fts_ex_mode_init(client);
+
+#if FTS_GESTURE_EN
+	fts_gesture_init(input_dev, client);
+#endif
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_init();
+#endif
+
+	fts_irq_enable();
+
+#if FTS_TEST_EN
+	fts_test_init(client);
+#endif
+
+#if defined(CONFIG_FB)
+	data->fb_notif.notifier_call = fb_notifier_callback;
+	err = fb_register_client(&data->fb_notif);
+	if (err)
+		FTS_ERROR("[FB]Unable to register fb_notifier: %d", err);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	data->early_suspend.level =
+	    EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL;
+	data->early_suspend.suspend = fts_ts_early_suspend;
+	data->early_suspend.resume = fts_ts_late_resume;
+	register_early_suspend(&data->early_suspend);
+#endif
+
+	FTS_FUNC_EXIT();
+	return 0;
+
+free_gpio:
+	if (gpio_is_valid(pdata->reset_gpio))
+		gpio_free(pdata->reset_gpio);
+	if (gpio_is_valid(pdata->irq_gpio))
+		gpio_free(pdata->irq_gpio);
+	return err;
+
+}
+
+/*****************************************************************************
+*  Name: fts_ts_remove
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_ts_remove(struct i2c_client *client)
+{
+	struct fts_ts_data *data = i2c_get_clientdata(client);
+
+	FTS_FUNC_ENTER();
+	cancel_work_sync(&data->touch_event_work);
+
+#if FTS_PSENSOR_EN
+	fts_sensor_remove(data);
+#endif
+
+#if FTS_POINT_REPORT_CHECK_EN
+	fts_point_report_check_exit();
+#endif
+
+	fts_ex_mode_exit(client);
+
+#if defined(CONFIG_FB)
+	if (fb_unregister_client(&data->fb_notif))
+		FTS_ERROR("Error occurred while unregistering fb_notifier.");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&data->early_suspend);
+#endif
+	free_irq(client->irq, data);
+
+	if (gpio_is_valid(data->pdata->reset_gpio))
+		gpio_free(data->pdata->reset_gpio);
+
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->irq_gpio);
+
+	input_unregister_device(data->input_dev);
+
+#if FTS_TEST_EN
+	fts_test_exit(client);
+#endif
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_exit();
+#endif
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_ts_suspend
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_ts_suspend(struct device *dev)
+{
+	struct fts_ts_data *data = dev_get_drvdata(dev);
+	int retval = 0;
+
+	FTS_FUNC_ENTER();
+	if (data->suspended) {
+		FTS_INFO("Already in suspend state");
+		FTS_FUNC_EXIT();
+		return -1;
+	}
+
+	fts_release_all_finger();
+
+#if FTS_GESTURE_EN
+	retval = fts_gesture_suspend(data->client);
+	if (retval == 0) {
+		/* Enter into gesture mode(suspend) */
+		retval = enable_irq_wake(fts_wq_data->client->irq);
+		if (retval)
+			FTS_ERROR("%s: set_irq_wake failed", __func__);
+		data->suspended = true;
+		FTS_FUNC_EXIT();
+		return 0;
+	}
+#endif
+
+#if FTS_PSENSOR_EN
+	if (fts_sensor_suspend(data) != 0) {
+		enable_irq_wake(data->client->irq);
+		data->suspended = true;
+		return 0;
+	}
+#endif
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_suspend();
+#endif
+
+	fts_irq_disable();
+
+	/* TP enter sleep mode */
+	retval =
+	    fts_i2c_write_reg(data->client, FTS_REG_POWER_MODE,
+			      FTS_REG_POWER_MODE_SLEEP_VALUE);
+	if (retval < 0)
+		FTS_ERROR("Set TP to sleep mode fail, ret=%d!", retval);
+	data->suspended = true;
+
+	FTS_FUNC_EXIT();
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_ts_resume
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_ts_resume(struct device *dev)
+{
+	struct fts_ts_data *data = dev_get_drvdata(dev);
+
+	FTS_FUNC_ENTER();
+	if (!data->suspended) {
+		FTS_DEBUG("Already in awake state");
+		FTS_FUNC_EXIT();
+		return -1;
+	}
+
+	if (!fts_chip_idc(data))
+		fts_reset_proc(200);
+
+	fts_tp_state_recovery(data->client);
+
+#if FTS_GESTURE_EN
+	if (fts_gesture_resume(data->client) == 0) {
+		int err;
+
+		err = disable_irq_wake(data->client->irq);
+		if (err)
+			FTS_ERROR("%s: disable_irq_wake failed", __func__);
+		data->suspended = false;
+		FTS_FUNC_EXIT();
+		return 0;
+	}
+#endif
+
+#if FTS_PSENSOR_EN
+	if (fts_sensor_resume(data) != 0) {
+		disable_irq_wake(data->client->irq);
+		data->suspended = false;
+		FTS_FUNC_EXIT();
+		return 0;
+	}
+#endif
+
+	data->suspended = false;
+
+	fts_irq_enable();
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_resume();
+#endif
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+* I2C Driver
+*****************************************************************************/
+static const struct i2c_device_id fts_ts_id[] = {
+	{FTS_DRIVER_NAME, 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, fts_ts_id);
+
+static struct of_device_id fts_match_table[] = {
+	{.compatible = "focaltech,fts",},
+	{},
+};
+
+static struct i2c_driver fts_ts_driver = {
+	.probe = fts_ts_probe,
+	.remove = fts_ts_remove,
+	.driver = {
+		   .name = FTS_DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = fts_match_table,
+		   },
+	.id_table = fts_ts_id,
+};
+
+/*****************************************************************************
+*  Name: fts_ts_init
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int __init fts_ts_init(void)
+{
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+	ret = i2c_add_driver(&fts_ts_driver);
+	if (ret != 0)
+		FTS_ERROR("Focaltech touch screen driver init failed!");
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+/*****************************************************************************
+*  Name: fts_ts_exit
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void __exit fts_ts_exit(void)
+{
+	i2c_del_driver(&fts_ts_driver);
+}
+
+module_init(fts_ts_init);
+module_exit(fts_ts_exit);
+
+MODULE_AUTHOR("FocalTech Driver Team");
+MODULE_DESCRIPTION("FocalTech Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
new file mode 100644
index 0000000..b8ef41f
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
@@ -0,0 +1,189 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.h
+
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_CORE_H__
+#define __LINUX_FOCALTECH_CORE_H__
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/init.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mount.h>
+#include <linux/netdevice.h>
+#include <linux/unistd.h>
+#include <linux/ioctl.h>
+#include "focaltech_common.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define LEN_FLASH_ECC_MAX                   0xFFFE
+
+#define FTS_WORKQUEUE_NAME                  "fts_wq"
+
+#define FTS_MAX_POINTS                      10
+#define FTS_KEY_WIDTH                       50
+#define FTS_ONE_TCH_LEN                     6
+#define POINT_READ_BUF  (3 + FTS_ONE_TCH_LEN * FTS_MAX_POINTS)
+
+#define FTS_MAX_ID                          0x0F
+#define FTS_TOUCH_X_H_POS                   3
+#define FTS_TOUCH_X_L_POS                   4
+#define FTS_TOUCH_Y_H_POS                   5
+#define FTS_TOUCH_Y_L_POS                   6
+#define FTS_TOUCH_PRE_POS                   7
+#define FTS_TOUCH_AREA_POS                  8
+#define FTS_TOUCH_POINT_NUM                 2
+#define FTS_TOUCH_EVENT_POS                 3
+#define FTS_TOUCH_ID_POS                    5
+#define FTS_COORDS_ARR_SIZE                 4
+
+#define FTS_TOUCH_DOWN      0
+#define FTS_TOUCH_UP        1
+#define FTS_TOUCH_CONTACT   2
+
+#define FTS_SYSFS_ECHO_ON(buf)		((strnicmp(buf, "1", 1)  == 0) || \
+					(strnicmp(buf, "on", 2) == 0))
+#define FTS_SYSFS_ECHO_OFF(buf)		((strnicmp(buf, "0", 1)  == 0) || \
+					(strnicmp(buf, "off", 3) == 0))
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+struct fts_ts_platform_data {
+	u32 fts_chip_type;
+	u32 irq_gpio;
+	u32 irq_gpio_flags;
+	u32 reset_gpio;
+	u32 reset_gpio_flags;
+	bool have_key;
+	u32 key_number;
+	u32 keys[4];
+	u32 key_y_coord;
+	u32 key_x_coords[4];
+	u32 x_max;
+	u32 y_max;
+	u32 x_min;
+	u32 y_min;
+	u32 max_touch_number;
+
+	bool swap;
+	bool scaling_down_half;
+};
+
+struct ts_event {
+	u16 au16_x[FTS_MAX_POINTS];	/*x coordinate */
+	u16 au16_y[FTS_MAX_POINTS];	/*y coordinate */
+	u16 pressure[FTS_MAX_POINTS];
+	/* touch event: 0 -- down; 1-- up; 2 -- contact */
+	u8 au8_touch_event[FTS_MAX_POINTS];
+	u8 au8_finger_id[FTS_MAX_POINTS];	/*touch ID */
+	u8 area[FTS_MAX_POINTS];
+	u8 touch_point;
+	u8 point_num;
+};
+
+struct fts_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct ts_event event;
+	const struct fts_ts_platform_data *pdata;
+#if FTS_PSENSOR_EN
+	struct fts_psensor_platform_data *psensor_pdata;
+#endif
+	struct work_struct touch_event_work;
+	struct workqueue_struct *ts_workqueue;
+	struct regulator *vdd;
+	struct regulator *vcc_i2c;
+	spinlock_t irq_lock;
+	u16 addr;
+	bool suspended;
+	u8 fw_ver[3];
+	u8 fw_vendor_id;
+	int touchs;
+	int irq_disable;
+
+#if defined(CONFIG_FB)
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+};
+
+#if FTS_PSENSOR_EN
+struct fts_psensor_platform_data {
+	struct input_dev *input_psensor_dev;
+	struct sensors_classdev ps_cdev;
+	int tp_psensor_opened;
+	char tp_psensor_data;	/* 0 near, 1 far */
+	struct fts_ts_data *data;
+};
+
+int fts_sensor_init(struct fts_ts_data *data);
+int fts_sensor_read_data(struct fts_ts_data *data);
+int fts_sensor_suspend(struct fts_ts_data *data);
+int fts_sensor_resume(struct fts_ts_data *data);
+int fts_sensor_remove(struct fts_ts_data *data);
+#endif
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+extern struct i2c_client *fts_i2c_client;
+extern struct fts_ts_data *fts_wq_data;
+extern struct input_dev *fts_input_dev;
+
+#endif /* __LINUX_FOCALTECH_CORE_H__ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c
new file mode 100644
index 0000000..565507d
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c
@@ -0,0 +1,473 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_esdcheck.c
+*
+*    Author: luoguojin
+*
+*   Created: 2016-08-03
+*
+*  Abstract: ESD check function
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release. By luougojin 2016-08-03
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_ESDCHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define ESDCHECK_WAIT_TIME              1000
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_esdcheck_st {
+	u8 active:1;
+	u8 suspend:1;
+	u8 proc_debug:1;	/* apk or adb is accessing I2C */
+	u8 intr:1;		/* 1- Interrupt trigger */
+	u8 unused:4;
+	u8 flow_work_hold_cnt;
+	u8 flow_work_cnt_last;
+	u32 hardware_reset_cnt;
+	u32 i2c_nack_cnt;
+	u32 i2c_dataerror_cnt;
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct delayed_work fts_esdcheck_work;
+static struct workqueue_struct *fts_esdcheck_workqueue;
+static struct fts_esdcheck_st fts_esdcheck_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+*  Name: lcd_esdcheck
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int lcd_need_reset;
+static int tp_need_recovery;	/* LCD reset cause Tp reset */
+int idc_esdcheck_lcderror(void)
+{
+	u8 val;
+	int ret;
+
+	FTS_DEBUG("[ESD]Check LCD ESD");
+	if ((tp_need_recovery == 1) && (lcd_need_reset == 0)) {
+		tp_need_recovery = 0;
+		/* LCD reset, need recover TP state */
+		fts_tp_state_recovery(fts_i2c_client);
+	}
+
+	ret = fts_i2c_read_reg(fts_i2c_client, FTS_REG_ESD_SATURATE, &val);
+	if (ret < 0) {
+		FTS_ERROR("[ESD]: Read ESD_SATURATE(0xED) failed ret=%d!", ret);
+		return -EIO;
+	}
+
+	if (val == 0xAA) {
+		/*
+		 * 1. Set flag lcd_need_reset = 1;
+		 * 2. LCD driver need reset(recovery) LCD and
+		 *    set lcd_need_reset to 0
+		 * 3. recover TP state
+		 */
+		FTS_INFO("LCD ESD, Execute LCD reset!");
+		lcd_need_reset = 1;
+		tp_need_recovery = 1;
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_tp_reset
+*  Brief: esd check algorithm
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_esdcheck_tp_reset(void)
+{
+	FTS_FUNC_ENTER();
+
+	fts_esdcheck_data.flow_work_hold_cnt = 0;
+	fts_esdcheck_data.hardware_reset_cnt++;
+
+	fts_reset_proc(200);
+	fts_tp_state_recovery(fts_i2c_client);
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: get_chip_id
+*  Brief: Read Chip Id 3 times
+*  Input:
+*  Output:
+*  Return:  1 - Read Chip Id 3 times failed
+*           0 - Read Chip Id pass
+*****************************************************************************/
+static bool get_chip_id(void)
+{
+	int err = 0;
+	int i = 0;
+	u8 reg_value = 0;
+	u8 reg_addr = 0;
+
+	for (i = 0; i < 3; i++) {
+		reg_addr = FTS_REG_CHIP_ID;
+		err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
+
+		if (err < 0) {
+			FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!",
+				  err);
+			fts_esdcheck_data.i2c_nack_cnt++;
+		} else {
+			/* Upgrade sometimes can't detect */
+			if ((reg_value == chip_types.chip_idh)
+						|| (reg_value == 0xEF))
+				break;
+			 else
+				fts_esdcheck_data.i2c_dataerror_cnt++;
+		}
+	}
+
+	/* if can't get correct data in 3 times, then need hardware reset */
+	if (i >= 3) {
+		FTS_ERROR
+		    ("[ESD]: Read Chip id 3 times failed,"
+					"need execute TP reset!!");
+		return 1;
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: get_flow_cnt
+*  Brief: Read flow cnt(0x91)
+*  Input:
+*  Output:
+*  Return:  1 - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
+*           0 - Reg 0x91(flow cnt) normal
+*****************************************************************************/
+static bool get_flow_cnt(void)
+{
+	int err = 0;
+	u8 reg_value = 0;
+	u8 reg_addr = 0;
+
+	reg_addr = FTS_REG_FLOW_WORK_CNT;
+	err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
+	if (err < 0) {
+		FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", err);
+		fts_esdcheck_data.i2c_nack_cnt++;
+	} else {
+		if (reg_value == fts_esdcheck_data.flow_work_cnt_last)
+			fts_esdcheck_data.flow_work_hold_cnt++;
+		else
+			fts_esdcheck_data.flow_work_hold_cnt = 0;
+
+		fts_esdcheck_data.flow_work_cnt_last = reg_value;
+	}
+
+	/*
+	 * if read flow work cnt 5 times and the value are
+	 * all the same, then need hardware_reset
+	 */
+	if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
+		FTS_DEBUG("[ESD]: Flow Work Cnt(reg0x91) keep a value"
+				"for 5 times, need execute TP reset!!");
+		return 1;
+	}
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: esdcheck_algorithm
+*  Brief: esd check algorithm
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int esdcheck_algorithm(void)
+{
+	int err = 0;
+	u8 reg_value = 0;
+	u8 reg_addr = 0;
+	bool hardware_reset = 0;
+
+	/* 1. esdcheck is interrupt, then return */
+	if (fts_esdcheck_data.intr == 1) {
+		FTS_INFO("[ESD]: In interrupt state,"
+				"not check esd, return immediately!!");
+		return 0;
+	}
+
+	/* 2. check power state, if suspend, no need check esd */
+	if (fts_esdcheck_data.suspend == 1) {
+		FTS_INFO("[ESD]: In suspend, not check esd,"
+					"return immediately!!");
+		/* because in suspend state, adb can be used,
+		 * when upgrade FW, will active ESD check(active = 1)
+		 * But in suspend, then will don't queue_delayed_work,
+		 * when resume, don't check ESD again
+		 */
+		fts_esdcheck_data.active = 0;
+		return 0;
+	}
+
+	/*
+	 * 3. check fts_esdcheck_data.proc_debug state,
+	 * if 1-proc busy, no need check esd
+	 */
+	if (fts_esdcheck_data.proc_debug == 1) {
+		FTS_INFO("[ESD]: In apk or adb command mode,"
+				"not check esd, return immediately!!");
+		return 0;
+	}
+
+	/* 4. In factory mode, can't check esd */
+	reg_addr = FTS_REG_WORKMODE;
+	err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
+	if (err < 0) {
+		fts_esdcheck_data.i2c_nack_cnt++;
+	} else if ((reg_value & 0x70) == FTS_REG_WORKMODE_FACTORY_VALUE) {
+		FTS_INFO("[ESD]: In factory mode,"
+				"not check esd, return immediately!!");
+		return 0;
+	}
+
+	/* 5. Get Chip ID */
+	hardware_reset = get_chip_id();
+
+	/*
+	 * 6. get Flow work cnt: 0x91 If no change for 5 times,
+	 * then ESD and reset
+	 */
+	if (!hardware_reset)
+		hardware_reset = get_flow_cnt();
+
+	/* 7. If need hardware reset, then handle it here */
+	if (hardware_reset == 1)
+		fts_esdcheck_tp_reset();
+
+	FTS_INFO("[ESD]: NoACK=%d, Error Data=%d, Hardware Reset=%d\n",
+		 fts_esdcheck_data.i2c_nack_cnt,
+		 fts_esdcheck_data.i2c_dataerror_cnt,
+		 fts_esdcheck_data.hardware_reset_cnt);
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_func
+*  Brief: fts_esdcheck_func
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void esdcheck_func(struct work_struct *work)
+{
+	FTS_FUNC_ENTER();
+
+	idc_esdcheck_lcderror();
+
+	esdcheck_algorithm();
+
+	if (fts_esdcheck_data.suspend == 0) {
+		queue_delayed_work(fts_esdcheck_workqueue, &fts_esdcheck_work,
+				   msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+	}
+
+	FTS_FUNC_EXIT();
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_set_intr
+*  Brief: interrupt flag (main used in interrupt tp report)
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_set_intr(bool intr)
+{
+	/* interrupt don't add debug message */
+	fts_esdcheck_data.intr = intr;
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_get_status(void)
+*  Brief: get current status
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_get_status(void)
+{
+	/* interrupt don't add debug message */
+	return fts_esdcheck_data.active;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_proc_busy
+*  Brief: When APK or ADB command access TP via driver,
+*  then need set proc_debug,
+*         then will not check ESD.
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_proc_busy(bool proc_debug)
+{
+	fts_esdcheck_data.proc_debug = proc_debug;
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_switch
+*  Brief: FTS esd check function switch.
+*  Input:   enable:  1 - Enable esd check
+*                    0 - Disable esd check
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_switch(bool enable)
+{
+	FTS_FUNC_ENTER();
+	if (enable == 1) {
+		if (fts_esdcheck_data.active == 0) {
+			FTS_INFO("[ESD]: ESD check start!!");
+			fts_esdcheck_data.active = 1;
+			queue_delayed_work(fts_esdcheck_workqueue,
+					   &fts_esdcheck_work,
+					   msecs_to_jiffies
+					   (ESDCHECK_WAIT_TIME));
+		}
+	} else {
+		if (fts_esdcheck_data.active == 1) {
+			FTS_INFO("[ESD]: ESD check stop!!");
+			fts_esdcheck_data.active = 0;
+			cancel_delayed_work_sync(&fts_esdcheck_work);
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_suspend
+*  Brief: Run when tp enter into suspend
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_suspend(void)
+{
+	FTS_FUNC_ENTER();
+	fts_esdcheck_switch(DISABLE);
+	fts_esdcheck_data.suspend = 1;
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_resume
+*  Brief: Run when tp resume
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_resume(void)
+{
+	FTS_FUNC_ENTER();
+	fts_esdcheck_switch(ENABLE);
+	fts_esdcheck_data.suspend = 0;
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_init
+*  Brief: Init and create a queue work to check esd
+*  Input:
+*  Output:
+*  Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_esdcheck_init(void)
+{
+	FTS_FUNC_ENTER();
+
+	INIT_DELAYED_WORK(&fts_esdcheck_work, esdcheck_func);
+	fts_esdcheck_workqueue = create_workqueue("fts_esdcheck_wq");
+	if (fts_esdcheck_workqueue == NULL)
+		FTS_INFO("[ESD]: Failed to create esd work queue!!");
+
+	memset((u8 *) &fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
+
+	fts_esdcheck_switch(ENABLE);
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_exit
+*  Brief: When FTS TP driver is removed,
+*  then call this function to destroy work queue
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_exit(void)
+{
+	FTS_FUNC_ENTER();
+
+	destroy_workqueue(fts_esdcheck_workqueue);
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+#endif /* FTS_ESDCHECK_EN */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c
new file mode 100644
index 0000000..6ab5d47
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c
@@ -0,0 +1,357 @@
+/*
+ *
+ * FocalTech ftxxxx TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_ex_mode.c
+*
+*    Author: Liu WeiGuang
+*
+*   Created: 2016-08-31
+*
+*  Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* 2.Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* 3.Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_mode_flag {
+	int fts_glove_mode_flag;
+	int fts_cover_mode_flag;
+	int fts_charger_mode_flag;
+};
+
+struct fts_mode_flag g_fts_mode_flag;
+
+/*****************************************************************************
+* 4.Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* 5.Global variable or extern global variabls/functions
+*****************************************************************************/
+int fts_enter_glove_mode(struct i2c_client *client, int mode);
+int fts_glove_init(struct i2c_client *client);
+int fts_glove_exit(struct i2c_client *client);
+
+int fts_enter_cover_mode(struct i2c_client *client, int mode);
+int fts_cover_init(struct i2c_client *client);
+int fts_cover_exit(struct i2c_client *client);
+
+int fts_enter_charger_mode(struct i2c_client *client, int mode);
+int fts_charger_init(struct i2c_client *client);
+int fts_charger_exit(struct i2c_client *client);
+
+/*****************************************************************************
+* 6.Static function prototypes
+*******************************************************************************/
+
+#if FTS_GLOVE_EN
+static ssize_t fts_touch_glove_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "Glove: %s\n",
+			g_fts_mode_flag.fts_glove_mode_flag ? "On" : "Off");
+}
+
+static ssize_t fts_touch_glove_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	int ret;
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		if (!g_fts_mode_flag.fts_glove_mode_flag) {
+			FTS_INFO("[Mode]enter glove mode");
+			ret = fts_enter_glove_mode(fts_i2c_client, true);
+			if (ret >= 0)
+				g_fts_mode_flag.fts_glove_mode_flag = true;
+		}
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		if (g_fts_mode_flag.fts_glove_mode_flag) {
+			FTS_INFO("[Mode]exit glove mode");
+			ret = fts_enter_glove_mode(fts_i2c_client, false);
+			if (ret >= 0)
+				g_fts_mode_flag.fts_glove_mode_flag = false;
+		}
+	}
+	FTS_INFO("[Mode]glove mode status:  %d",
+		 g_fts_mode_flag.fts_glove_mode_flag);
+	return count;
+}
+
+/************************************************************************
+* Name: fts_enter_glove_mode
+* Brief:  change glove mode
+* Input:  glove mode
+* Output: no
+* Return: success >=0, otherwise failed
+***********************************************************************/
+int fts_enter_glove_mode(struct i2c_client *client, int mode)
+{
+	int ret = 0;
+	static u8 buf_addr[2] = { 0 };
+	static u8 buf_value[2] = { 0 };
+
+	buf_addr[0] = FTS_REG_GLOVE_MODE_EN;
+
+	if (mode)
+		buf_value[0] = 0x01;
+	else
+		buf_value[0] = 0x00;
+
+	ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]);
+	if (ret < 0)
+		FTS_ERROR("[Mode]fts_enter_glove_mode write value fail");
+
+	return ret;
+
+}
+
+/* read and write glove mode
+*   read example: cat  fts_touch_glove_mode---read  glove mode
+*   write example:echo 01 > fts_touch_glove_mode ---write glove mode to 01
+*
+*/
+static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR, fts_touch_glove_show,
+		   fts_touch_glove_store);
+
+#endif
+
+#if FTS_COVER_EN
+static ssize_t fts_touch_cover_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "Cover: %s\n",
+			g_fts_mode_flag.fts_cover_mode_flag ? "On" : "Off");
+}
+
+static ssize_t fts_touch_cover_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	int ret;
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		if (!g_fts_mode_flag.fts_cover_mode_flag) {
+			FTS_INFO("[Mode]enter cover mode");
+			ret = fts_enter_cover_mode(fts_i2c_client, true);
+			if (ret >= 0)
+				g_fts_mode_flag.fts_cover_mode_flag = true;
+		}
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		if (g_fts_mode_flag.fts_cover_mode_flag) {
+			FTS_INFO("[Mode]exit cover mode");
+			ret = fts_enter_cover_mode(fts_i2c_client, false);
+			if (ret >= 0)
+				g_fts_mode_flag.fts_cover_mode_flag = false;
+		}
+	}
+	FTS_INFO("[Mode]cover mode status:  %d",
+		 g_fts_mode_flag.fts_cover_mode_flag);
+	return count;
+}
+
+/************************************************************************
+* Name: fts_enter_cover_mode
+* Brief:  change cover mode
+* Input:  cover mode
+* Output: no
+* Return: success >=0, otherwise failed
+***********************************************************************/
+int fts_enter_cover_mode(struct i2c_client *client, int mode)
+{
+	int ret = 0;
+	static u8 buf_addr[2] = { 0 };
+	static u8 buf_value[2] = { 0 };
+
+	buf_addr[0] = FTS_REG_COVER_MODE_EN;
+
+	if (mode)
+		buf_value[0] = 0x01;
+	else
+		buf_value[0] = 0x00;
+
+	ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]);
+	if (ret < 0)
+		FTS_ERROR("[Mode] fts_enter_cover_mode write value fail\n");
+
+	return ret;
+
+}
+
+/* read and write cover mode
+*   read example: cat  fts_touch_cover_mode---read  cover mode
+*   write example:echo 01 > fts_touch_cover_mode ---write cover mode to 01
+*
+*/
+static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR, fts_touch_cover_show,
+		   fts_touch_cover_store);
+
+#endif
+
+#if FTS_CHARGER_EN
+static ssize_t fts_touch_charger_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "Charger: %s\n",
+			g_fts_mode_flag.fts_charger_mode_flag ? "On" : "Off");
+}
+
+static ssize_t fts_touch_charger_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	int ret;
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		if (!g_fts_mode_flag.fts_charger_mode_flag) {
+			FTS_INFO("[Mode]enter charger mode");
+			ret = fts_enter_charger_mode(fts_i2c_client, true);
+			if (ret >= 0)
+				g_fts_mode_flag.fts_charger_mode_flag = true;
+		}
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		if (g_fts_mode_flag.fts_charger_mode_flag) {
+			FTS_INFO("[Mode]exit charger mode");
+			ret = fts_enter_charger_mode(fts_i2c_client, false);
+			if (ret >= 0)
+				g_fts_mode_flag.fts_charger_mode_flag = false;
+		}
+	}
+	FTS_INFO("[Mode]charger mode status: %d",
+		 g_fts_mode_flag.fts_charger_mode_flag);
+	return count;
+}
+
+/************************************************************************
+* Name: fts_enter_charger_mode
+* Brief:  change charger mode
+* Input:  charger mode
+* Output: no
+* Return: success >=0, otherwise failed
+***********************************************************************/
+int fts_enter_charger_mode(struct i2c_client *client, int mode)
+{
+	int ret = 0;
+	static u8 buf_addr[2] = { 0 };
+	static u8 buf_value[2] = { 0 };
+
+	buf_addr[0] = FTS_REG_CHARGER_MODE_EN;
+
+	if (mode)
+		buf_value[0] = 0x01;
+	else
+		buf_value[0] = 0x00;
+
+	ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]);
+	if (ret < 0)
+		FTS_DEBUG("[Mode]fts_enter_charger_mode write value fail");
+
+	return ret;
+
+}
+
+/* read and write charger mode
+*   read example: cat  fts_touch_charger_mode---read  charger mode
+*   write example:echo 01 > fts_touch_charger_mode ---write charger mode to 01
+*
+*/
+static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR, fts_touch_charger_show,
+		   fts_touch_charger_store);
+
+#endif
+
+static struct attribute *fts_touch_mode_attrs[] = {
+#if FTS_GLOVE_EN
+	&dev_attr_fts_glove_mode.attr,
+#endif
+
+#if FTS_COVER_EN
+	&dev_attr_fts_cover_mode.attr,
+#endif
+
+#if FTS_CHARGER_EN
+	&dev_attr_fts_charger_mode.attr,
+#endif
+
+	NULL,
+};
+
+static struct attribute_group fts_touch_mode_group = {
+	.attrs = fts_touch_mode_attrs,
+};
+
+int fts_ex_mode_init(struct i2c_client *client)
+{
+	int err = 0;
+
+	g_fts_mode_flag.fts_glove_mode_flag = false;
+	g_fts_mode_flag.fts_cover_mode_flag = false;
+	g_fts_mode_flag.fts_charger_mode_flag = false;
+
+	err = sysfs_create_group(&client->dev.kobj, &fts_touch_mode_group);
+	if (0 != err) {
+		FTS_ERROR("[Mode]create sysfs failed.");
+		sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group);
+		return -EIO;
+	}
+
+	FTS_DEBUG("[Mode]create sysfs succeeded");
+
+	return err;
+
+}
+
+int fts_ex_mode_exit(struct i2c_client *client)
+{
+	sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group);
+	return 0;
+}
+
+int fts_ex_mode_recovery(struct i2c_client *client)
+{
+	int ret = 0;
+#if FTS_GLOVE_EN
+	if (g_fts_mode_flag.fts_glove_mode_flag)
+		ret = fts_enter_glove_mode(client, true);
+#endif
+
+#if FTS_COVER_EN
+	if (g_fts_mode_flag.fts_cover_mode_flag)
+		ret = fts_enter_cover_mode(client, true);
+#endif
+
+#if FTS_CHARGER_EN
+	if (g_fts_mode_flag.fts_charger_mode_flag)
+		ret = fts_enter_charger_mode(client, true);
+#endif
+
+	return ret;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c
new file mode 100644
index 0000000..9faabb8
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c
@@ -0,0 +1,652 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_gestrue.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#if FTS_GESTURE_EN
+/******************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define  KEY_GESTURE_U                          KEY_U
+#define  KEY_GESTURE_UP                         KEY_UP
+#define  KEY_GESTURE_DOWN                       KEY_DOWN
+#define  KEY_GESTURE_LEFT                       KEY_LEFT
+#define  KEY_GESTURE_RIGHT                      KEY_RIGHT
+#define  KEY_GESTURE_O                          KEY_O
+#define  KEY_GESTURE_E                          KEY_E
+#define  KEY_GESTURE_M                          KEY_M
+#define  KEY_GESTURE_L                          KEY_L
+#define  KEY_GESTURE_W                          KEY_W
+#define  KEY_GESTURE_S                          KEY_S
+#define  KEY_GESTURE_V                          KEY_V
+#define  KEY_GESTURE_C                          KEY_C
+#define  KEY_GESTURE_Z                          KEY_Z
+
+#define GESTURE_LEFT                            0x20
+#define GESTURE_RIGHT                           0x21
+#define GESTURE_UP                              0x22
+#define GESTURE_DOWN                            0x23
+#define GESTURE_DOUBLECLICK                     0x24
+#define GESTURE_O                               0x30
+#define GESTURE_W                               0x31
+#define GESTURE_M                               0x32
+#define GESTURE_E                               0x33
+#define GESTURE_L                               0x44
+#define GESTURE_S                               0x46
+#define GESTURE_V                               0x54
+#define GESTURE_Z                               0x41
+#define GESTURE_C                               0x34
+#define FTS_GESTRUE_POINTS                      255
+#define FTS_GESTRUE_POINTS_HEADER               8
+#define FTS_GESTURE_OUTPUT_ADRESS               0xD3
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_gesture_st {
+	u8 header[FTS_GESTRUE_POINTS_HEADER];
+	u16 coordinate_x[FTS_GESTRUE_POINTS];
+	u16 coordinate_y[FTS_GESTRUE_POINTS];
+	u8 mode;
+	u8 active;
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_gesture_st fts_gesture_data;
+extern struct fts_ts_data *fts_wq_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static ssize_t fts_gesture_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+static ssize_t fts_gesture_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count);
+static ssize_t fts_gesture_buf_show(struct device *dev,
+				    struct device_attribute *attr, char *buf);
+static ssize_t fts_gesture_buf_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count);
+
+/* sysfs gesture node
+ *   read example: cat  fts_gesture_mode        ---read gesture mode
+ *   write example:echo 01 > fts_gesture_mode   ---write gesture mode to 01
+ *
+ */
+static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show,
+		   fts_gesture_store);
+/*
+ *   read example: cat  fts_gesture_buf        ---read gesture buf
+ */
+static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR, fts_gesture_buf_show,
+		   fts_gesture_buf_store);
+static struct attribute *fts_gesture_mode_attrs[] = {
+
+	&dev_attr_fts_gesture_mode.attr,
+	&dev_attr_fts_gesture_buf.attr,
+	NULL,
+};
+
+static struct attribute_group fts_gesture_group = {
+	.attrs = fts_gesture_mode_attrs,
+};
+
+/************************************************************************
+* Name: fts_gesture_show
+* Brief:  no
+* Input: device, device attribute, char buf
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int count;
+	u8 val;
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+
+	mutex_lock(&fts_input_dev->mutex);
+	fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val);
+	count =
+	    sprintf(buf, "Gesture Mode: %s\n",
+		    fts_gesture_data.mode ? "On" : "Off");
+	count += sprintf(buf + count, "Reg(0xD0) = %d\n", val);
+	mutex_unlock(&fts_input_dev->mutex);
+
+	return count;
+}
+
+/************************************************************************
+* Name: fts_gesture_store
+* Brief:  no
+* Input: device, device attribute, char buf, char count
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	mutex_lock(&fts_input_dev->mutex);
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		FTS_INFO("[GESTURE]enable gesture");
+		fts_gesture_data.mode = ENABLE;
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		FTS_INFO("[GESTURE]disable gesture");
+		fts_gesture_data.mode = DISABLE;
+	}
+
+	mutex_unlock(&fts_input_dev->mutex);
+
+	return count;
+}
+
+/************************************************************************
+* Name: fts_gesture_buf_show
+* Brief:  no
+* Input: device, device attribute, char buf
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_buf_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	int count;
+	int i = 0;
+
+	mutex_lock(&fts_input_dev->mutex);
+	count =
+	    snprintf(buf, PAGE_SIZE, "Gesture ID: 0x%x\n",
+		     fts_gesture_data.header[0]);
+	count +=
+	    snprintf(buf + count, PAGE_SIZE, "Gesture PointNum: %d\n",
+		     fts_gesture_data.header[1]);
+	count += snprintf(buf + count, PAGE_SIZE, "Gesture Point Buf:\n");
+	for (i = 0; i < fts_gesture_data.header[1]; i++) {
+		count +=
+		    snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i,
+			     fts_gesture_data.coordinate_x[i],
+			     fts_gesture_data.coordinate_y[i]);
+		if ((i + 1) % 4 == 0)
+			count += snprintf(buf + count, PAGE_SIZE, "\n");
+	}
+	count += snprintf(buf + count, PAGE_SIZE, "\n");
+	mutex_unlock(&fts_input_dev->mutex);
+
+	return count;
+}
+
+/************************************************************************
+* Name: fts_gesture_buf_store
+* Brief:  no
+* Input: device, device attribute, char buf, char count
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_buf_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	/* place holder for future use */
+	return -EPERM;
+}
+
+/*****************************************************************************
+*   Name: fts_create_gesture_sysfs
+*  Brief:
+*  Input:
+* Output: None
+* Return: 0-success or error
+*****************************************************************************/
+int fts_create_gesture_sysfs(struct i2c_client *client)
+{
+	int ret = 0;
+
+	ret = sysfs_create_group(&client->dev.kobj, &fts_gesture_group);
+	if (ret != 0) {
+		FTS_ERROR
+		    ("[GESTURE]fts_gesture_mode_group(sysfs) create failed!");
+		sysfs_remove_group(&client->dev.kobj, &fts_gesture_group);
+		return ret;
+	}
+	return 0;
+}
+
+/*****************************************************************************
+*   Name: fts_gesture_recovery
+*  Brief: recovery gesture state when reset
+*  Input:
+* Output: None
+* Return:
+*****************************************************************************/
+void fts_gesture_recovery(struct i2c_client *client)
+{
+	if (fts_gesture_data.mode && fts_gesture_data.active) {
+		fts_i2c_write_reg(client, 0xD1, 0xff);
+		fts_i2c_write_reg(client, 0xD2, 0xff);
+		fts_i2c_write_reg(client, 0xD5, 0xff);
+		fts_i2c_write_reg(client, 0xD6, 0xff);
+		fts_i2c_write_reg(client, 0xD7, 0xff);
+		fts_i2c_write_reg(client, 0xD8, 0xff);
+		fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, ENABLE);
+	}
+}
+
+/*****************************************************************************
+*   Name: fts_gesture_init
+*  Brief:
+*  Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+int fts_gesture_init(struct input_dev *input_dev, struct i2c_client *client)
+{
+	FTS_FUNC_ENTER();
+	input_set_capability(input_dev, EV_KEY, KEY_POWER);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C);
+
+	__set_bit(KEY_GESTURE_RIGHT, input_dev->keybit);
+	__set_bit(KEY_GESTURE_LEFT, input_dev->keybit);
+	__set_bit(KEY_GESTURE_UP, input_dev->keybit);
+	__set_bit(KEY_GESTURE_DOWN, input_dev->keybit);
+	__set_bit(KEY_GESTURE_U, input_dev->keybit);
+	__set_bit(KEY_GESTURE_O, input_dev->keybit);
+	__set_bit(KEY_GESTURE_E, input_dev->keybit);
+	__set_bit(KEY_GESTURE_M, input_dev->keybit);
+	__set_bit(KEY_GESTURE_W, input_dev->keybit);
+	__set_bit(KEY_GESTURE_L, input_dev->keybit);
+	__set_bit(KEY_GESTURE_S, input_dev->keybit);
+	__set_bit(KEY_GESTURE_V, input_dev->keybit);
+	__set_bit(KEY_GESTURE_C, input_dev->keybit);
+	__set_bit(KEY_GESTURE_Z, input_dev->keybit);
+
+	fts_create_gesture_sysfs(client);
+	fts_gesture_data.mode = 0;
+	fts_gesture_data.active = 0;
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/************************************************************************
+* Name: fts_gesture_exit
+* Brief:  remove sys
+* Input: i2c info
+* Output: no
+* Return: no
+***********************************************************************/
+int fts_gesture_exit(struct i2c_client *client)
+{
+	sysfs_remove_group(&client->dev.kobj, &fts_gesture_group);
+	return 0;
+}
+
+/*****************************************************************************
+*   Name: fts_check_gesture
+*  Brief:
+*  Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+static void fts_check_gesture(struct input_dev *input_dev, int gesture_id)
+{
+	char *envp[2];
+	int gesture;
+
+	FTS_FUNC_ENTER();
+	switch (gesture_id) {
+	case GESTURE_LEFT:
+		envp[0] = "GESTURE=LEFT";
+		gesture = KEY_GESTURE_LEFT;
+		break;
+	case GESTURE_RIGHT:
+		envp[0] = "GESTURE=RIGHT";
+		gesture = KEY_GESTURE_RIGHT;
+		break;
+	case GESTURE_UP:
+		envp[0] = "GESTURE=UP";
+		gesture = KEY_GESTURE_UP;
+		break;
+	case GESTURE_DOWN:
+		envp[0] = "GESTURE=DOWN";
+		gesture = KEY_GESTURE_DOWN;
+		break;
+	case GESTURE_DOUBLECLICK:
+		envp[0] = "GESTURE=DOUBLE_CLICK";
+		gesture = KEY_POWER;
+		break;
+	case GESTURE_O:
+		envp[0] = "GESTURE=O";
+		gesture = KEY_GESTURE_O;
+		break;
+	case GESTURE_W:
+		envp[0] = "GESTURE=W";
+		gesture = KEY_GESTURE_W;
+		break;
+	case GESTURE_M:
+		envp[0] = "GESTURE=M";
+		gesture = KEY_GESTURE_M;
+		break;
+	case GESTURE_E:
+		envp[0] = "GESTURE=E";
+		gesture = KEY_GESTURE_E;
+		break;
+	case GESTURE_L:
+		envp[0] = "GESTURE=L";
+		gesture = KEY_GESTURE_L;
+		break;
+	case GESTURE_S:
+		envp[0] = "GESTURE=S";
+		gesture = KEY_GESTURE_S;
+		break;
+	case GESTURE_V:
+		envp[0] = "GESTURE=V";
+		gesture = KEY_GESTURE_V;
+		break;
+	case GESTURE_Z:
+		envp[0] = "GESTURE=Z";
+		gesture = KEY_GESTURE_Z;
+		break;
+	case GESTURE_C:
+		envp[0] = "GESTURE=C";
+		gesture = KEY_GESTURE_C;
+		break;
+	default:
+		envp[0] = "GESTURE=NONE";
+		gesture = -1;
+		break;
+	}
+	FTS_DEBUG("envp[0]: %s", envp[0]);
+	/* report event key */
+	/*if (gesture != -1)
+	   {
+	   input_report_key(input_dev, gesture, 1);
+	   input_sync(input_dev);
+	   input_report_key(input_dev, gesture, 0);
+	   input_sync(input_dev);
+	   } */
+
+	envp[1] = NULL;
+	kobject_uevent_env(&fts_wq_data->client->dev.kobj, KOBJ_CHANGE, envp);
+	sysfs_notify(&fts_wq_data->client->dev.kobj, NULL, "GESTURE_ID");
+
+	FTS_FUNC_EXIT();
+}
+
+/************************************************************************
+*   Name: fts_gesture_readdata
+* Brief: read data from TP register
+* Input: no
+* Output: no
+* Return: fail <0
+***********************************************************************/
+static int fts_gesture_read_buffer(struct i2c_client *client, u8 *buf,
+				   int read_bytes)
+{
+	int remain_bytes;
+	int ret;
+	int i;
+
+	if (read_bytes <= I2C_BUFFER_LENGTH_MAXINUM) {
+		ret = fts_i2c_read(client, buf, 1, buf, read_bytes);
+	} else {
+		ret =
+		    fts_i2c_read(client, buf, 1, buf,
+				 I2C_BUFFER_LENGTH_MAXINUM);
+		remain_bytes = read_bytes - I2C_BUFFER_LENGTH_MAXINUM;
+		for (i = 1; remain_bytes > 0; i++) {
+			if (remain_bytes <= I2C_BUFFER_LENGTH_MAXINUM)
+				ret =
+				    fts_i2c_read(client, buf, 0,
+						 buf +
+						 I2C_BUFFER_LENGTH_MAXINUM * i,
+						 remain_bytes);
+			else
+				ret =
+				    fts_i2c_read(client, buf, 0,
+						 buf +
+						 I2C_BUFFER_LENGTH_MAXINUM * i,
+						 I2C_BUFFER_LENGTH_MAXINUM);
+			remain_bytes -= I2C_BUFFER_LENGTH_MAXINUM;
+		}
+	}
+
+	return ret;
+}
+
+/************************************************************************
+*   Name: fts_gesture_readdata
+* Brief: read data from TP register
+* Input: no
+* Output: no
+* Return: fail <0
+***********************************************************************/
+int fts_gesture_readdata(struct i2c_client *client)
+{
+	u8 buf[FTS_GESTRUE_POINTS * 4] = { 0 };
+	int ret = -1;
+	int i = 0;
+	int gestrue_id = 0;
+	int read_bytes = 0;
+	u8 pointnum;
+
+	FTS_FUNC_ENTER();
+	/* init variable before read gesture point */
+	memset(fts_gesture_data.header, 0, FTS_GESTRUE_POINTS_HEADER);
+	memset(fts_gesture_data.coordinate_x, 0,
+	       FTS_GESTRUE_POINTS * sizeof(u16));
+	memset(fts_gesture_data.coordinate_y, 0,
+	       FTS_GESTRUE_POINTS * sizeof(u16));
+
+	buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+	ret = fts_i2c_read(client, buf, 1, buf, FTS_GESTRUE_POINTS_HEADER);
+	if (ret < 0) {
+		FTS_ERROR("[GESTURE]Read gesture header data failed!!");
+		FTS_FUNC_EXIT();
+		return ret;
+	}
+
+	/* FW recognize gesture */
+	if (chip_types.chip_idh == 0x54 || chip_types.chip_idh == 0x58
+	    || chip_types.chip_idh == 0x86 || chip_types.chip_idh == 0x87
+	    || chip_types.chip_idh == 0x64) {
+		memcpy(fts_gesture_data.header, buf, FTS_GESTRUE_POINTS_HEADER);
+		gestrue_id = buf[0];
+		pointnum = buf[1];
+		read_bytes = ((int)pointnum) * 4 + 2;
+		buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+		FTS_DEBUG("[GESTURE]PointNum=%d", pointnum);
+		ret = fts_gesture_read_buffer(client, buf, read_bytes);
+		if (ret < 0) {
+			FTS_ERROR("[GESTURE]Read gesture touch data failed!!");
+			FTS_FUNC_EXIT();
+			return ret;
+		}
+
+		fts_check_gesture(fts_input_dev, gestrue_id);
+		for (i = 0; i < pointnum; i++) {
+			fts_gesture_data.coordinate_x[i] =
+			    (((s16) buf[0 + (4 * i + 2)]) & 0x0F) << 8 |
+			    (((s16) buf[1 + (4 * i + 2)]) & 0xFF);
+			fts_gesture_data.coordinate_y[i] =
+			    (((s16) buf[2 + (4 * i + 2)]) & 0x0F) << 8 |
+			    (((s16) buf[3 + (4 * i + 2)]) & 0xFF);
+		}
+		FTS_FUNC_EXIT();
+		return 0;
+	}
+	/* other IC's gestrue in driver */
+	if (0x24 == buf[0]) {
+		gestrue_id = 0x24;
+		fts_check_gesture(fts_input_dev, gestrue_id);
+		FTS_DEBUG("[GESTURE]%d check_gesture gestrue_id", gestrue_id);
+		FTS_FUNC_EXIT();
+		return -1;
+	}
+
+	/* Host Driver recognize gesture */
+	pointnum = buf[1];
+	read_bytes = ((int)pointnum) * 4 + 2;
+	buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+	ret = fts_gesture_read_buffer(client, buf, read_bytes);
+	if (ret < 0) {
+		FTS_ERROR ("[GESTURE]Driver recognize gesture -"
+					"Read gesture touch data failed!!");
+		FTS_FUNC_EXIT();
+		return ret;
+	}
+
+	/*
+	 * Host Driver recognize gesture, need gesture lib.a
+	 * Not use now for compatibility
+	 gestrue_id = fetch_object_sample(buf, pointnum);
+	 */
+	gestrue_id = 0x24;
+	fts_check_gesture(fts_input_dev, gestrue_id);
+	FTS_DEBUG("[GESTURE]%d read gestrue_id", gestrue_id);
+
+	for (i = 0; i < pointnum; i++) {
+		fts_gesture_data.coordinate_x[i] =
+		    (((s16) buf[0 + (4 * i + 8)]) & 0x0F) << 8 |
+		    (((s16) buf[1 + (4 * i + 8)]) & 0xFF);
+		fts_gesture_data.coordinate_y[i] =
+		    (((s16) buf[2 + (4 * i + 8)]) & 0x0F) << 8 |
+		    (((s16) buf[3 + (4 * i + 8)]) & 0xFF);
+	}
+	FTS_FUNC_EXIT();
+	return -1;
+}
+
+/*****************************************************************************
+*   Name: fts_gesture_suspend
+*  Brief:
+*  Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+int fts_gesture_suspend(struct i2c_client *i2c_client)
+{
+	int i;
+	u8 state;
+
+	FTS_FUNC_ENTER();
+
+	/* gesture not enable, return immediately */
+	if (fts_gesture_data.mode == 0) {
+		FTS_DEBUG("gesture is disabled");
+		FTS_FUNC_EXIT();
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++) {
+		fts_i2c_write_reg(i2c_client, 0xd1, 0xff);
+		fts_i2c_write_reg(i2c_client, 0xd2, 0xff);
+		fts_i2c_write_reg(i2c_client, 0xd5, 0xff);
+		fts_i2c_write_reg(i2c_client, 0xd6, 0xff);
+		fts_i2c_write_reg(i2c_client, 0xd7, 0xff);
+		fts_i2c_write_reg(i2c_client, 0xd8, 0xff);
+		fts_i2c_write_reg(i2c_client, FTS_REG_GESTURE_EN, 0x01);
+		msleep(1);
+		fts_i2c_read_reg(i2c_client, FTS_REG_GESTURE_EN, &state);
+		if (state == 1)
+			break;
+	}
+
+	if (i >= 5) {
+		FTS_ERROR("[GESTURE]Enter into gesture(suspend) failed!\n");
+		FTS_FUNC_EXIT();
+		return -1;
+	}
+
+	fts_gesture_data.active = 1;
+	FTS_DEBUG("[GESTURE]Enter into gesture(suspend) successfully!");
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*   Name: fts_gesture_resume
+*  Brief:
+*  Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+int fts_gesture_resume(struct i2c_client *client)
+{
+	int i;
+	u8 state;
+
+	FTS_FUNC_ENTER();
+
+	/* gesture not enable, return immediately */
+	if (fts_gesture_data.mode == 0) {
+		FTS_DEBUG("gesture is disabled");
+		FTS_FUNC_EXIT();
+		return -1;
+	}
+
+	fts_gesture_data.active = 0;
+	for (i = 0; i < 5; i++) {
+		fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, 0x00);
+		msleep(1);
+		fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state);
+		if (state == 0)
+			break;
+	}
+
+	if (i >= 5)
+		FTS_ERROR("[GESTURE]Clear gesture(resume) failed!\n");
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+#endif
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c
new file mode 100644
index 0000000..75cd917
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c
@@ -0,0 +1,209 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/************************************************************************
+*
+* File Name: focaltech_i2c.c
+*
+*    Author: fupeipei
+*
+*   Created: 2016-08-04
+*
+*  Abstract: i2c communication with TP
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release. By fupeipei 2016-08-04
+************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static DEFINE_MUTEX(i2c_rw_access);
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+
+/************************************************************************
+* Name: fts_i2c_read
+* Brief: i2c read
+* Input: i2c info, write buf, write len, read buf, read len
+* Output: get data in the 3rd buf
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen,
+		 char *readbuf, int readlen)
+{
+	int ret = 0;
+
+	mutex_lock(&i2c_rw_access);
+
+	if (readlen > 0) {
+		if (writelen > 0) {
+			struct i2c_msg msgs[] = {
+				{
+				 .addr = client->addr,
+				 .flags = 0,
+				 .len = writelen,
+				 .buf = writebuf,
+				 },
+				{
+				 .addr = client->addr,
+				 .flags = I2C_M_RD,
+				 .len = readlen,
+				 .buf = readbuf,
+				 },
+			};
+			ret = i2c_transfer(client->adapter, msgs, 2);
+			if (ret < 0) {
+				FTS_ERROR("[IIC]: i2c_transfer(write)"
+					"error,ret=%d!!", ret);
+			}
+		} else {
+			struct i2c_msg msgs[] = {
+				{
+				 .addr = client->addr,
+				 .flags = I2C_M_RD,
+				 .len = readlen,
+				 .buf = readbuf,
+				 },
+			};
+			ret = i2c_transfer(client->adapter, msgs, 1);
+			if (ret < 0) {
+				FTS_ERROR("[IIC]: i2c_transfer(read)"
+					"error, ret=%d!!", ret);
+			}
+		}
+	}
+
+	mutex_unlock(&i2c_rw_access);
+	return ret;
+}
+
+/************************************************************************
+* Name: fts_i2c_write
+* Brief: i2c write
+* Input: i2c info, write buf, write len
+* Output: no
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen)
+{
+	int ret = 0;
+
+	mutex_lock(&i2c_rw_access);
+	if (writelen > 0) {
+		struct i2c_msg msgs[] = {
+			{
+			 .addr = client->addr,
+			 .flags = 0,
+			 .len = writelen,
+			 .buf = writebuf,
+			 },
+		};
+		ret = i2c_transfer(client->adapter, msgs, 1);
+		if (ret < 0) {
+			FTS_ERROR("%s: i2c_transfer(write) error, ret=%d",
+				  __func__, ret);
+		}
+	}
+	mutex_unlock(&i2c_rw_access);
+
+	return ret;
+}
+
+/************************************************************************
+* Name: fts_i2c_write_reg
+* Brief: write register
+* Input: i2c info, reg address, reg value
+* Output: no
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue)
+{
+	u8 buf[2] = { 0 };
+
+	buf[0] = regaddr;
+	buf[1] = regvalue;
+	return fts_i2c_write(client, buf, sizeof(buf));
+}
+
+/************************************************************************
+* Name: fts_i2c_read_reg
+* Brief: read register
+* Input: i2c info, reg address, reg value
+* Output: get reg value
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue)
+{
+	return fts_i2c_read(client, &regaddr, 1, regvalue, 1);
+}
+
+/************************************************************************
+* Name: fts_i2c_init
+* Brief: fts i2c init
+* Input:
+* Output:
+* Return:
+***********************************************************************/
+int fts_i2c_init(void)
+{
+	FTS_FUNC_ENTER();
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/************************************************************************
+* Name: fts_i2c_exit
+* Brief: fts i2c exit
+* Input:
+* Output:
+* Return:
+***********************************************************************/
+int fts_i2c_exit(void)
+{
+	FTS_FUNC_ENTER();
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c
new file mode 100644
index 0000000..0fa5617
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c
@@ -0,0 +1,151 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_point_report_check.c
+*
+*    Author: WangTao
+*
+*   Created: 2016-11-16
+*
+*  Abstract: point report check function
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release. By WangTao 2016-11-16
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_POINT_REPORT_CHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define POINT_REPORT_CHECK_WAIT_TIME              200
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct delayed_work fts_point_report_check_work;
+static struct workqueue_struct *fts_point_report_check_workqueue;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+
+/*****************************************************************************
+*  Name: fts_point_report_check_func
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_point_report_check_func(struct work_struct *work)
+{
+
+#if FTS_MT_PROTOCOL_B_EN
+	unsigned int finger_count = 0;
+
+	FTS_FUNC_ENTER();
+
+	for (finger_count = 0; finger_count < FTS_MAX_POINTS; finger_count++) {
+		input_mt_slot(fts_input_dev, finger_count);
+		input_mt_report_slot_state(fts_input_dev, MT_TOOL_FINGER,
+					   false);
+	}
+#else
+	input_mt_sync(fts_input_dev);
+#endif
+	input_report_key(fts_input_dev, BTN_TOUCH, 0);
+	input_sync(fts_input_dev);
+
+	FTS_FUNC_EXIT();
+}
+
+void fts_point_report_check_queue_work(void)
+{
+
+	cancel_delayed_work(&fts_point_report_check_work);
+	queue_delayed_work(fts_point_report_check_workqueue,
+			   &fts_point_report_check_work,
+			   msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
+
+}
+
+/*****************************************************************************
+*  Name: fts_point_report_check_init
+*  Brief:
+*  Input:
+*  Output:
+*  Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_point_report_check_init(void)
+{
+	FTS_FUNC_ENTER();
+
+	INIT_DELAYED_WORK(&fts_point_report_check_work,
+			  fts_point_report_check_func);
+	fts_point_report_check_workqueue =
+	    create_workqueue("fts_point_report_check_func_wq");
+	if (fts_point_report_check_workqueue == NULL) {
+		FTS_ERROR("[POINT_REPORT]: Failed to create"
+				"fts_point_report_check_workqueue!!");
+	} else {
+		FTS_DEBUG("[POINT_REPORT]: Success to create"
+				"fts_point_report_check_workqueue!!");
+	}
+
+	FTS_FUNC_EXIT();
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_point_report_check_exit
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_point_report_check_exit(void)
+{
+	FTS_FUNC_ENTER();
+
+	destroy_workqueue(fts_point_report_check_workqueue);
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+#endif /* FTS_POINT_REPORT_CHECK_EN */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c b/drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c
new file mode 100644
index 0000000..4a62ee6
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_esdcheck.c
+*
+*    Author: Focaltech Driver Team
+*
+*   Created: 2016-08-03
+*
+*  Abstract: Sensor
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release. By luougojin 2016-08-03
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_PSENSOR_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+/* psensor register address*/
+#define FTS_REG_PSENSOR_ENABLE                  0xB0
+#define FTS_REG_PSENSOR_STATUS                  0x01
+
+/* psensor register bits*/
+#define FTS_PSENSOR_ENABLE_MASK                 0x01
+#define FTS_PSENSOR_STATUS_NEAR                 0xC0
+#define FTS_PSENSOR_STATUS_FAR                  0xE0
+#define FTS_PSENSOR_FAR_TO_NEAR                 0
+#define FTS_PSENSOR_NEAR_TO_FAR                 1
+#define FTS_PSENSOR_ORIGINAL_STATE_FAR          1
+#define FTS_PSENSOR_WAKEUP_TIMEOUT              500
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct sensors_classdev __maybe_unused sensors_proximity_cdev = {
+	.name = "fts-proximity",
+	.vendor = "FocalTech",
+	.version = 1,
+	.handle = SENSORS_PROXIMITY_HANDLE,
+	.type = SENSOR_TYPE_PROXIMITY,
+	.max_range = "5.0",
+	.resolution = "5.0",
+	.sensor_power = "0.1",
+	.min_delay = 0,
+	.fifo_reserved_event_count = 0,
+	.fifo_max_event_count = 0,
+	.enabled = 0,
+	.delay_msec = 200,
+	.sensors_enable = NULL,
+	.sensors_poll_delay = NULL,
+};
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+*  Name: fts_psensor_support_enabled
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static inline bool fts_psensor_support_enabled(void)
+{
+	/*return config_enabled(CONFIG_TOUCHSCREEN_FTS_PSENSOR); */
+	return FTS_PSENSOR_EN;
+}
+
+/*****************************************************************************
+*  Name: fts_psensor_enable
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_psensor_enable(struct fts_ts_data *data, int enable)
+{
+	u8 state;
+	int ret = -1;
+
+	if (data->client == NULL)
+		return;
+
+	fts_i2c_read_reg(data->client, FTS_REG_PSENSOR_ENABLE, &state);
+	if (enable)
+		state |= FTS_PSENSOR_ENABLE_MASK;
+	else
+		state &= ~FTS_PSENSOR_ENABLE_MASK;
+
+	ret = fts_i2c_write_reg(data->client, FTS_REG_PSENSOR_ENABLE, state);
+	if (ret < 0)
+		FTS_ERROR("write psensor switch command failed");
+}
+
+/*****************************************************************************
+*  Name: fts_psensor_enable_set
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_psensor_enable_set(struct sensors_classdev *sensors_cdev,
+				  unsigned int enable)
+{
+	struct fts_psensor_platform_data *psensor_pdata =
+	    container_of(sensors_cdev,
+			 struct fts_psensor_platform_data, ps_cdev);
+	struct fts_ts_data *data = psensor_pdata->data;
+	struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev;
+
+	mutex_lock(&input_dev->mutex);
+	fts_psensor_enable(data, enable);
+	psensor_pdata->tp_psensor_data = FTS_PSENSOR_ORIGINAL_STATE_FAR;
+	if (enable)
+		psensor_pdata->tp_psensor_opened = 1;
+	else
+		psensor_pdata->tp_psensor_opened = 0;
+	mutex_unlock(&input_dev->mutex);
+	return enable;
+}
+
+/*****************************************************************************
+*  Name: fts_read_tp_psensor_data
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static int fts_read_tp_psensor_data(struct fts_ts_data *data)
+{
+	u8 psensor_status;
+	char tmp;
+	int ret = 1;
+
+	fts_i2c_read_reg(data->client, FTS_REG_PSENSOR_STATUS, &psensor_status);
+
+	tmp = data->psensor_pdata->tp_psensor_data;
+	if (psensor_status == FTS_PSENSOR_STATUS_NEAR)
+		data->psensor_pdata->tp_psensor_data = FTS_PSENSOR_FAR_TO_NEAR;
+	else if (psensor_status == FTS_PSENSOR_STATUS_FAR)
+		data->psensor_pdata->tp_psensor_data = FTS_PSENSOR_NEAR_TO_FAR;
+
+	if (tmp != data->psensor_pdata->tp_psensor_data) {
+		FTS_ERROR("%s sensor data changed", __func__);
+		ret = 0;
+	}
+	return ret;
+}
+
+int fts_sensor_read_data(struct fts_ts_data *data)
+{
+	int ret = 0;
+
+	if (fts_psensor_support_enabled()
+	    && data->psensor_pdata->tp_psensor_opened) {
+		ret = fts_read_tp_psensor_data(data);
+		if (!ret) {
+			if (data->suspended) {
+				pm_wakeup_event(&data->client->dev,
+						FTS_PSENSOR_WAKEUP_TIMEOUT);
+			}
+			input_report_abs(data->psensor_pdata->input_psensor_dev,
+					 ABS_DISTANCE,
+					 data->psensor_pdata->tp_psensor_data);
+			input_sync(data->psensor_pdata->input_psensor_dev);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int fts_sensor_suspend(struct fts_ts_data *data)
+{
+	int ret = 0;
+
+	if (fts_psensor_support_enabled()
+	    && device_may_wakeup(&data->client->dev)
+	    && data->psensor_pdata->tp_psensor_opened) {
+		ret = enable_irq_wake(data->client->irq);
+		if (ret != 0)
+			FTS_ERROR("%s: set_irq_wake failed", __func__);
+		data->suspended = true;
+		return 1;
+	}
+
+	return 0;
+}
+
+int fts_sensor_resume(struct fts_ts_data *data)
+{
+	int ret = 0;
+
+	if (fts_psensor_support_enabled()
+	    && device_may_wakeup(&data->client->dev)
+	    && data->psensor_pdata->tp_psensor_opened) {
+		ret = disable_irq_wake(data->client->irq);
+		if (ret)
+			FTS_ERROR("%s: disable_irq_wake failed", __func__);
+		data->suspended = false;
+		return 1;
+	}
+
+	return 0;
+}
+
+int fts_sensor_init(struct fts_ts_data *data)
+{
+	struct fts_psensor_platform_data *psensor_pdata;
+	struct input_dev *psensor_input_dev;
+	int err;
+
+	if (fts_psensor_support_enabled()) {
+		device_init_wakeup(&data->client->dev, 1);
+		psensor_pdata =
+		    devm_kzalloc(&data->client->dev,
+				 sizeof(struct fts_psensor_platform_data),
+				 GFP_KERNEL);
+		if (!psensor_pdata) {
+			FTS_ERROR("Failed to allocate memory");
+			goto irq_free;
+		}
+		data->psensor_pdata = psensor_pdata;
+
+		psensor_input_dev = input_allocate_device();
+		if (!psensor_input_dev) {
+			FTS_ERROR("Failed to allocate device");
+			goto free_psensor_pdata;
+		}
+
+		__set_bit(EV_ABS, psensor_input_dev->evbit);
+		input_set_abs_params(psensor_input_dev, ABS_DISTANCE, 0, 1, 0,
+				     0);
+		psensor_input_dev->name = "proximity";
+		psensor_input_dev->id.bustype = BUS_I2C;
+		psensor_input_dev->dev.parent = &data->client->dev;
+		data->psensor_pdata->input_psensor_dev = psensor_input_dev;
+
+		err = input_register_device(psensor_input_dev);
+		if (err) {
+			FTS_ERROR("Unable to register device, err=%d", err);
+			goto free_psensor_input_dev;
+		}
+
+		psensor_pdata->ps_cdev = sensors_proximity_cdev;
+		psensor_pdata->ps_cdev.sensors_enable = fts_psensor_enable_set;
+		psensor_pdata->data = data;
+
+		err =
+		    sensors_classdev_register(&data->client->dev,
+					      &psensor_pdata->ps_cdev);
+		if (err)
+			goto unregister_psensor_input_device;
+	}
+
+	return 0;
+unregister_psensor_input_device:
+	if (fts_psensor_support_enabled())
+		input_unregister_device(data->psensor_pdata->input_psensor_dev);
+free_psensor_input_dev:
+	if (fts_psensor_support_enabled())
+		input_free_device(data->psensor_pdata->input_psensor_dev);
+free_psensor_pdata:
+	if (fts_psensor_support_enabled()) {
+		devm_kfree(&data->client->dev, psensor_pdata);
+		data->psensor_pdata = NULL;
+	}
+irq_free:
+	if (fts_psensor_support_enabled())
+		device_init_wakeup(&data->client->dev, 0);
+	free_irq(data->client->irq, data);
+
+	return 1;
+}
+
+int fts_sensor_remove(struct fts_ts_data *data)
+{
+	if (fts_psensor_support_enabled()) {
+		device_init_wakeup(&data->client->dev, 0);
+		sensors_classdev_unregister(&data->psensor_pdata->ps_cdev);
+		input_unregister_device(data->psensor_pdata->input_psensor_dev);
+		devm_kfree(&data->client->dev, data->psensor_pdata);
+		data->psensor_pdata = NULL;
+	}
+	return 0;
+}
+#endif /* FTS_PSENSOR_EN */
diff --git a/include/dt-bindings/input/input.h b/include/dt-bindings/input/input.h
index bcf0ae1..84690c3 100644
--- a/include/dt-bindings/input/input.h
+++ b/include/dt-bindings/input/input.h
@@ -15,4 +15,6 @@
 #define MATRIX_KEY(row, col, code)	\
 	((((row) & 0xFF) << 24) | (((col) & 0xFF) << 16) | ((code) & 0xFFFF))
 
+#define FT6236 0x6236D003
+
 #endif /* _DT_BINDINGS_INPUT_INPUT_H */