From a813214788f6e7b512df54f1c659cd0bdc9ac175 Mon Sep 17 00:00:00 2001
From: huangcm <1263938474@qq.com>
Date: Wed, 26 Feb 2025 10:47:01 +0000
Subject: [PATCH] feat(mipi): add 5' mipi & touchscreen config

---
 longan/device/config/chips/a133/configs/c3/kickpi-k5c.dts                                                            |   19 
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_common.h                          |  193 +
 longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.h                                     |    1 
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash/focaltech_upgrade_ft8756m.c |  446 ++
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.h                            |  426 ++
 longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/Makefile                                         |    1 
 longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/Makefile                                                |    2 
 longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.c                                            |    1 
 longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.c                                     |    2 
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_config.h                          |  362 +
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.c                            | 2411 ++++++++++++
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_i2c.c                             |  335 +
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_fun.c                          | 1269 ++++++
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_gesture.c                         |  511 ++
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_mode.c                         |  390 ++
 longan/kernel/linux-4.9/arch/arm64/boot/dts/sunxi/lcd-mipi-5-720-1280.dtsi                                           |  260 +
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Kconfig                                     |   10 
 longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.c                            |  374 +
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.h                           |  235 +
 longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.h                            |   20 
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.c                           | 2273 +++++++++++
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_proximity.c                       |  827 ++++
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_point_report_check.c              |  185 
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Makefile                                    |   14 
 longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.h                                   |   23 
 longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_esdcheck.c                        |  442 ++
 longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.c                                   |  392 ++
 longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.h                                            |    1 
 longan/kernel/linux-4.9/drivers/input/touchscreen/Makefile                                                           |    1 
 29 files changed, 11,416 insertions(+), 10 deletions(-)

diff --git a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/Makefile b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/Makefile
index f8c33eb..067fce1 100644
--- a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/Makefile
+++ b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/Makefile
@@ -53,6 +53,7 @@
 disp-$(CONFIG_LCD_SUPPORT_K101_IM2BYL02_L_800X1280) += lcd/K101_IM2BYL02_L_800X1280.o
 disp-y				+= lcd/mipi_8_800x1280.o
 disp-y				+= lcd/mipi_10_800x1280.o
+disp-y				+= lcd/mipi_5_720x1280.o
 
 ifeq ($(CONFIG_MACH_SUN8IW6),y)
     export MIPI_DSI_IP_VERSION := 28
diff --git a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.c b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.c
new file mode 100755
index 0000000..b1b9353
--- /dev/null
+++ b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.c
@@ -0,0 +1,374 @@
+#include "mipi_5_720x1280.h"
+
+static void lcd_power_on(u32 sel);
+static void lcd_power_off(u32 sel);
+static void lcd_bl_open(u32 sel);
+static void lcd_bl_close(u32 sel);
+
+static void lcd_panel_init1(u32 sel);
+static void lcd_panel_exit(u32 sel);
+
+#define panel_reset(sel, val) sunxi_lcd_gpio_set_value(sel, 0, val)
+
+static void lcd_cfg_panel_info(panel_extend_para *info)
+{
+	u32 i = 0, j = 0;
+	u32 items;
+	u8 lcd_gamma_tbl[][2] = {
+		{0, 0},
+		{15, 15},
+		{30, 30},
+		{45, 45},
+		{60, 60},
+		{75, 75},
+		{90, 90},
+		{105, 105},
+		{120, 120},
+		{135, 135},
+		{150, 150},
+		{165, 165},
+		{180, 180},
+		{195, 195},
+		{210, 210},
+		{225, 225},
+		{240, 240},
+		{255, 255},
+	};
+
+	u32 lcd_cmap_tbl[2][3][4] = {
+	{
+		{LCD_CMAP_G0, LCD_CMAP_B1, LCD_CMAP_G2, LCD_CMAP_B3},
+		{LCD_CMAP_B0, LCD_CMAP_R1, LCD_CMAP_B2, LCD_CMAP_R3},
+		{LCD_CMAP_R0, LCD_CMAP_G1, LCD_CMAP_R2, LCD_CMAP_G3},
+		},
+		{
+		{LCD_CMAP_B3, LCD_CMAP_G2, LCD_CMAP_B1, LCD_CMAP_G0},
+		{LCD_CMAP_R3, LCD_CMAP_B2, LCD_CMAP_R1, LCD_CMAP_B0},
+		{LCD_CMAP_G3, LCD_CMAP_R2, LCD_CMAP_G1, LCD_CMAP_R0},
+		},
+	};
+
+	items = sizeof(lcd_gamma_tbl) / 2;
+	for (i = 0; i < items - 1; i++) {
+		u32 num = lcd_gamma_tbl[i+1][0] - lcd_gamma_tbl[i][0];
+
+		for (j = 0; j < num; j++) {
+			u32 value = 0;
+
+			value = lcd_gamma_tbl[i][1] +
+				((lcd_gamma_tbl[i+1][1] - lcd_gamma_tbl[i][1])
+				* j) / num;
+			info->lcd_gamma_tbl[lcd_gamma_tbl[i][0] + j] =
+							(value<<16)
+							+ (value<<8) + value;
+		}
+	}
+	info->lcd_gamma_tbl[255] = (lcd_gamma_tbl[items-1][1]<<16) +
+					(lcd_gamma_tbl[items-1][1]<<8)
+					+ lcd_gamma_tbl[items-1][1];
+
+	memcpy(info->lcd_cmap_tbl, lcd_cmap_tbl, sizeof(lcd_cmap_tbl));
+
+}
+
+static s32 lcd_open_flow(u32 sel)
+{
+	LCD_OPEN_FUNC(sel, lcd_power_on, 50);
+	LCD_OPEN_FUNC(sel, lcd_panel_init1, 20);
+	LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 20);
+	lcd_bl_open(sel);
+
+	return 0;
+}
+
+static s32 lcd_close_flow(u32 sel)
+{
+	LCD_CLOSE_FUNC(sel, lcd_bl_close, 0);
+	LCD_CLOSE_FUNC(sel, lcd_panel_exit, 1);
+	LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 10);
+	LCD_CLOSE_FUNC(sel, lcd_power_off, 0);
+
+	return 0;
+}
+
+static void lcd_power_on(u32 sel)
+{
+	sunxi_lcd_pin_cfg(sel, 1);
+
+	panel_reset(sel, 0);
+	sunxi_lcd_power_enable(sel, 0);
+	sunxi_lcd_power_enable(sel, 1);
+	sunxi_lcd_delay_ms(50);
+	panel_reset(sel, 1);
+	sunxi_lcd_delay_ms(10);
+	panel_reset(sel, 0);
+	sunxi_lcd_delay_ms(20);
+	panel_reset(sel, 1);
+	sunxi_lcd_delay_ms(120);
+}
+
+static void lcd_power_off(u32 sel)
+{
+	panel_reset(sel, 0);
+	sunxi_lcd_delay_ms(1);
+	sunxi_lcd_power_disable(sel, 1);
+	sunxi_lcd_delay_ms(1);
+	sunxi_lcd_power_disable(sel, 0);
+	sunxi_lcd_pin_cfg(sel, 0);
+}
+
+static void lcd_bl_open(u32 sel)
+{
+	sunxi_lcd_pwm_enable(sel);
+	sunxi_lcd_backlight_enable(sel);
+}
+
+static void lcd_bl_close(u32 sel)
+{
+	sunxi_lcd_backlight_disable(sel);
+	sunxi_lcd_pwm_disable(sel);
+}
+
+#define ROWS 171
+#define COLS 44
+
+u8 data[ROWS][COLS] = {
+    // CMD2 ENABLE
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x04, 0xFF, 0x87, 0x56, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x03, 0xFF, 0x87, 0x56},
+    // Panel Resolution 720x1280
+    {0x15, 0x00, 0x02, 0x00, 0xA1},
+    {0x39, 0x00, 0x07, 0xB3, 0x02, 0xD0, 0x05, 0x00, 0x20, 0xFC},
+    // TCON
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xF6, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xF6, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x07, 0xC0, 0x02, 0x14, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xF6, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xC1},
+    {0x39, 0x00, 0x09, 0xC0, 0x01, 0x27, 0x00, 0xEA, 0x00, 0xC5, 0x01, 0x5F},
+    {0x15, 0x00, 0x02, 0x00, 0xD7},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xC5, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xA3},
+    {0x39, 0x00, 0x07, 0xC1, 0x00, 0x20, 0x00, 0x20, 0x00, 0x02},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCE, 0x01, 0x81, 0x09, 0x13, 0x00, 0x78, 0x00, 0x78, 0x00, 0x50, 0x00, 0x50, 0x00, 0x78, 0x00, 0x78},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x10, 0xCE, 0x00, 0x8E, 0x0E, 0xB6, 0x00, 0x8E, 0x80, 0x09, 0x13, 0x00, 0x04, 0x00, 0x1C, 0x1F, 0x16},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x04, 0xCE, 0x20, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x04, 0xCE, 0x22, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xD1},
+    {0x39, 0x00, 0x08, 0xCE, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xE1},
+    {0x39, 0x00, 0x0C, 0xCE, 0x08, 0x02, 0x4D, 0x02, 0x4D, 0x02, 0x4D, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xF1},
+    {0x39, 0x00, 0x0A, 0xCE, 0x0C, 0x06, 0x0A, 0x00, 0xE9, 0x00, 0xE9, 0x00, 0xF8},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x05, 0xCF, 0x00, 0x00, 0x6E, 0x72},
+    {0x15, 0x00, 0x02, 0x00, 0xB5},
+    {0x39, 0x00, 0x05, 0xCF, 0x02, 0x02, 0x94, 0x98},
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x05, 0xCF, 0x04, 0x04, 0xD8, 0xDC},
+    {0x15, 0x00, 0x02, 0x00, 0xC5},
+    {0x39, 0x00, 0x05, 0xCF, 0x00, 0x00, 0x08, 0x0C},
+    // Scan Mode
+    {0x15, 0x00, 0x02, 0x00, 0xE8},
+    {0x15, 0x00, 0x02, 0xC0, 0x40},
+    // VST1&VST2
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x09, 0xC2, 0x84, 0x00, 0x05, 0x89, 0x83, 0x00, 0x05, 0x89},
+    // CKV1-3
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x10, 0xC2, 0x82, 0x04, 0x00, 0x05, 0x89, 0x81, 0x04, 0x00, 0x05, 0x89, 0x00, 0x04, 0x00, 0x05, 0x89},
+    // CKV4-6
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x10, 0xC2, 0x01, 0x04, 0x00, 0x05, 0x89, 0x02, 0x04, 0x00, 0x05, 0x89, 0x03, 0x04, 0x00, 0x05, 0x89},
+    // CKV7-8
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x0B, 0xC2, 0x04, 0x04, 0x00, 0x05, 0x89, 0x05, 0x04, 0x00, 0x05, 0x89},
+    {0x15, 0x00, 0x02, 0x00, 0xE0},
+    {0x39, 0x00, 0x05, 0xC2, 0x77, 0x77, 0x77, 0x77},
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x05, 0xC3, 0x99, 0x99, 0x99, 0x99},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCB, 0x00, 0xC5, 0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x0A, 0x05, 0xC5, 0x00, 0x05, 0x05, 0x00, 0xC0},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x11, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x05, 0xCB, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x05, 0xCB, 0x10, 0x51, 0x94, 0x50},
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x05, 0xCB, 0x10, 0x51, 0x94, 0x50},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCC, 0x2D, 0x2D, 0x2D, 0x2D, 0x07, 0x09, 0x0B, 0x0D, 0x25, 0x25, 0x03, 0x2D, 0x22, 0x2D, 0x24, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x09, 0xCC, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCD, 0x2D, 0x2D, 0x2D, 0x2D, 0x06, 0x08, 0x0A, 0x0C, 0x25, 0x00, 0x02, 0x2D, 0x22, 0x2D, 0x24, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x09, 0xCD, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x11, 0xCC, 0x2D, 0x2D, 0x2D, 0x2D, 0x08, 0x06, 0x0C, 0x0A, 0x25, 0x25, 0x02, 0x2D, 0x24, 0x2D, 0x23, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x09, 0xCC, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x11, 0xCD, 0x2D, 0x2D, 0x2D, 0x2D, 0x09, 0x07, 0x0D, 0x0B, 0x25, 0x00, 0x03, 0x2D, 0x24, 0x2D, 0x23, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x09, 0xCD, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x15, 0x00, 0x02, 0xA7, 0x13},
+    {0x15, 0x00, 0x02, 0x00, 0x82},
+    {0x39, 0x00, 0x03, 0xA7, 0x22, 0x02},
+    {0x15, 0x00, 0x02, 0x00, 0x85},
+    {0x15, 0x00, 0x02, 0xC4, 0x1C},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x04, 0xC4, 0x8D, 0xD8, 0x8D},
+    {0x15, 0x00, 0x02, 0x00, 0x93},
+    {0x15, 0x00, 0x02, 0xC5, 0x37},
+    {0x15, 0x00, 0x02, 0x00, 0x97},
+    {0x15, 0x00, 0x02, 0xC5, 0x37},
+    {0x15, 0x00, 0x02, 0x00, 0xB6},
+    {0x39, 0x00, 0x03, 0xC5, 0x2D, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x9A},
+    {0x15, 0x00, 0x02, 0xC5, 0x19},
+    {0x15, 0x00, 0x02, 0x00, 0x9C},
+    {0x15, 0x00, 0x02, 0xC5, 0x19},
+    {0x15, 0x00, 0x02, 0x00, 0x97},
+    {0x15, 0x00, 0x02, 0xC4, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x9B},
+    {0x15, 0x00, 0x02, 0xF5, 0x4B},
+    {0x15, 0x00, 0x02, 0x00, 0x93},
+    {0x39, 0x00, 0x03, 0xF5, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x9D},
+    {0x15, 0x00, 0x02, 0xF5, 0x49},
+    {0x15, 0x00, 0x02, 0x00, 0x82},
+    {0x39, 0x00, 0x03, 0xF5, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x8C},
+    {0x39, 0x00, 0x04, 0xC3, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x84},
+    {0x39, 0x00, 0x03, 0xC5, 0x28, 0x28},
+    {0x15, 0x00, 0x02, 0x00, 0xA4},
+    {0x15, 0x00, 0x02, 0xD7, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x03, 0xF5, 0x59, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0x84},
+    {0x39, 0x00, 0x04, 0xF5, 0x59, 0x59, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0x96},
+    {0x15, 0x00, 0x02, 0xF5, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0xA6},
+    {0x15, 0x00, 0x02, 0xF5, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0xCA},
+    {0x15, 0x00, 0x02, 0xC0, 0x80},
+    {0x15, 0x00, 0x02, 0x00, 0xB1},
+    {0x15, 0x00, 0x02, 0xF5, 0x1F},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x03, 0xD8, 0x29, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x86},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x22, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0x96},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xA6},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x3E, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xE9},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xA3},
+    {0x39, 0x00, 0x07, 0xCE, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xB3},
+    {0x39, 0x00, 0x07, 0xCE, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    // gamma2.2
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE1, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE2, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE3, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE4, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE5, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE6, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    // EN GND 4 frame
+    {0x15, 0x00, 0x02, 0x00, 0xCC},
+    {0x15, 0x00, 0x02, 0xC0, 0x13},
+    // LVD detect Voltage
+    {0x15, 0x00, 0x02, 0x00, 0x82},
+    {0x15, 0x00, 0x02, 0xC5, 0x35},
+    {0x15, 0x00, 0x02, 0x00, 0x94},
+    {0x39, 0x00, 0x04, 0xC5, 0x02, 0x01, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x98},
+    {0x39, 0x00, 0x03, 0xC5, 0x25, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x9B},
+    {0x15, 0x00, 0x02, 0xC5, 0x21},
+    {0x15, 0x00, 0x02, 0x00, 0x9D},
+    {0x15, 0x00, 0x02, 0xC5, 0x25},
+    // Write ID
+    {0x15, 0x00, 0x02, 0x00, 0xD0},
+    {0x15, 0x00, 0x02, 0xD0, 0x56},
+    //LCD initial code End
+    {0x05, 0x78, 0x01, 0x11},
+    {0x05, 0x00, 0x01, 0x29},
+    {0x15, 0x00, 0x02, 0x35, 0x00}
+};
+
+static void lcd_panel_init1(u32 sel)
+{
+    int i;
+    u8 command; 
+    u8 *para;
+    u32 para_num;
+    u8 *tmp;
+	printk(KERN_ERR"------uboot----> mipi_5_720x1280 init\n");
+	sunxi_lcd_dsi_clk_enable(sel);
+	sunxi_lcd_delay_ms(5);
+
+    for(i = 0; i < ROWS; i++) {
+        tmp = data[i];
+        if (tmp[0] == 0x05) {
+            sunxi_lcd_dsi_dcs_write_0para(sel, tmp[3]);
+        } else {
+            command = tmp[3];
+            para_num = tmp[2] - 1;
+            para = tmp + 4;
+            sunxi_lcd_dsi_dcs_write(sel, command, para, para_num);
+        }
+        if (tmp[1] != 0x0)
+            sunxi_lcd_delay_ms(tmp[1]);
+    }
+}
+
+
+static void lcd_panel_exit(u32 sel)
+{
+	sunxi_lcd_dsi_dcs_write_0para(sel, 0x28);
+	sunxi_lcd_delay_ms(1);
+	sunxi_lcd_dsi_dcs_write_0para(sel, 0x10);
+	sunxi_lcd_delay_ms(1);
+}
+
+/*sel: 0:lcd0; 1:lcd1*/
+static s32 lcd_user_defined_func(u32 sel, u32 para1, u32 para2, u32 para3)
+{
+	return 0;
+}
+
+__lcd_panel_t mipi_5_720x1280_panel = {
+	/* panel driver name, must mach the name of
+	 * lcd_drv_name in sys_config.fex
+	 */
+	.name = "mipi_5_720x1280",
+	.func = {
+		.cfg_panel_info = lcd_cfg_panel_info,
+		.cfg_open_flow = lcd_open_flow,
+		.cfg_close_flow = lcd_close_flow,
+		.lcd_user_defined_func = lcd_user_defined_func,
+	},
+};
diff --git a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.h b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.h
new file mode 100755
index 0000000..8cc84d0
--- /dev/null
+++ b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/mipi_5_720x1280.h
@@ -0,0 +1,20 @@
+/* drivers/video/sunxi/disp2/disp/lcd/k101im2qa04.h
+ *
+ * Copyright (c) 2017 Allwinnertech Co., Ltd.
+ * Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
+ *
+ * k101im2qa04 panel driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _MIPI_5_720X1280_H
+#define _MIPI_5_720X1280_H
+
+#include "panels.h"
+
+extern __lcd_panel_t mipi_5_720x1280_panel;
+
+#endif /*End of file*/
diff --git a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.c b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.c
index c0de2f0..00f46da 100644
--- a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.c
+++ b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.c
@@ -138,7 +138,7 @@
 	&mipi_8_800x1280_panel,
 	&mipi_10_800x1280_panel,
 	/* add new panel below */
-
+    &mipi_5_720x1280_panel,
 	NULL,
 };
 
diff --git a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.h b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.h
index 03486a9..2acdd3a 100644
--- a/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.h
+++ b/longan/brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd/panels.h
@@ -239,5 +239,6 @@
 
 extern __lcd_panel_t mipi_8_800x1280_panel;
 extern __lcd_panel_t mipi_10_800x1280_panel;
+extern __lcd_panel_t mipi_5_720x1280_panel;
 
 #endif
diff --git a/longan/device/config/chips/a133/configs/c3/kickpi-k5c.dts b/longan/device/config/chips/a133/configs/c3/kickpi-k5c.dts
index c53fb05..bf96176 100755
--- a/longan/device/config/chips/a133/configs/c3/kickpi-k5c.dts
+++ b/longan/device/config/chips/a133/configs/c3/kickpi-k5c.dts
@@ -443,14 +443,14 @@
 
 					pmu_chg_ic_temp = <0>;
 					pmu_battery_rdc= <93>;
-					pmu_battery_cap = <5067>;
+					pmu_battery_cap = <5000>;
 					pmu_runtime_chgcur = <1000>;
 					pmu_suspend_chgcur = <2000>;
 					pmu_shutdown_chgcur = <2000>;
 					pmu_init_chgvol = <4200>;
 					pmu_battery_warning_level1 = <15>;
 					pmu_battery_warning_level2 = <0>;
-					pmu_chgled_func = <0>;
+					pmu_chgled_func = <1>;
 					pmu_chgled_type = <0>;
 					ocv_coulumb_100 = <1>;
 					pmu_bat_para1 = <0>;
@@ -476,12 +476,12 @@
 					pmu_bat_para21 = <75>;
 					pmu_bat_para22 = <80>;
 					pmu_bat_para23 = <83>;
-					pmu_bat_para24 = <84>;
-					pmu_bat_para25 = <86>;
-					pmu_bat_para26 = <90>;
-					pmu_bat_para27 = <94>;
-					pmu_bat_para28 = <97>;
-					pmu_bat_para29 = <99>;
+					pmu_bat_para24 = <88>;
+					pmu_bat_para25 = <92>;
+					pmu_bat_para26 = <97>;
+					pmu_bat_para27 = <100>;
+					pmu_bat_para28 = <100>;
+					pmu_bat_para29 = <100>;
 					pmu_bat_para30 = <100>;
 					pmu_bat_para31 = <100>;
 					pmu_bat_para32 = <100>;
@@ -1465,6 +1465,7 @@
 
 
 //#include "lcd-lvds-21-1920-1080.dtsi"
-#include "lcd-lvds-7-1024-600.dtsi"
+//#include "lcd-lvds-7-1024-600.dtsi"
 //#include "lcd-mipi-10-800-1280.dtsi"
 //#include "lcd-mipi-8-800-1280.dtsi"
+#include "lcd-mipi-5-720-1280.dtsi"
diff --git a/longan/kernel/linux-4.9/arch/arm64/boot/dts/sunxi/lcd-mipi-5-720-1280.dtsi b/longan/kernel/linux-4.9/arch/arm64/boot/dts/sunxi/lcd-mipi-5-720-1280.dtsi
new file mode 100755
index 0000000..0fcc8b1
--- /dev/null
+++ b/longan/kernel/linux-4.9/arch/arm64/boot/dts/sunxi/lcd-mipi-5-720-1280.dtsi
@@ -0,0 +1,260 @@
+&soc {
+/*----------------------------------------------------------------------------------
+disp init configuration
+
+disp_mode             (0:screen0<screen0,fb0>)
+screenx_output_type   (0:none; 1:lcd; 2:tv; 3:hdmi;5:vdpo)
+screenx_output_mode   (used for hdmi output, 0:480i 1:576i 2:480p 3:576p 4:720p50)
+                      (5:720p60 6:1080i50 7:1080i60 8:1080p24 9:1080p50 10:1080p60)
+screenx_output_format (for hdmi, 0:RGB 1:yuv444 2:yuv422 3:yuv420)
+screenx_output_bits   (for hdmi, 0:8bit 1:10bit 2:12bit 2:16bit)
+screenx_output_eotf   (for hdmi, 0:reserve 4:SDR 16:HDR10 18:HLG)
+screenx_output_cs     (for hdmi, 0:undefined  257:BT709 260:BT601  263:BT2020)
+screenx_output_dvi_hdmi (for hdmi, 0:undefined 1:dvi mode 2:hdmi mode)
+screen0_output_range   (for hdmi, 0:default 1:full 2:limited)
+screen0_output_scan    (for hdmi, 0:no data 1:overscan 2:underscan)
+screen0_output_aspect_ratio  (for hdmi, 8-same as original picture 9-4:3 10-16:9 11-14:9)
+fbx format            (4:RGB655 5:RGB565 6:RGB556 7:ARGB1555 8:RGBA5551 9:RGB888 10:ARGB8888 12:ARGB4444)
+fbx pixel sequence    (0:ARGB 1:BGRA 2:ABGR 3:RGBA)
+fb0_scaler_mode_enable(scaler mode enable, used FE)
+fbx_width,fbx_height  (framebuffer horizontal/vertical pixels, fix to output resolution while equal 0)
+lcdx_backlight        (lcd init backlight,the range:[0,256],default:197
+lcdx_yy               (lcd init screen bright/contrast/saturation/hue, value:0~100, default:50/50/57/50)
+lcd0_contrast         (LCD contrast, 0~100)
+lcd0_saturation       (LCD saturation, 0~100)
+lcd0_hue              (LCD hue, 0~100)
+framebuffer software rotation setting:
+disp_rotation_used:   (0:disable; 1:enable,you must set fbX_width to lcd_y,
+set fbX_height to lcd_x)
+degreeX:              (X:screen index; 0:0 degree; 1:90 degree; 3:270 degree)
+degreeX_Y:            (X:screen index; Y:layer index 0~15; 0:0 degree; 1:90 degree; 3:270 degree)
+devX_output_type : config output type in bootGUI framework in UBOOT-2018.
+				   (0:none; 1:lcd; 2:tv; 4:hdmi;)
+devX_output_mode : config output resolution(see include/video/sunxi_display2.h) of bootGUI framework in UBOOT-2018
+devX_screen_id   : config display index of bootGUI framework in UBOOT-2018
+devX_do_hpd      : whether do hpd detectation or not in UBOOT-2018
+chn_cfg_mode     : Hardware DE channel allocation config. 0:single display with 6
+				   channel, 1:dual display with 4 channel in main display and 2 channel in second
+                   display, 2:dual display with 3 channel in main display and 3 channel in second
+                   in display.
+----------------------------------------------------------------------------------*/
+                disp: disp@06000000 {
+                        disp_init_enable         = <1>;
+                        disp_mode                = <0>;
+
+                        screen0_output_type      = <1>;
+                        screen0_output_mode      = <4>;
+
+                        screen1_output_type      = <1>;
+                        screen1_output_mode      = <4>;
+
+                        screen1_output_format    = <0>;
+                        screen1_output_bits      = <0>;
+                        screen1_output_eotf      = <4>;
+                        screen1_output_cs        = <257>;
+                        screen1_output_dvi_hdmi  = <2>;
+                        screen1_output_range     = <2>;
+                        screen1_output_scan      = <0>;
+                        screen1_output_aspect_ratio = <8>;
+
+                        dev0_output_type         = <1>;
+                        dev0_output_mode         = <4>;
+                        dev0_screen_id           = <0>;
+                        dev0_do_hpd              = <0>;
+
+                        dev1_output_type         = <4>;
+                        dev1_output_mode         = <10>;
+                        dev1_screen_id           = <1>;
+                        dev1_do_hpd              = <1>;
+
+                        def_output_dev           = <0>;
+                        hdmi_mode_check          = <1>;
+
+                        fb0_format               = <0>;
+                        fb0_width                = <720>;
+                        fb0_height               = <1280>;
+
+                        fb1_format               = <0>;
+                        fb1_width                = <0>;
+                        fb1_height               = <0>;
+                        chn_cfg_mode             = <1>;
+
+                        disp_para_zone           = <1>;
+                        dc1sw-supply = <&reg_dc1sw>;
+                        eldo3-supply = <&reg_eldo3>;
+                        dldo2-supply = <&reg_dldo2>;
+                        dcdc1-supply = <&reg_dcdc1>;
+                };
+/*----------------------------------------------------------------------------------
+;lcd0 configuration
+
+;lcd_if:               0:hv(sync+de); 1:8080; 2:ttl; 3:lvds; 4:dsi; 5:edp; 6:extend dsi
+;lcd_hv_if             0:Parallel RGB; 8:Serial RGB; 10:Dummy RGB; 11: RGB Dummy;12:CCIR656
+;lcd_hv_clk_phase      0:0 degree;1:90 degree;2:180 degree;3:270 degree
+;lcd_hv_sync_polarity  0:vs low,hs low; 1:vs high,hslow; 2:vs low,hs high; 3:vs high,hs high
+;lcd_hv_syuv_seq       0:YUYV; 1:YVYU; 2:UYVY; 3:VYUY
+;lcd_cpu_if            0:18bit/1 cycle parallel(RGB666); 4:16bit/1cycle parallel (RGB565)
+;                      6:18bit/3 cycle parallel(RGB666); 7:16bit/2cycle parallel (RGB565)
+;lcd_cpu_te            0:frame auto trigger; 1:frame triggered by te rising edge; 2:frame triggered by te falling edge;
+;lcd_dsi_if            0:video mode; 1: Command mode; 2:video burst mode
+;lcd_dsi_te            0:frame auto trigger; 1:frame triggered by te rising edge; 2:frame triggered by te falling edge;
+;lcd_x:                lcd horizontal resolution
+;lcd_y:                lcd vertical resolution
+;lcd_width:            width of lcd in mm
+;lcd_height:           height of lcd in mm
+;lcd_dclk_freq:        in MHZ unit
+;lcd_pwm_freq:         in HZ unit
+;lcd_pwm_pol:          lcd backlight PWM polarity
+;lcd_pwm_max_limit     lcd backlight PWM max limit(<=255)
+;lcd_hbp:              hsync back porch(pixel) + hsync plus width(pixel);
+;lcd_ht:               hsync total cycle(pixel)
+;lcd_vbp:              vsync back porch(line) + vysnc plus width(line)
+;lcd_vt:               vysnc total cycle(line)
+;lcd_hspw:             hsync plus width(pixel)
+;lcd_vspw:             vysnc plus width(pixel)
+;lcd_lvds_if:          0:single link;  1:dual link
+;lcd_lvds_colordepth:  0:8bit; 1:6bit
+;lcd_lvds_mode:        0:NS mode; 1:JEIDA mode
+;lcd_frm:              0:disable; 1:enable rgb666 dither; 2:enable rgb656 dither
+;lcd_io_phase:         0:noraml; 1:intert phase(0~3bit: vsync phase; 4~7bit:hsync phase;
+;                      8~11bit:dclk phase; 12~15bit:de phase)
+;lcd_gamma_en          lcd gamma correction enable
+;lcd_bright_curve_en   lcd bright curve correction enable
+;lcd_cmap_en           lcd color map function enable
+;deu_mode              0:smoll lcd screen; 1:large lcd screen(larger than 10inch)
+;lcdgamma4iep:         Smart Backlight parameter, lcd gamma vale * 10;
+;                      decrease it while lcd is not bright enough; increase while lcd is too bright
+;smart_color           90:normal lcd screen 65:retina lcd screen(9.7inch)
+;Pin setting for special function ie.LVDS, RGB data or vsync
+;   name(donot care) = port:PD12<pin function><pull up or pull down><drive ability><output level>
+;Pin setting for gpio:
+;   lcd_gpio_X     = port:PD12<pin function><pull up or pull down><drive ability><output level>
+;Pin setting for backlight enable pin
+;   lcd_bl_en     = port:PD12<pin function><pull up or pull down><drive ability><output level>
+;fsync setting, pulse to csi
+;lcd_fsync_en          (0:disable fsync,1:enable)
+;lcd_fsync_act_time    (active time of fsync, unit:pixel)
+;lcd_fsync_dis_time    (disactive time of fsync, unit:pixel)
+;lcd_fsync_pol         (0:positive;1:negative)
+;gpio config: <&pio for cpu or &r_pio for cpus, port, port num, pio function,
+pull up or pull down(default 0), driver level(default 1), data>
+;For dual link lvds: use lvds2link_pins_a  and lvds2link_pins_b instead
+;For rgb24: use rgb24_pins_a  and rgb24_pins_b instead
+;For lvds1: use lvds1_pins_a  and lvds1_pins_b instead
+;For lvds0: use lvds0_pins_a  and lvds0_pins_b instead
+;----------------------------------------------------------------------------------*/
+                lcd0: lcd0@01c0c000 {
+                        lcd_used            = <1>;
+
+                        lcd_driver_name     = "mipi_5_720x1280";
+                        lcd_backlight       = <50>;
+                        lcd_if              = <4>;
+
+                        lcd_x               = <720>;
+                        lcd_y               = <1280>;
+                        lcd_width           = <62>;
+                        lcd_height          = <110>;
+                        lcd_dclk_freq       = <68>;
+
+                        lcd_pwm_used        = <1>;
+                        lcd_pwm_ch          = <2>;
+                        lcd_pwm_freq        = <50000>;
+                        lcd_pwm_pol         = <1>;
+                        lcd_pwm_max_limit   = <255>;
+
+                        //lcd_hbp             = <18>;
+                        //lcd_ht              = <934>;
+                        //lcd_hspw            = <4>;
+
+                        //lcd_vbp             = <38>;
+                        //lcd_vt              = <1344>;
+                        //lcd_vspw            = <2>;
+
+			lcd_hbp             = <47>;
+			lcd_ht              = <782>;
+			lcd_hspw            = <15>;
+			lcd_vbp             = <27>;
+			lcd_vt              = <1322>;
+			lcd_vspw            = <15>;
+
+
+                        lcd_frm             = <0>;
+                        lcd_gamma_en        = <0>;
+                        lcd_bright_curve_en = <0>;
+                        lcd_cmap_en         = <0>;
+
+                        deu_mode            = <0>;
+                        lcdgamma4iep        = <22>;
+                        smart_color         = <90>;
+
+                        lcd_dsi_if          = <0>;
+                        lcd_dsi_lane        = <4>;
+                        lcd_dsi_format      = <0>;
+                        lcd_dsi_te          = <0>;
+                        lcd_dsi_eotp        = <0>;
+
+			//lcd_lvds_if	    = <0>;
+			//lcd_lvds_colordepth = <0>;
+			//lcd_lvds_mode 	    = <1>;		
+
+                        lcd_pin_power = "dcdc1";
+                        lcd_pin_power1 = "eldo3";
+                        //lcd_pin_power2 = "dldo2";
+
+                        lcd_power = "dc1sw";
+			lcd_bl_en = <&pio PD 21 1 0 3 1>;
+			
+			// reset
+                        lcd_gpio_0 = <&pio PE 5 1 0 3 1>;
+			// power
+                        //lcd_gpio_1 = <&pio PD 21 1 0 3 1>;
+
+                        pinctrl-0 = <&dsi4lane_pins_a>;
+                        pinctrl-1 = <&dsi4lane_pins_b>;
+                        //pinctrl-0 = <&lvds0_pins_a>;
+                        //pinctrl-1 = <&lvds0_pins_b>;
+                };
+	};
+
+&twi0 {
+    myft5x06: my-ft5x06@38 {
+        compatible = "focaltech,fts";
+        reg = <0x38>; 
+		status = "okay";
+ 
+        // focaltech,reset-gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;
+        // focaltech,irq-gpio = <&gpio3 RK_PA3 IRQ_TYPE_LEVEL_LOW>;
+
+        focaltech,reset-gpio = <&pio PE 4 1 0xffffffff 0xffffffff 1>;
+        focaltech,irq-gpio = <&pio PE 3 6 0xffffffff 0xffffffff 0>;
+        
+        // pinctrl-names = "default";
+        // pinctrl-0 = <&touch1_gpio>;
+
+		focaltech,display-coords = <0 0 720 1280>;
+		focaltech,max-touch-number = <10>;
+ 
+    };
+
+    /*
+    ctp {
+        compatible = "gt9xx_ts";
+        ctp_used = <1>;
+        device_type = "ctp";
+        status = "okay";
+        ctp_twi_id = <0x0>;
+        ctp_name = "gt9xx";
+        reg = <0x5D>;
+        ctp_screen_max_x = <1024>;
+        ctp_screen_max_y = <600>;
+        ctp_revert_x_flag = <0x1>;
+        ctp_revert_y_flag = <0x1>;
+        ctp_exchange_x_y_flag = <0x0>;
+        ctp_int_port = <&pio PE 3 6 0xffffffff 0xffffffff 0>;
+        ctp_wakeup =   <&pio PE 4 1 0xffffffff 0xffffffff 1>;
+        ctp_power_ldo = <3300>;
+        ctp_power_ldo_vol = <3300>;
+        ctp_power = <3300>;
+    };
+    */
+};
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/Makefile b/longan/kernel/linux-4.9/drivers/input/touchscreen/Makefile
index 37ddecd..b9cb4a8 100644
--- a/longan/kernel/linux-4.9/drivers/input/touchscreen/Makefile
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/Makefile
@@ -97,3 +97,4 @@
 obj-$(CONFIG_TOUCHSCREEN_FTS_TS)    += focaltech_touch/
 obj-$(CONFIG_INPUT_TOUCHSCREEN)   += gt9xxnew/
 obj-$(CONFIG_INPUT_TOUCHSCREEN)	  += gt9xx/
+obj-y += focaltech_touch_ft8756/
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Kconfig b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Kconfig
new file mode 100755
index 0000000..9d34d74
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Kconfig
@@ -0,0 +1,10 @@
+#
+# Focaltech Touchscreen driver configuration
+#
+
+config TOUCHSCREEN_FTS
+    tristate "Focaltech Touchscreen"
+    default m
+    help
+      Say Y here if you have Focaltech touch panel.
+      If unsure, say N.
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Makefile b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Makefile
new file mode 100755
index 0000000..bd32eb9
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/Makefile
@@ -0,0 +1,14 @@
+# Makefile for the focaltech touchscreen drivers.
+
+obj-y += focaltech_tp.o
+focaltech_tp-y := focaltech_core.o \
+		focaltech_ex_fun.o \
+		focaltech_ex_mode.o \
+		focaltech_gesture.o \
+		focaltech_esdcheck.o \
+		focaltech_point_report_check.o \
+		focaltech_proximity.o
+
+focaltech_tp-y += focaltech_i2c.o
+focaltech_tp-y += focaltech_flash.o
+focaltech_tp-y += focaltech_flash/focaltech_upgrade_ft8756m.o
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_common.h b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_common.h
new file mode 100755
index 0000000..77ae3f4
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_common.h
@@ -0,0 +1,193 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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 V4.2 20240407"
+
+#define BYTE_OFF_0(x)           (u8)((x) & 0xFF)
+#define BYTE_OFF_8(x)           (u8)(((x) >> 8) & 0xFF)
+#define BYTE_OFF_16(x)          (u8)(((x) >> 16) & 0xFF)
+#define BYTE_OFF_24(x)          (u8)(((x) >> 24) & 0xFF)
+#define FLAGBIT(x)              (0x00000001 << (x))
+#define FLAGBITS(x, y)          ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x)))
+
+#define FLAG_ICSERIALS_LEN      8
+#define FLAG_HID_BIT            10
+#define FLAG_IDC_BIT            11
+#define INTERVAL_READ_REG       200  /* unit:ms */
+#define TIMEOUT_READ_REG        1000 /* unit:ms */
+
+#define FTS_DELAY_RESUME_RESET  50  /* unit:ms */
+
+#define IC_SERIALS              (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1))
+#define IC_TO_SERIALS(x)        ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1))
+#define FTS_CHIP_IDC            ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT))
+#define FTS_HID_SUPPORTTED      ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT))
+
+#define FTS_MAX_CHIP_IDS        8
+
+#define FTS_CHIP_TYPE_MAPPING {{0x15, 0x87, 0x56, 0x87, 0x56, 0xF7, 0xA6, 0x00, 0x00}}
+
+
+#define FILE_NAME_LENGTH                    128
+#define ENABLE                              1
+#define DISABLE                             0
+#define VALID                               1
+#define INVALID                             0
+#define FTS_CMD_START1                      0x55
+#define FTS_CMD_START2                      0xAA
+#define FTS_CMD_START_DELAY                 12
+#define FTS_CMD_READ_ID                     0x90
+#define FTS_CMD_READ_ID_LEN                 4
+#define FTS_CMD_READ_ID_LEN_INCELL          1
+#define FTS_CMD_READ_BOOT_STATE             0xD0
+#define FTS_CMD_READ_INFO                   0x5E
+
+/*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_ESDCHECK_DISABLE            0x8D
+#define FTS_REG_CHIP_ID                     0xA3
+#define FTS_REG_CHIP_ID2                    0x9F
+#define FTS_REG_POWER_MODE                  0xA5
+#define FTS_REG_POWER_MODE_SLEEP            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_FW_MODE                     0xB4
+#define FTS_REG_FACE_DEC_MODE_STATUS        0x01
+#define FTS_REG_IDE_PARA_VER_ID             0xB5
+#define FTS_REG_IDE_PARA_STATUS             0xB6
+#define FTS_REG_CHARGER_MODE_EN             0x8B
+#define FTS_REG_EDGEPALM_MODE_EN            0x8C
+#define FTS_REG_GLOVE_MODE_EN               0xC0
+#define FTS_REG_COVER_MODE_EN               0xC1
+#define FTS_REG_EARPHONE_MODE_EN            0xC3
+#define FTS_REG_GESTURE_EN                  0xD0
+#define FTS_REG_GESTURE_OUTPUT_ADDRESS      0xD3
+#define FTS_REG_PANEL_ID                    0xE3
+#define FTS_REG_LIC_VER                     0xE4
+#define FTS_REG_ESD_SATURATE                0xED
+#define FTS_REG_FOD_MODE_EN                 0xCF
+#define FTS_VAL_FOD_ENABLE                  0x02
+#define FTS_REG_FOD_DATA                    0xE1
+#define FTS_REG_TPINFO                      0x96
+#define FTS_REG_TPCFG                       0x9D
+#define FTS_MAX_RETRIES_READID              10
+#define FTS_MAX_RETRIES_READID_RESUME       5
+#define FTS_MAX_RETRIES_READ_BOOTID         5
+#define FTS_MAX_RETRIES_WRITEREG            5
+
+#define FTS_SYSFS_ECHO_ON(buf)      (buf[0] == '1')
+#define FTS_SYSFS_ECHO_OFF(buf)     (buf[0] == '0')
+
+#define kfree_safe(pbuf) do {\
+    if (pbuf) {\
+        kfree(pbuf);\
+        pbuf = NULL;\
+    }\
+} while(0)
+
+/*****************************************************************************
+*  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 {
+    u16 type;
+    u8 chip_idh;
+    u8 chip_idl;
+    u8 rom_idh;
+    u8 rom_idl;
+    u8 pb_idh;
+    u8 pb_idl;
+    u8 bl_idh;
+    u8 bl_idl;
+};
+
+struct ft_chip_id_t {
+    u16 type;
+    u16 chip_ids[FTS_MAX_CHIP_IDS];
+};
+
+struct ts_ic_info {
+    bool is_incell;
+    bool hid_supported;
+    struct ft_chip_t ids;
+    struct ft_chip_id_t cid;
+};
+
+/*****************************************************************************
+* DEBUG function define here
+*****************************************************************************/
+#if FTS_DEBUG_EN
+#define FTS_DEBUG(fmt, args...) do { \
+    printk(KERN_DEBUG "[FTS_TS]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+
+#define FTS_FUNC_ENTER() do { \
+    printk(KERN_DEBUG "[FTS_TS]%s: Enter\n", __func__); \
+} while (0)
+
+#define FTS_FUNC_EXIT() do { \
+    printk(KERN_DEBUG "[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \
+} while (0)
+#else /* #if FTS_DEBUG_EN*/
+#define FTS_DEBUG(fmt, args...)
+#define FTS_FUNC_ENTER()
+#define FTS_FUNC_EXIT()
+#endif
+
+#define FTS_INFO(fmt, args...) do { \
+    printk(KERN_ERR "[FTS_TS/I]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+
+#define FTS_ERROR(fmt, args...) do { \
+    printk(KERN_ERR "[FTS_TS/E]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+#endif /* __LINUX_FOCALTECH_COMMON_H__ */
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_config.h b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_config.h
new file mode 100755
index 0000000..c13ef80
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_config.h
@@ -0,0 +1,362 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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 _FT8607             0x86070809
+#define _FT8006U            0x8006D80B
+#define _FT8006S            0x8006A80B
+#define _FT8613             0x8613080C
+#define _FT8719             0x8719080D
+#define _FT8739             0x8739080E
+#define _FT8615             0x8615080F
+#define _FT8201             0x82010810
+#define _FT8201AA           0x8201A810
+#define _FT8006P            0x86220811
+#define _FT8613S            0x8613C814
+#define _FT8756             0x87560815
+#define _FT8656             0x86560815
+#define _FT8756M            0x8756A815
+#define _FT8302             0x83020816
+#define _FT8009             0x80090817
+#define _FT8006S_AA         0x86320819
+#define _FT7250             0x7250081A
+#define _FT7120             0x7120081B
+#define _FT8720             0x8720081C
+#define _FT8726             0x8726081C
+#define _FT8720H            0x8720E81C
+#define _FT8720M            0x8720F81C
+#define _FT8016             0x8016081D
+#define _FT8006S_AB         0x8642081F
+#define _FT8722             0x87220820
+#define _FT8201AB           0x8201B821
+#define _FT8203             0x82030821
+#define _FT8201M            0x8201C821
+#define _FT8006S_AN         0x8006B822
+#define _FT7131             0x71310823
+#define _FT7250AB           0x7250B824
+#define _FT7130             0x71300825
+#define _FT8205             0x82050826
+#define _FT2389             0x23890827
+#define _FT8057             0x80570828
+#define _FT8057S            0x8057C828
+#define _FT8725             0x87250829
+#define _FT7135             0x71350829
+#define _FT8206             0x8206082A
+#define _FT8057P            0x8057082B
+#define _FT7133             0x7133082B
+#define _FT8205P            0x8205082C
+#define _FT8201P            0x8201082C
+#define _FT8201S            0x8201C82C
+#define _FT7250P            0x7250082C
+
+
+#define _FT5426             0x54260402
+#define _FT5436             0x54360402
+#define _FT5526             0x55260402
+#define _FT5446             0x54460402
+#define _FT5346             0x53460402
+#define _FT7661             0x76610402
+#define _FT7511             0x75110402
+#define _FT7421             0x74210402
+#define _FT7681             0x76810402
+#define _FT3417             0x34170402
+#define _FT3517             0x35170402
+#define _FT3327             0x33270402
+#define _FT3427             0x34270402
+#define _FT7311             0x73110402
+#define _FT5526_V00         0x5526C402
+
+#define _FT5726             0x57260401
+#define _FT5826S            0x5826C401
+#define _FT7811             0x78110401
+#define _FT3617             0x36170401
+#define _FT3717             0x37170401
+
+#define _FT6236U            0x6236D003
+#define _FT6336G            0x6336A003
+#define _FT6336U            0x6336D003
+#define _FT6436U            0x6436D003
+#define _FT6436T            0x6436E003
+
+#define _FT3267             0x32670004
+#define _FT3367             0x33670004
+
+#define _FT5446_Q03         0x5446C482
+#define _FT5446_P03         0x5446A481
+#define _FT5446_N03         0x5446A489
+#define _FT5426_003         0x5426D482
+#define _FT5526_003         0x5526D482
+#define _FT3437_003         0x34370482
+
+#define _FT3518             0x35180481
+#define _FT3518U            0x3518D481
+#define _FT3558             0x35580481
+#define _FT3528             0x35280481
+#define _FT5536             0x55360481
+#define _FT3418             0x34180481
+#define _FT5536G            0x5536A481
+
+#define _FT5446U            0x5446D083
+#define _FT5456U            0x5456D083
+#define _FT5426U            0x5426D083
+
+#define _FT7302             0x73020084
+#define _FT7202             0x72020084
+#define _FT3308             0x33080084
+#define _FT6446             0x64460084
+
+#define _FT6346U            0x6346D085
+#define _FT6346G            0x6346A085
+#define _FT3067             0x30670085
+#define _FT3068             0x30680085
+#define _FT3168             0x31680085
+#define _FT3268             0x32680085
+#define _FT6146             0x61460085
+#define _FT3168G            0x3168A085
+#define _FT6347             0x63470085
+
+#define _FT5726_003         0x5726D486
+#define _FT5726_V03         0x5726C486
+#define _FT3617_003         0x3617D486
+
+#define _FT3618             0x36180487
+#define _FT5646             0x56460487
+#define _FT3A58             0x3A580487
+#define _FT3B58             0x3B580487
+#define _FT3D58             0x3D580487
+#define _FT3D59             0x3D590487
+#define _FT5936             0x59360487
+#define _FT5A36             0x5A360487
+#define _FT5B36             0x5B360487
+#define _FT5D36             0x5D360487
+#define _FT5946             0x59460487
+#define _FT5A46             0x5A460487
+#define _FT5B46             0x5B460487
+#define _FT5D46             0x5D460487
+
+#define _FT3658U            0x3658D488
+#define _FT3658G            0x3658A488
+
+#define _FT3519             0x35190489
+#define _FT3519T            0x3519E489
+#define _FT3519U            0x3519D489
+#define _FT3419             0x34190489
+#define _FT5536U_003        0x5536D489
+#define _FT5426G            0x5426A489
+#define _FT3437_N03         0x34370489
+#define _FT7411             0x74110489
+#define _FT7411A            0x7411A489
+#define _FT7511A            0x7511A489
+#define _FT7511U            0x7511D489
+
+#define _FT3680             0x3680008A
+#define _FT368A             0x368A008A
+#define _FT3681             0x3681008A
+#define _FT3881             0x3881008A
+
+#define _FT3169             0x3169008B
+#define _FT3269             0x3269008B
+
+#define _FT3658S            0x3658C48C
+
+#define _FT3A81             0x3A81048D
+#define _FT3B81             0x3B81048D
+#define _FT3D81             0x3D81048D
+
+#define _FT5B36U            0x5B36D48E
+
+#define _FT3682             0x3682008F
+#define _FT3C82             0x3C82008F
+
+#define _FT3683G            0x56720090
+#define _FT3683U            0x3683D090
+#define _FT7510             0x75100090
+#define _FT7512             0x75120090
+#define _FT3683             0x36830092
+
+#define _FT3D81_003         0x3D81B491
+
+#define _FT3510             0x35100093
+#define _FT3610             0x36100093
+#define _FT3383             0x33830093
+
+
+
+/*************************************************/
+
+/*
+ * choose your ic chip type of focaltech
+ */
+#define FTS_CHIP_TYPE   _FT8756M
+
+/******************* Enables *********************/
+/*********** 1 to enable, 0 to disable ***********/
+
+/*
+ * show debug log info
+ * enable it for debug, disable it for release
+ */
+#define FTS_DEBUG_EN                            1
+
+/*
+ * 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                  0
+
+/*
+ * Stylus PEN enable
+ * 1:enable(default),0:disable
+*/
+#define FTS_PEN_EN                              0
+
+/*
+ * Proximity function enable
+ * default: disable
+ */
+#define FTS_PSENSOR_EN                          0
+
+/*
+ * FOD function enable
+ * default: disable
+ */
+#define FTS_FOD_EN                              0
+
+/*
+ * ESD check & protection
+ * default: disable
+ */
+#define FTS_ESDCHECK_EN                         0
+
+/*
+ * Pinctrl enable
+ * default: disable
+ */
+#define FTS_PINCTRL_EN                          0
+
+/* power policy:
+ * FTS_POWER_SOURCE_CUST_EN,FTS_POWER_SUSPEND_OFF_EN
+ *  0,0:typically for IDC chip,not control power when probing and suspending.
+ *  0,1:only for IDC,LCD driver sets IDC power to off when suspending,so need
+ *      set tp_rst pin to low in tp driver.
+ *  1,0:typically for OLED touch chip, not set power to off when suspending.
+ *  1,1:typically for touch chip,control power when probing and suspending.
+ */
+/*
+ * Customer power enable
+ * enable it when customer need control TP power
+ * default: disable
+ */
+#define FTS_POWER_SOURCE_CUST_EN                0
+
+/* Set power to off state when entering into suspend
+ * default:disable
+ */
+#define FTS_POWER_SUSPEND_OFF_EN                0
+
+
+/****************************************************/
+
+/********************** Upgrade ****************************/
+/*
+ * auto upgrade
+ */
+#define FTS_AUTO_UPGRADE_EN                     1
+
+/* for none flash project, set it to be the using FW number */
+#define FTS_MULTI_FW_NUM                        0
+
+/*
+ * auto upgrade for lcd cfg
+ */
+#define FTS_AUTO_LIC_UPGRADE_EN                 0
+
+/*
+ * Numbers of modules support
+ */
+#define FTS_GET_MODULE_NUM                      0
+
+/*
+ * module_id: mean vendor_id generally, also maybe gpio or lcm_id...
+ * If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID
+ * FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them
+ * FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID
+ * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID
+ */
+#define FTS_MODULE_ID                           0x0000
+#define FTS_MODULE2_ID                          0x0000
+#define FTS_MODULE3_ID                          0x0000
+
+/*
+ * Need set the following when get firmware via firmware_request()
+ * For example: if module'vendor is tianma,
+ * #define FTS_MODULE_NAME                        "tianma"
+ * then file_name will be "focaltech_ts_fw_tianma"
+ * You should rename fw to "focaltech_ts_fw_tianma", and push it into
+ * etc/firmware or by customers
+ */
+#define FTS_MODULE_NAME                         ""
+#define FTS_MODULE2_NAME                        ""
+#define FTS_MODULE3_NAME                        ""
+
+/*
+ * FW.i file for auto upgrade, you must replace it with your own
+ * define your own fw_file, the sample one to be replaced is invalid
+ * NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_MODULE_ID
+ */
+#define FTS_UPGRADE_FW_FILE                     "include/firmware/fw_sample.i"
+
+/*
+ * if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_MODULE2_ID
+ * define your own fw_file, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW2_FILE                    "include/firmware/fw_sample.i"
+
+/*
+ * if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_MODULE3_ID
+ * define your own fw_file, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW3_FILE                    "include/firmware/fw_sample.i"
+
+/*********************************************************/
+
+#endif /* _LINUX_FOCLATECH_CONFIG_H_ */
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.c
new file mode 100755
index 0000000..3281e42
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.c
@@ -0,0 +1,2411 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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: entrance for focaltech ts driver
+*
+* Version: V1.0
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+
+// #if IS_ENABLED(CONFIG_DRM)
+// #if IS_ENABLED(CONFIG_DRM_PANEL)
+// #include <drm/drm_panel.h>
+// #else
+// #include <linux/msm_drm_notify.h>
+// #endif //CONFIG_DRM_PANEL
+
+// #elif IS_ENABLED(CONFIG_FB)
+// #include <linux/notifier.h>
+// #include <linux/fb.h>
+// #endif //CONFIG_DRM
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_PEN_NAME                 "fts_ts,pen"
+
+#if FTS_POWER_SOURCE_CUST_EN
+#define FTS_VTG_MIN_UV                      2800000
+#define FTS_VTG_MAX_UV                      3300000
+#define FTS_IOVCC_VTG_MIN_UV                1800000
+#define FTS_IOVCC_VTG_MAX_UV                1800000
+#endif
+
+#define FTS_WAKELOCK_TIMEOUT                5000
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct fts_ts_data *fts_data;
+
+#if FTS_FOD_EN
+static int fts_fod_recovery(struct fts_ts_data *ts_data);
+#endif
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+void fts_msleep(unsigned long msecs)
+{
+    if (msecs > 20) {
+        msleep(msecs);
+    } else if (msecs > 0) {
+        usleep_range(msecs * 1000, (msecs + 2) * 1000);
+    }
+}
+
+int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h)
+{
+    int i = 0;
+    struct ft_chip_id_t *cid = &ts_data->ic_info.cid;
+    u8 cid_h = 0x0;
+
+    if (cid->type == 0)
+        return -ENODATA;
+
+    for (i = 0; i < FTS_MAX_CHIP_IDS; i++) {
+        cid_h = ((cid->chip_ids[i] >> 8) & 0x00FF);
+        if (cid_h && (id_h == cid_h)) {
+            return 0;
+        }
+    }
+
+    return -ENODATA;
+}
+
+/*****************************************************************************
+*  Name: fts_wait_tp_to_valid
+*  Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG),
+*         need call when reset/power on/resume...
+*  Input:
+*  Output:
+*  Return: return 0 if tp valid, otherwise return error code
+*****************************************************************************/
+int fts_wait_tp_to_valid(void)
+{
+    int ret = 0;
+    int i = 0;
+    u8 idh = 0xFF;
+    struct fts_ts_data *ts_data = fts_data;
+    u8 chip_idh = ts_data->ic_info.ids.chip_idh;
+
+    for (i = 0; i < FTS_MAX_RETRIES_READID; i++) {
+        ret = fts_read_reg(FTS_REG_CHIP_ID, &idh);
+        if ((idh == chip_idh) || (fts_check_cid(ts_data, idh) == 0)) {
+            FTS_INFO("TP Ready,Device ID:0x%02x", idh);
+            return 0;
+        }
+        if ((i + 1) < FTS_MAX_RETRIES_READID) fts_msleep((i + 1) * 20);
+    }
+
+    FTS_ERROR("TP Not Ready,ReadData:0x%02x,ret:%d", idh, ret);
+    return -EIO;
+}
+
+/*****************************************************************************
+*  Name: fts_tp_state_recovery
+*  Brief: Need execute this function when reset
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_tp_state_recovery(struct fts_ts_data *ts_data)
+{
+    fts_wait_tp_to_valid();
+    fts_ex_mode_recovery(ts_data);
+#if FTS_PSENSOR_EN
+    if (ts_data->proximity_mode) {
+        fts_proximity_recovery(ts_data);
+        return;
+    }
+#endif
+#if FTS_FOD_EN
+    fts_fod_recovery(ts_data);
+#endif
+    fts_gesture_recovery(ts_data);
+}
+
+static int fts_reset_pre(struct fts_ts_data *ts_data, int value)
+{
+    u16 ic_type = ts_data->ic_info.ids.type;
+    if (!value) {
+        if ((ic_type == 0x90) || (ic_type == 0x92) || (ic_type == 0x93)) {
+            if (!ts_data->power_disabled) {
+                FTS_DEBUG("write regb6");
+                fts_write_reg(0xB6, 1);
+                fts_msleep(20);
+            }
+        }
+    }
+    return 0;
+}
+
+static int fts_reset_post(struct fts_ts_data *ts_data, int value)
+{
+
+    return 0;
+}
+
+int fts_set_reset(struct fts_ts_data *ts_data, int value)
+{
+    int ret = 0;
+
+    FTS_INFO("set reset to %d", !!value);
+    fts_reset_pre(ts_data, value);
+    ret = gpio_direction_output(ts_data->pdata->reset_gpio, !!value);
+    if (ret) {
+        FTS_ERROR("[GPIO]set reset gpio to %d failed", !!value);
+        return ret;
+    }
+    fts_reset_post(ts_data, value);
+    return 0;
+}
+
+int fts_reset_proc(struct fts_ts_data *ts_data, int force, int hdelayms)
+{
+    if (force || (!ts_data->fw_loading)) {
+        fts_set_reset(ts_data, 0);
+        fts_msleep(2);
+        fts_set_reset(ts_data, 1);
+        if (hdelayms) fts_msleep(hdelayms);
+    } else {
+        FTS_INFO("fw upgrade in process, no reset");
+    }
+
+    return 0;
+}
+
+void fts_irq_disable(void)
+{
+    unsigned long irqflags;
+
+    FTS_FUNC_ENTER();
+    spin_lock_irqsave(&fts_data->irq_lock, irqflags);
+
+    if (!fts_data->irq_disabled) {
+        disable_irq_nosync(fts_data->irq);
+        fts_data->irq_disabled = true;
+    }
+
+    spin_unlock_irqrestore(&fts_data->irq_lock, irqflags);
+    FTS_FUNC_EXIT();
+}
+
+void fts_irq_enable(void)
+{
+    unsigned long irqflags = 0;
+
+    FTS_FUNC_ENTER();
+    spin_lock_irqsave(&fts_data->irq_lock, irqflags);
+
+    if (fts_data->irq_disabled) {
+        enable_irq(fts_data->irq);
+        fts_data->irq_disabled = false;
+    }
+
+    spin_unlock_irqrestore(&fts_data->irq_lock, irqflags);
+    FTS_FUNC_EXIT();
+}
+
+int fts_hid2std(int mode)
+{
+    int ret = 0;
+    u8 buf[3] = {0xEB, 0xAA, 0x09};
+    u8 val[3] = { 0 };
+
+    if (fts_data->bus_type != BUS_TYPE_I2C)
+        return 0;
+
+    if (mode == 1) {
+        /* Don't need delay */
+        ret = fts_read(buf, 3, val, 3);
+        if (ret < 0) {
+            FTS_ERROR("send hid2std cmd failed");
+            return ret;
+        }
+    } else {
+        ret = fts_write(buf, 3);
+        if (ret < 0) {
+            FTS_ERROR("hid2std cmd write fail");
+            return ret;
+        }
+
+        fts_msleep(10);
+        ret = fts_read(NULL, 0, val, 3);
+        if (ret < 0) {
+            FTS_ERROR("hid2std cmd read fail");
+            return ret;
+        }
+    }
+
+    if ((0xEB == val[0]) && (0xAA == val[1]) && (0x08 == val[2])) {
+        FTS_INFO("hidi2c change to stdi2c successful");
+    } else {
+        FTS_INFO("hidi2c change to stdi2c not support or fail");
+    }
+    return 0;
+}
+
+static int fts_match_cid(struct fts_ts_data *ts_data,
+                         u16 type, u8 id_h, u8 id_l, bool force)
+{
+#ifdef FTS_CHIP_ID_MAPPING
+    u32 i = 0;
+    u32 j = 0;
+    struct ft_chip_id_t chip_id_list[] = FTS_CHIP_ID_MAPPING;
+    u32 cid_entries = sizeof(chip_id_list) / sizeof(struct ft_chip_id_t);
+    u16 id = (id_h << 8) + id_l;
+
+    memset(&ts_data->ic_info.cid, 0, sizeof(struct ft_chip_id_t));
+    for (i = 0; i < cid_entries; i++) {
+        if (!force && (type == chip_id_list[i].type)) {
+            break;
+        } else if (force && (type == chip_id_list[i].type)) {
+            FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type);
+            ts_data->ic_info.cid = chip_id_list[i];
+            return 0;
+        }
+    }
+
+    if (i >= cid_entries) {
+        return -ENODATA;
+    }
+
+    for (j = 0; j < FTS_MAX_CHIP_IDS; j++) {
+        if (id == chip_id_list[i].chip_ids[j]) {
+            FTS_DEBUG("cid:%x==%x", id, chip_id_list[i].chip_ids[j]);
+            FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type);
+            ts_data->ic_info.cid = chip_id_list[i];
+            return 0;
+        }
+    }
+
+    return -ENODATA;
+#else
+    return -EINVAL;
+#endif
+}
+
+
+static int fts_get_chip_types(
+    struct fts_ts_data *ts_data,
+    u8 id_h, u8 id_l, bool fw_valid)
+{
+    u32 i = 0;
+    struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING;
+    u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t);
+
+    if ((0x0 == id_h) || (0x0 == id_l)) {
+        FTS_ERROR("id_h/id_l is 0");
+        return -EINVAL;
+    }
+
+    FTS_INFO("verify id:0x%02x%02x", id_h, id_l);
+    for (i = 0; i < ctype_entries; i++) {
+        if (VALID == fw_valid) {
+            if (((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl))
+                || (!fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 0)))
+                break;
+        } else {
+            if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl))
+                || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl))
+                || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl))) {
+                break;
+            }
+        }
+    }
+
+    if (i >= ctype_entries) {
+        return -ENODATA;
+    }
+
+    fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 1);
+    ts_data->ic_info.ids = ctype[i];
+    return 0;
+}
+
+static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id)
+{
+    int ret = 0;
+    u8 chip_id[2] = { 0 };
+    u8 id_cmd[4] = { 0 };
+    u32 id_cmd_len = 0;
+
+    id_cmd[0] = FTS_CMD_START1;
+    id_cmd[1] = FTS_CMD_START2;
+    ret = fts_write(id_cmd, 2);
+    if (ret < 0) {
+        FTS_ERROR("start cmd write fail");
+        return ret;
+    }
+
+    fts_msleep(FTS_CMD_START_DELAY);
+    id_cmd[0] = FTS_CMD_READ_ID;
+    id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00;
+    if (ts_data->ic_info.is_incell)
+        id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
+    else
+        id_cmd_len = FTS_CMD_READ_ID_LEN;
+    ret = fts_read(id_cmd, id_cmd_len, chip_id, 2);
+    if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
+        FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]);
+        return -EIO;
+    }
+
+    id[0] = chip_id[0];
+    id[1] = chip_id[1];
+    return 0;
+}
+
+/*****************************************************************************
+* Name: fts_get_ic_information
+* Brief: read chip id to get ic information, after run the function, driver w-
+*        ill know which IC is it.
+*        If cant get the ic information, maybe not focaltech's touch IC, need
+*        unregister the driver
+* Input:
+* Output:
+* Return: return 0 if get correct ic information, otherwise return error code
+*****************************************************************************/
+static int fts_get_ic_information(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    int cnt = 0;
+    u8 chip_id[2] = { 0 };
+
+    ts_data->ic_info.is_incell = FTS_CHIP_IDC;
+    ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED;
+
+
+    do {
+        ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]);
+        ret = fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]);
+        if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
+            FTS_INFO("chip id read invalid, read:0x%02x%02x",
+                     chip_id[0], chip_id[1]);
+        } else {
+            ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID);
+            if (!ret)
+                break;
+            else
+                FTS_INFO("TP not ready, read:0x%02x%02x",
+                         chip_id[0], chip_id[1]);
+        }
+
+        cnt++;
+        fts_msleep(INTERVAL_READ_REG);
+    } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+    if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) {
+        FTS_INFO("fw is invalid, need read boot id");
+        for (cnt = 0; cnt < FTS_MAX_RETRIES_READ_BOOTID; cnt++) {
+            if (cnt < 2) {
+                if (ts_data->ic_info.hid_supported) fts_hid2std(0);
+            } else {
+                fts_reset_proc(ts_data, true, 0);
+                mdelay(FTS_CMD_START_DELAY + (cnt - 2) * 8);
+                if (ts_data->ic_info.hid_supported) {
+                    fts_hid2std(1);
+                    fts_write_reg(0x55, 0xAA);
+                    fts_msleep(FTS_CMD_START_DELAY);
+                    fts_hid2std(1);
+                }
+            }
+
+            ret = fts_read_bootid(ts_data, &chip_id[0]);
+            if (ret <  0) {
+                FTS_ERROR("read boot id fail");
+                continue;
+            }
+
+            ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID);
+            if (ret < 0) {
+                FTS_ERROR("can't get ic informaton");
+                continue;
+            }
+            break;
+        }
+    }
+
+    FTS_INFO("get ic information, chip id = 0x%02x%02x(cid type=0x%x)",
+             ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl,
+             ts_data->ic_info.cid.type);
+
+    return ret;
+}
+
+#if FTS_READ_CUSTOMER_INFO
+static int fts_read_customer_information(struct fts_ts_data *ts_data)
+{
+    /* If some customer's code had written to touch chip, please do the followings:
+     *  Step 1: read customer information here.
+     *  Step 2: save customer information to ts_data->customer_info variable, maximum 32.
+     *  Step 3: return ts_data->customer_info to APP via sysfs node.
+     *
+     * Warning: please check the information is read from FW or not, if from FW, please
+     *          take care that FW maybe isn't valid.
+     */
+
+
+    FTS_INFO("customer info:%s", ts_data->customer_info);
+    return 0;
+}
+#endif
+
+#if FTS_FOD_EN
+static void fts_fod_set_reg(int value)
+{
+    int i = 0;
+    u8 fod_val = value ? FTS_VAL_FOD_ENABLE : DISABLE;
+    u8 regval = 0xFF;
+
+    for (i = 0; i < FTS_MAX_RETRIES_WRITEREG; i++) {
+        fts_read_reg(FTS_REG_FOD_MODE_EN, &regval);
+        if (regval == fod_val)
+            break;
+        fts_write_reg(FTS_REG_FOD_MODE_EN, fod_val);
+        fts_msleep(1);
+    }
+
+    if (i >= FTS_MAX_RETRIES_WRITEREG)
+        FTS_ERROR("set fod mode to %x failed,reg_val:%x", fod_val, regval);
+    else if (i > 0)
+        FTS_INFO("set fod mode to %x successfully", fod_val);
+}
+
+void fts_fod_enable(int enable)
+{
+    struct fts_ts_data *ts_data = fts_data;
+
+    ts_data->fod_fp_down = false;
+    if (enable) {
+        FTS_INFO("Fod enable");
+        ts_data->fod_mode = ENABLE;
+        fts_fod_set_reg(FTS_VAL_FOD_ENABLE);
+    } else {
+        FTS_INFO("Fod disable");
+        ts_data->fod_mode = DISABLE;
+        fts_fod_set_reg(DISABLE);
+    }
+}
+
+/*****************************************************************************
+* Name: fts_fod_readdata
+* Brief: read fod value from TP, check whether having FOD event or not,
+*        and report the state to host if need.
+*
+* Input: ts_data
+* Output:
+* Return: return negative code if error occurs,return 0 or 1 if success.
+*         return 0 if continue report finger touches.
+*         return 1(FTS_RETVAL_IGNORE_TOUCHES) if you want to ingore this
+*         finger reporting, As default, the following situation will report 1:
+*               a.System in suspend state, now not handle gesture.
+*****************************************************************************/
+static int fts_fod_readdata(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    int fod_x = 0;
+    int fod_y = 0;
+    int fod_pointid = 0;
+    int fod_down = 0;
+    u8 fod_val[FTS_FOD_BUF_LEN] = { 0 };
+    u8 fod_cmd = FTS_REG_FOD_DATA;
+
+    ret = fts_read(&fod_cmd, 1, fod_val, FTS_FOD_BUF_LEN);
+    if (ret < 0) {
+        FTS_ERROR("read fod data failed,ret=%d", ret);
+        return ret;
+    }
+
+    if (fod_val[1] == 0x26) {
+        fod_pointid = fod_val[0];
+        fod_x = (fod_val[4] << 8) + fod_val[5];
+        fod_y = (fod_val[6] << 8) + fod_val[7];
+        fod_down = (fod_val[8] == 0) ? 1 : 0;
+        FTS_DEBUG("FOD data:%x %x %x %x[%x,%x][%x]", fod_val[0], fod_val[1],
+                  fod_val[2], fod_val[3], fod_x, fod_x, fod_val[8]);
+        if (fod_down) {
+            /* FOD down, need do something to tell host */
+            ts_data->fod_fp_down = true;
+        } else {
+            /* FOD up, need do something to tell host */
+            ts_data->fod_fp_down = false;
+        }
+
+        ret = (ts_data->suspended) ? FTS_RETVAL_IGNORE_TOUCHES : 0;
+    } else {
+        ret = 0;
+    }
+
+    return ret;
+}
+
+static int fts_fod_recovery(struct fts_ts_data *ts_data)
+{
+    if (ts_data->fod_mode) {
+        fts_fod_set_reg(FTS_VAL_FOD_ENABLE);
+    }
+    return 0;
+}
+
+/*****************************************************************************
+* Name: fts_fod_checkdown
+* Brief: check fod down event is triggered, it's used to reset TP or not when
+*        resuming.
+*
+* Input: ts_data
+* Output:
+* Return: return 1 if having fod down event, or else return 0
+*****************************************************************************/
+static int fts_fod_checkdown(struct fts_ts_data *ts_data)
+{
+    return (ts_data->fod_mode && ts_data->fod_fp_down);
+}
+
+static int fts_fod_suspend(struct fts_ts_data *ts_data)
+{
+    ts_data->fod_fp_down = false;
+    fts_fod_set_reg(FTS_VAL_FOD_ENABLE);
+    return 0;
+}
+
+static int fts_fod_resume(struct fts_ts_data *ts_data)
+{
+    if (!fts_fod_checkdown(ts_data)) fts_fod_set_reg(FTS_VAL_FOD_ENABLE);
+    ts_data->fod_fp_down = false;
+    return 0;
+}
+#endif
+
+/*****************************************************************************
+*  Reprot related
+*****************************************************************************/
+static void fts_show_touch_buffer(u8 *data, u32 datalen)
+{
+    u32 i = 0;
+    u32 count = 0;
+    char *tmpbuf = NULL;
+
+    tmpbuf = kzalloc(1024, GFP_KERNEL);
+    if (!tmpbuf) {
+        FTS_ERROR("tmpbuf zalloc fail");
+        return;
+    }
+
+    for (i = 0; i < datalen; i++) {
+        count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]);
+        if (count >= 1024)
+            break;
+    }
+    FTS_DEBUG("touch_buf:%s", tmpbuf);
+
+    if (tmpbuf) {
+        kfree(tmpbuf);
+        tmpbuf = NULL;
+    }
+}
+
+void fts_release_all_finger(void)
+{
+    struct fts_ts_data *ts_data = fts_data;
+    struct input_dev *input_dev = ts_data->input_dev;
+#if FTS_MT_PROTOCOL_B_EN
+    u32 finger_count = 0;
+    u32 max_touches = ts_data->pdata->max_touch_number;
+#endif
+
+    mutex_lock(&ts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+    for (finger_count = 0; finger_count < max_touches; finger_count++) {
+        input_mt_slot(input_dev, finger_count);
+        input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+    }
+#else
+    input_mt_sync(input_dev);
+#endif
+    input_report_key(input_dev, BTN_TOUCH, 0);
+    input_sync(input_dev);
+
+#if FTS_PEN_EN
+    input_report_key(ts_data->pen_dev, BTN_TOOL_PEN, 0);
+    input_report_key(ts_data->pen_dev, BTN_TOUCH, 0);
+    input_sync(ts_data->pen_dev);
+#endif
+
+    ts_data->touch_points = 0;
+    ts_data->key_state = 0;
+    mutex_unlock(&ts_data->report_mutex);
+}
+
+/*****************************************************************************
+* Name: fts_input_report_key
+* Brief: process key events,need report key-event if key enable.
+*        if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50),
+*        need report it to key event.
+*        x_dim: parse from dts, means key x_coordinate, dimension:+-50
+*        y_dim: parse from dts, means key y_coordinate, dimension:+-50
+* Input:
+* Output:
+* Return: return 0 if it's key event, otherwise return error code
+*****************************************************************************/
+static int fts_input_report_key(struct fts_ts_data *ts_data, struct ts_event *kevent)
+{
+    int i = 0;
+    int x = kevent->x;
+    int y = kevent->y;
+    int *x_dim = &ts_data->pdata->key_x_coords[0];
+    int *y_dim = &ts_data->pdata->key_y_coords[0];
+
+    if (!ts_data->pdata->have_key) {
+        return -EINVAL;
+    }
+    for (i = 0; i < ts_data->pdata->key_number; i++) {
+        if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) &&
+            (y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) {
+            if (EVENT_DOWN(kevent->flag)
+                && !(ts_data->key_state & (1 << i))) {
+                input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 1);
+                ts_data->key_state |= (1 << i);
+                FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y);
+            } else if (EVENT_UP(kevent->flag)
+                       && (ts_data->key_state & (1 << i))) {
+                input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 0);
+                ts_data->key_state &= ~(1 << i);
+                FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y);
+            }
+            return 0;
+        }
+    }
+    return -EINVAL;
+}
+
+#if FTS_MT_PROTOCOL_B_EN
+static int fts_input_report_b(struct fts_ts_data *ts_data, struct ts_event *events)
+{
+    int i = 0;
+    int touch_down_point_cur = 0;
+    int touch_point_pre = ts_data->touch_points;
+    u32 max_touch_num = ts_data->pdata->max_touch_number;
+    bool touch_event_coordinate = false;
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    for (i = 0; i < ts_data->touch_event_num; i++) {
+        if (fts_input_report_key(ts_data, &events[i]) == 0) {
+            continue;
+        }
+
+        touch_event_coordinate = true;
+        if (EVENT_DOWN(events[i].flag)) {
+            input_mt_slot(input_dev, events[i].id);
+            input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+#if FTS_REPORT_PRESSURE_EN
+            input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p);
+#endif
+            input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
+            input_report_abs(input_dev, ABS_MT_TOUCH_MINOR, events[i].minor);
+
+            input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x);
+            input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y);
+
+            touch_down_point_cur |= (1 << events[i].id);
+            touch_point_pre |= (1 << events[i].id);
+
+            if ((ts_data->log_level >= 2) ||
+                ((1 == ts_data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) {
+                FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+                          events[i].id, events[i].x, events[i].y,
+                          events[i].p, events[i].area);
+            }
+        } else {
+            input_mt_slot(input_dev, events[i].id);
+            input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+            touch_point_pre &= ~(1 << events[i].id);
+            if (ts_data->log_level >= 1) FTS_DEBUG("[B]P%d UP!", events[i].id);
+        }
+    }
+
+    if (unlikely(touch_point_pre ^ touch_down_point_cur)) {
+        for (i = 0; i < max_touch_num; i++)  {
+            if ((1 << i) & (touch_point_pre ^ touch_down_point_cur)) {
+                if (ts_data->log_level >= 1) FTS_DEBUG("[B]P%d UP!", i);
+                input_mt_slot(input_dev, i);
+                input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+            }
+        }
+    }
+
+    if (touch_down_point_cur)
+        input_report_key(input_dev, BTN_TOUCH, 1);
+    else if (touch_event_coordinate || ts_data->touch_points) {
+        if (ts_data->touch_points && (ts_data->log_level >= 1))
+            FTS_DEBUG("[B]Points All Up!");
+        input_report_key(input_dev, BTN_TOUCH, 0);
+    }
+
+    ts_data->touch_points = touch_down_point_cur;
+    input_sync(input_dev);
+    return 0;
+}
+#else
+static int fts_input_report_a(struct fts_ts_data *ts_data, struct ts_event *events)
+{
+    int i = 0;
+    int touch_down_point_num_cur = 0;
+    bool touch_event_coordinate = false;
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    for (i = 0; i < ts_data->touch_event_num; i++) {
+        if (fts_input_report_key(ts_data, &events[i]) == 0) {
+            continue;
+        }
+
+        touch_event_coordinate = true;
+        if (EVENT_DOWN(events[i].flag)) {
+            input_report_abs(input_dev, ABS_MT_TRACKING_ID, events[i].id);
+#if FTS_REPORT_PRESSURE_EN
+            input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p);
+#endif
+            input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
+            input_report_abs(input_dev, ABS_MT_TOUCH_MINOR, events[i].minor);
+
+            input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x);
+            input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y);
+            input_mt_sync(input_dev);
+
+            touch_down_point_num_cur++;
+            if ((ts_data->log_level >= 2) ||
+                ((1 == ts_data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) {
+                FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+                          events[i].id, events[i].x, events[i].y,
+                          events[i].p, events[i].area);
+            }
+        }
+    }
+
+    if (touch_down_point_num_cur)
+        input_report_key(input_dev, BTN_TOUCH, 1);
+    else if (touch_event_coordinate || ts_data->touch_points) {
+        if (ts_data->touch_points && (ts_data->log_level >= 1))
+            FTS_DEBUG("[A]Points All Up!");
+        input_report_key(input_dev, BTN_TOUCH, 0);
+        input_mt_sync(input_dev);
+    }
+
+    ts_data->touch_points = touch_down_point_num_cur;
+    input_sync(input_dev);
+    return 0;
+}
+#endif
+
+#if FTS_PEN_EN
+static int fts_input_pen_report(struct fts_ts_data *ts_data, u8 *pen_buf)
+{
+    struct input_dev *pen_dev = ts_data->pen_dev;
+    struct pen_event *pevt = &ts_data->pevent;
+
+    /*get information of stylus*/
+    pevt->inrange = (pen_buf[2] & 0x20) ? 1 : 0;
+    pevt->tip = (pen_buf[2] & 0x01) ? 1 : 0;
+    pevt->flag = pen_buf[3] >> 6;
+#if FTS_PEN_HIRES_EN
+    pevt->id = 0;
+    pevt->x = ((u32)((pen_buf[3] & 0x0F) << 12) + (pen_buf[4] << 4) + ((pen_buf[5] >> 4) & 0x0F));
+    pevt->y = ((u32)((pen_buf[5] & 0x0F) << 12) + (pen_buf[6] << 4) + ((pen_buf[7] >> 4) & 0x0F));
+    pevt->x = (pevt->x * FTS_PEN_HIRES_X ) / FTS_HI_RES_X_MAX;
+    pevt->y = (pevt->y * FTS_PEN_HIRES_X ) / FTS_HI_RES_X_MAX;
+#else
+    pevt->id = pen_buf[5] >> 4;
+    pevt->x = ((pen_buf[3] & 0x0F) << 8) + pen_buf[4];
+    pevt->y = ((pen_buf[5] & 0x0F) << 8) + pen_buf[6];
+#endif
+    pevt->p = ((pen_buf[7] & 0x0F) << 8) + pen_buf[8];
+    pevt->tilt_x = (short)((pen_buf[9] << 8) + pen_buf[10]);
+    pevt->tilt_y = (short)((pen_buf[11] << 8) + pen_buf[12]);
+    pevt->azimuth = ((pen_buf[13] << 8) + pen_buf[14]);
+    pevt->tool_type = BTN_TOOL_PEN;
+
+    input_report_key(pen_dev, BTN_STYLUS, !!(pen_buf[2] & 0x02));
+    input_report_key(pen_dev, BTN_STYLUS2, !!(pen_buf[2] & 0x08));
+
+    switch (ts_data->pen_etype) {
+    case STYLUS_DEFAULT:
+        if (pevt->tip && pevt->p) {
+            if ((ts_data->log_level >= 2) || (!pevt->down))
+                FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d DOWN",
+                          pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag,
+                          pevt->tilt_x, pevt->tilt_y);
+            input_report_abs(pen_dev, ABS_X, pevt->x);
+            input_report_abs(pen_dev, ABS_Y, pevt->y);
+            input_report_abs(pen_dev, ABS_PRESSURE, pevt->p);
+            input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x);
+            input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y);
+            input_report_key(pen_dev, BTN_TOUCH, 1);
+            input_report_key(pen_dev, BTN_TOOL_PEN, 1);
+            pevt->down = 1;
+        } else if (!pevt->tip && pevt->down) {
+            FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d UP",
+                      pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag,
+                      pevt->tilt_x, pevt->tilt_y);
+            input_report_abs(pen_dev, ABS_X, pevt->x);
+            input_report_abs(pen_dev, ABS_Y, pevt->y);
+            input_report_abs(pen_dev, ABS_PRESSURE, pevt->p);
+            input_report_key(pen_dev, BTN_TOUCH, 0);
+            input_report_key(pen_dev, BTN_TOOL_PEN, 0);
+            pevt->down = 0;
+        }
+        input_sync(pen_dev);
+        break;
+    case STYLUS_HOVER:
+        if (ts_data->log_level >= 1)
+            FTS_DEBUG("[PEN][%02X]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d,%d",
+                      pen_buf[2], pevt->x, pevt->y, pevt->p, pevt->tip,
+                      pevt->flag, pevt->tilt_x, pevt->tilt_y, pevt->azimuth);
+        input_report_abs(pen_dev, ABS_X, pevt->x);
+        input_report_abs(pen_dev, ABS_Y, pevt->y);
+        input_report_abs(pen_dev, ABS_Z, pevt->azimuth);
+        input_report_abs(pen_dev, ABS_PRESSURE, pevt->p);
+        input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x);
+        input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y);
+        input_report_key(pen_dev, BTN_TOOL_PEN, EVENT_DOWN(pevt->flag));
+        input_report_key(pen_dev, BTN_TOUCH, pevt->tip);
+        input_sync(pen_dev);
+        break;
+    default:
+        FTS_ERROR("Unknown stylus event");
+        break;
+    }
+
+    return 0;
+}
+#endif
+
+static int fts_input_report_touch(struct fts_ts_data *ts_data, u8 *touch_buf)
+{
+    int i = 0;
+    int event_num = 0;
+    int finger_num = 0;
+    int pointid = 0;
+    int base = 0;
+    int max_touch_num = ts_data->pdata->max_touch_number;
+    struct ts_event *events = ts_data->events;
+
+    finger_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F;
+    if (finger_num > max_touch_num) {
+        FTS_ERROR("invalid point_num(%d)", finger_num);
+        return -EIO;
+    }
+
+    for (i = 0; i < max_touch_num; i++) {
+        base = FTS_ONE_TCH_LEN * i + 2;
+        pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4;
+        if (pointid >= FTS_MAX_ID)
+            break;
+        else if (pointid >= max_touch_num) {
+            FTS_ERROR("ID(%d) beyond max_touch_number", pointid);
+            return -EINVAL;
+        }
+
+        events[i].id = pointid;
+        events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6;
+#if FTS_TOUCH_HIRES_EN
+        events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 12) \
+                      + ((touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF) << 4) \
+                      + ((touch_buf[FTS_TOUCH_OFF_PRE + base] >> 4) & 0x0F);
+        events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 12) \
+                      + ((touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF) << 4) \
+                      + (touch_buf[FTS_TOUCH_OFF_PRE + base] & 0x0F);
+        events[i].x = (events[i].x * FTS_TOUCH_HIRES_X ) / FTS_HI_RES_X_MAX;
+        events[i].y = (events[i].y * FTS_TOUCH_HIRES_X ) / FTS_HI_RES_X_MAX;
+        events[i].p = 0x3F;
+#if FTS_REPORT_PRESSURE_EN
+        FTS_ERROR("high solution project doesn't support pressure property");
+#endif
+#else
+        events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \
+                      + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF);
+        events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \
+                      + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF);
+        events[i].p =  touch_buf[FTS_TOUCH_OFF_PRE + base];
+        if (events[i].p <= 0) events[i].p = 0x3F;
+#endif
+        events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base];
+        if (events[i].area <= 0) events[i].area = 0x09;
+        events[i].minor = events[i].area;
+
+        event_num++;
+        if (EVENT_DOWN(events[i].flag) && (finger_num == 0)) {
+            FTS_INFO("abnormal touch data from fw");
+            return -EIO;
+        }
+    }
+
+    if (event_num == 0) {
+        FTS_INFO("no touch point information(%02x)", touch_buf[2]);
+        return -EIO;
+    }
+    ts_data->touch_event_num = event_num;
+
+    mutex_lock(&ts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+    fts_input_report_b(ts_data, events);
+#else
+    fts_input_report_a(ts_data, events);
+#endif
+    mutex_unlock(&ts_data->report_mutex);
+    return 0;
+}
+
+static int fts_input_report_touch_pv2(struct fts_ts_data *ts_data, u8 *touch_buf)
+{
+    int i = 0;
+    int event_num = 0;
+    int pointid = 0;
+    int base = 0;
+    int max_touch_num = ts_data->pdata->max_touch_number;
+    struct ts_event *events = ts_data->events;
+
+    event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F;
+    if (!event_num || (event_num > max_touch_num)) {
+        FTS_ERROR("invalid touch event num(%d)", event_num);
+        return -EIO;
+    }
+
+    ts_data->touch_event_num = event_num;
+    for (i = 0; i < event_num; i++) {
+        base = FTS_ONE_TCH_LEN_V2 * i + 4;
+        pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4;
+        if (pointid >= max_touch_num) {
+            FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)",
+                      pointid, max_touch_num);
+            return -EINVAL;
+        }
+
+        events[i].id = pointid;
+        events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6;
+
+        events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 12) \
+                      + ((touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF) << 4) \
+                      + ((touch_buf[FTS_TOUCH_OFF_PRE + base] >> 4) & 0x0F);
+
+        events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 12) \
+                      + ((touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF) << 4) \
+                      + (touch_buf[FTS_TOUCH_OFF_PRE + base] & 0x0F);
+#if FTS_TOUCH_HIRES_EN
+        events[i].x = (events[i].x * FTS_TOUCH_HIRES_X ) / FTS_HI_RES_X_MAX;
+        events[i].y = (events[i].y * FTS_TOUCH_HIRES_X ) / FTS_HI_RES_X_MAX;
+#else
+        events[i].x = events[i].x  / FTS_HI_RES_X_MAX;
+        events[i].y = events[i].y  / FTS_HI_RES_X_MAX;
+#endif
+        events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base];
+        events[i].minor = touch_buf[FTS_TOUCH_OFF_MINOR + base];
+        events[i].p = 0x3F;
+#if FTS_REPORT_PRESSURE_EN
+        FTS_ERROR("The pressure property isn't supported");
+#endif
+        if (events[i].area <= 0) events[i].area = 0x09;
+        if (events[i].minor <= 0) events[i].minor = 0x09;
+    }
+
+    mutex_lock(&ts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+    fts_input_report_b(ts_data, events);
+#else
+    fts_input_report_a(ts_data, events);
+#endif
+    mutex_unlock(&ts_data->report_mutex);
+    return 0;
+}
+
+int fts_input_report_buffer(struct fts_ts_data *ts_data, u8 *report_buf)
+{
+    int ret = 0;
+    int touch_etype = 0;
+
+    if (!ts_data || !report_buf) {
+        FTS_ERROR("ts_data/report_buf is null");
+        return -EINVAL;
+    }
+
+    touch_etype = ((report_buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F);
+    switch (touch_etype) {
+    case TOUCH_DEFAULT:
+        ret = fts_input_report_touch(ts_data, report_buf);
+        break;
+
+    case TOUCH_PROTOCOL_v2:
+        ret = fts_input_report_touch_pv2(ts_data, report_buf);
+        break;
+
+#if FTS_PEN_EN
+    case TOUCH_PEN:
+        mutex_lock(&ts_data->report_mutex);
+        ret = fts_input_pen_report(ts_data, report_buf);
+        mutex_unlock(&ts_data->report_mutex);
+        break;
+#endif
+
+    default:
+        FTS_INFO("unknown touch event(%d)", touch_etype);
+        break;
+    }
+
+    return ret;
+}
+
+static int fts_read_touchdata_spi(struct fts_ts_data *ts_data, u8 *buf)
+{
+    int ret = 0;
+
+    ts_data->touch_addr = 0x01;
+    ret = fts_read(&ts_data->touch_addr, 1, buf, ts_data->touch_size);
+
+
+    if (ret < 0) {
+        FTS_ERROR("touch data(%x) abnormal,ret:%d", buf[1], ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int fts_read_touchdata_i2c(struct fts_ts_data *ts_data, u8 *buf)
+{
+    int ret = 0;
+    u32 touch_max_size = 0;
+    u32 max_touch_num = ts_data->pdata->max_touch_number;
+    u8 event = 0xFF;
+
+    ts_data->touch_addr = 0x01;
+    ret = fts_read(&ts_data->touch_addr, 1, buf, ts_data->touch_size);
+    if (ret < 0) {
+        FTS_ERROR("read touchdata fails,ret:%d", ret);
+        return ret;
+    }
+
+    event = (buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F;
+    if (event == TOUCH_DEFAULT) {
+        if (buf[ts_data->touch_size - 1] != 0xFF)
+            touch_max_size = max_touch_num * FTS_ONE_TCH_LEN + 2;
+    } else if (event == TOUCH_PROTOCOL_v2) {
+        touch_max_size = (buf[FTS_TOUCH_E_NUM] & 0x0F) * FTS_ONE_TCH_LEN_V2 + 4;
+    }
+#if FTS_PEN_EN
+    else if (event == TOUCH_PEN) {
+        touch_max_size = FTS_SIZE_PEN;
+        if (touch_max_size > ts_data->touch_size) {
+            FTS_INFO("read next touch message of pen,size:%d-%d",
+                     touch_max_size, ts_data->touch_size);
+        }
+    }
+#endif
+
+    if (touch_max_size > ts_data->touch_size) {
+        ts_data->ta_size = touch_max_size;
+        ts_data->touch_addr += ts_data->touch_size;
+        ret = fts_read(&ts_data->touch_addr, 1, buf + ts_data->touch_size, \
+                       touch_max_size - ts_data->touch_size);
+        if (ret < 0) {
+            FTS_ERROR("read touchdata2 fails,ret:%d", ret);
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static int fts_read_parse_touchdata(struct fts_ts_data *ts_data, u8 *touch_buf)
+{
+    int ret = 0;
+
+    memset(touch_buf, 0xFF, FTS_MAX_TOUCH_BUF);
+    ts_data->ta_size = ts_data->touch_size;
+
+    /*read touch data*/
+    if (ts_data->bus_type == BUS_TYPE_SPI)
+        ret = fts_read_touchdata_spi(ts_data, touch_buf);
+    else if (ts_data->bus_type == BUS_TYPE_I2C)
+        ret = fts_read_touchdata_i2c(ts_data, touch_buf);
+    else FTS_ERROR("unknown bus type:%d", ts_data->bus_type);
+    if (ret < 0) {
+        FTS_ERROR("unknown BUS type");
+        return TOUCH_ERROR;
+    }
+
+    if (ts_data->log_level >= 3)
+        fts_show_touch_buffer(touch_buf, ts_data->ta_size);
+
+    if (ret)
+        return TOUCH_IGNORE;
+
+    if ((touch_buf[1] == 0xFF) && (touch_buf[2] == 0xFF)
+        && (touch_buf[3] == 0xFF) && (touch_buf[4] == 0xFF)) {
+        FTS_INFO("touch buff is 0xff, FW initialized");
+        return TOUCH_FW_INIT;
+    }
+
+#if FTS_PSENSOR_EN
+    if (ts_data->proximity_mode) {
+        if (fts_proximity_readdata(ts_data) == FTS_RETVAL_IGNORE_TOUCHES)
+            return TOUCH_IGNORE;
+    }
+#endif
+
+#if FTS_FOD_EN
+    if (ts_data->fod_mode) {
+        if (fts_fod_readdata(ts_data) == FTS_RETVAL_IGNORE_TOUCHES)
+            return TOUCH_IGNORE;
+    }
+#endif
+
+    if (ts_data->suspended && ts_data->gesture_support) {
+        if (fts_gesture_readdata(ts_data, touch_buf) == FTS_RETVAL_IGNORE_TOUCHES)
+            return TOUCH_IGNORE;
+    }
+
+    if (ts_data->suspended) {
+        FTS_INFO("In suspend state, not report touch points");
+        return TOUCH_IGNORE;
+    }
+
+    return ((touch_buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F);
+}
+
+static int fts_irq_read_report(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    int touch_etype = 0;
+    u8 *touch_buf = ts_data->touch_buf;
+
+    touch_etype = fts_read_parse_touchdata(ts_data, touch_buf);
+    switch (touch_etype) {
+    case TOUCH_DEFAULT:
+        ret = fts_input_report_touch(ts_data, touch_buf);
+        break;
+
+    case TOUCH_PROTOCOL_v2:
+        ret = fts_input_report_touch_pv2(ts_data, touch_buf);
+        break;
+
+#if FTS_PEN_EN
+    case TOUCH_PEN:
+        mutex_lock(&ts_data->report_mutex);
+        ret = fts_input_pen_report(ts_data, touch_buf);
+        mutex_unlock(&ts_data->report_mutex);
+        break;
+#endif
+
+    case TOUCH_FW_INIT:
+        fts_release_all_finger();
+        fts_tp_state_recovery(ts_data);
+        break;
+
+    case TOUCH_IGNORE:
+    case TOUCH_ERROR:
+    case TOUCH_FWDBG:
+        break;
+
+    default:
+        FTS_INFO("unknown touch event(%d)", touch_etype);
+        break;
+    }
+
+    return ret;
+}
+
+static irqreturn_t fts_irq_handler(int irq, void *data)
+{
+    struct fts_ts_data *ts_data = fts_data;
+#if IS_ENABLED(CONFIG_PM) && FTS_PATCH_COMERR_PM
+    int ret = 0;
+
+    if ((ts_data->suspended) && (ts_data->pm_suspend)) {
+        ret = wait_for_completion_timeout(
+                  &ts_data->pm_completion,
+                  msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM));
+        if (!ret) {
+            FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq");
+            return IRQ_HANDLED;
+        }
+    }
+#endif
+
+    if (ts_data->suspended)
+        __pm_wakeup_event(ts_data->p_ws, jiffies_to_msecs(FTS_WAKELOCK_TIMEOUT));
+
+
+    ts_data->intr_jiffies = jiffies;
+    fts_prc_queue_work(ts_data);
+    fts_irq_read_report(ts_data);
+    if (ts_data->touch_analysis_support && ts_data->ta_flag) {
+        ts_data->ta_flag = 0;
+        if (ts_data->ta_buf && ts_data->ta_size)
+            memcpy(ts_data->ta_buf, ts_data->touch_buf, ts_data->ta_size);
+        wake_up_interruptible(&ts_data->ts_waitqueue);
+    }
+
+    return IRQ_HANDLED;
+}
+
+static int fts_irq_registration(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    struct fts_ts_platform_data *pdata = ts_data->pdata;
+
+    ts_data->irq = gpio_to_irq(pdata->irq_gpio);
+    pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+    FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags);
+    ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler,
+                               pdata->irq_gpio_flags,
+                               FTS_DRIVER_NAME, ts_data);
+
+    return ret;
+}
+
+#if FTS_PEN_EN
+static int fts_input_pen_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    struct input_dev *pen_dev;
+    struct fts_ts_platform_data *pdata = ts_data->pdata;
+    u32 pen_x_max = pdata->x_max;
+    u32 pen_y_max = pdata->y_max;
+
+    FTS_FUNC_ENTER();
+    pen_dev = input_allocate_device();
+    if (!pen_dev) {
+        FTS_ERROR("Failed to allocate memory for input_pen device");
+        return -ENOMEM;
+    }
+
+#if FTS_PEN_HIRES_EN
+    pen_x_max = (pdata->x_max + 1) * FTS_PEN_HIRES_X - 1;
+    pen_y_max = (pdata->y_max + 1) * FTS_PEN_HIRES_X - 1;
+#endif
+    pen_dev->dev.parent = ts_data->dev;
+    pen_dev->name = FTS_DRIVER_PEN_NAME;
+    pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+    __set_bit(ABS_X, pen_dev->absbit);
+    __set_bit(ABS_Y, pen_dev->absbit);
+    __set_bit(BTN_STYLUS, pen_dev->keybit);
+    __set_bit(BTN_STYLUS2, pen_dev->keybit);
+    __set_bit(BTN_TOUCH, pen_dev->keybit);
+    __set_bit(BTN_TOOL_PEN, pen_dev->keybit);
+    __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit);
+    input_set_abs_params(pen_dev, ABS_X, pdata->x_min, pen_x_max, 0, 0);
+    input_set_abs_params(pen_dev, ABS_Y, pdata->y_min, pen_y_max, 0, 0);
+    input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0);
+    input_set_abs_params(pen_dev, ABS_TILT_X, -9000, 9000, 0, 0);
+    input_set_abs_params(pen_dev, ABS_TILT_Y, -9000, 9000, 0, 0);
+    input_set_abs_params(pen_dev, ABS_Z, 0, 36000, 0, 0);
+
+    ret = input_register_device(pen_dev);
+    if (ret) {
+        FTS_ERROR("Input device registration failed");
+        input_free_device(pen_dev);
+        pen_dev = NULL;
+        return ret;
+    }
+
+    ts_data->pen_dev = pen_dev;
+    ts_data->pen_etype = STYLUS_DEFAULT;
+    FTS_FUNC_EXIT();
+    return 0;
+}
+#endif
+
+static int fts_input_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    int key_num = 0;
+    struct fts_ts_platform_data *pdata = ts_data->pdata;
+    struct input_dev *input_dev;
+    u32 touch_x_max = pdata->x_max;
+    u32 touch_y_max = pdata->y_max;
+
+    FTS_FUNC_ENTER();
+    input_dev = input_allocate_device();
+    if (!input_dev) {
+        FTS_ERROR("Failed to allocate memory for input device");
+        return -ENOMEM;
+    }
+
+    /* Init and register Input device */
+    input_dev->name = FTS_DRIVER_NAME;
+    if (ts_data->bus_type == BUS_TYPE_I2C)
+        input_dev->id.bustype = BUS_I2C;
+    else
+        input_dev->id.bustype = BUS_SPI;
+    input_dev->dev.parent = ts_data->dev;
+
+    input_set_drvdata(input_dev, ts_data);
+
+    __set_bit(EV_SYN, input_dev->evbit);
+    __set_bit(EV_ABS, input_dev->evbit);
+    __set_bit(EV_KEY, input_dev->evbit);
+    __set_bit(BTN_TOUCH, input_dev->keybit);
+    __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+    if (pdata->have_key) {
+        FTS_INFO("set key capabilities");
+        for (key_num = 0; key_num < pdata->key_number; key_num++)
+            input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]);
+    }
+
+#if FTS_TOUCH_HIRES_EN
+    touch_x_max = (pdata->x_max + 1) * FTS_TOUCH_HIRES_X - 1;
+    touch_y_max = (pdata->y_max + 1) * FTS_TOUCH_HIRES_X - 1;
+#endif
+
+#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, touch_x_max, 0, 0);
+    input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, touch_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
+
+    ret = input_register_device(input_dev);
+    if (ret) {
+        FTS_ERROR("Input device registration failed");
+        input_set_drvdata(input_dev, NULL);
+        input_free_device(input_dev);
+        input_dev = NULL;
+        return ret;
+    }
+
+#if FTS_PEN_EN
+    ret = fts_input_pen_init(ts_data);
+    if (ret) {
+        FTS_ERROR("Input-pen device registration failed");
+        input_set_drvdata(input_dev, NULL);
+        input_free_device(input_dev);
+        input_dev = NULL;
+        return ret;
+    }
+#endif
+
+    ts_data->input_dev = input_dev;
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+static int fts_buffer_init(struct fts_ts_data *ts_data)
+{
+    ts_data->touch_buf = (u8 *)kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL);
+    if (!ts_data->touch_buf) {
+        FTS_ERROR("failed to alloc memory for touch buf");
+        return -ENOMEM;
+    }
+
+    if (ts_data->bus_type == BUS_TYPE_SPI)
+        ts_data->touch_size = FTS_TOUCH_DATA_LEN_V2;
+    else if (ts_data->bus_type == BUS_TYPE_I2C)
+        ts_data->touch_size = FTS_SIZE_DEFAULT_V2;
+    else FTS_ERROR("unknown bus type:%d", ts_data->bus_type);
+
+    ts_data->touch_analysis_support = 0;
+    ts_data->ta_flag = 0;
+    ts_data->ta_size = 0;
+
+    return 0;
+}
+
+#if FTS_PINCTRL_EN
+static int fts_pinctrl_init(struct fts_ts_data *ts)
+{
+    int ret = 0;
+
+    ts->pinctrl = devm_pinctrl_get(ts->dev);
+    if (IS_ERR_OR_NULL(ts->pinctrl)) {
+        FTS_ERROR("Failed to get pinctrl, please check dts");
+        ret = PTR_ERR(ts->pinctrl);
+        goto err_pinctrl_get;
+    }
+
+    ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active");
+    if (IS_ERR_OR_NULL(ts->pins_active)) {
+        FTS_ERROR("Pin state[active] not found");
+        ret = PTR_ERR(ts->pins_active);
+        goto err_pinctrl_lookup;
+    }
+
+    ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend");
+    if (IS_ERR_OR_NULL(ts->pins_suspend)) {
+        FTS_ERROR("Pin state[suspend] not found");
+        ret = PTR_ERR(ts->pins_suspend);
+        goto err_pinctrl_lookup;
+    }
+
+    ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release");
+    if (IS_ERR_OR_NULL(ts->pins_release)) {
+        FTS_ERROR("Pin state[release] not found");
+        ret = PTR_ERR(ts->pins_release);
+    }
+
+    return 0;
+err_pinctrl_lookup:
+    if (ts->pinctrl) {
+        devm_pinctrl_put(ts->pinctrl);
+    }
+err_pinctrl_get:
+    ts->pinctrl = NULL;
+    ts->pins_release = NULL;
+    ts->pins_suspend = NULL;
+    ts->pins_active = NULL;
+    return ret;
+}
+#endif /* FTS_PINCTRL_EN */
+
+#if FTS_POWER_SOURCE_CUST_EN
+/*****************************************************************************
+* Power Control
+*****************************************************************************/
+static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable)
+{
+    int ret = 0;
+
+    if (IS_ERR_OR_NULL(ts_data->vdd)) {
+        FTS_ERROR("vdd is invalid");
+        return -EINVAL;
+    }
+
+    FTS_FUNC_ENTER();
+    if (enable) {
+        if (ts_data->power_disabled) {
+            fts_set_reset(ts_data, 0);
+            fts_msleep(2);
+            FTS_INFO("set power to on");
+            ret = regulator_enable(ts_data->vdd);
+            if (ret) {
+                FTS_ERROR("enable vdd regulator failed,ret=%d", ret);
+            }
+
+            if (!IS_ERR_OR_NULL(ts_data->iovcc)) {
+                ret = regulator_enable(ts_data->iovcc);
+                if (ret) {
+                    FTS_ERROR("enable iovcc regulator failed,ret=%d", ret);
+                }
+            }
+            fts_msleep(2);
+            fts_set_reset(ts_data, 1);
+            ts_data->power_disabled = false;
+        }
+    } else {
+        if (!ts_data->power_disabled) {
+            fts_set_reset(ts_data, 0);
+            fts_msleep(2);
+            FTS_INFO("set power to off");
+            if (!IS_ERR_OR_NULL(ts_data->iovcc)) {
+                ret = regulator_disable(ts_data->iovcc);
+                if (ret) {
+                    FTS_ERROR("disable iovcc regulator failed,ret=%d", ret);
+                }
+            }
+            ret = regulator_disable(ts_data->vdd);
+            if (ret) {
+                FTS_ERROR("disable vdd regulator failed,ret=%d", ret);
+            }
+            ts_data->power_disabled = true;
+        }
+    }
+
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+/*****************************************************************************
+* Name: fts_power_source_init
+* Brief: Init regulator power:vdd/vcc_io(if have), generally, no vcc_io
+*        vdd---->vdd-supply in dts, kernel will auto add "-supply" to parse
+*        Must be call after fts_gpio_configure() execute,because this function
+*        will operate reset-gpio which request gpio in fts_gpio_configure()
+* Input:
+* Output:
+* Return: return 0 if init power successfully, otherwise return error code
+*****************************************************************************/
+static int fts_power_source_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    FTS_FUNC_ENTER();
+    ts_data->vdd = regulator_get(ts_data->dev, "vdd");
+    if (IS_ERR_OR_NULL(ts_data->vdd)) {
+        ret = PTR_ERR(ts_data->vdd);
+        FTS_ERROR("get vdd regulator failed,ret=%d", ret);
+        return ret;
+    }
+
+    if (regulator_count_voltages(ts_data->vdd) > 0) {
+        ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV,
+                                    FTS_VTG_MAX_UV);
+        if (ret) {
+            FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret);
+            regulator_put(ts_data->vdd);
+            return ret;
+        }
+    }
+
+    ts_data->iovcc = regulator_get(ts_data->dev, "iovcc");
+    if (!IS_ERR_OR_NULL(ts_data->iovcc)) {
+        if (regulator_count_voltages(ts_data->iovcc) > 0) {
+            ret = regulator_set_voltage(ts_data->iovcc,
+                                        FTS_IOVCC_VTG_MIN_UV,
+                                        FTS_IOVCC_VTG_MAX_UV);
+            if (ret) {
+                FTS_ERROR("iovcc regulator set_vtg failed,ret=%d", ret);
+                regulator_put(ts_data->iovcc);
+            }
+        }
+    }
+
+    ret = fts_power_source_ctrl(ts_data, ENABLE);
+    if (ret) {
+        FTS_ERROR("fail to enable power(regulator)");
+    }
+
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+static int fts_power_source_exit(struct fts_ts_data *ts_data)
+{
+    fts_power_source_ctrl(ts_data, DISABLE);
+
+    if (!IS_ERR_OR_NULL(ts_data->vdd)) {
+        if (regulator_count_voltages(ts_data->vdd) > 0)
+            regulator_set_voltage(ts_data->vdd, 0, FTS_VTG_MAX_UV);
+        regulator_put(ts_data->vdd);
+    }
+
+    if (!IS_ERR_OR_NULL(ts_data->iovcc)) {
+        if (regulator_count_voltages(ts_data->iovcc) > 0)
+            regulator_set_voltage(ts_data->iovcc, 0, FTS_IOVCC_VTG_MAX_UV);
+        regulator_put(ts_data->iovcc);
+    }
+
+    return 0;
+}
+#endif /* FTS_POWER_SOURCE_CUST_EN */
+
+static int fts_power_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    ts_data->power_disabled = true;
+#if FTS_POWER_SOURCE_CUST_EN
+    ret = fts_power_source_init(ts_data);
+    if (ret) {
+        FTS_ERROR("fail to get power(regulator)");
+        return ret;
+    }
+#else
+#if (!FTS_CHIP_IDC)
+    ret = fts_set_reset(ts_data, 0);
+    if (ret) return ret;
+#endif
+    fts_msleep(2);
+    ret = fts_set_reset(ts_data, 1);
+    if (ret) return ret;
+#endif /* FTS_POWER_SOURCE_CUST_EN */
+
+    /* Init BUS pins(SPI/I2C) after powring on if enabling FTS_PINCTRL_EN*/
+#if FTS_PINCTRL_EN
+    fts_pinctrl_init(ts_data);
+    if (ts_data->pinctrl && ts_data->pins_active) {
+        ret = pinctrl_select_state(ts_data->pinctrl, ts_data->pins_active);
+        if (ret < 0) {
+            FTS_ERROR("Set bus pins to active state failed,ret=%d", ret);
+        }
+    }
+#endif
+
+    fts_msleep(200);
+    return 0;
+}
+
+// static int fts_power_suspend(struct fts_ts_data *ts_data)
+// {
+//     int ret = 0;
+
+//     FTS_FUNC_ENTER();
+//     FTS_INFO("make TP enter into sleep mode");
+//     ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP);
+//     if (ret < 0) {
+//         FTS_ERROR("set TP to sleep mode failed, ret=%d", ret);
+//     }
+
+// #if FTS_POWER_SUSPEND_OFF_EN
+// #if FTS_PINCTRL_EN
+//     if (ts_data->pinctrl && ts_data->pins_suspend) {
+//         if (pinctrl_select_state(ts_data->pinctrl, ts_data->pins_suspend)) {
+//             FTS_ERROR("Set bus pins to suspend state failed");
+//         }
+//     }
+// #endif /* FTS_PINCTRL_EN */
+
+// #if FTS_POWER_SOURCE_CUST_EN
+//     if (fts_power_source_ctrl(ts_data, DISABLE)) {
+//         FTS_ERROR("set power to off failed");
+//     }
+// #else
+//     FTS_ERROR("FTS_POWER_SOURCE_CUST_EN=0,FTS_POWER_SUSPEND_OFF_EN=1");
+// #endif /* FTS_POWER_SOURCE_CUST_EN */
+// #endif /* FTS_POWER_SUSPEND_OFF_EN */
+
+//     FTS_FUNC_EXIT();
+//     return 0;
+// }
+
+// static int fts_power_resume(struct fts_ts_data *ts_data)
+// {
+//     FTS_FUNC_ENTER();
+// #if FTS_POWER_SUSPEND_OFF_EN
+// #if FTS_POWER_SOURCE_CUST_EN
+//     if (fts_power_source_ctrl(ts_data, ENABLE)) {
+//         FTS_ERROR("set power to on failed");
+//     }
+//     fts_msleep(FTS_DELAY_RESUME_RESET);
+// #else
+//     FTS_ERROR("FTS_POWER_SOURCE_CUST_EN=0,FTS_POWER_SUSPEND_OFF_EN=1");
+// #endif /* FTS_POWER_SOURCE_CUST_EN */
+
+// #if FTS_PINCTRL_EN
+//     if (ts_data->pinctrl && ts_data->pins_active) {
+//         if (pinctrl_select_state(ts_data->pinctrl, ts_data->pins_active)) {
+//             FTS_ERROR("Set bus pins to active state failed");
+//         }
+//     }
+// #endif /* FTS_PINCTRL_EN */
+
+// #else /* else FTS_POWER_SUSPEND_OFF_EN */
+//     if (!ts_data->ic_info.is_incell) {
+//         fts_reset_proc(ts_data, false, FTS_DELAY_RESUME_RESET);
+//     }
+// #endif /* FTS_POWER_SUSPEND_OFF_EN */
+
+//     FTS_FUNC_EXIT();
+//     return 0;
+// }
+
+static int fts_gpio_configure(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    FTS_FUNC_ENTER();
+    /* request irq gpio */
+    if (gpio_is_valid(ts_data->pdata->irq_gpio)) {
+        ret = gpio_request(ts_data->pdata->irq_gpio, "fts_irq_gpio");
+        if (ret) {
+            FTS_ERROR("[GPIO]irq gpio request failed");
+            goto err_irq_gpio_req;
+        }
+
+        ret = gpio_direction_input(ts_data->pdata->irq_gpio);
+        if (ret) {
+            FTS_ERROR("[GPIO]set_direction for irq gpio failed");
+            goto err_irq_gpio_dir;
+        }
+    }
+
+    /* request reset gpio */
+    if (gpio_is_valid(ts_data->pdata->reset_gpio)) {
+        ret = gpio_request(ts_data->pdata->reset_gpio, "fts_reset_gpio");
+        if (ret) {
+            FTS_ERROR("[GPIO]reset gpio request failed");
+            goto err_irq_gpio_dir;
+        }
+    }
+
+    FTS_FUNC_EXIT();
+    return 0;
+
+err_irq_gpio_dir:
+    if (gpio_is_valid(ts_data->pdata->irq_gpio))
+        gpio_free(ts_data->pdata->irq_gpio);
+err_irq_gpio_req:
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+static int fts_bus_init(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+    ts_data->bus_tx_buf = kzalloc(FTS_MAX_BUS_BUF, GFP_KERNEL);
+    if (NULL == ts_data->bus_tx_buf) {
+        FTS_ERROR("failed to allocate memory for bus_tx_buf");
+        return -ENOMEM;
+    }
+
+    ts_data->bus_rx_buf = kzalloc(FTS_MAX_BUS_BUF, GFP_KERNEL);
+    if (NULL == ts_data->bus_rx_buf) {
+        FTS_ERROR("failed to allocate memory for bus_rx_buf");
+        return -ENOMEM;
+    }
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+static int fts_get_dt_coords(struct device *dev, char *name,
+                             struct fts_ts_platform_data *pdata)
+{
+    int ret = 0;
+    u32 coords[FTS_COORDS_ARR_SIZE] = { 0 };
+    struct property *prop;
+    struct device_node *np = dev->of_node;
+    int coords_size;
+
+    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, size:%d", name, coords_size);
+        return -EINVAL;
+    }
+
+    ret = of_property_read_u32_array(np, name, coords, coords_size);
+    if (ret < 0) {
+        FTS_ERROR("Unable to read %s, please check dts", name);
+        pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT;
+        pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT;
+        pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT;
+        pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT;
+        return -ENODATA;
+    } else {
+        pdata->x_min = coords[0];
+        pdata->y_min = coords[1];
+        pdata->x_max = coords[2];
+        pdata->y_max = coords[3];
+    }
+
+    FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max,
+             pdata->y_min, pdata->y_max);
+    return 0;
+}
+
+static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
+{
+    int ret = 0;
+    struct device_node *np = dev->of_node;
+    u32 temp_val = 0;
+
+    FTS_FUNC_ENTER();
+    if (!np || !pdata) {
+        FTS_ERROR("np/pdata is null");
+        return -EINVAL;
+    }
+
+    ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata);
+    if (ret < 0)
+        FTS_ERROR("Unable to get display-coords");
+
+    /* key */
+    pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
+    if (pdata->have_key) {
+        ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number);
+        if (ret < 0)
+            FTS_ERROR("Key number undefined!");
+
+        ret = of_property_read_u32_array(np, "focaltech,keys",
+                                         pdata->keys, pdata->key_number);
+        if (ret < 0)
+            FTS_ERROR("Keys undefined!");
+        else if (pdata->key_number > FTS_MAX_KEYS)
+            pdata->key_number = FTS_MAX_KEYS;
+
+        ret = of_property_read_u32_array(np, "focaltech,key-x-coords",
+                                         pdata->key_x_coords,
+                                         pdata->key_number);
+        if (ret < 0)
+            FTS_ERROR("Key Y Coords undefined!");
+
+        ret = of_property_read_u32_array(np, "focaltech,key-y-coords",
+                                         pdata->key_y_coords,
+                                         pdata->key_number);
+        if (ret < 0)
+            FTS_ERROR("Key X Coords undefined!");
+
+        FTS_INFO("VK Number:%d, key:(%d,%d,%d), "
+                 "coords:(%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_y_coords[0],
+                 pdata->key_x_coords[1], pdata->key_y_coords[1],
+                 pdata->key_x_coords[2], pdata->key_y_coords[2]);
+    }
+
+    /* 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");
+
+    ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
+    if (ret < 0) {
+        FTS_ERROR("Unable to get max-touch-number, please check dts");
+        pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT;
+    } else {
+        if (temp_val < 2)
+            pdata->max_touch_number = 2; /* max_touch_number must >= 2 */
+        else if (temp_val > FTS_MAX_POINTS_SUPPORT)
+            pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT;
+        else
+            pdata->max_touch_number = temp_val;
+    }
+
+    FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d",
+             pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio);
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+// static int fts_ts_suspend(struct device *dev)
+// {
+//     struct fts_ts_data *ts_data = fts_data;
+
+//     FTS_FUNC_ENTER();
+//     if (ts_data->suspended) {
+//         FTS_INFO("Already in suspend state");
+//         return 0;
+//     }
+
+//     if (ts_data->fw_loading) {
+//         FTS_INFO("fw upgrade in process, can't suspend");
+//         return 0;
+//     }
+
+//     ts_data->need_work_in_suspend = false;
+//     fts_esdcheck_suspend(ts_data);
+// #if FTS_PSENSOR_EN
+//     if (ts_data->proximity_mode) {
+//         fts_proximity_suspend(ts_data);
+//         ts_data->need_work_in_suspend = true;
+//         fts_release_all_finger();
+//         ts_data->suspended = true;
+//         return 0;
+//     }
+// #endif
+
+// #if FTS_FOD_EN
+//     if (ts_data->fod_mode) {
+//         fts_fod_suspend(ts_data);
+//         ts_data->need_work_in_suspend = true;
+//     }
+// #endif
+
+//     if (ts_data->gesture_support) {
+//         fts_gesture_suspend(ts_data);
+//         ts_data->need_work_in_suspend = true;
+//     }
+
+//     if (ts_data->need_work_in_suspend) {
+//         if (enable_irq_wake(ts_data->irq)) {
+//             FTS_ERROR("enable_irq_wake(irq:%d) fail", ts_data->irq);
+//         }
+//     } else {
+//         fts_irq_disable();
+//         fts_power_suspend(ts_data);
+//     }
+
+//     fts_release_all_finger();
+//     ts_data->suspended = true;
+//     FTS_FUNC_EXIT();
+//     return 0;
+// }
+
+// static int fts_ts_resume(struct device *dev)
+// {
+//     struct fts_ts_data *ts_data = fts_data;
+
+//     FTS_FUNC_ENTER();
+//     if (!ts_data->suspended) {
+//         FTS_DEBUG("Already in awake state");
+//         return 0;
+//     }
+
+//     if (ts_data->fw_loading) {
+//         FTS_INFO("fw upgrade in process, don't resume");
+//         return 0;
+//     }
+
+//     ts_data->suspended = false;
+//     fts_release_all_finger();
+// #if FTS_PSENSOR_EN
+//     if (ts_data->proximity_mode) {
+//         fts_wait_tp_to_valid();
+//         fts_proximity_resume(ts_data);
+//         fts_esdcheck_resume(ts_data);
+//         if (ts_data->gesture_support) fts_gesture_resume(ts_data);
+//         return 0;
+//     }
+// #endif
+
+//     if (ts_data->need_work_in_suspend) {
+// #if FTS_FOD_EN
+//         if ((!ts_data->ic_info.is_incell) && (!fts_fod_checkdown(ts_data))) {
+//             fts_reset_proc(ts_data, false, FTS_DELAY_RESUME_RESET);
+//         }
+// #else
+//         if (!ts_data->ic_info.is_incell) {
+//             fts_reset_proc(ts_data, false, FTS_DELAY_RESUME_RESET);
+//         }
+// #endif
+//     } else {
+//         fts_power_resume(ts_data);
+//     }
+
+//     fts_enter_normal_fw();
+//     if (ts_data->gesture_support) {
+//         fts_gesture_resume(ts_data);
+//     }
+// #if FTS_FOD_EN
+//     if (ts_data->fod_mode) {
+//         fts_fod_resume(ts_data);
+//     }
+// #endif
+//     fts_ex_mode_recovery(ts_data);
+//     fts_esdcheck_resume(ts_data);
+
+//     if (ts_data->need_work_in_suspend) {
+//         if (disable_irq_wake(ts_data->irq)) {
+//             FTS_ERROR("disable_irq_wake(irq:%d) fail", ts_data->irq);
+//         }
+//     } else {
+//         fts_irq_enable();
+//     }
+
+//     FTS_FUNC_EXIT();
+//     return 0;
+// }
+
+static void fts_resume_work(struct work_struct *work)
+{
+    struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, resume_work);
+    // fts_ts_resume(ts_data->dev);
+}
+
+
+// #if IS_ENABLED(CONFIG_DRM)
+// #if IS_ENABLED(CONFIG_DRM_PANEL)
+// static struct drm_panel *active_panel;
+
+// static int drm_check_dt(struct fts_ts_data *ts_data)
+// {
+//     int i = 0;
+//     int count = 0;
+//     struct device_node *node = NULL;
+//     struct drm_panel *panel = NULL;
+//     struct device_node *np = NULL;
+
+//     if (ts_data && ts_data->dev && ts_data->dev->of_node) {
+//         np = ts_data->dev->of_node;
+//         count = of_count_phandle_with_args(np, "panel", NULL);
+//         if (count <= 0) {
+//             FTS_ERROR("find drm_panel count(%d) fail", count);
+//             return -ENODEV;
+//         }
+
+//         for (i = 0; i < count; i++) {
+//             node = of_parse_phandle(np, "panel", i);
+//             panel = of_drm_find_panel(node);
+//             of_node_put(node);
+//             if (!IS_ERR(panel)) {
+//                 FTS_INFO("find drm_panel successfully");
+//                 active_panel = panel;
+//                 return 0;
+//             }
+//         }
+//     }
+
+//     FTS_ERROR("no find drm_panel");
+//     return -ENODEV;
+// }
+// #endif //CONFIG_DRM_PANEL
+// #endif //CONFIG_DRM
+
+
+// static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *v)
+// {
+//     struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, fb_notif);
+//     FTS_FUNC_ENTER();
+//     if (ts_data && v) {
+// #if IS_ENABLED(CONFIG_DRM)
+
+// #if IS_ENABLED(CONFIG_DRM_PANEL)
+//         int blank_value = *((int *)(((struct drm_panel_notifier *)v)->data));
+//         const unsigned long event_enum[2] = {DRM_PANEL_EARLY_EVENT_BLANK, DRM_PANEL_EVENT_BLANK};
+//         const int blank_enum[2] = {DRM_PANEL_BLANK_POWERDOWN, DRM_PANEL_BLANK_UNBLANK};
+// #else //CONFIG_DRM_PANEL
+//         int blank_value = *((int *)(((struct msm_drm_notifier *)v)->data));
+//         const unsigned long event_enum[2] = {MSM_DRM_EARLY_EVENT_BLANK, MSM_DRM_EVENT_BLANK};
+//         const int blank_enum[2] = {MSM_DRM_BLANK_POWERDOWN, MSM_DRM_BLANK_UNBLANK};
+// #endif //CONFIG_DRM_PANEL
+
+// #elif IS_ENABLED(CONFIG_FB)
+//         const unsigned long event_enum[2] = {FB_EARLY_EVENT_BLANK, FB_EVENT_BLANK};
+//         const int blank_enum[2] = {FB_BLANK_POWERDOWN, FB_BLANK_UNBLANK};
+//         int blank_value = *((int *)(((struct fb_event *)v)->data));
+// #endif //CONFIG_DRM
+//         FTS_INFO("notifier,event:%lu,blank:%d", event, blank_value);
+//         if ((blank_enum[1] == blank_value) && (event_enum[1] == event)) {
+//             queue_work(fts_data->ts_workqueue, &fts_data->resume_work);
+//         } else if ((blank_enum[0] == blank_value) && (event_enum[0] == event)) {
+//             cancel_work_sync(&fts_data->resume_work);
+//             fts_ts_suspend(ts_data->dev);
+//         } else {
+//             FTS_DEBUG("notifier,event:%lu,blank:%d, not care", event, blank_value);
+//         }
+//     } else {
+//         FTS_ERROR("ts_data/v is null");
+//         return -EINVAL;
+//     }
+//     FTS_FUNC_EXIT();
+//     return 0;
+// }
+
+
+static int fts_notifier_callback_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    FTS_FUNC_ENTER();
+// #if IS_ENABLED(CONFIG_DRM)
+//     ts_data->fb_notif.notifier_call = fb_notifier_callback;
+// #if IS_ENABLED(CONFIG_DRM_PANEL)
+//     ret = drm_check_dt(ts_data);
+//     if (ret) FTS_ERROR("parse drm-panel fail");
+//     FTS_INFO("init notifier with drm_panel_notifier_register");
+//     if (active_panel) {
+//         ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif);
+//         if (ret) FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d", ret);
+//     }
+// #else
+//     FTS_INFO("init notifier with msm_drm_register_client");
+//     ret = msm_drm_register_client(&ts_data->fb_notif);
+//     if (ret) FTS_ERROR("[DRM]msm_drm_register_client fail: %d", ret);
+// #endif //CONFIG_DRM_PANEL
+
+// #elif IS_ENABLED(CONFIG_FB)
+//     FTS_INFO("init notifier with fb_register_client");
+//     ts_data->fb_notif.notifier_call = fb_notifier_callback;
+//     ret = fb_register_client(&ts_data->fb_notif);
+//     if (ret) {
+//         FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret);
+//     }
+
+// #endif //CONFIG_DRM
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+// static int fts_notifier_callback_exit(struct fts_ts_data *ts_data)
+// {
+//     FTS_FUNC_ENTER();
+// #if IS_ENABLED(CONFIG_DRM)
+// #if IS_ENABLED(CONFIG_DRM_PANEL)
+//     if (active_panel)
+//         drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif);
+// #else
+//     if (msm_drm_unregister_client(&ts_data->fb_notif))
+//         FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.");
+// #endif
+
+// #elif IS_ENABLED(CONFIG_FB)
+//     if (fb_unregister_client(&ts_data->fb_notif))
+//         FTS_ERROR("[FB]Error occurred while unregistering fb_notifier.");
+// #endif //CONFIG_DRM
+//     FTS_FUNC_EXIT();
+//     return 0;
+// }
+
+
+int fts_ts_probe_entry(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    FTS_FUNC_ENTER();
+    FTS_INFO("%s", FTS_DRIVER_VERSION);
+    fts_data = ts_data;
+    ts_data->pdata = kzalloc(sizeof(struct fts_ts_platform_data), GFP_KERNEL);
+    if (!ts_data->pdata) {
+        FTS_ERROR("allocate memory for platform_data fail");
+        return -ENOMEM;
+    }
+
+    ret = fts_parse_dt(ts_data->dev, ts_data->pdata);
+    if (ret) {
+        FTS_ERROR("device-tree parse fail");
+    }
+
+    ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq");
+    if (!ts_data->ts_workqueue) {
+        FTS_ERROR("create fts workqueue fail");
+    } else {
+        INIT_WORK(&ts_data->resume_work, fts_resume_work);
+    }
+    spin_lock_init(&ts_data->irq_lock);
+    mutex_init(&ts_data->report_mutex);
+    mutex_init(&ts_data->bus_lock);
+    init_waitqueue_head(&ts_data->ts_waitqueue);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
+    wakeup_source_init(&ts_data->ws, "fts_ws");
+    ts_data->p_ws = &ts_data->ws;
+#else
+    ts_data->p_ws = wakeup_source_register(ts_data->dev, "fts_ws");
+#endif
+
+    ret = fts_bus_init(ts_data);
+    if (ret) {
+        FTS_ERROR("bus initialize fail");
+        goto err_bus_init;
+    }
+
+    ret = fts_buffer_init(ts_data);
+    if (ret) {
+        FTS_ERROR("buffer init fail");
+        goto err_bus_init;
+    }
+
+    ret = fts_gpio_configure(ts_data);
+    if (ret) {
+        FTS_ERROR("configure the gpios fail");
+        goto err_gpio_config;
+    }
+
+    ret = fts_power_init(ts_data);
+    if (ret) {
+        FTS_ERROR("fail to init power");
+        goto err_power_init;
+    }
+
+    ret = fts_get_ic_information(ts_data);
+    if (ret) {
+        FTS_ERROR("not focal IC, unregister driver");
+        goto err_power_init;
+    }
+
+    ret = fts_input_init(ts_data);
+    if (ret) {
+        FTS_ERROR("input initialize fail");
+        goto err_power_init;
+    }
+
+#if FTS_READ_CUSTOMER_INFO
+    ret = fts_read_customer_information(ts_data);
+    if (ret) {
+        FTS_ERROR("read customer information fail");
+    }
+#endif
+
+    ret = fts_create_apk_debug_channel(ts_data);
+    if (ret) {
+        FTS_ERROR("create apk debug node fail");
+    }
+
+    ret = fts_create_sysfs(ts_data);
+    if (ret) {
+        FTS_ERROR("create sysfs node fail");
+    }
+
+
+
+    ret = fts_point_report_check_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init point report check fail");
+    }
+
+    ret = fts_ex_mode_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init glove/cover/charger fail");
+    }
+
+    ret = fts_gesture_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init gesture fail");
+    }
+
+#if FTS_PSENSOR_EN
+    ret = fts_proximity_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init proximity fail");
+    }
+#endif
+
+
+    ret = fts_esdcheck_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init esd check fail");
+    }
+
+    ret = fts_irq_registration(ts_data);
+    if (ret) {
+        FTS_ERROR("request irq failed");
+        goto err_irq_req;
+    }
+
+    ret = fts_fwupg_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init fw upgrade fail");
+    }
+
+#if IS_ENABLED(CONFIG_PM) && FTS_PATCH_COMERR_PM
+    init_completion(&ts_data->pm_completion);
+    ts_data->pm_suspend = false;
+#endif
+
+    ret = fts_notifier_callback_init(ts_data);
+    if (ret) {
+        FTS_ERROR("init notifier callback fail");
+    }
+
+    FTS_FUNC_EXIT();
+    return 0;
+
+err_irq_req:
+    fts_esdcheck_exit(ts_data);
+#if FTS_PSENSOR_EN
+    fts_proximity_exit(ts_data);
+#endif
+    fts_gesture_exit(ts_data);
+    fts_ex_mode_exit(ts_data);
+    fts_point_report_check_exit(ts_data);
+    fts_remove_sysfs(ts_data);
+    fts_release_apk_debug_channel(ts_data);
+    input_unregister_device(ts_data->input_dev);
+#if FTS_PEN_EN
+    input_unregister_device(ts_data->pen_dev);
+#endif
+err_power_init:
+#if FTS_PINCTRL_EN
+    if (ts_data->pinctrl) {
+        if (ts_data->pins_release) {
+            pinctrl_select_state(ts_data->pinctrl, ts_data->pins_release);
+        }
+        devm_pinctrl_put(ts_data->pinctrl);
+        ts_data->pinctrl = NULL;
+    }
+#endif
+#if FTS_POWER_SOURCE_CUST_EN
+    fts_power_source_exit(ts_data);
+#endif
+    if (gpio_is_valid(ts_data->pdata->reset_gpio))
+        gpio_free(ts_data->pdata->reset_gpio);
+    if (gpio_is_valid(ts_data->pdata->irq_gpio))
+        gpio_free(ts_data->pdata->irq_gpio);
+err_gpio_config:
+    kfree_safe(ts_data->touch_buf);
+err_bus_init:
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
+    wakeup_source_trash(&ts_data->ws);
+    ts_data->p_ws = NULL;
+#else
+    wakeup_source_unregister(ts_data->p_ws);
+#endif
+    cancel_work_sync(&ts_data->resume_work);
+    if (ts_data->ts_workqueue) destroy_workqueue(ts_data->ts_workqueue);
+    kfree_safe(ts_data->bus_tx_buf);
+    kfree_safe(ts_data->bus_rx_buf);
+    kfree_safe(ts_data->pdata);
+
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+int fts_ts_remove_entry(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+    cancel_work_sync(&ts_data->resume_work);
+    // fts_notifier_callback_exit(ts_data);
+    free_irq(ts_data->irq, ts_data);
+    fts_fwupg_exit(ts_data);
+    fts_esdcheck_exit(ts_data);
+#if FTS_PSENSOR_EN
+    fts_proximity_exit(ts_data);
+#endif
+    fts_gesture_exit(ts_data);
+    fts_ex_mode_exit(ts_data);
+    fts_point_report_check_exit(ts_data);
+    fts_remove_sysfs(ts_data);
+    fts_release_apk_debug_channel(ts_data);
+    input_unregister_device(ts_data->input_dev);
+#if FTS_PEN_EN
+    input_unregister_device(ts_data->pen_dev);
+#endif
+    if (ts_data->ts_workqueue) destroy_workqueue(ts_data->ts_workqueue);
+    if (gpio_is_valid(ts_data->pdata->reset_gpio))
+        gpio_free(ts_data->pdata->reset_gpio);
+    if (gpio_is_valid(ts_data->pdata->irq_gpio))
+        gpio_free(ts_data->pdata->irq_gpio);
+
+#if FTS_PINCTRL_EN
+    if (ts_data->pinctrl) {
+        if (ts_data->pins_release) {
+            pinctrl_select_state(ts_data->pinctrl, ts_data->pins_release);
+        }
+        devm_pinctrl_put(ts_data->pinctrl);
+        ts_data->pinctrl = NULL;
+    }
+#endif
+
+#if FTS_POWER_SOURCE_CUST_EN
+    fts_power_source_exit(ts_data);
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
+    wakeup_source_trash(&ts_data->ws);
+    ts_data->p_ws = NULL;
+#else
+    wakeup_source_unregister(ts_data->p_ws);
+#endif
+    kfree_safe(ts_data->touch_buf);
+    kfree_safe(ts_data->bus_tx_buf);
+    kfree_safe(ts_data->bus_rx_buf);
+    kfree_safe(ts_data->pdata);
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.h b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.h
new file mode 100755
index 0000000..e23b0a0
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_core.h
@@ -0,0 +1,426 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/uaccess.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
+#include <linux/ktime.h>
+#include <linux/timekeeping.h>
+#endif
+#include "focaltech_common.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_NAME                     "fts_ts"
+
+#define FTS_MAX_POINTS_SUPPORT              10 /* constant value, can't be changed */
+#define FTS_MAX_KEYS                        4
+#define FTS_KEY_DIM                         10
+#define FTS_COORDS_ARR_SIZE                 4
+#define FTS_ONE_TCH_LEN                     6
+#define FTS_ONE_TCH_LEN_V2                  8
+#define FTS_TOUCH_DATA_LEN_V2  (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN_V2 + 4)
+
+
+#define FTS_TOUCH_DATA_LEN  (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 2)
+
+#define FTS_GESTURE_POINTS_MAX              6
+#define FTS_GESTURE_DATA_LEN               (FTS_GESTURE_POINTS_MAX * 4 + 4)
+
+#define FTS_SIZE_PEN                        15
+#define FTS_SIZE_DEFAULT                    15
+#define FTS_SIZE_DEFAULT_V2                 21
+
+
+#define FTS_MAX_ID                          0x0A
+#define FTS_TOUCH_OFF_E_XH                  0
+#define FTS_TOUCH_OFF_XL                    1
+#define FTS_TOUCH_OFF_ID_YH                 2
+#define FTS_TOUCH_OFF_YL                    3
+#define FTS_TOUCH_OFF_PRE                   4
+#define FTS_TOUCH_OFF_AREA                  5
+#define FTS_TOUCH_OFF_MINOR                 6
+
+#define FTS_TOUCH_E_NUM                     1
+#define FTS_X_MIN_DISPLAY_DEFAULT           0
+#define FTS_Y_MIN_DISPLAY_DEFAULT           0
+#define FTS_X_MAX_DISPLAY_DEFAULT           (720 - 1)
+#define FTS_Y_MAX_DISPLAY_DEFAULT           (1280 - 1)
+
+#define FTS_TOUCH_DOWN                      0
+#define FTS_TOUCH_UP                        1
+#define FTS_TOUCH_CONTACT                   2
+#define EVENT_DOWN(flag)                    ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag))
+#define EVENT_UP(flag)                      (FTS_TOUCH_UP == flag)
+
+#define FTS_MAX_COMPATIBLE_TYPE             8
+#define FTS_MAX_COMMMAND_LENGTH             16
+
+#define FTS_MAX_TOUCH_BUF                   4096
+#define FTS_MAX_BUS_BUF                     4096
+
+#define FTS_MAX_CUSTOMER_INFO               32
+#define FTS_FOD_BUF_LEN                     9
+
+#define FTS_RETVAL_IGNORE_TOUCHES           1
+
+
+/*****************************************************************************
+*  Alternative mode (When something goes wrong, the modules may be able to solve the problem.)
+*****************************************************************************/
+/*
+ * For commnication error in PM(deep sleep) state
+ */
+#define FTS_PATCH_COMERR_PM                 0
+#define FTS_TIMEOUT_COMERR_PM               700
+
+/*
+ * For high resolution
+ * Set FTS_TOUCH_HIRES_EN to 1 to support high resolution reporting of touch finger.
+ * Set FTS_PEN_HIRES_EN to 1 to support high resolution reporting of stylus pen.
+ *
+ * FTS_XXX_HIRES_X, a multiple relative to the original resolution
+ * FTS_HI_RES_X_MAX, const value, can't be modified
+ */
+#define FTS_TOUCH_HIRES_EN                  0
+#define FTS_TOUCH_HIRES_X                   10
+
+#define FTS_PEN_HIRES_EN                    1
+#define FTS_PEN_HIRES_X                     10
+
+
+#define FTS_HI_RES_X_MAX                    16
+
+
+/* If need read customer info when probing, max:FTS_MAX_CUSTOMER_INFO */
+#define FTS_READ_CUSTOMER_INFO              0
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct ftxxxx_proc {
+    struct proc_dir_entry *proc_entry;
+    u8 opmode;
+    u8 cmd_len;
+    u8 cmd[FTS_MAX_COMMMAND_LENGTH];
+};
+
+struct fts_ts_platform_data {
+    u32 irq_gpio;
+    u32 irq_gpio_flags;
+    u32 reset_gpio;
+    u32 reset_gpio_flags;
+    bool have_key;
+    u32 key_number;
+    u32 keys[FTS_MAX_KEYS];
+    u32 key_y_coords[FTS_MAX_KEYS];
+    u32 key_x_coords[FTS_MAX_KEYS];
+    u32 x_max;
+    u32 y_max;
+    u32 x_min;
+    u32 y_min;
+    u32 max_touch_number;
+};
+
+struct ts_event {
+    int x;      /*x coordinate */
+    int y;      /*y coordinate */
+    int p;      /* pressure */
+    int flag;   /* touch event flag: 0 -- down; 1-- up; 2 -- contact */
+    int id;     /*touch ID */
+    int area;
+    int minor;
+};
+
+struct pen_event {
+    int down;
+    int inrange;
+    int tip;
+    int x;      /*x coordinate */
+    int y;      /*y coordinate */
+    int p;      /* pressure */
+    int flag;   /* touch event flag: 0 -- down; 1-- up; 2 -- contact */
+    int id;     /*touch ID */
+    int tilt_x;
+    int tilt_y;
+    int azimuth;
+    int tool_type;
+};
+
+struct fts_ts_data {
+    struct i2c_client *client;
+    struct spi_device *spi;
+    u32 spi_speed;
+    struct device *dev;
+    struct input_dev *input_dev;
+    struct input_dev *pen_dev;
+    struct fts_ts_platform_data *pdata;
+    struct ts_ic_info ic_info;
+    struct workqueue_struct *ts_workqueue;
+    struct work_struct resume_work;
+    struct delayed_work esdcheck_work;
+    struct delayed_work prc_work;
+    struct delayed_work fwdbg_work;
+    wait_queue_head_t ts_waitqueue;
+    struct ftxxxx_proc proc;
+    struct ftxxxx_proc proc_ta;
+    spinlock_t irq_lock;
+    struct mutex report_mutex;
+    struct mutex bus_lock;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
+    struct wakeup_source ws;
+#endif
+    struct wakeup_source *p_ws;
+    unsigned long intr_jiffies;
+    int irq;
+    int log_level;
+    int fw_is_running;      /* confirm fw is running when using spi:default 0 */
+    int dummy_byte;
+#if IS_ENABLED(CONFIG_PM) && FTS_PATCH_COMERR_PM
+    struct completion pm_completion;
+    bool pm_suspend;
+#endif
+    bool suspended;
+    bool need_work_in_suspend;
+    bool fw_loading;
+    bool irq_disabled;
+    bool power_disabled;
+    bool glove_mode;
+    bool cover_mode;
+    bool charger_mode;
+    bool earphone_mode;
+    bool edgepalm_mode;
+    bool touch_analysis_support;
+    bool prc_support;
+    bool prc_mode;
+    bool esd_support;
+    bool fod_mode;
+    bool proximity_mode;
+    bool fhp_mode;
+
+    bool fwdbg_support;
+    bool gesture_support;   /* gesture enable or disable, default: disable */
+    u8 gesture_bmode;       /*gesture buffer mode*/
+
+    int fod_fp_down;
+    int edgepalm_value;
+    int fwdbg_value;
+
+    u8 pen_etype;
+    struct pen_event pevent;
+    struct ts_event events[FTS_MAX_POINTS_SUPPORT];    /* multi-touch */
+    u8 touch_addr;
+    u32 touch_size;
+    u8 *touch_buf;
+    int touch_event_num;
+    int touch_points;
+    int key_state;
+    int ta_flag;
+    u32 ta_size;
+    u8 *ta_buf;
+
+    u8 *bus_tx_buf;
+    u8 *bus_rx_buf;
+    int bus_type;
+    int bus_ver;
+    char customer_info[FTS_MAX_CUSTOMER_INFO];
+    struct regulator *vdd;
+    struct regulator *iovcc;
+#if FTS_PINCTRL_EN
+    struct pinctrl *pinctrl;
+    struct pinctrl_state *pins_active;
+    struct pinctrl_state *pins_suspend;
+    struct pinctrl_state *pins_release;
+#endif
+    struct notifier_block fb_notif;
+};
+
+enum _FTS_BUS_TYPE {
+    BUS_TYPE_NONE,
+    BUS_TYPE_I2C,
+    BUS_TYPE_SPI,
+};
+
+enum _FTS_BUS_VER {
+    BUS_VER_DEFAULT = 1,
+    BUS_VER_V2,
+};
+
+enum _FTS_TOUCH_ETYPE {
+    TOUCH_DEFAULT = 0x00,
+    TOUCH_PROTOCOL_v2 = 0x02,
+    TOUCH_PEN = 0x0B,
+    TOUCH_FW_INIT = 0x81,
+    TOUCH_IGNORE = 0xFE,
+    TOUCH_FWDBG = 0x0E,
+    TOUCH_ERROR = 0xFF,
+};
+
+enum _FTS_STYLUS_ETYPE {
+    STYLUS_DEFAULT,
+    STYLUS_HOVER,
+};
+
+enum _FTS_GESTURE_BMODE {
+    GESTURE_BM_REG,
+    GESTURE_BM_TOUCH,
+};
+
+enum _FTS_FW_MODE {
+    FW_MODE_NORMAL = 0xAA,
+    FW_MODE_FACTORY = 0x55,
+    FW_MODE_GESTURE = 0x66,
+};
+
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+extern struct fts_ts_data *fts_data;
+
+
+/* communication interface */
+int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen);
+int fts_read_reg(u8 addr, u8 *value);
+int fts_write(u8 *writebuf, u32 writelen);
+int fts_write_reg(u8 addr, u8 value);
+int fts_bus_configure(struct fts_ts_data *ts_data, u8 *buf, u32 size);
+int fts_bus_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen);
+int fts_bus_set_speed(struct fts_ts_data *ts_data, u32 speed);
+int fts_hid2std(int mode);
+int fts_ts_probe_entry(struct fts_ts_data *ts_data);
+int fts_ts_remove_entry(struct fts_ts_data *ts_data);
+
+/* Gesture functions */
+int fts_gesture_init(struct fts_ts_data *ts_data);
+int fts_gesture_exit(struct fts_ts_data *ts_data);
+void fts_gesture_recovery(struct fts_ts_data *ts_data);
+int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data);
+int fts_gesture_suspend(struct fts_ts_data *ts_data);
+int fts_gesture_resume(struct fts_ts_data *ts_data);
+
+#if FTS_FOD_EN
+void fts_fod_enable(int enable);
+#endif
+
+/* Apk and functions */
+int fts_create_apk_debug_channel(struct fts_ts_data *);
+void fts_release_apk_debug_channel(struct fts_ts_data *);
+
+/* ADB functions */
+int fts_create_sysfs(struct fts_ts_data *ts_data);
+int fts_remove_sysfs(struct fts_ts_data *ts_data);
+
+/* ESD */
+int fts_esdcheck_init(struct fts_ts_data *ts_data);
+int fts_esdcheck_exit(struct fts_ts_data *ts_data);
+void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable);
+void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug);
+void fts_esdcheck_suspend(struct fts_ts_data *ts_data);
+void fts_esdcheck_resume(struct fts_ts_data *ts_data);
+bool fts_esdcheck_is_running(struct fts_ts_data *ts_data);
+
+
+/* Host test */
+
+/* Point Report Check*/
+int fts_point_report_check_init(struct fts_ts_data *ts_data);
+int fts_point_report_check_exit(struct fts_ts_data *ts_data);
+void fts_prc_queue_work(struct fts_ts_data *ts_data);
+
+/* FW upgrade */
+int fts_fwupg_init(struct fts_ts_data *ts_data);
+int fts_fwupg_exit(struct fts_ts_data *ts_data);
+int fts_upgrade_bin(char *fw_name, bool force);
+int fts_enter_test_environment(bool test_state);
+int fts_enter_normal_fw(void);
+
+/* Other */
+void fts_msleep(unsigned long msecs);
+int fts_set_reset(struct fts_ts_data *ts_data, int value);
+int fts_reset_proc(struct fts_ts_data *ts_data, int force, int hdelayms);
+int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h);
+int fts_wait_tp_to_valid(void);
+void fts_release_all_finger(void);
+void fts_tp_state_recovery(struct fts_ts_data *ts_data);
+int fts_ex_mode_init(struct fts_ts_data *ts_data);
+int fts_ex_mode_exit(struct fts_ts_data *ts_data);
+int fts_ex_mode_recovery(struct fts_ts_data *ts_data);
+int fts_input_report_buffer(struct fts_ts_data *ts_data, u8 *touch_buf);
+
+void fts_irq_disable(void);
+void fts_irq_enable(void);
+
+#if FTS_PSENSOR_EN
+int fts_proximity_init(struct fts_ts_data *ts_data);
+int fts_proximity_exit(struct fts_ts_data *ts_data);
+int fts_proximity_readdata(struct fts_ts_data *ts_data);
+int fts_proximity_suspend(struct fts_ts_data *ts_data);
+int fts_proximity_resume(struct fts_ts_data *ts_data);
+int fts_proximity_recovery(struct fts_ts_data *ts_data);
+#endif
+
+
+#endif /* __LINUX_FOCALTECH_CORE_H__ */
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_esdcheck.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_esdcheck.c
new file mode 100755
index 0000000..5394f0d
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_esdcheck.c
@@ -0,0 +1,442 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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: ESD check function
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release. By luougojin 2016-08-03
+*        v1.1: By luougojin 2017-02-15
+*            1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define ESDCHECK_WAIT_TIME              1000    /* ms */
+#define LCD_ESD_PATCH                   0
+#define ESDCHECK_INTRCNT_MAX            2
+#define ESD_INTR_INTERVALS              200    /* unit:ms */
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_esdcheck_st {
+    u8      mode                : 1;    /* 1- esd check is running 0- esd check is stop */
+    u8      suspend             : 1;
+    u8      proc_debug          : 1;    /* apk or adb use */
+    u8      intr                : 1;    /* 1- Interrupt trigger */
+    u8      unused              : 4;
+    u8      intr_cnt;
+    u8      flow_work_hold_cnt;         /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */
+    u8      flow_work_cnt_last;         /* Save Flow Work Cnt(reg0x91) value */
+    u32     hardware_reset_cnt;
+    u32     nack_cnt;
+    u32     dataerror_cnt;
+};
+
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_esdcheck_st fts_esdcheck_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+#if LCD_ESD_PATCH
+int lcd_need_reset;
+static int tp_need_recovery; /* LCD reset cause Tp reset */
+int idc_esdcheck_lcderror(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    u8 val = 0;
+
+    FTS_DEBUG("check LCD ESD");
+    if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) {
+        tp_need_recovery = 0;
+        /* LCD reset, need recover TP state */
+        fts_release_all_finger();
+        fts_tp_state_recovery(ts_data);
+    }
+
+    ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val);
+    if ( ret < 0) {
+        FTS_ERROR("read reg0xED fail,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, need execute LCD reset");
+        lcd_need_reset = 1;
+        tp_need_recovery = 1;
+    }
+
+    return 0;
+}
+#endif
+
+static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+
+    fts_esdcheck_data.flow_work_hold_cnt = 0;
+    fts_esdcheck_data.hardware_reset_cnt++;
+
+    fts_reset_proc(ts_data, false, 200);
+    fts_release_all_finger();
+    fts_tp_state_recovery(ts_data);
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+static bool get_chip_id(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    int i = 0;
+    u8 idh = 0;
+    u8 chip_id = ts_data->ic_info.ids.chip_idh;
+
+    for (i = 0; i < 3; i++) {
+        ret = fts_read_reg(FTS_REG_CHIP_ID, &idh);
+        if (ret < 0) {
+            FTS_ERROR("read chip id fail,ret:%d", ret);
+            fts_esdcheck_data.nack_cnt++;
+        } else {
+            if ((idh == chip_id) || (fts_check_cid(ts_data, idh) == 0)) {
+                break;
+            } else {
+                FTS_DEBUG("read chip_id:%x,retry:%d", idh, i);
+                fts_esdcheck_data.dataerror_cnt++;
+            }
+        }
+        fts_msleep(10);
+    }
+
+    /* if can't get correct data in 3 times, then need hardware reset */
+    if (i >= 3) {
+        FTS_ERROR("read chip id 3 times fail, need execute TP reset");
+        return true;
+    }
+
+    return false;
+}
+
+/*****************************************************************************
+*  Name: get_flow_cnt
+*  Brief: Read flow cnt(0x91)
+*  Input:
+*  Output:
+*  Return:  1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
+*           0(false) - Reg 0x91(flow cnt) normal
+*****************************************************************************/
+static bool get_flow_cnt(struct fts_ts_data *ts_data)
+{
+    int     ret = 0;
+    u8      reg_value = 0;
+    u8      reg_addr = 0;
+
+    reg_addr = FTS_REG_FLOW_WORK_CNT;
+    ret = fts_read(&reg_addr, 1, &reg_value, 1);
+    if (ret < 0) {
+        FTS_ERROR("read reg0x91 fail,ret:%d", ret);
+        fts_esdcheck_data.nack_cnt++;
+    } else {
+        if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) {
+            FTS_DEBUG("reg0x91,val:%x,last:%x", 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;
+    }
+
+    /* Flow Work Cnt keep a value for 5 times, need execute TP reset */
+    if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
+        FTS_DEBUG("reg0x91 keep a value for 5 times, need execute TP reset");
+        return true;
+    }
+
+    return false;
+}
+
+static int esdcheck_algorithm(struct fts_ts_data *ts_data)
+{
+    int     ret = 0;
+    u8      reg_value = 0;
+    u8      reg_addr = 0;
+    bool    hardware_reset = 0;
+    unsigned long intr_timeout = msecs_to_jiffies(ESD_INTR_INTERVALS);
+
+    /* 1. esdcheck is interrupt, then return */
+    intr_timeout += ts_data->intr_jiffies;
+    if (time_before(jiffies, intr_timeout)) {
+        return 0;
+    }
+
+    /* 2. check power state, if suspend, no need check esd */
+    if (fts_esdcheck_data.suspend == 1) {
+        FTS_DEBUG("In suspend, not check esd");
+        /* 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
+         */
+        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("In apk/adb command mode, not check esd");
+        return 0;
+    }
+
+    /* 4. In factory mode, can't check esd */
+    reg_addr = FTS_REG_WORKMODE;
+    ret = fts_read_reg(reg_addr, &reg_value);
+    if ( ret < 0 ) {
+        fts_esdcheck_data.nack_cnt++;
+    } else if ( (reg_value & 0x70) ==  FTS_REG_WORKMODE_FACTORY_VALUE) {
+        FTS_DEBUG("in factory mode(%x), no check esd", reg_value);
+        return 0;
+    }
+
+    /* 5. Get Chip ID */
+    hardware_reset = get_chip_id(ts_data);
+
+    /* 6. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */
+    if (!hardware_reset) {
+        hardware_reset = get_flow_cnt(ts_data);
+    }
+
+    /* 7. If need hardware reset, then handle it here */
+    if (hardware_reset == 1) {
+        FTS_DEBUG("NoACK=%d, Error Data=%d, Hardware Reset=%d",
+                  fts_esdcheck_data.nack_cnt,
+                  fts_esdcheck_data.dataerror_cnt,
+                  fts_esdcheck_data.hardware_reset_cnt);
+        fts_esdcheck_tp_reset(ts_data);
+    }
+
+    return 0;
+}
+
+static void esdcheck_func(struct work_struct *work)
+{
+    struct fts_ts_data *ts_data = container_of(work,
+                                  struct fts_ts_data, esdcheck_work.work);
+
+    if (ts_data->esd_support) {
+#if LCD_ESD_PATCH
+        idc_esdcheck_lcderror(ts_data);
+#endif
+        esdcheck_algorithm(ts_data);
+        queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work,
+                           msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+    }
+}
+
+/*****************************************************************************
+*  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:
+*****************************************************************************/
+void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug)
+{
+    if (ts_data->esd_support) {
+        fts_esdcheck_data.proc_debug = proc_debug;
+    }
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_switch
+*  Brief: FTS esd check function switch.
+*  Input:   enable:  1 - Enable esd check
+*                    0 - Disable esd check
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable)
+{
+    if (ts_data->esd_support) {
+        if (fts_esdcheck_data.mode ^ enable) {
+            if (enable) {
+                FTS_INFO("ESD check start");
+                fts_esdcheck_data.flow_work_hold_cnt = 0;
+                fts_esdcheck_data.flow_work_cnt_last = 0;
+                fts_esdcheck_data.intr = 0;
+                fts_esdcheck_data.intr_cnt = 0;
+                fts_esdcheck_data.mode = ENABLE;
+                queue_delayed_work(ts_data->ts_workqueue,
+                                   &ts_data->esdcheck_work,
+                                   msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+            } else {
+                FTS_INFO("ESD check stop");
+                cancel_delayed_work_sync(&ts_data->esdcheck_work);
+                fts_esdcheck_data.mode = DISABLE;
+            }
+        }
+    }
+}
+
+void fts_esdcheck_suspend(struct fts_ts_data *ts_data)
+{
+    if (ts_data->esd_support) {
+        FTS_FUNC_ENTER();
+        fts_esdcheck_switch(ts_data, DISABLE);
+        fts_esdcheck_data.suspend = 1;
+        fts_esdcheck_data.intr = 0;
+        fts_esdcheck_data.intr_cnt = 0;
+        FTS_FUNC_EXIT();
+    }
+}
+
+void fts_esdcheck_resume(struct fts_ts_data *ts_data)
+{
+    if (ts_data->esd_support) {
+        FTS_FUNC_ENTER();
+        fts_esdcheck_switch(ts_data, ENABLE);
+        fts_esdcheck_data.suspend = 0;
+        fts_esdcheck_data.intr = 0;
+        fts_esdcheck_data.intr_cnt = 0;
+        FTS_FUNC_EXIT();
+    }
+}
+
+static ssize_t fts_esdcheck_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_INFO("enable esdcheck");
+        ts_data->esd_support = ENABLE;
+        if (!ts_data->suspended) fts_esdcheck_switch(ts_data, ENABLE);
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_INFO("disable esdcheck");
+        fts_esdcheck_switch(ts_data, DISABLE);
+        ts_data->esd_support = DISABLE;
+    }
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_esdcheck_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \
+                     ts_data->esd_support ? "On" : "Off");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+/* sysfs esd node
+ *   read example: cat  fts_esd_mode        ---read esd mode
+ *   write example:echo 01 > fts_esd_mode   ---make esdcheck enable
+ *
+ */
+static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store);
+
+static struct attribute *fts_esd_mode_attrs[] = {
+
+    &dev_attr_fts_esd_mode.attr,
+    NULL,
+};
+
+static struct attribute_group fts_esdcheck_group = {
+    .attrs = fts_esd_mode_attrs,
+};
+
+bool fts_esdcheck_is_running(struct fts_ts_data *ts_data)
+{
+    return fts_esdcheck_data.mode;
+}
+
+int fts_esdcheck_init(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+
+    if (ts_data->ts_workqueue) {
+        INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func);
+    } else {
+        FTS_ERROR("fts workqueue is NULL, can't run esd check function");
+        return -EINVAL;
+    }
+
+    memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
+    fts_esdcheck_data.mode = DISABLE;
+    ts_data->esd_support = FTS_ESDCHECK_EN;
+    if (sysfs_create_group(&ts_data->dev->kobj, &fts_esdcheck_group)) {
+        FTS_ERROR("fts_create_esd_sysfs(sysfs) create fail");
+        sysfs_remove_group(&ts_data->dev->kobj, &fts_esdcheck_group);
+    }
+    fts_esdcheck_switch(ts_data, ENABLE);
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+int fts_esdcheck_exit(struct fts_ts_data *ts_data)
+{
+    fts_esdcheck_data.mode = DISABLE;
+    ts_data->esd_support = DISABLE;
+    cancel_delayed_work_sync(&ts_data->esdcheck_work);
+    sysfs_remove_group(&ts_data->dev->kobj, &fts_esdcheck_group);
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_fun.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_fun.c
new file mode 100755
index 0000000..224a34e
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_fun.c
@@ -0,0 +1,1269 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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_fun.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define PROC_UPGRADE                            0
+#define PROC_READ_REGISTER                      1
+#define PROC_WRITE_REGISTER                     2
+#define PROC_AUTOCLB                            4
+#define PROC_UPGRADE_INFO                       5
+#define PROC_WRITE_DATA                         6
+#define PROC_READ_DATA                          7
+#define PROC_SET_TEST_FLAG                      8
+#define PROC_SET_SLAVE_ADDR                     10
+#define PROC_HW_RESET                           11
+#define PROC_READ_STATUS                        12
+#define PROC_SET_BOOT_MODE                      13
+#define PROC_ENTER_TEST_ENVIRONMENT             14
+#define PROC_WRITE_DATA_DIRECT                  16
+#define PROC_READ_DATA_DIRECT                   17
+#define PROC_CONFIGURE                          18
+#define PROC_CONFIGURE_INTR                     20
+#define PROC_GET_DRIVER_INFO                    21
+#define PROC_NAME                               "ftxxxx-debug"
+#define PROC_BUF_SIZE                           256
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+enum {
+    RWREG_OP_READ = 0,
+    RWREG_OP_WRITE = 1,
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct rwreg_operation_t {
+    int type;           /*  0: read, 1: write */
+    int reg;            /*  register */
+    int len;            /*  read/write length */
+    int val;            /*  length = 1; read: return value, write: op return */
+    int res;            /*  0: success, otherwise: fail */
+    char *opbuf;        /*  length >= 1, read return value, write: op return */
+} rw_op;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static ssize_t fts_debug_write(
+    struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
+{
+    u8 *writebuf = NULL;
+    u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+    int buflen = count;
+    int writelen = 0;
+    int ret = 0;
+    char tmp[PROC_BUF_SIZE];
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
+    struct fts_ts_data *ts_data = pde_data(file_inode(filp));
+#else
+    struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp));
+#endif
+    struct ftxxxx_proc *proc = &ts_data->proc;
+
+    if (buflen < 1) {
+        FTS_ERROR("apk proc count(%d) fail", buflen);
+        return -EINVAL;
+    }
+
+    if (buflen > PROC_BUF_SIZE) {
+        writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+        if (NULL == writebuf) {
+            FTS_ERROR("apk proc wirte buf zalloc fail");
+            return -ENOMEM;
+        }
+    } else {
+        writebuf = tmpbuf;
+    }
+
+    if (copy_from_user(writebuf, buff, buflen)) {
+        FTS_ERROR("[APK]: copy from user error!!");
+        ret = -EFAULT;
+        goto proc_write_err;
+    }
+
+    proc->opmode = writebuf[0];
+    if (buflen == 1) {
+        ret = buflen;
+        goto proc_write_err;
+    }
+
+    switch (proc->opmode) {
+    case PROC_SET_TEST_FLAG:
+        FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]);
+        if (writebuf[1] == 0) {
+            if (!ts_data->suspended) fts_esdcheck_switch(ts_data, ENABLE);
+        } else {
+            fts_esdcheck_switch(ts_data, DISABLE);
+        }
+        break;
+
+    case PROC_READ_REGISTER:
+        proc->cmd[0] = writebuf[1];
+        break;
+
+    case PROC_WRITE_REGISTER:
+        ret = fts_write_reg(writebuf[1], writebuf[2]);
+        if (ret < 0) {
+            FTS_ERROR("PROC_WRITE_REGISTER write error");
+            goto proc_write_err;
+        }
+        break;
+
+    case PROC_READ_DATA:
+        writelen = buflen - 1;
+        if (writelen >= FTS_MAX_COMMMAND_LENGTH) {
+            FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen);
+            goto proc_write_err;
+        }
+        memcpy(proc->cmd, writebuf + 1, writelen);
+        proc->cmd_len = writelen;
+        if (ts_data->bus_type == BUS_TYPE_I2C) {
+            ret = fts_write(writebuf + 1, writelen);
+            if (ret < 0) {
+                FTS_ERROR("PROC_READ_DATA write error");
+                goto proc_write_err;
+            }
+        }
+        break;
+
+    case PROC_WRITE_DATA:
+        writelen = buflen - 1;
+        ret = fts_write(writebuf + 1, writelen);
+        if (ret < 0) {
+            FTS_ERROR("PROC_WRITE_DATA write error");
+            goto proc_write_err;
+        }
+        break;
+
+    case PROC_SET_SLAVE_ADDR:
+        if (ts_data->bus_type == BUS_TYPE_I2C) {
+            fts_bus_configure(ts_data, &writebuf[1], buflen - 1);
+        }
+        break;
+
+    case PROC_HW_RESET:
+        if (buflen < PROC_BUF_SIZE) {
+            memcpy(tmp, writebuf + 1, buflen - 1);
+            tmp[buflen - 1] = '\0';
+            if (strncmp(tmp, "focal_driver", 12) == 0) {
+                FTS_INFO("APK execute HW Reset");
+                fts_reset_proc(ts_data, false, 0);
+            }
+        }
+        break;
+
+    case PROC_SET_BOOT_MODE:
+        FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]);
+        if (0 == writebuf[1]) {
+            ts_data->fw_is_running = true;
+        } else {
+            ts_data->fw_is_running = false;
+        }
+        break;
+
+    case PROC_ENTER_TEST_ENVIRONMENT:
+        FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]);
+        if (0 == writebuf[1]) {
+            fts_enter_test_environment(0);
+        } else {
+            fts_enter_test_environment(1);
+        }
+        break;
+
+    case PROC_READ_DATA_DIRECT:
+        writelen = buflen - 1;
+        if (writelen >= FTS_MAX_COMMMAND_LENGTH) {
+            FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen);
+            goto proc_write_err;
+        }
+        memcpy(proc->cmd, writebuf + 1, writelen);
+        proc->cmd_len = writelen;
+        break;
+
+    case PROC_WRITE_DATA_DIRECT:
+        writelen = buflen - 1;
+        ret = fts_bus_transfer_direct(writebuf + 1, writelen, NULL, 0);
+        if (ret < 0) {
+            FTS_ERROR("PROC_WRITE_DATA_DIRECT write error");
+            goto proc_write_err;
+        }
+        break;
+
+    case PROC_CONFIGURE:
+        if (ts_data->bus_type == BUS_TYPE_SPI) {
+            fts_bus_configure(ts_data, &writebuf[1], buflen - 1);
+        }
+        break;
+
+    case PROC_CONFIGURE_INTR:
+        if (writebuf[1] == 0)
+            fts_irq_disable();
+        else
+            fts_irq_enable();
+        break;
+
+    default:
+        break;
+    }
+
+    ret = buflen;
+proc_write_err:
+    if ((buflen > PROC_BUF_SIZE) && writebuf) {
+        kfree(writebuf);
+        writebuf = NULL;
+    }
+    return ret;
+}
+
+static ssize_t fts_debug_read(
+    struct file *filp, char __user *buff, size_t count, loff_t *ppos)
+{
+    int ret = 0;
+    int num_read_chars = 0;
+    int buflen = count;
+    u8 *readbuf = NULL;
+    u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
+    struct fts_ts_data *ts_data = pde_data(file_inode(filp));
+#else
+    struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp));
+#endif
+    struct ftxxxx_proc *proc = &ts_data->proc;
+
+    if (buflen <= 0) {
+        FTS_ERROR("apk proc read count(%d) fail", buflen);
+        return -EINVAL;
+    }
+
+    if (buflen > PROC_BUF_SIZE) {
+        readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+        if (NULL == readbuf) {
+            FTS_ERROR("apk proc buf zalloc fail");
+            return -ENOMEM;
+        }
+    } else {
+        readbuf = tmpbuf;
+    }
+
+    switch (proc->opmode) {
+    case PROC_READ_REGISTER:
+        num_read_chars = 1;
+        ret = fts_read_reg(proc->cmd[0], &readbuf[0]);
+        if (ret < 0) {
+            FTS_ERROR("PROC_READ_REGISTER read error");
+            goto proc_read_err;
+        }
+        break;
+
+    case PROC_READ_DATA:
+        num_read_chars = buflen;
+        if (ts_data->bus_type == BUS_TYPE_SPI)
+            ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars);
+        else if (ts_data->bus_type == BUS_TYPE_I2C)
+            ret = fts_read(NULL, 0, readbuf, num_read_chars);
+        else FTS_ERROR("unknown bus type:%d", ts_data->bus_type);
+        if (ret < 0) {
+            FTS_ERROR("PROC_READ_DATA read error");
+            goto proc_read_err;
+        }
+        break;
+
+    case PROC_READ_DATA_DIRECT:
+        num_read_chars = buflen;
+        ret = fts_bus_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars);
+        if (ret < 0) {
+            FTS_ERROR("PROC_READ_DATA_DIRECT read error");
+            goto proc_read_err;
+        }
+        break;
+
+    case PROC_GET_DRIVER_INFO:
+        if (buflen >= 64) {
+            num_read_chars = buflen;
+            readbuf[0] = ts_data->bus_type;
+            snprintf(&readbuf[32], buflen - 32, "%s", FTS_DRIVER_VERSION);
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    ret = num_read_chars;
+proc_read_err:
+    if ((num_read_chars > 0) && copy_to_user(buff, readbuf, num_read_chars)) {
+        FTS_ERROR("copy to user error");
+        ret = -EFAULT;
+    }
+
+    if ((buflen > PROC_BUF_SIZE) && readbuf) {
+        kfree(readbuf);
+        readbuf = NULL;
+    }
+    return ret;
+}
+
+/*/proc/fts_ta*/
+static int fts_ta_open(struct inode *inode, struct file *file)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
+    struct fts_ts_data *ts_data = pde_data(inode);
+#else
+    struct fts_ts_data *ts_data = PDE_DATA(inode);
+#endif
+
+    if (ts_data->touch_analysis_support) {
+        FTS_INFO("fts_ta open");
+        ts_data->ta_buf = kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL);
+        if (!ts_data->ta_buf) {
+            FTS_ERROR("kzalloc for ta_buf fails");
+            return -ENOMEM;
+        }
+    }
+    return 0;
+}
+
+static int fts_ta_release(struct inode *inode, struct file *file)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
+    struct fts_ts_data *ts_data = pde_data(inode);
+#else
+    struct fts_ts_data *ts_data = PDE_DATA(inode);
+#endif
+
+    if (ts_data->touch_analysis_support) {
+        FTS_INFO("fts_ta close");
+        ts_data->ta_flag = 0;
+        if (ts_data->ta_buf) {
+            kfree(ts_data->ta_buf);
+            ts_data->ta_buf = NULL;
+        }
+    }
+    return 0;
+}
+
+static ssize_t fts_ta_read(
+    struct file *filp, char __user *buff, size_t count, loff_t *ppos)
+{
+    int read_num = (int)count;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
+    struct fts_ts_data *ts_data = pde_data(file_inode(filp));
+#else
+    struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp));
+#endif
+
+    if (!ts_data->touch_analysis_support || !ts_data->ta_buf) {
+        FTS_ERROR("touch_analysis is disabled, or ta_buf is NULL");
+        return -EINVAL;
+    }
+
+    if (!(filp->f_flags & O_NONBLOCK)) {
+        ts_data->ta_flag = 1;
+        wait_event_interruptible(ts_data->ts_waitqueue, !ts_data->ta_flag);
+    }
+
+    read_num = (ts_data->ta_size < read_num) ? ts_data->ta_size : read_num;
+    if ((read_num > 0) && (copy_to_user(buff, ts_data->ta_buf, read_num))) {
+        FTS_ERROR("copy to user error");
+        return -EFAULT;
+    }
+
+    return read_num;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
+static const struct proc_ops fts_proc_fops = {
+    .proc_read   = fts_debug_read,
+    .proc_write  = fts_debug_write,
+};
+
+static const struct proc_ops fts_procta_fops = {
+    .proc_open = fts_ta_open,
+    .proc_release = fts_ta_release,
+    .proc_read = fts_ta_read,
+};
+#else
+static const struct file_operations fts_proc_fops = {
+    .owner  = THIS_MODULE,
+    .read   = fts_debug_read,
+    .write  = fts_debug_write,
+};
+
+static const struct file_operations fts_procta_fops = {
+    .open = fts_ta_open,
+    .release = fts_ta_release,
+    .read = fts_ta_read,
+};
+#endif
+
+int fts_create_apk_debug_channel(struct fts_ts_data *ts_data)
+{
+    struct ftxxxx_proc *proc = &ts_data->proc;
+    proc->proc_entry = proc_create_data(PROC_NAME, 0777, NULL, &fts_proc_fops, ts_data);
+    if (NULL == proc->proc_entry) {
+        FTS_ERROR("create proc entry fail");
+        return -ENOMEM;
+    }
+
+    ts_data->proc_ta.proc_entry = proc_create_data("fts_ta", 0777, NULL, \
+                                  &fts_procta_fops, ts_data);
+    if (!ts_data->proc_ta.proc_entry) {
+        FTS_ERROR("create proc_ta entry fail");
+        return -ENOMEM;
+    }
+
+    FTS_INFO("Create proc entry success!");
+    return 0;
+}
+
+void fts_release_apk_debug_channel(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+    if (ts_data->proc.proc_entry)
+        proc_remove(ts_data->proc.proc_entry);
+    if (ts_data->proc_ta.proc_entry)
+        proc_remove(ts_data->proc_ta.proc_entry);
+    FTS_FUNC_EXIT();
+}
+
+/************************************************************************
+ * sysfs interface
+ ***********************************************************************/
+/* fts_hw_reset interface */
+static ssize_t fts_hw_reset_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+    ssize_t count = 0;
+
+    mutex_lock(&input_dev->mutex);
+    fts_reset_proc(ts_data, false, 0);
+    count = snprintf(buf, PAGE_SIZE, "hw reset executed\n");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_hw_reset_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    return -EPERM;
+}
+
+/* fts_irq interface */
+static ssize_t fts_irq_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    ssize_t count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct irq_desc *desc = irq_to_desc(ts_data->irq);
+
+    count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth);
+
+    return count;
+}
+
+static ssize_t fts_irq_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_INFO("enable irq");
+        fts_irq_enable();
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_INFO("disable irq");
+        fts_irq_disable();
+    }
+    mutex_unlock(&input_dev->mutex);
+    return count;
+}
+
+/* fts_boot_mode interface */
+static ssize_t fts_bootmode_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    FTS_FUNC_ENTER();
+    mutex_lock(&input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_INFO("[EX-FUN]set to boot mode");
+        ts_data->fw_is_running = false;
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_INFO("[EX-FUN]set to fw mode");
+        ts_data->fw_is_running = true;
+    }
+    mutex_unlock(&input_dev->mutex);
+    FTS_FUNC_EXIT();
+
+    return count;
+}
+
+static ssize_t fts_bootmode_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    ssize_t count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    FTS_FUNC_ENTER();
+    mutex_lock(&input_dev->mutex);
+    if (true == ts_data->fw_is_running) {
+        count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n");
+    } else {
+        count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n");
+    }
+    mutex_unlock(&input_dev->mutex);
+    FTS_FUNC_EXIT();
+
+    return count;
+}
+
+/* fts_tpfwver interface */
+static ssize_t fts_tpfwver_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int ret = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+    ssize_t num_read_chars = 0;
+    u8 fwver = 0;
+
+    mutex_lock(&input_dev->mutex);
+
+    ret = fts_read_reg(FTS_REG_FW_VER, &fwver);
+    if ((ret < 0) || (fwver == 0xFF) || (fwver == 0x00))
+        num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n");
+    else
+        num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver);
+
+    mutex_unlock(&input_dev->mutex);
+    return num_read_chars;
+}
+
+static ssize_t fts_tpfwver_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    return -EPERM;
+}
+
+/* fts_rw_reg */
+static ssize_t fts_tprwreg_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count;
+    int i;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+
+    if (rw_op.len < 0) {
+        count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n");
+    } else if (rw_op.len == 1) {
+        if (RWREG_OP_READ == rw_op.type) {
+            if (rw_op.res == 0) {
+                count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val);
+            } else {
+                count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg,  rw_op.res);
+            }
+        } else {
+            if (rw_op.res == 0) {
+                count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg,  rw_op.val);
+            } else {
+                count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg,  rw_op.res);
+            }
+        }
+    } else {
+        if (RWREG_OP_READ == rw_op.type) {
+            count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len);
+            count += snprintf(buf + count, PAGE_SIZE, "Result: ");
+            if (rw_op.res) {
+                count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res);
+            } else {
+                if (rw_op.opbuf) {
+                    for (i = 0; i < rw_op.len; i++) {
+                        count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
+                    }
+                    count += snprintf(buf + count, PAGE_SIZE, "\n");
+                }
+            }
+        } else {
+            ;
+            count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1);
+            count += snprintf(buf + count, PAGE_SIZE, "Write Data: ");
+            if (rw_op.opbuf) {
+                for (i = 1; i < rw_op.len; i++) {
+                    count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
+                }
+                count += snprintf(buf + count, PAGE_SIZE, "\n");
+            }
+            if (rw_op.res) {
+                count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res);
+            } else {
+                count += snprintf(buf + count, PAGE_SIZE, "Result: success\n");
+            }
+        }
+        /*if (rw_op.opbuf) {
+            kfree(rw_op.opbuf);
+            rw_op.opbuf = NULL;
+        }*/
+    }
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static int shex_to_int(const char *hex_buf, int size)
+{
+    int i;
+    int base = 1;
+    int value = 0;
+    char single;
+
+    for (i = size - 1; i >= 0; i--) {
+        single = hex_buf[i];
+
+        if ((single >= '0') && (single <= '9')) {
+            value += (single - '0') * base;
+        } else if ((single >= 'a') && (single <= 'z')) {
+            value += (single - 'a' + 10) * base;
+        } else if ((single >= 'A') && (single <= 'Z')) {
+            value += (single - 'A' + 10) * base;
+        } else {
+            return -EINVAL;
+        }
+
+        base *= 16;
+    }
+
+    return value;
+}
+
+
+static u8 shex_to_u8(const char *hex_buf, int size)
+{
+    return (u8)shex_to_int(hex_buf, size);
+}
+/*
+ * Format buf:
+ * [0]: '0' write, '1' read(reserved)
+ * [1-2]: addr, hex
+ * [3-4]: length, hex
+ * [5-6]...[n-(n+1)]: data, hex
+ */
+static int fts_parse_buf(const char *buf, size_t cmd_len)
+{
+    int length;
+    int i;
+    char *tmpbuf;
+
+    rw_op.reg = shex_to_u8(buf + 1, 2);
+    length = shex_to_int(buf + 3, 2);
+
+    if (buf[0] == '1') {
+        rw_op.len = length;
+        rw_op.type = RWREG_OP_READ;
+        FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len);
+    } else {
+        if (cmd_len < (length * 2 + 5)) {
+            pr_err("data invalided!\n");
+            return -EINVAL;
+        }
+        FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length);
+
+        /* first byte is the register addr */
+        rw_op.type = RWREG_OP_WRITE;
+        rw_op.len = length + 1;
+    }
+
+    if (rw_op.len > 0) {
+        tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL);
+        if (!tmpbuf) {
+            FTS_ERROR("allocate memory failed!\n");
+            return -ENOMEM;
+        }
+
+        if (RWREG_OP_WRITE == rw_op.type) {
+            tmpbuf[0] = rw_op.reg & 0xFF;
+            FTS_DEBUG("write buffer: ");
+            for (i = 1; i < rw_op.len; i++) {
+                tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2);
+                FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF);
+            }
+        }
+        rw_op.opbuf = tmpbuf;
+    }
+
+    return rw_op.len;
+}
+
+static ssize_t fts_tprwreg_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+    ssize_t cmd_length = 0;
+
+    mutex_lock(&input_dev->mutex);
+    cmd_length = count - 1; //remove "\n"
+
+    if (rw_op.opbuf) {
+        kfree(rw_op.opbuf);
+        rw_op.opbuf = NULL;
+    }
+
+    FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf);
+    /* compatible old ops */
+    if (2 == cmd_length) {
+        rw_op.type = RWREG_OP_READ;
+        rw_op.len = 1;
+        rw_op.reg = shex_to_int(buf, 2);
+    } else if (4 == cmd_length) {
+        rw_op.type = RWREG_OP_WRITE;
+        rw_op.len = 1;
+        rw_op.reg = shex_to_int(buf, 2);
+        rw_op.val = shex_to_int(buf + 2, 2);
+    } else if (cmd_length < 5) {
+        FTS_ERROR("Invalid cmd buffer");
+        mutex_unlock(&input_dev->mutex);
+        return -EINVAL;
+    } else {
+        rw_op.len = fts_parse_buf(buf, cmd_length);
+    }
+
+    if (rw_op.len < 0) {
+        FTS_ERROR("cmd buffer error!");
+
+    } else {
+        if (RWREG_OP_READ == rw_op.type) {
+            if (rw_op.len == 1) {
+                u8 reg, val;
+                reg = rw_op.reg & 0xFF;
+                rw_op.res = fts_read_reg(reg, &val);
+                rw_op.val = val;
+            } else {
+                char reg;
+                reg = rw_op.reg & 0xFF;
+
+                rw_op.res = fts_read(&reg, 1, rw_op.opbuf, rw_op.len);
+            }
+
+            if (rw_op.res < 0) {
+                FTS_ERROR("Could not read 0x%02x", rw_op.reg);
+            } else {
+                FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len);
+                rw_op.res = 0;
+            }
+
+        } else {
+            if (rw_op.len == 1) {
+                u8 reg, val;
+                reg = rw_op.reg & 0xFF;
+                val = rw_op.val & 0xFF;
+                rw_op.res = fts_write_reg(reg, val);
+            } else {
+                rw_op.res = fts_write(rw_op.opbuf, rw_op.len);
+            }
+            if (rw_op.res < 0) {
+                FTS_ERROR("Could not write 0x%02x", rw_op.reg);
+
+            } else {
+                FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len);
+                rw_op.res = 0;
+            }
+        }
+    }
+
+    mutex_unlock(&input_dev->mutex);
+    return count;
+}
+
+/* fts_upgrade_bin interface */
+static ssize_t fts_fwupgradebin_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    return -EPERM;
+}
+
+static ssize_t fts_fwupgradebin_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    char fwname[FILE_NAME_LENGTH] = { 0 };
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) {
+        FTS_ERROR("fw bin name's length(%d) fail", (int)count);
+        return -EINVAL;
+    }
+    memset(fwname, 0, sizeof(fwname));
+    snprintf(fwname, FILE_NAME_LENGTH, "%s", buf);
+    fwname[count - 1] = '\0';
+
+    FTS_INFO("upgrade with bin file through sysfs node");
+    mutex_lock(&input_dev->mutex);
+    fts_upgrade_bin(fwname, 0);
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+/* fts_force_upgrade interface */
+static ssize_t fts_fwforceupg_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    return -EPERM;
+}
+
+static ssize_t fts_fwforceupg_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    char fwname[FILE_NAME_LENGTH];
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) {
+        FTS_ERROR("fw bin name's length(%d) fail", (int)count);
+        return -EINVAL;
+    }
+    memset(fwname, 0, sizeof(fwname));
+    snprintf(fwname, FILE_NAME_LENGTH, "%s", buf);
+    fwname[count - 1] = '\0';
+
+    FTS_INFO("force upgrade through sysfs node");
+    mutex_lock(&input_dev->mutex);
+    fts_upgrade_bin(fwname, 1);
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+/* fts_driver_info interface */
+static ssize_t fts_driverinfo_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct fts_ts_platform_data *pdata = ts_data->pdata;
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count += snprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n",
+                      FTS_DRIVER_VERSION);
+
+    count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n",
+                      pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max);
+
+    count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n",
+                      pdata->max_touch_number);
+
+    count += snprintf(buf + count, PAGE_SIZE,
+                      "reset gpio:%d,int gpio:%d,irq:%d\n",
+                      pdata->reset_gpio, pdata->irq_gpio, ts_data->irq);
+
+    count += snprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n",
+                      ts_data->ic_info.ids.chip_idh,
+                      ts_data->ic_info.ids.chip_idl);
+    mutex_unlock(&input_dev->mutex);
+    return count;
+}
+
+static ssize_t fts_driverinfo_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    return -EPERM;
+}
+
+/* fts_dump_reg interface */
+static ssize_t fts_dumpreg_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+
+    fts_read_reg(FTS_REG_POWER_MODE, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_FW_VER, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_LIC_VER, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_VENDOR_ID, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_GESTURE_EN, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_INT_CNT, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val);
+
+    fts_read_reg(FTS_REG_FLOW_WORK_CNT, &val);
+    count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val);
+
+    mutex_unlock(&input_dev->mutex);
+    return count;
+}
+
+static ssize_t fts_dumpreg_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    return -EPERM;
+}
+
+/* fts_dump_reg interface */
+static ssize_t fts_tpbuf_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    int i = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n");
+    for (i = 0; i < FTS_TOUCH_DATA_LEN; i++) {
+        count += snprintf(buf + count, PAGE_SIZE, "%02x ", ts_data->touch_buf[i]);
+    }
+    count += snprintf(buf + count, PAGE_SIZE, "\n");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_tpbuf_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    return -EPERM;
+}
+
+/* fts_log_level node */
+static ssize_t fts_log_level_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n",
+                      ts_data->log_level);
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_log_level_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    int value = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    FTS_FUNC_ENTER();
+    mutex_lock(&input_dev->mutex);
+    sscanf(buf, "%d", &value);
+    FTS_DEBUG("log level:%d->%d", ts_data->log_level, value);
+    ts_data->log_level = value;
+    mutex_unlock(&input_dev->mutex);
+    FTS_FUNC_EXIT();
+
+    return count;
+}
+
+/* fts_pen node */
+static ssize_t fts_pen_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count += snprintf(buf + count, PAGE_SIZE, "pen event:%s\n",
+                      ts_data->pen_etype ? "hover" : "default");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_pen_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    int value = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    FTS_FUNC_ENTER();
+    mutex_lock(&input_dev->mutex);
+    sscanf(buf, "%d", &value);
+    FTS_DEBUG("pen event:%d->%d", ts_data->pen_etype, value);
+    ts_data->pen_etype = value;
+    mutex_unlock(&input_dev->mutex);
+    FTS_FUNC_EXIT();
+
+    return count;
+}
+
+/* fts_touch_size node */
+static ssize_t fts_touchsize_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count += snprintf(buf + count, PAGE_SIZE, "touch size:%d\n", ts_data->touch_size);
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_touchsize_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    int value = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    FTS_FUNC_ENTER();
+    mutex_lock(&input_dev->mutex);
+    sscanf(buf, "%d", &value);
+    if ((value > 2) && (value < FTS_MAX_TOUCH_BUF)) {
+        FTS_DEBUG("touch size:%d->%d", ts_data->touch_size, value);
+        ts_data->touch_size = value;
+    } else
+        FTS_DEBUG("touch size:%d invalid", value);
+    mutex_unlock(&input_dev->mutex);
+    FTS_FUNC_EXIT();
+
+    return count;
+}
+
+/* fts_ta_mode node */
+static ssize_t fts_tamode_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count += snprintf(buf + count, PAGE_SIZE, "touch analysis:%s\n", \
+                      ts_data->touch_analysis_support ? "Enable" : "Disable");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_tamode_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    int value = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    FTS_FUNC_ENTER();
+    mutex_lock(&input_dev->mutex);
+    sscanf(buf, "%d", &value);
+    ts_data->touch_analysis_support = !!value;
+    FTS_DEBUG("set touch analysis:%d", ts_data->touch_analysis_support);
+    mutex_unlock(&input_dev->mutex);
+    FTS_FUNC_EXIT();
+
+    return count;
+}
+
+#if FTS_FOD_EN
+/* fts_fod_mode node */
+static ssize_t fts_fod_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(FTS_REG_FOD_MODE_EN, &val);
+    count = snprintf(buf, PAGE_SIZE, "FOD Mode:%s\n", ts_data->fod_mode ? "On" : "Off");
+    count += snprintf(buf + count, PAGE_SIZE, "Reg(0xCF)=%d\n", val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_fod_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        fts_fod_enable(ENABLE);
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        fts_fod_enable(DISABLE);
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+#endif
+
+/* get the fw version  example:cat fw_version */
+static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store);
+
+/* read and write register(s)
+*   All data type is **HEX**
+*   Single Byte:
+*       read:   echo 88 > rw_reg ---read register 0x88
+*       write:  echo 8807 > rw_reg ---write 0x07 into register 0x88
+*   Multi-bytes:
+*       [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex]
+*       rw-flag: 0, write; 1, read
+*       read:  echo 10005           > rw_reg ---read reg 0x00-0x05
+*       write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05
+*  Get result:
+*       cat rw_reg
+*/
+static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store);
+/*  upgrade from fw bin file   example:echo "*.bin" > fts_upgrade_bin */
+static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store);
+static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_store);
+static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store);
+static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store);
+static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store);
+static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store);
+static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store);
+static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store);
+static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store);
+static DEVICE_ATTR(fts_pen, S_IRUGO | S_IWUSR, fts_pen_show, fts_pen_store);
+static DEVICE_ATTR(fts_touch_size, S_IRUGO | S_IWUSR, fts_touchsize_show, fts_touchsize_store);
+static DEVICE_ATTR(fts_ta_mode, S_IRUGO | S_IWUSR, fts_tamode_show, fts_tamode_store);
+#if FTS_FOD_EN
+static DEVICE_ATTR(fts_fod_mode, S_IRUGO | S_IWUSR, fts_fod_show, fts_fod_store);
+#endif
+
+/* add your attr in here*/
+static struct attribute *fts_attributes[] = {
+    &dev_attr_fts_fw_version.attr,
+    &dev_attr_fts_rw_reg.attr,
+    &dev_attr_fts_dump_reg.attr,
+    &dev_attr_fts_upgrade_bin.attr,
+    &dev_attr_fts_force_upgrade.attr,
+    &dev_attr_fts_driver_info.attr,
+    &dev_attr_fts_hw_reset.attr,
+    &dev_attr_fts_irq.attr,
+    &dev_attr_fts_boot_mode.attr,
+    &dev_attr_fts_touch_point.attr,
+    &dev_attr_fts_log_level.attr,
+    &dev_attr_fts_pen.attr,
+    &dev_attr_fts_touch_size.attr,
+    &dev_attr_fts_ta_mode.attr,
+#if FTS_FOD_EN
+    &dev_attr_fts_fod_mode.attr,
+#endif
+    NULL
+};
+
+static struct attribute_group fts_attribute_group = {
+    .attrs = fts_attributes
+};
+
+int fts_create_sysfs(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group);
+    if (ret) {
+        FTS_ERROR("[EX]: sysfs_create_group() failed!!");
+        sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group);
+        return -ENOMEM;
+    } else {
+        FTS_INFO("[EX]: sysfs_create_group() succeeded!!");
+    }
+
+    return ret;
+}
+
+int fts_remove_sysfs(struct fts_ts_data *ts_data)
+{
+    sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group);
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_mode.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_mode.c
new file mode 100755
index 0000000..1abd6e1
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_ex_mode.c
@@ -0,0 +1,390 @@
+/*
+ *
+ * FocalTech ftxxxx TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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: Focaltech Driver Team
+*
+* 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
+*****************************************************************************/
+enum _ex_mode {
+    MODE_GLOVE = 0,
+    MODE_COVER,
+    MODE_CHARGER,
+    MODE_EARPHONE,
+    MODE_EDGEPALM
+};
+
+/*****************************************************************************
+* 4.Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* 5.Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* 6.Static function prototypes
+*******************************************************************************/
+static int fts_ex_mode_set_reg(u8 mode_regaddr, u8 mode_regval)
+{
+    int i = 0;
+    u8 val = 0xFF;
+
+    for (i = 0; i < FTS_MAX_RETRIES_WRITEREG; i++) {
+        fts_read_reg(mode_regaddr, &val);
+        if (val == mode_regval)
+            break;
+        fts_write_reg(mode_regaddr, mode_regval);
+        fts_msleep(1);
+    }
+
+    if (i >= FTS_MAX_RETRIES_WRITEREG) {
+        FTS_ERROR("set mode(%x) to %x failed,read val:%x", mode_regaddr, mode_regval, val);
+        return -EIO;
+    } else if (i > 0) {
+        FTS_INFO("set mode(%x) to %x successfully", mode_regaddr, mode_regval);
+    }
+    return 0;
+}
+
+static int fts_ex_mode_switch(enum _ex_mode mode, int value)
+{
+    int ret = 0;
+
+    switch (mode) {
+    case MODE_GLOVE:
+        ret = fts_ex_mode_set_reg(FTS_REG_GLOVE_MODE_EN, (value ? 0x01 : 0x00));
+        if (ret) FTS_ERROR("Set MODE_GLOVE to %d failed", value);
+        break;
+    case MODE_COVER:
+        ret = fts_ex_mode_set_reg(FTS_REG_COVER_MODE_EN, (value ? 0x01 : 0x00));
+        if (ret) FTS_ERROR("Set MODE_COVER to %d failed", value);
+        break;
+    case MODE_CHARGER:
+        ret = fts_ex_mode_set_reg(FTS_REG_CHARGER_MODE_EN, (value ? 0x01 : 0x00));
+        if (ret) FTS_ERROR("Set MODE_CHARGER to %d failed", value);
+        break;
+    case MODE_EARPHONE:
+        ret = fts_ex_mode_set_reg(FTS_REG_EARPHONE_MODE_EN, (value ? 0x01 : 0x00));
+        if (ret) FTS_ERROR("Set MODE_EARPHONE to %d failed", value);
+        break;
+    case MODE_EDGEPALM:
+        /* FW defines the following values: 0:vertical, 1:horizontal, USB on the right,
+         *                                  2:horizontal, USB on the left
+         * If host set the value not defined above, you should have a transition.
+         */
+        ret = fts_ex_mode_set_reg(FTS_REG_EDGEPALM_MODE_EN, (u8)value);
+        if (ret) FTS_ERROR("Set MODE_EDGEPALM to %d failed", value);
+        break;
+    default:
+        FTS_ERROR("mode(%d) unsupport", mode);
+        ret = -EINVAL;
+        break;
+    }
+
+    return ret;
+}
+
+static ssize_t fts_glove_mode_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 reg_addr = FTS_REG_GLOVE_MODE_EN;
+    u8 reg_val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(reg_addr, &reg_val);
+    count = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n",
+                     ts_data->glove_mode ? "On" : "Off");
+    count += snprintf(buf + count, PAGE_SIZE, "Glove Reg:0x%02x,val:%d\n", reg_addr, reg_val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_glove_mode_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_DEBUG("enter glove mode");
+        ts_data->glove_mode = ENABLE;
+        fts_ex_mode_switch(MODE_GLOVE, ENABLE);
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("exit glove mode");
+        ts_data->glove_mode = DISABLE;
+        fts_ex_mode_switch(MODE_GLOVE, DISABLE);
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+    return count;
+}
+
+
+static ssize_t fts_cover_mode_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 reg_addr = FTS_REG_COVER_MODE_EN;
+    u8 reg_val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(reg_addr, &reg_val);
+    count = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n",
+                     ts_data->cover_mode ? "On" : "Off");
+    count += snprintf(buf + count, PAGE_SIZE, "Cover Reg:0x%02x,val:%d\n", reg_addr, reg_val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_cover_mode_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_DEBUG("enter cover mode");
+        ts_data->cover_mode = ENABLE;
+        fts_ex_mode_switch(MODE_COVER, ENABLE);
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("exit cover mode");
+        ts_data->cover_mode = DISABLE;
+        fts_ex_mode_switch(MODE_COVER, DISABLE);
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+    return count;
+}
+
+static ssize_t fts_charger_mode_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 reg_addr = FTS_REG_CHARGER_MODE_EN;
+    u8 reg_val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(reg_addr, &reg_val);
+    count = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n",
+                     ts_data->charger_mode ? "On" : "Off");
+    count += snprintf(buf + count, PAGE_SIZE, "Charger Reg:0x%02x,val:%d\n", reg_addr, reg_val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_charger_mode_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_DEBUG("enter charger mode");
+        ts_data->charger_mode = ENABLE;
+        fts_ex_mode_switch(MODE_CHARGER, ENABLE);
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("exit charger mode");
+        ts_data->charger_mode = DISABLE;
+        fts_ex_mode_switch(MODE_CHARGER, DISABLE);
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+    return count;
+}
+
+/* sysfs node: fts_earphone_mode */
+static ssize_t fts_earphone_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 reg_addr = FTS_REG_EARPHONE_MODE_EN;
+    u8 reg_val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(reg_addr, &reg_val);
+    count = snprintf(buf + count, PAGE_SIZE, "Earphone Mode:%s\n",
+                     ts_data->earphone_mode ? "On" : "Off");
+    count += snprintf(buf + count, PAGE_SIZE, "Earphone Reg:0x%02x,val:%d\n", reg_addr, reg_val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_earphone_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_DEBUG("enter earphone mode");
+        ts_data->earphone_mode = ENABLE;
+        fts_ex_mode_switch(MODE_EARPHONE, ENABLE);
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("exit earphone mode");
+        ts_data->earphone_mode = DISABLE;
+        fts_ex_mode_switch(MODE_EARPHONE, DISABLE);
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+    return count;
+}
+
+/* sysfs node: fts_edgepalm_mode */
+static ssize_t fts_edgepalm_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 reg_addr = FTS_REG_EDGEPALM_MODE_EN;
+    u8 reg_val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(reg_addr, &reg_val);
+    count = snprintf(buf + count, PAGE_SIZE, "Edgepalm Mode:%s,value:%d\n",
+                     ts_data->edgepalm_mode ? "On" : "Off", ts_data->edgepalm_value);
+    count += snprintf(buf + count, PAGE_SIZE, "Edgepalm Reg:0x%02x,val:%d\n", reg_addr, reg_val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_edgepalm_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    int value = 0;
+    int n = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    n = sscanf(buf, "%d", &value);
+    if (n == 1) {
+        ts_data->edgepalm_value = value;
+        ts_data->edgepalm_mode = !!value;
+        fts_ex_mode_switch(MODE_EDGEPALM, value);
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+    return count;
+}
+
+
+/* read and write charger mode
+ * read example: cat fts_glove_mode        ---read  glove mode
+ * write example:echo 1 > fts_glove_mode   ---write glove mode to 01
+ */
+static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR, fts_glove_mode_show, fts_glove_mode_store);
+static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR, fts_cover_mode_show, fts_cover_mode_store);
+static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR, fts_charger_mode_show, fts_charger_mode_store);
+static DEVICE_ATTR(fts_earphone_mode, S_IRUGO | S_IWUSR, fts_earphone_show, fts_earphone_store);
+static DEVICE_ATTR(fts_edgepalm_mode, S_IRUGO | S_IWUSR, fts_edgepalm_show, fts_edgepalm_store);
+
+static struct attribute *fts_touch_mode_attrs[] = {
+    &dev_attr_fts_glove_mode.attr,
+    &dev_attr_fts_cover_mode.attr,
+    &dev_attr_fts_charger_mode.attr,
+    &dev_attr_fts_earphone_mode.attr,
+    &dev_attr_fts_edgepalm_mode.attr,
+    NULL,
+};
+
+static struct attribute_group fts_touch_mode_group = {
+    .attrs = fts_touch_mode_attrs,
+};
+
+int fts_ex_mode_recovery(struct fts_ts_data *ts_data)
+{
+    if (ts_data->glove_mode) {
+        fts_ex_mode_switch(MODE_GLOVE, ENABLE);
+    }
+
+    if (ts_data->cover_mode) {
+        fts_ex_mode_switch(MODE_COVER, ENABLE);
+    }
+
+    if (ts_data->charger_mode) {
+        fts_ex_mode_switch(MODE_CHARGER, ENABLE);
+    }
+
+    if (ts_data->earphone_mode) {
+        fts_ex_mode_switch(MODE_EARPHONE, ENABLE);
+    }
+
+    if (ts_data->edgepalm_mode) {
+        fts_ex_mode_switch(MODE_EDGEPALM, ts_data->edgepalm_value);
+    }
+
+    return 0;
+}
+
+int fts_ex_mode_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    ts_data->glove_mode = DISABLE;
+    ts_data->cover_mode = DISABLE;
+    ts_data->charger_mode = DISABLE;
+    ts_data->earphone_mode = DISABLE;
+    ts_data->edgepalm_mode = DISABLE;
+
+    ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+    if (ret < 0) {
+        FTS_ERROR("create sysfs(ex_mode) fail");
+        sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+        return ret;
+    } else {
+        FTS_DEBUG("create sysfs(ex_mode) successfully");
+    }
+
+    return 0;
+}
+
+int fts_ex_mode_exit(struct fts_ts_data *ts_data)
+{
+    sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.c
new file mode 100755
index 0000000..b10ef53
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.c
@@ -0,0 +1,2273 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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_flash.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#include "focaltech_flash.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_FW_REQUEST_SUPPORT                      1
+/* Example: focaltech_ts_fw_tianma.bin */
+#define FTS_FW_NAME_PREX_WITH_REQUEST               "focaltech_ts_fw_"
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+u8 fw_file[] = {
+#include FTS_UPGRADE_FW_FILE
+};
+
+u8 fw_file2[] = {
+#include FTS_UPGRADE_FW2_FILE
+};
+
+u8 fw_file3[] = {
+#include FTS_UPGRADE_FW3_FILE
+};
+
+struct upgrade_module module_list[] = {
+    {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)},
+    {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)},
+    {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)},
+};
+
+struct upgrade_func *upgrade_func_list[] = {
+    &upgrade_func_ft8756,
+};
+
+struct fts_upgrade *fwupgrade;
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static bool fts_fwupg_check_state(
+    struct fts_upgrade *upg, enum FW_STATUS rstate);
+
+/************************************************************************
+* Name: fts_fwupg_get_boot_state
+* Brief: read boot id(rom/pram/bootloader), confirm boot environment
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_fwupg_get_boot_state(
+    struct fts_upgrade *upg,
+    enum FW_STATUS *fw_sts)
+{
+    int ret = 0;
+    u8 cmd[4] = { 0 };
+    u32 cmd_len = 0;
+    u8 val[2] = { 0 };
+    struct ft_chip_t *ids = NULL;
+
+    FTS_INFO("**********read boot id**********");
+    if ((!upg) || (!upg->func) || (!upg->ts_data) || (!fw_sts)) {
+        FTS_ERROR("upg/func/ts_data/fw_sts is null");
+        return -EINVAL;
+    }
+
+    if (upg->func->hid_supported)
+        fts_hid2std(0);
+
+    cmd[0] = FTS_CMD_START1;
+    cmd[1] = FTS_CMD_START2;
+    if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)
+        cmd_len = 1;
+    else
+        cmd_len = 2;
+    ret = fts_write(cmd, cmd_len);
+    if (ret < 0) {
+        FTS_ERROR("write 55 cmd fail");
+        return ret;
+    }
+
+    fts_msleep(FTS_CMD_START_DELAY);
+    cmd[0] = FTS_CMD_READ_ID;
+    cmd[1] = cmd[2] = cmd[3] = 0x00;
+    if (upg->ts_data->ic_info.is_incell ||
+        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+        cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
+    else
+        cmd_len = FTS_CMD_READ_ID_LEN;
+    ret = fts_read(cmd, cmd_len, val, 2);
+    if (ret < 0) {
+        FTS_ERROR("write 90 cmd fail");
+        return ret;
+    }
+    FTS_INFO("read boot id:0x%02x%02x", val[0], val[1]);
+
+    ids = &upg->ts_data->ic_info.ids;
+    if ((val[0] == ids->rom_idh) && (val[1] == ids->rom_idl)) {
+        FTS_INFO("tp run in romboot");
+        *fw_sts = FTS_RUN_IN_ROM;
+    } else if ((val[0] == ids->pb_idh) && (val[1] == ids->pb_idl)) {
+        FTS_INFO("tp run in pramboot");
+        *fw_sts = FTS_RUN_IN_PRAM;
+    } else if ((val[0] == ids->bl_idh) && (val[1] == ids->bl_idl)) {
+        FTS_INFO("tp run in bootloader");
+        *fw_sts = FTS_RUN_IN_BOOTLOADER;
+    }
+
+    return 0;
+}
+
+static int fts_fwupg_reset_to_boot(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    u8 reg = FTS_REG_UPGRADE;
+
+    FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment");
+    if (upg && upg->func && upg->func->is_reset_register_BC) {
+        reg = FTS_REG_UPGRADE2;
+    }
+
+    ret = fts_write_reg(reg, FTS_UPGRADE_AA);
+    if (ret < 0) {
+        FTS_ERROR("write FC=0xAA fail");
+        return ret;
+    }
+    fts_msleep(FTS_DELAY_UPGRADE_AA);
+
+    ret = fts_write_reg(reg, FTS_UPGRADE_55);
+    if (ret < 0) {
+        FTS_ERROR("write FC=0x55 fail");
+        return ret;
+    }
+
+    fts_msleep(FTS_DELAY_UPGRADE_RESET);
+    return 0;
+}
+
+/************************************************************************
+* Name: fts_fwupg_reset_to_romboot
+* Brief: reset to romboot, to load pramboot
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_fwupg_reset_to_romboot(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    int i = 0;
+    u8 cmd = FTS_CMD_RESET;
+    enum FW_STATUS state = FTS_RUN_IN_ERROR;
+
+    ret = fts_write(&cmd, 1);
+    if (ret < 0) {
+        FTS_ERROR("pram/rom/bootloader reset cmd write fail");
+        return ret;
+    }
+    mdelay(10);
+
+    for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+        ret = fts_fwupg_get_boot_state(upg, &state);
+        if (FTS_RUN_IN_ROM == state)
+            break;
+        fts_msleep(5);
+    }
+    if (i >= FTS_UPGRADE_LOOP) {
+        FTS_ERROR("reset to romboot fail");
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static u16 fts_crc16_calc_host(u8 *pbuf, u32 length)
+{
+    u16 ecc = 0;
+    u32 i = 0;
+    u32 j = 0;
+
+    for ( i = 0; i < length; i += 2 ) {
+        ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1]));
+        for (j = 0; j < 16; j ++) {
+            if (ecc & 0x01)
+                ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF);
+            else
+                ecc >>= 1;
+        }
+    }
+
+    return ecc;
+}
+
+static u16 fts_pram_ecc_calc_host(u8 *pbuf, u32 length)
+{
+    return fts_crc16_calc_host(pbuf, length);
+}
+
+static int fts_pram_ecc_cal_algo(
+    struct fts_upgrade *upg,
+    u32 start_addr,
+    u32 ecc_length)
+{
+    int ret = 0;
+    int i = 0;
+    int ecc = 0;
+    u8 val[2] = { 0 };
+    u8 tmp = 0;
+    u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 };
+
+    FTS_INFO("read out pramboot checksum");
+    if ((!upg) || (!upg->func)) {
+        FTS_ERROR("upg/func is null");
+        return -EINVAL;
+    }
+
+    cmd[0] = FTS_ROMBOOT_CMD_ECC;
+    cmd[1] = BYTE_OFF_16(start_addr);
+    cmd[2] = BYTE_OFF_8(start_addr);
+    cmd[3] = BYTE_OFF_0(start_addr);
+    cmd[4] = BYTE_OFF_16(ecc_length);
+    cmd[5] = BYTE_OFF_8(ecc_length);
+    cmd[6] = BYTE_OFF_0(ecc_length);
+    ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN);
+    if (ret < 0) {
+        FTS_ERROR("write pramboot ecc cal cmd fail");
+        return ret;
+    }
+
+    cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH;
+    for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) {
+        fts_msleep(1);
+        ret = fts_read(cmd, 1, val, 1);
+        if (ret < 0) {
+            FTS_ERROR("ecc_finish read cmd fail");
+            return ret;
+        }
+        if (upg->func->new_return_value_from_ic ||
+            (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+            tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5;
+        } else {
+            tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_00;
+        }
+        if (tmp == val[0])
+            break;
+    }
+    if (i >= FTS_ECC_FINISH_TIMEOUT) {
+        FTS_ERROR("wait ecc finish fail");
+        return -EIO;
+    }
+
+    cmd[0] = FTS_ROMBOOT_CMD_ECC_READ;
+    ret = fts_read(cmd, 1, val, 2);
+    if (ret < 0) {
+        FTS_ERROR("read pramboot ecc fail");
+        return ret;
+    }
+
+    ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF;
+    return ecc;
+}
+
+static int fts_pram_ecc_cal_xor(void)
+{
+    int ret = 0;
+    u8 reg_val = 0;
+
+    FTS_INFO("read out pramboot checksum");
+
+    ret = fts_read_reg(FTS_ROMBOOT_CMD_ECC, &reg_val);
+    if (ret < 0) {
+        FTS_ERROR("read pramboot ecc fail");
+        return ret;
+    }
+
+    return (int)reg_val;
+}
+
+static int fts_pram_ecc_cal(struct fts_upgrade *upg, u32 saddr, u32 len)
+{
+    if ((!upg) || (!upg->func)) {
+        FTS_ERROR("upg/func is null");
+        return -EINVAL;
+    }
+
+    if ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) ||
+        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+        return fts_pram_ecc_cal_algo(upg, saddr, len);
+    } else {
+        return fts_pram_ecc_cal_xor();
+    }
+}
+
+static int fts_pram_write_buf(struct fts_upgrade *upg, u8 *buf, u32 len)
+{
+    int ret = 0;
+    u32 i = 0;
+    u32 j = 0;
+    u32 offset = 0;
+    u32 remainder = 0;
+    u32 packet_number;
+    u32 packet_len = 0;
+    u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
+    u8 ecc_tmp = 0;
+    int ecc_in_host = 0;
+    u32 cmdlen = 0;
+
+    FTS_INFO("write pramboot to pram");
+    if ((!upg) || (!upg->func) || !buf) {
+        FTS_ERROR("upg/func/buf is null");
+        return -EINVAL;
+    }
+
+    FTS_INFO("pramboot len=%d", len);
+    if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) {
+        FTS_ERROR("pramboot length(%d) fail", len);
+        return -EINVAL;
+    }
+
+    packet_number = len / FTS_FLASH_PACKET_LENGTH;
+    remainder = len % FTS_FLASH_PACKET_LENGTH;
+    if (remainder > 0)
+        packet_number++;
+    packet_len = FTS_FLASH_PACKET_LENGTH;
+
+    for (i = 0; i < packet_number; i++) {
+        offset = i * FTS_FLASH_PACKET_LENGTH;
+        /* last packet */
+        if ((i == (packet_number - 1)) && remainder)
+            packet_len = remainder;
+
+        if ((upg->ts_data->bus_type == BUS_TYPE_SPI) && (upg->ts_data->bus_ver == BUS_VER_V2)) {
+            packet_buf[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR;
+            packet_buf[1] = BYTE_OFF_16(offset);
+            packet_buf[2] = BYTE_OFF_8(offset);
+            packet_buf[3] = BYTE_OFF_0(offset);
+
+            ret = fts_write(packet_buf, FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN);
+            if (ret < 0) {
+                FTS_ERROR("pramboot set write address(%d) fail", i);
+                return ret;
+            }
+
+            packet_buf[0] = FTS_ROMBOOT_CMD_WRITE;
+            cmdlen = 1;
+        } else {
+            packet_buf[0] = FTS_ROMBOOT_CMD_WRITE;
+            packet_buf[1] = BYTE_OFF_16(offset);
+            packet_buf[2] = BYTE_OFF_8(offset);
+            packet_buf[3] = BYTE_OFF_0(offset);
+
+            packet_buf[4] = BYTE_OFF_8(packet_len);
+            packet_buf[5] = BYTE_OFF_0(packet_len);
+            cmdlen = 6;
+        }
+
+        for (j = 0; j < packet_len; j++) {
+            packet_buf[cmdlen + j] = buf[offset + j];
+            if (ECC_CHECK_MODE_XOR == upg->func->pram_ecc_check_mode) {
+                ecc_tmp ^= packet_buf[cmdlen + j];
+            }
+        }
+
+        ret = fts_write(packet_buf, packet_len + cmdlen);
+        if (ret < 0) {
+            FTS_ERROR("pramboot write data(%d) fail", i);
+            return ret;
+        }
+    }
+
+    if ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) ||
+        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+        ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len);
+    } else {
+        ecc_in_host = (int)ecc_tmp;
+    }
+
+    return ecc_in_host;
+}
+
+static int fts_pram_start(void)
+{
+    u8 cmd = FTS_ROMBOOT_CMD_START_APP;
+    int ret = 0;
+
+    FTS_INFO("remap to start pramboot");
+
+    ret = fts_write(&cmd, 1);
+    if (ret < 0) {
+        FTS_ERROR("write start pram cmd fail");
+        return ret;
+    }
+    fts_msleep(FTS_DELAY_PRAMBOOT_START);
+
+    return 0;
+}
+
+static int fts_pram_write_remap(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    int ecc_in_host = 0;
+    int ecc_in_tp = 0;
+    u8 *pb_buf = NULL;
+    u32 pb_len = 0;
+
+    FTS_INFO("write pram and remap");
+    if (!upg || !upg->func || !upg->func->pramboot) {
+        FTS_ERROR("upg/func/pramboot is null");
+        return -EINVAL;
+    }
+
+    if (upg->func->pb_length < FTS_MIN_LEN) {
+        FTS_ERROR("pramboot length(%d) fail", upg->func->pb_length);
+        return -EINVAL;
+    }
+
+    pb_buf = upg->func->pramboot;
+    pb_len = upg->func->pb_length;
+
+    /* write pramboot to pram */
+    ecc_in_host = fts_pram_write_buf(upg, pb_buf, pb_len);
+    if (ecc_in_host < 0) {
+        FTS_ERROR( "write pramboot fail");
+        return ecc_in_host;
+    }
+
+    /* read out checksum */
+    ecc_in_tp = fts_pram_ecc_cal(upg, 0, pb_len);
+    if (ecc_in_tp < 0) {
+        FTS_ERROR( "read pramboot ecc fail");
+        return ecc_in_tp;
+    }
+
+    FTS_INFO("pram ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+    /*  pramboot checksum != fw checksum, upgrade fail */
+    if (ecc_in_host != ecc_in_tp) {
+        FTS_ERROR("pramboot ecc check fail");
+        return -EIO;
+    }
+
+    /*start pram*/
+    ret = fts_pram_start();
+    if (ret < 0) {
+        FTS_ERROR("pram start fail");
+        return ret;
+    }
+
+    return 0;
+}
+
+static int fts_pram_init(void)
+{
+    int ret = 0;
+    u8 reg_val = 0;
+    u8 wbuf[3] = { 0 };
+
+    FTS_INFO("pramboot initialization");
+
+    /* read flash ID */
+    wbuf[0] = FTS_CMD_FLASH_TYPE;
+    ret = fts_read(wbuf, 1, &reg_val, 1);
+    if (ret < 0) {
+        FTS_ERROR("read flash type fail");
+        return ret;
+    }
+
+    /* set flash clk */
+    wbuf[0] = FTS_CMD_FLASH_TYPE;
+    wbuf[1] = reg_val;
+    wbuf[2] = 0x00;
+    ret = fts_write(wbuf, 3);
+    if (ret < 0) {
+        FTS_ERROR("write flash type fail");
+        return ret;
+    }
+
+    return 0;
+}
+
+static int fts_pram_write_init(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    bool state = 0;
+    enum FW_STATUS status = FTS_RUN_IN_ERROR;
+
+    FTS_INFO("**********pram write and init**********");
+    if ((NULL == upg) || (NULL == upg->func)) {
+        FTS_ERROR("upgrade/func is null");
+        return -EINVAL;
+    }
+
+    if (!upg->func->pramboot_supported) {
+        FTS_ERROR("ic not support pram");
+        return -EINVAL;
+    }
+
+    FTS_DEBUG("check whether tp is in romboot or not ");
+    /* need reset to romboot when non-romboot state */
+    ret = fts_fwupg_get_boot_state(upg, &status);
+    if (status != FTS_RUN_IN_ROM) {
+        if (FTS_RUN_IN_PRAM == status) {
+            FTS_INFO("tp is in pramboot, need send reset cmd before upgrade");
+            ret = fts_pram_init();
+            if (ret < 0) {
+                FTS_ERROR("pramboot(before) init fail");
+                return ret;
+            }
+        }
+
+        FTS_INFO("tp isn't in romboot, need send reset to romboot");
+        ret = fts_fwupg_reset_to_romboot(upg);
+        if (ret < 0) {
+            FTS_ERROR("reset to romboot fail");
+            return ret;
+        }
+    }
+
+    /* check the length of the pramboot */
+    ret = fts_pram_write_remap(upg);
+    if (ret < 0) {
+        FTS_ERROR("pram write fail, ret=%d", ret);
+        return ret;
+    }
+
+    FTS_DEBUG("after write pramboot, confirm run in pramboot");
+    state = fts_fwupg_check_state(upg, FTS_RUN_IN_PRAM);
+    if (!state) {
+        FTS_ERROR("not in pramboot");
+        return -EIO;
+    }
+
+    ret = fts_pram_init();
+    if (ret < 0) {
+        FTS_ERROR("pramboot init fail");
+        return ret;
+    }
+
+    return 0;
+}
+
+static bool fts_fwupg_check_fw_valid(void)
+{
+    int ret = 0;
+
+    ret = fts_wait_tp_to_valid();
+    if (ret < 0) {
+        FTS_INFO("tp fw invaild");
+        return false;
+    }
+
+    FTS_INFO("tp fw vaild");
+    return true;
+}
+
+/************************************************************************
+* Name: fts_fwupg_check_state
+* Brief: confirm tp run in which mode: romboot/pramboot/bootloader
+* Input:
+* Output:
+* Return: return true if state is match, otherwise return false
+***********************************************************************/
+static bool fts_fwupg_check_state(
+    struct fts_upgrade *upg, enum FW_STATUS rstate)
+{
+    int ret = 0;
+    int i = 0;
+    enum FW_STATUS cstate = FTS_RUN_IN_ERROR;
+
+    for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+        ret = fts_fwupg_get_boot_state(upg, &cstate);
+        /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */
+        if (cstate == rstate)
+            return true;
+        fts_msleep(FTS_DELAY_READ_ID);
+
+        /* try to hardware reset to boot mode */
+        if ((rstate == FTS_RUN_IN_BOOTLOADER) || (rstate == FTS_RUN_IN_ROM)) {
+            if (i >= FTS_UPGRADE_RESET_LOOP) {
+                fts_reset_proc(upg->ts_data, true, 0);
+                mdelay(FTS_CMD_START_DELAY + ((i - FTS_UPGRADE_RESET_LOOP) * 4));
+                if (upg->func->hid_supported) {
+                    fts_hid2std(1);
+                    fts_write_reg(0x55, 0xAA);
+                    fts_msleep(FTS_CMD_START_DELAY);
+                    fts_hid2std(1);
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+/************************************************************************
+* Name: fts_fwupg_reset_in_boot
+* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+int fts_fwupg_reset_in_boot(void)
+{
+    int ret = 0;
+    u8 cmd = FTS_CMD_RESET;
+
+    FTS_INFO("reset in boot environment");
+    ret = fts_write(&cmd, 1);
+    if (ret < 0) {
+        FTS_ERROR("pram/rom/bootloader reset cmd write fail");
+        return ret;
+    }
+
+    fts_msleep(FTS_DELAY_UPGRADE_RESET);
+    return 0;
+}
+
+/************************************************************************
+* Name: fts_fwupg_enter_into_boot
+* Brief: enter into boot environment, ready for upgrade
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+int fts_fwupg_enter_into_boot(void)
+{
+    int ret = 0;
+    bool fwvalid = false;
+    bool state = false;
+    struct fts_upgrade *upg = fwupgrade;
+
+    FTS_INFO("***********enter into pramboot/bootloader***********");
+    if ((!upg) || (NULL == upg->func)) {
+        FTS_ERROR("upgrade/func is null");
+        return -EINVAL;
+    }
+
+    fwvalid = fts_fwupg_check_fw_valid();
+    if (fwvalid) {
+        ret = fts_fwupg_reset_to_boot(upg);
+        if (ret < 0) {
+            FTS_ERROR("enter into romboot/bootloader fail");
+            return ret;
+        }
+    } else if (upg->func->read_boot_id_need_reset) {
+        ret = fts_fwupg_reset_in_boot();
+        if (ret < 0) {
+            FTS_ERROR("reset before read boot id when fw invalid fail");
+            return ret;
+        }
+    }
+
+    if (upg->func->pramboot_supported) {
+        FTS_INFO("pram supported, write pramboot and init");
+        /* pramboot */
+        if (upg->func->write_pramboot_private)
+            ret = upg->func->write_pramboot_private();
+        else
+            ret = fts_pram_write_init(upg);
+        if (ret < 0) {
+            FTS_ERROR("pram write_init fail");
+            return ret;
+        }
+    } else {
+        FTS_DEBUG("pram not supported, confirm in bootloader");
+        /* bootloader */
+        state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER);
+        if (!state) {
+            FTS_ERROR("fw not in bootloader, fail");
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************
+* Name: fts_fwupg_hwrst_to_boot
+* Brief: Hardware reset to enter into boot environment, ready for upgrade
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+int fts_fwupg_hwrst_to_boot(void)
+{
+    int ret = 0;
+    int i = 0;
+    bool state = false;
+    enum FW_STATUS cstate = FTS_RUN_IN_ERROR;
+    struct fts_upgrade *upg = fwupgrade;
+
+    FTS_INFO("***********enter into pramboot/bootloader***********");
+    if ((!upg) || (!upg->func) || (!upg->ts_data)) {
+        FTS_ERROR("upgrade/func/ts_data is null");
+        return -EINVAL;
+    }
+
+    FTS_INFO("enter into boot environment");
+    for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+        /* hardware tp reset to boot */
+        fts_reset_proc(upg->ts_data, true, 0);
+        mdelay(FTS_CMD_START_DELAY + i * 2);
+        fts_fwupg_get_boot_state(upg, &cstate);
+        if (cstate != FTS_RUN_IN_ERROR) {
+            FTS_INFO("get boot state:%d", cstate);
+            break;
+        }
+    }
+
+    if (upg->func->pramboot_supported) {
+        FTS_INFO("pram supported, write pramboot and init");
+        /* pramboot */
+        if (upg->func->write_pramboot_private)
+            ret = upg->func->write_pramboot_private();
+        else
+            ret = fts_pram_write_init(upg);
+        if (ret < 0) {
+            FTS_ERROR("pram write_init fail");
+            return ret;
+        }
+    } else {
+        FTS_DEBUG("pram not supported, confirm in bootloader");
+        /* bootloader */
+        state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER);
+        if (!state) {
+            FTS_ERROR("fw not in bootloader, fail");
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_check_flash_status
+ * Brief: read status from tp
+ * Input: flash_status: correct value from tp
+ *        retries: read retry times
+ *        retries_delay: retry delay
+ * Output:
+ * Return: return true if flash status check pass, otherwise return false
+***********************************************************************/
+static bool fts_fwupg_check_flash_status(
+    u16 flash_status,
+    int retries,
+    int retries_delay)
+{
+    int ret = 0;
+    int i = 0;
+    u8 cmd = 0;
+    u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+    u16 read_status = 0;
+
+    for (i = 0; i < retries; i++) {
+        cmd = FTS_CMD_FLASH_STATUS;
+        ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN);
+        read_status = (((u16)val[0]) << 8) + val[1];
+        if (flash_status == read_status) {
+            /* FTS_DEBUG("[UPGRADE]flash status ok"); */
+            return true;
+        }
+        /* FTS_DEBUG("flash status fail,ok:%04x read:%04x, retries:%d", flash_status, read_status, i); */
+        fts_msleep(retries_delay);
+    }
+
+    FTS_ERROR("flash status fail,ok:%04x,read:%04x,retries:%d",
+              flash_status, read_status, i);
+    return false;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_erase
+ * Brief: erase flash area
+ * Input: delay - delay after erase
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_erase(u32 delay)
+{
+    int ret = 0;
+    u8 cmd = 0;
+    bool flag = false;
+
+    FTS_INFO("**********erase now**********");
+
+    /*send to erase flash*/
+    cmd = FTS_CMD_ERASE_APP;
+    ret = fts_write(&cmd, 1);
+    if (ret < 0) {
+        FTS_ERROR("erase cmd fail");
+        return ret;
+    }
+    fts_msleep(delay);
+
+    /* read status 0xF0AA: success */
+    flag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ERASE_OK,
+                                        FTS_RETRIES_REASE,
+                                        FTS_RETRIES_DELAY_REASE);
+    if (!flag) {
+        FTS_ERROR("ecc flash status check fail");
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_ecc_cal
+ * Brief: calculate and get ecc from tp
+ * Input: saddr - start address need calculate ecc
+ *        len - length need calculate ecc
+ * Output:
+ * Return: return data ecc of tp if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_ecc_cal(u32 saddr, u32 len)
+{
+    int ret = 0;
+    u32 i = 0;
+    u32 cmdlen = FTS_CMD_ECC_CAL_LEN;
+    u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 };
+    u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+    int ecc = 0;
+    int ecc_len = 0;
+    u32 packet_num = 0;
+    u32 packet_len = 0;
+    u32 remainder = 0;
+    u32 addr = 0;
+    u32 offset = 0;
+    bool bflag = false;
+    struct fts_upgrade *upg = fwupgrade;
+
+    FTS_INFO( "**********read out checksum**********");
+    if ((NULL == upg) || (NULL == upg->func)) {
+        FTS_ERROR("upgrade/func is null");
+        return -EINVAL;
+    }
+
+    /* check sum init */
+    wbuf[0] = FTS_CMD_ECC_INIT;
+    ret = fts_write(wbuf, 1);
+    if (ret < 0) {
+        FTS_ERROR("ecc init cmd write fail");
+        return ret;
+    }
+
+    if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) {
+        packet_num = 1;
+        remainder = 0;
+        packet_len = len;
+    } else {
+        packet_num = len / FTS_MAX_LEN_ECC_CALC;
+        remainder = len % FTS_MAX_LEN_ECC_CALC;
+        if (remainder)
+            packet_num++;
+        packet_len = FTS_MAX_LEN_ECC_CALC;
+    }
+    FTS_INFO("ecc calc num:%d, remainder:%d", packet_num, remainder);
+
+    /* send commond to start checksum */
+    wbuf[0] = FTS_CMD_ECC_CAL;
+    for (i = 0; i < packet_num; i++) {
+        offset = FTS_MAX_LEN_ECC_CALC * i;
+        addr = saddr + offset;
+        wbuf[1] = BYTE_OFF_16(addr);
+        wbuf[2] = BYTE_OFF_8(addr);
+        wbuf[3] = BYTE_OFF_0(addr);
+
+        if ((upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+            wbuf[4] = BYTE_OFF_16(packet_len);
+            wbuf[5] = BYTE_OFF_8(packet_len);
+            wbuf[6] = BYTE_OFF_0(packet_len);
+            cmdlen = FTS_CMD_ECC_CAL_LEN;
+        } else {
+            if ((i == (packet_num - 1)) && remainder)
+                packet_len = remainder;
+            wbuf[4] = BYTE_OFF_8(packet_len);
+            wbuf[5] = BYTE_OFF_0(packet_len);
+            cmdlen = FTS_CMD_ECC_CAL_LEN - 1;
+        }
+
+        FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len);
+        ret = fts_write(wbuf, cmdlen);
+        if (ret < 0) {
+            FTS_ERROR("ecc calc cmd write fail");
+            return ret;
+        }
+
+        fts_msleep(packet_len / 256);
+
+        /* read status if check sum is finished */
+        bflag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ECC_OK,
+                                             FTS_RETRIES_ECC_CAL,
+                                             FTS_RETRIES_DELAY_ECC_CAL);
+        if (!bflag) {
+            FTS_ERROR("ecc flash status read fail");
+            return -EIO;
+        }
+    }
+
+    ecc_len = 1;
+    if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) ||
+        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+        ecc_len = 2;
+    }
+
+    /* read out check sum */
+    wbuf[0] = FTS_CMD_ECC_READ;
+    ret = fts_read(wbuf, 1, val, ecc_len);
+    if (ret < 0) {
+        FTS_ERROR( "ecc read cmd write fail");
+        return ret;
+    }
+
+    if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) ||
+        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+        ecc = (int)((u16)(val[0] << 8) + val[1]);
+    } else {
+        ecc = (int)val[0];
+    }
+
+    return ecc;
+}
+
+/************************************************************************
+ * Name: fts_flash_write_buf
+ * Brief: write buf data to flash address
+ * Input: saddr - start address data write to flash
+ *        buf - data buffer
+ *        len - data length
+ *        delay - delay after write
+ * Output:
+ * Return: return data ecc of host if success, otherwise return error code
+ ***********************************************************************/
+int fts_flash_write_buf(
+    u32 saddr,
+    u8 *buf,
+    u32 len,
+    u32 delay)
+{
+    int ret = 0;
+    u32 i = 0;
+    u32 j = 0;
+    u32 packet_number = 0;
+    u32 packet_len = 0;
+    u32 addr = 0;
+    u32 offset = 0;
+    u32 remainder = 0;
+    u32 cmdlen = 0;
+    u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
+    u8 ecc_tmp = 0;
+    int ecc_in_host = 0;
+    u8 cmd = 0;
+    u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+    u16 read_status = 0;
+    u16 wr_ok = 0;
+    struct fts_upgrade *upg = fwupgrade;
+
+    FTS_INFO( "**********write data to flash**********");
+    if ((!upg) || (!upg->func || !buf || !len)) {
+        FTS_ERROR("upgrade/func/buf/len is invalid");
+        return -EINVAL;
+    }
+
+    FTS_INFO("data buf start addr=0x%x, len=0x%x", saddr, len);
+    packet_number = len / FTS_FLASH_PACKET_LENGTH;
+    remainder = len % FTS_FLASH_PACKET_LENGTH;
+    if (remainder > 0)
+        packet_number++;
+    packet_len = FTS_FLASH_PACKET_LENGTH;
+    FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder);
+
+    for (i = 0; i < packet_number; i++) {
+        offset = i * FTS_FLASH_PACKET_LENGTH;
+        addr = saddr + offset;
+
+        /* last packet */
+        if ((i == (packet_number - 1)) && remainder)
+            packet_len = remainder;
+
+        if ((upg->ts_data->bus_type == BUS_TYPE_SPI) && (upg->ts_data->bus_ver == BUS_VER_V2)) {
+            packet_buf[0] = FTS_CMD_SET_WFLASH_ADDR;
+            packet_buf[1] = BYTE_OFF_16(addr);
+            packet_buf[2] = BYTE_OFF_8(addr);
+            packet_buf[3] = BYTE_OFF_0(addr);
+            ret = fts_write(packet_buf, FTS_LEN_SET_ADDR);
+            if (ret < 0) {
+                FTS_ERROR("set flash address fail");
+                return ret;
+            }
+
+            packet_buf[0] = FTS_CMD_WRITE;
+            cmdlen = 1;
+        } else {
+            packet_buf[0] = FTS_CMD_WRITE;
+            packet_buf[1] = BYTE_OFF_16(addr);
+            packet_buf[2] = BYTE_OFF_8(addr);
+            packet_buf[3] = BYTE_OFF_0(addr);
+            packet_buf[4] = BYTE_OFF_8(packet_len);
+            packet_buf[5] = BYTE_OFF_0(packet_len);
+            cmdlen = 6;
+        }
+
+        for (j = 0; j < packet_len; j++) {
+            packet_buf[cmdlen + j] = buf[offset + j];
+            ecc_tmp ^= packet_buf[cmdlen + j];
+        }
+
+        ret = fts_write(packet_buf, packet_len + cmdlen);
+        if (ret < 0) {
+            FTS_ERROR("app write fail");
+            return ret;
+        }
+        mdelay(delay);
+
+        /* read status */
+        wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len;
+        for (j = 0; j < FTS_RETRIES_WRITE; j++) {
+            cmd = FTS_CMD_FLASH_STATUS;
+            ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN);
+            read_status = (((u16)val[0]) << 8) + val[1];
+            /*  FTS_INFO("%x %x", wr_ok, read_status); */
+            if (wr_ok == read_status) {
+                break;
+            }
+            mdelay(FTS_RETRIES_DELAY_WRITE);
+        }
+    }
+
+    ecc_in_host = (int)ecc_tmp;
+    if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) ||
+        (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+        ecc_in_host = (int)fts_crc16_calc_host(buf, len);
+    }
+
+    return ecc_in_host;
+}
+
+/************************************************************************
+ * Name: fts_flash_read_buf
+ * Brief: read data from flash
+ * Input: saddr - start address data write to flash
+ *        buf - buffer to store data read from flash
+ *        len - read length
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ *
+ * Warning: can't call this function directly, need call in boot environment
+ ***********************************************************************/
+int fts_flash_read_buf(u32 saddr, u8 *buf, u32 len)
+{
+    int ret = 0;
+    u32 i = 0;
+    u32 packet_number = 0;
+    u32 packet_len = 0;
+    u32 addr = 0;
+    u32 offset = 0;
+    u32 remainder = 0;
+    u8 wbuf[FTS_CMD_READ_LEN_SPI] = { 0 };
+    struct fts_upgrade *upg = fwupgrade;
+
+    if (!upg || !buf || !len) {
+        FTS_ERROR("upgrade/buf is NULL or len is 0");
+        return -EINVAL;
+    }
+
+    packet_number = len / FTS_FLASH_PACKET_LENGTH;
+    remainder = len % FTS_FLASH_PACKET_LENGTH;
+    if (remainder > 0) {
+        packet_number++;
+    }
+    packet_len = FTS_FLASH_PACKET_LENGTH;
+    FTS_INFO("read packet_number:%d, remainder:%d", packet_number, remainder);
+
+
+    for (i = 0; i < packet_number; i++) {
+        offset = i * FTS_FLASH_PACKET_LENGTH;
+        addr = saddr + offset;
+        /* last packet */
+        if ((i == (packet_number - 1)) && remainder)
+            packet_len = remainder;
+
+        if (upg->ts_data->bus_type == BUS_TYPE_I2C) {
+            wbuf[0] = FTS_CMD_READ;
+            wbuf[1] = BYTE_OFF_16(addr);
+            wbuf[2] = BYTE_OFF_8(addr);
+            wbuf[3] = BYTE_OFF_0(addr);
+            ret = fts_write(wbuf, FTS_CMD_READ_LEN);
+            if (ret < 0) {
+                FTS_ERROR("pram/bootloader write 03 command fail");
+                return ret;
+            }
+
+            fts_msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */
+            ret = fts_read(NULL, 0, buf + offset, packet_len);
+            if (ret < 0) {
+                FTS_ERROR("pram/bootloader read 03 command fail");
+                return ret;
+            }
+        } else if (upg->ts_data->bus_type == BUS_TYPE_SPI) {
+            FTS_INFO("bus ver:%d", upg->ts_data->bus_ver);
+            if (upg->ts_data->bus_ver == BUS_VER_V2) {
+                wbuf[0] = FTS_CMD_SET_RFLASH_ADDR;
+                wbuf[1] = BYTE_OFF_16(addr);
+                wbuf[2] = BYTE_OFF_8(addr);
+                wbuf[3] = BYTE_OFF_0(addr);
+                ret = fts_write(wbuf, FTS_LEN_SET_ADDR);
+                if (ret < 0) {
+                    FTS_ERROR("set flash address fail");
+                    return ret;
+                }
+
+                fts_msleep(FTS_CMD_READ_DELAY);
+                wbuf[0] = FTS_CMD_READ;
+                ret = fts_read(wbuf, 1, buf + offset, packet_len);
+                if (ret < 0) {
+                    FTS_ERROR("pram/bootloader read 03(SPI_V2) command fail");
+                    return ret;
+                }
+            } else if (upg->ts_data->bus_ver == BUS_VER_DEFAULT) {
+                wbuf[0] = FTS_CMD_READ;
+                wbuf[1] = BYTE_OFF_16(addr);
+                wbuf[2] = BYTE_OFF_8(addr);
+                wbuf[3] = BYTE_OFF_0(addr);
+                wbuf[4] = BYTE_OFF_8(packet_len);
+                wbuf[5] = BYTE_OFF_0(packet_len);
+                ret = fts_read(wbuf, FTS_CMD_READ_LEN_SPI, \
+                               buf + offset, packet_len);
+                if (ret < 0) {
+                    FTS_ERROR("pram/bootloader read 03(SPI) command fail");
+                    return ret;
+                }
+            }
+        } else FTS_ERROR("unknown bus type:%d", upg->ts_data->bus_type);
+    }
+
+    return 0;
+}
+
+/************************************************************************
+ * Name: fts_flash_read
+ * Brief:
+ * Input:  addr  - address of flash
+ *         len   - length of read
+ * Output: buf   - data read from flash
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+static int fts_flash_read(u32 addr, u8 *buf, u32 len)
+{
+    int ret = 0;
+
+    FTS_INFO("***********read flash***********");
+    if ((NULL == buf) || (0 == len)) {
+        FTS_ERROR("buf is NULL or len is 0");
+        return -EINVAL;
+    }
+
+    ret = fts_fwupg_enter_into_boot();
+    if (ret < 0) {
+        FTS_ERROR("enter into pramboot/bootloader fail");
+        goto read_flash_err;
+    }
+
+    ret = fts_flash_read_buf(addr, buf, len);
+    if (ret < 0) {
+        FTS_ERROR("read flash fail");
+        goto read_flash_err;
+    }
+
+read_flash_err:
+    /* reset to normal boot */
+    ret = fts_fwupg_reset_in_boot();
+    if (ret < 0) {
+        FTS_ERROR("reset to normal boot fail");
+    }
+    return ret;
+}
+
+static int fts_read_file_default(char *file_name, u8 **file_buf)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
+    int ret = 0;
+    char file_path[FILE_NAME_LENGTH] = { 0 };
+    struct file *filp = NULL;
+    struct inode *inode;
+    mm_segment_t old_fs;
+    loff_t pos;
+    loff_t file_len = 0;
+
+    if ((NULL == file_name) || (NULL == file_buf)) {
+        FTS_ERROR("filename/filebuf is NULL");
+        return -EINVAL;
+    }
+
+    snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name);
+    filp = filp_open(file_path, O_RDONLY, 0);
+    if (IS_ERR(filp)) {
+        FTS_ERROR("open %s file fail", file_path);
+        return -ENOENT;
+    }
+
+#if 1
+    inode = filp->f_inode;
+#else
+    /* reserved for linux earlier verion */
+    inode = filp->f_dentry->d_inode;
+#endif
+
+    file_len = inode->i_size;
+    *file_buf = (u8 *)vmalloc(file_len);
+    if (NULL == *file_buf) {
+        FTS_ERROR("file buf malloc fail");
+        filp_close(filp, NULL);
+        return -ENOMEM;
+    }
+    old_fs = get_fs();
+    set_fs(KERNEL_DS);
+    pos = 0;
+    ret = vfs_read(filp, *file_buf, file_len , &pos);
+    if (ret < 0)
+        FTS_ERROR("read file fail");
+    FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos);
+    filp_close(filp, NULL);
+    set_fs(old_fs);
+    return ret;
+#else
+    FTS_INFO("not support vfs_read to get fw file");
+    return -EINVAL;
+#endif
+}
+
+static int fts_read_file_request_firmware(char *file_name, u8 **file_buf)
+{
+#if FTS_FW_REQUEST_SUPPORT
+    int ret = 0;
+    const struct firmware *fw = NULL;
+    char fwname[FILE_NAME_LENGTH] = { 0 };
+    struct fts_upgrade *upg = fwupgrade;
+
+    snprintf(fwname, FILE_NAME_LENGTH, "%s", file_name);
+    ret = request_firmware(&fw, fwname, upg->ts_data->dev);
+    if (0 == ret) {
+        FTS_INFO("firmware(%s) request successfully", fwname);
+        *file_buf = vmalloc(fw->size);
+        if (NULL == *file_buf) {
+            FTS_ERROR("fw buffer vmalloc fail");
+            ret = -ENOMEM;
+        } else {
+            memcpy(*file_buf, fw->data, fw->size);
+            ret = fw->size;
+        }
+    } else {
+        FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret);
+        ret = -EIO;
+    }
+
+    if (fw != NULL) {
+        release_firmware(fw);
+        fw = NULL;
+    }
+
+    return ret;
+#else
+    FTS_INFO("not support request_firmware to get fw file");
+    return -EINVAL;
+#endif
+}
+
+static int fts_read_file(char *file_name, u8 **file_buf)
+{
+    int ret = 0;
+
+    ret = fts_read_file_request_firmware(file_name, file_buf);
+    if (ret < 0) {
+        ret = fts_read_file_default(file_name, file_buf);
+        if (ret < 0) {
+            FTS_INFO("get fw file(default) abnormal");
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+int fts_upgrade_bin(char *fw_name, bool force)
+{
+    int ret = 0;
+    int irq_need_recovery = false;
+    int esd_need_recovery = false;
+    u32 fw_file_len = 0;
+    u8 *fw_file_buf = NULL;
+    struct fts_upgrade *upg = fwupgrade;
+
+    FTS_INFO("start upgrade with fw bin");
+    if ((!upg) || (!upg->func) || !upg->ts_data) {
+        FTS_ERROR("upgrade/func/ts_data is null");
+        return -EINVAL;
+    }
+
+    if (upg->ts_data->fw_loading) {
+        FTS_INFO("fw is loading, not download again");
+        return -EINVAL;
+    }
+
+    upg->ts_data->fw_loading = 1;
+    if (!upg->ts_data->irq_disabled) {
+        fts_irq_disable();
+        irq_need_recovery = true;
+    }
+    if (fts_esdcheck_is_running(upg->ts_data)) {
+        fts_esdcheck_switch(upg->ts_data, DISABLE);
+        esd_need_recovery = true;
+    }
+
+    ret = fts_read_file(fw_name, &fw_file_buf);
+    if ((ret < 0) || (ret < FTS_MIN_LEN)) {
+        FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret);
+        goto err_bin;
+    }
+
+    fw_file_len = ret;
+    FTS_INFO("fw bin file len:%d", fw_file_len);
+    if (force) {
+        if (upg->func->force_upgrade) {
+            ret = upg->func->force_upgrade(fw_file_buf, fw_file_len);
+        } else {
+            FTS_INFO("force_upgrade function is null, no upgrade");
+            goto err_bin;
+        }
+    } else {
+#if FTS_AUTO_LIC_UPGRADE_EN
+        if (upg->func->lic_upgrade) {
+            ret = upg->func->lic_upgrade(fw_file_buf, fw_file_len);
+        } else {
+            FTS_INFO("lic_upgrade function is null, no upgrade");
+        }
+#endif
+        if (upg->func->upgrade) {
+            ret = upg->func->upgrade(fw_file_buf, fw_file_len);
+        } else {
+            FTS_INFO("upgrade function is null, no upgrade");
+        }
+    }
+
+    if (ret < 0) {
+        FTS_ERROR("upgrade fw bin failed");
+        fts_fwupg_reset_in_boot();
+        goto err_bin;
+    }
+
+    FTS_INFO("upgrade fw bin success");
+    if (esd_need_recovery) fts_esdcheck_switch(upg->ts_data, ENABLE);
+    ret = 0;
+
+err_bin:
+    if (irq_need_recovery) fts_irq_enable();
+    upg->ts_data->fw_loading = 0;
+
+    if (fw_file_buf) {
+        vfree(fw_file_buf);
+        fw_file_buf = NULL;
+    }
+    return ret;
+}
+
+int fts_enter_test_environment(bool test_state)
+{
+    return 0;
+}
+#if FTS_AUTO_LIC_UPGRADE_EN
+static int fts_lic_get_vid_in_tp(u16 *vid)
+{
+    int ret = 0;
+    u8 val[2] = { 0 };
+
+    if (NULL == vid) {
+        FTS_ERROR("vid is NULL");
+        return -EINVAL;
+    }
+
+    ret = fts_read_reg(FTS_REG_VENDOR_ID, &val[0]);
+    if (fts_data->ic_info.is_incell)
+        ret = fts_read_reg(FTS_REG_PANEL_ID, &val[1]);
+    if (ret < 0) {
+        FTS_ERROR("read vid from tp fail");
+        return ret;
+    }
+
+    *vid = *(u16 *)val;
+    return 0;
+}
+
+static int fts_lic_get_vid_in_host(struct fts_upgrade *upg, u16 *vid)
+{
+    u8 val[2] = { 0 };
+    u8 *licbuf = NULL;
+    u32 conf_saddr = 0;
+
+    if (!upg || !upg->func || !upg->ts_data || !upg->lic || !vid) {
+        FTS_ERROR("upgrade/func/ts_data/get_hlic_ver/lic/vid is null");
+        return -EINVAL;
+    }
+
+    if (upg->lic_length < FTS_MAX_LEN_SECTOR) {
+        FTS_ERROR("lic length(%x) fail", upg->lic_length);
+        return -EINVAL;
+    }
+
+    licbuf  = upg->lic;
+    conf_saddr = upg->func->fwcfgoff;
+    val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF];
+    if (upg->ts_data->ic_info.is_incell)
+        val[1] = licbuf[conf_saddr + FTS_CONIFG_PANELID_OFF];
+
+    *vid = *(u16 *)val;
+    return 0;
+}
+
+static int fts_lic_get_ver_in_tp(u8 *ver)
+{
+    int ret = 0;
+
+    if (NULL == ver) {
+        FTS_ERROR("ver is NULL");
+        return -EINVAL;
+    }
+
+    ret = fts_read_reg(FTS_REG_LIC_VER, ver);
+    if (ret < 0) {
+        FTS_ERROR("read lcd initcode ver from tp fail");
+        return ret;
+    }
+
+    return 0;
+}
+
+static int fts_lic_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+    int ret = 0;
+
+    if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) {
+        FTS_ERROR("upgrade/func/get_hlic_ver/lic is null");
+        return -EINVAL;
+    }
+
+    ret = upg->func->get_hlic_ver(upg->lic);
+    if (ret < 0) {
+        FTS_ERROR("get host lcd initial code version fail");
+        return ret;
+    }
+
+    *ver = (u8)ret;
+    return ret;
+}
+
+static bool fts_lic_need_upgrade(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    u8 initcode_ver_in_tp = 0;
+    u8 initcode_ver_in_host = 0;
+    u16 vid_in_tp = 0;
+    u16 vid_in_host = 0;
+    bool fwvalid = false;
+
+    fwvalid = fts_fwupg_check_fw_valid();
+    if ( !fwvalid) {
+        FTS_INFO("fw is invalid, no upgrade lcd init code");
+        return false;
+    }
+
+    ret = fts_lic_get_vid_in_host(upg, &vid_in_host);
+    if (ret < 0) {
+        FTS_ERROR("vendor id in host invalid");
+        return false;
+    }
+
+    ret = fts_lic_get_vid_in_tp(&vid_in_tp);
+    if (ret < 0) {
+        FTS_ERROR("vendor id in tp invalid");
+        return false;
+    }
+
+    FTS_DEBUG("vid in tp:0x%04x, host:0x%04x", vid_in_tp, vid_in_host);
+    if (vid_in_tp != vid_in_host) {
+        FTS_INFO("vendor id in tp&host are different, no upgrade lic");
+        return false;
+    }
+
+    ret = fts_lic_get_ver_in_host(upg, &initcode_ver_in_host);
+    if (ret < 0) {
+        FTS_ERROR("init code in host invalid");
+        return false;
+    }
+
+    ret = fts_lic_get_ver_in_tp(&initcode_ver_in_tp);
+    if (ret < 0) {
+        FTS_ERROR("read reg0xE4 fail");
+        return false;
+    }
+
+    FTS_DEBUG("lcd initial code version in tp:%x, host:%x",
+              initcode_ver_in_tp, initcode_ver_in_host);
+    if (0xA5 == initcode_ver_in_tp) {
+        FTS_INFO("lcd init code ver is 0xA5, don't upgade init code");
+        return false;
+    } else if (0xFF == initcode_ver_in_tp) {
+        FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code");
+        return true;
+    } else if (initcode_ver_in_tp < initcode_ver_in_host)
+        return true;
+    else
+        return false;
+}
+
+static int fts_lic_upgrade(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    bool hlic_upgrade = false;
+    int upgrade_count = 0;
+    u8 ver = 0;
+
+    FTS_INFO("lcd initial code auto upgrade function");
+    if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) {
+        FTS_ERROR("lcd upgrade function is null");
+        return -EINVAL;
+    }
+
+    hlic_upgrade = fts_lic_need_upgrade(upg);
+    FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade);
+    if (hlic_upgrade) {
+        FTS_INFO("lcd initial code need upgrade, upgrade begin...");
+        do {
+            FTS_INFO("lcd initial code upgrade times:%d", upgrade_count);
+            upgrade_count++;
+
+            ret = upg->func->lic_upgrade(upg->lic, upg->lic_length);
+            if (ret < 0) {
+                fts_fwupg_reset_in_boot();
+            } else {
+                fts_lic_get_ver_in_tp(&ver);
+                FTS_INFO("success upgrade to lcd initcode ver:%02x", ver);
+                break;
+            }
+        } while (upgrade_count < 2);
+    } else {
+        FTS_INFO("lcd initial code don't need upgrade");
+    }
+
+    return ret;
+}
+#endif /* FTS_AUTO_LIC_UPGRADE_EN */
+
+
+static int fts_param_get_ver_in_tp(u8 *ver)
+{
+    int ret = 0;
+
+    if (NULL == ver) {
+        FTS_ERROR("ver is NULL");
+        return -EINVAL;
+    }
+
+    ret = fts_read_reg(FTS_REG_IDE_PARA_VER_ID, ver);
+    if (ret < 0) {
+        FTS_ERROR("read fw param ver from tp fail");
+        return ret;
+    }
+
+    if ((0x00 == *ver) || (0xFF == *ver)) {
+        FTS_INFO("param version in tp invalid");
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static int fts_param_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+    if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) {
+        FTS_ERROR("upgrade/func/fw/ver is NULL");
+        return -EINVAL;
+    }
+
+    if (upg->fw_length < upg->func->paramcfgveroff) {
+        FTS_ERROR("fw len(%x) < paramcfg ver offset(%x)",
+                  upg->fw_length, upg->func->paramcfgveroff);
+        return -EINVAL;
+    }
+
+    FTS_INFO("fw paramcfg version offset:%x", upg->func->paramcfgveroff);
+    *ver = upg->fw[upg->func->paramcfgveroff];
+
+    if ((0x00 == *ver) || (0xFF == *ver)) {
+        FTS_INFO("param version in host invalid");
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/*
+ * return: < 0 : error
+ *         == 0: no ide
+ *         == 1: ide
+ */
+static int fts_param_ide_in_host(struct fts_upgrade *upg)
+{
+    u32 off = 0;
+
+    if ((!upg) || (!upg->func) || (!upg->fw)) {
+        FTS_ERROR("upgrade/func/fw is NULL");
+        return -EINVAL;
+    }
+
+    if (upg->fw_length < upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN) {
+        FTS_INFO("fw len(%x) < paramcfg offset(%x), no IDE",
+                 upg->fw_length, upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN);
+        return 0;
+    }
+
+    off = upg->func->paramcfgoff;
+    if (0 == memcmp(&upg->fw[off], FTS_FW_IDE_SIG, FTS_FW_IDE_SIG_LEN)) {
+        FTS_INFO("fw in host is IDE version");
+        return 1;
+    }
+
+    FTS_INFO("fw in host isn't IDE version");
+    return 0;
+}
+
+/*
+ * return: < 0 : error
+ *         0   : no ide
+ *         1   : ide
+ */
+static int fts_param_ide_in_tp(u8 *val)
+{
+    int ret = 0;
+
+    ret = fts_read_reg(FTS_REG_IDE_PARA_STATUS, val);
+    if (ret < 0) {
+        FTS_ERROR("read IDE PARAM STATUS in tp fail");
+        return ret;
+    }
+
+    if ((*val != 0xFF) && ((*val & 0x80) == 0x80)) {
+        FTS_INFO("fw in tp is IDE version");
+        return 1;
+    }
+
+    FTS_INFO("fw in tp isn't IDE version");
+    return 0;
+}
+
+/************************************************************************
+ * fts_param_need_upgrade - check fw paramcfg need upgrade or not
+ *
+ * Return:  < 0 : error if paramcfg need upgrade
+ *          0   : no need upgrade
+ *          1   : need upgrade app + param
+ *          2   : need upgrade param
+ ***********************************************************************/
+static int fts_param_need_upgrade(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    u8 val = 0;
+    int ide_in_host = 0;
+    int ide_in_tp = 0;
+    u8 ver_in_host = 0;
+    u8 ver_in_tp = 0;
+    bool fwvalid = false;
+
+    fwvalid = fts_fwupg_check_fw_valid();
+    if ( !fwvalid) {
+        FTS_INFO("fw is invalid, upgrade app+param");
+        return 1;
+    }
+
+    ide_in_host = fts_param_ide_in_host(upg);
+    if (ide_in_host < 0) {
+        FTS_INFO("fts_param_ide_in_host fail");
+        return ide_in_host;
+    }
+
+    ide_in_tp = fts_param_ide_in_tp(&val);
+    if (ide_in_tp < 0) {
+        FTS_INFO("fts_param_ide_in_tp fail");
+        return ide_in_tp;
+    }
+
+    if ((0 == ide_in_host) && (0 == ide_in_tp)) {
+        FTS_INFO("fw in host&tp are both no ide");
+        return 0;
+    } else if (ide_in_host != ide_in_tp) {
+        FTS_INFO("fw in host&tp not equal, need upgrade app+param");
+        return 1;
+    } else if ((1 == ide_in_host) && (1 == ide_in_tp)) {
+        FTS_INFO("fw in host&tp are both ide");
+        if ((val & 0x7F) != 0x00) {
+            FTS_INFO("param invalid, need upgrade param");
+            return 2;
+        }
+
+        ret = fts_param_get_ver_in_host(upg, &ver_in_host);
+        if (ret < 0) {
+            FTS_ERROR("param version in host invalid");
+            return ret;
+        }
+
+        ret = fts_param_get_ver_in_tp(&ver_in_tp);
+        if (ret < 0) {
+            FTS_ERROR("get IDE param ver in tp fail");
+            return ret;
+        }
+
+        FTS_INFO("fw paramcfg version in tp:%x, host:%x",
+                 ver_in_tp, ver_in_host);
+        if (ver_in_tp != ver_in_host) {
+            return 2;
+        }
+    }
+
+    return 0;
+}
+
+static int fts_fwupg_get_ver_in_tp(u8 *ver)
+{
+    int ret = 0;
+
+    if (NULL == ver) {
+        FTS_ERROR("ver is NULL");
+        return -EINVAL;
+    }
+
+    ret = fts_read_reg(FTS_REG_FW_VER, ver);
+    if (ret < 0) {
+        FTS_ERROR("read fw ver from tp fail");
+        return ret;
+    }
+
+    return 0;
+}
+
+static int fts_fwupg_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+    if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) {
+        FTS_ERROR("upgrade/func/fw/ver is NULL");
+        return -EINVAL;
+    }
+
+    if (upg->fw_length < upg->func->fwveroff) {
+        FTS_ERROR("fw len(0x%0x) < fw ver offset(0x%x)",
+                  upg->fw_length, upg->func->fwveroff);
+        return -EINVAL;
+    }
+
+    FTS_INFO("fw version offset:0x%x", upg->func->fwveroff);
+    *ver = upg->fw[upg->func->fwveroff];
+    return 0;
+}
+
+static bool fts_fwupg_need_upgrade(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    bool fwvalid = false;
+    u8 fw_ver_in_host = 0;
+    u8 fw_ver_in_tp = 0;
+
+    fwvalid = fts_fwupg_check_fw_valid();
+    if (fwvalid) {
+        ret = fts_fwupg_get_ver_in_host(upg, &fw_ver_in_host);
+        if (ret < 0) {
+            FTS_ERROR("get fw ver in host fail");
+            return false;
+        }
+
+        ret = fts_fwupg_get_ver_in_tp(&fw_ver_in_tp);
+        if (ret < 0) {
+            FTS_ERROR("get fw ver in tp fail");
+            return false;
+        }
+
+        FTS_INFO("fw version in tp:%x, host:%x", fw_ver_in_tp, fw_ver_in_host);
+        if (fw_ver_in_tp != fw_ver_in_host) {
+            return true;
+        }
+    } else {
+        FTS_INFO("fw invalid, need upgrade fw");
+        return true;
+    }
+
+    return false;
+}
+
+/************************************************************************
+ * Name: fts_fw_upgrade
+ * Brief: fw upgrade main entry, run in following steps
+ *        1. check fw version(A6), not equal, will upgrade app(+param)
+ *        2. if fw version equal, will check ide, will upgrade app(+param)
+ *        in the follow situation
+ *          a. host&tp IDE's type are not equal, will upgrade app+param
+ *          b. host&tp are both IDE's type, and param's version are not
+ *          equal, will upgrade param
+ * Input:
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_upgrade(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    bool upgrade_flag = false;
+    int upgrade_count = 0;
+    u8 ver = 0;
+
+    FTS_INFO("fw auto upgrade function");
+    if ((NULL == upg) || (NULL == upg->func)) {
+        FTS_ERROR("upg/upg->func is null");
+        return -EINVAL;
+    }
+
+    upgrade_flag = fts_fwupg_need_upgrade(upg);
+    FTS_INFO("fw upgrade flag:%d", upgrade_flag);
+    do {
+        upgrade_count++;
+        if (upgrade_flag) {
+            FTS_INFO("upgrade fw app(times:%d)", upgrade_count);
+            if (upg->func->upgrade) {
+                ret = upg->func->upgrade(upg->fw, upg->fw_length);
+                if (ret < 0) {
+                    fts_fwupg_reset_in_boot();
+                } else {
+                    fts_fwupg_get_ver_in_tp(&ver);
+                    FTS_INFO("success upgrade to fw version %02x", ver);
+                    break;
+                }
+            } else {
+                FTS_ERROR("upgrade func/upgrade is null, return immediately");
+                ret = -ENODATA;
+                break;
+            }
+        } else {
+            if (upg->func->param_upgrade) {
+                ret = fts_param_need_upgrade(upg);
+                if (ret <= 0) {
+                    FTS_INFO("param don't need upgrade");
+                    break;
+                } else if (1 == ret) {
+                    FTS_INFO("force upgrade fw app(times:%d)", upgrade_count);
+                    if (upg->func->upgrade) {
+                        ret = upg->func->upgrade(upg->fw, upg->fw_length);
+                        if (ret < 0) {
+                            fts_fwupg_reset_in_boot();
+                        } else {
+                            break;
+                        }
+                    }
+                } else if (2 == ret) {
+                    FTS_INFO("upgrade param area(times:%d)", upgrade_count);
+                    ret = upg->func->param_upgrade(upg->fw, upg->fw_length);
+                    if (ret < 0) {
+                        fts_fwupg_reset_in_boot();
+                    } else {
+                        fts_param_get_ver_in_tp(&ver);
+                        FTS_INFO("success upgrade to fw param version %02x", ver);
+                        break;
+                    }
+                } else
+                    break;
+            } else {
+                break;
+            }
+        }
+    } while (upgrade_count < 2);
+
+    return ret;
+}
+
+/************************************************************************
+ * fts_fwupg_auto_upgrade - upgrade main entry
+ ***********************************************************************/
+static int fts_fwupg_auto_upgrade(struct fts_upgrade *upg)
+{
+    int ret = 0;
+
+    FTS_INFO("********************FTS enter upgrade********************");
+    if (!upg || !upg->ts_data) {
+        FTS_ERROR("upg/ts_data is null");
+        return -EINVAL;
+    }
+
+    ret = fts_fwupg_upgrade(upg);
+    if (ret < 0)
+        FTS_ERROR("**********tp fw(app/param) upgrade failed**********");
+    else
+        FTS_INFO("**********tp fw(app/param) no upgrade/upgrade success**********");
+
+#if FTS_AUTO_LIC_UPGRADE_EN
+    ret = fts_lic_upgrade(upg);
+    if (ret < 0)
+        FTS_ERROR("**********lcd init code upgrade failed**********");
+    else
+        FTS_INFO("**********lcd init code no upgrade/upgrade success**********");
+#endif
+
+    FTS_INFO("********************FTS exit upgrade********************");
+    return ret;
+}
+
+static int fts_fwupg_get_moduleid(struct fts_upgrade *upg, int *module_id)
+{
+    int ret = 0;
+    bool fwvalid = false;
+    u8 vendor_id = 0;
+    u8 panel_id = 0;
+    u8 cmd = FTS_CMD_READ_FWCFG;
+    u32 fwcfg_addr = 0;
+    u8 cfgbuf[FTS_HEADER_LEN] = { 0 };
+
+    FTS_INFO("read module id from tp");
+    if ((!upg) || (!upg->func) || (!upg->ts_data) || (!module_id)) {
+        FTS_ERROR("upgrade/func/ts_data/vid is null");
+        return -EINVAL;
+    }
+
+    fwvalid = fts_fwupg_check_fw_valid();
+    if (fwvalid) {
+        ret = fts_read_reg(FTS_REG_VENDOR_ID, &vendor_id);
+        if (upg->ts_data->ic_info.is_incell)
+            ret = fts_read_reg(FTS_REG_PANEL_ID, &panel_id);
+    } else {
+        if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_1) {
+            ret = fts_read(&cmd, 1, cfgbuf, FTS_HEADER_LEN);
+        } else {
+            fwcfg_addr =  upg->func->fwcfgoff;
+            ret = fts_flash_read(fwcfg_addr, cfgbuf, FTS_HEADER_LEN);
+        }
+
+        if ((cfgbuf[FTS_CONIFG_VENDORID_OFF] +
+             cfgbuf[FTS_CONIFG_VENDORID_OFF + 1]) == 0xFF)
+            vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF];
+        if (upg->ts_data->ic_info.is_incell) {
+            if ((cfgbuf[FTS_CONIFG_PANELID_OFF] +
+                 cfgbuf[FTS_CONIFG_PANELID_OFF + 1]) == 0xFF)
+                panel_id = cfgbuf[FTS_CONIFG_PANELID_OFF];
+        }
+    }
+
+    if (ret < 0) {
+        FTS_ERROR("fail to get module id from tp");
+        return ret;
+    }
+
+    *module_id = (int)((panel_id << 8) + vendor_id);
+    return 0;
+}
+
+
+int fts_enter_normal_fw(void)
+{
+
+    fts_wait_tp_to_valid();
+    return 0;
+}
+
+static int fts_fwupg_get_module_info(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    int i = 0;
+    struct upgrade_module *info = &module_list[0];
+
+    if (!upg || !upg->ts_data) {
+        FTS_ERROR("upg/ts_data is null");
+        return -EINVAL;
+    }
+
+    if (FTS_GET_MODULE_NUM > 1) {
+        /* support multi modules, must read correct module id */
+        ret = fts_fwupg_get_moduleid(upg, &upg->module_id);
+        if (ret < 0) {
+            FTS_ERROR("get module id failed");
+            return ret;
+        }
+        FTS_INFO("module id:%04x", upg->module_id);
+        for (i = 0; i < FTS_GET_MODULE_NUM; i++) {
+            info = &module_list[i];
+            if (upg->module_id == info->id) {
+                FTS_INFO("module id match, get module info pass");
+                break;
+            }
+        }
+        if (i >= FTS_GET_MODULE_NUM) {
+            FTS_ERROR("no module id match, don't get file");
+            return -ENODATA;
+        }
+    }
+
+    upg->module_info = info;
+    return 0;
+}
+
+static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    const struct firmware *fw = NULL;
+    u8 *tmpbuf = NULL;
+    char fwname[FILE_NAME_LENGTH] = { 0 };
+
+    if (!upg || !upg->ts_data || !upg->ts_data->dev) {
+        FTS_ERROR("upg/ts_data/dev is null");
+        return -EINVAL;
+    }
+
+    snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \
+             FTS_FW_NAME_PREX_WITH_REQUEST, \
+             upg->module_info->vendor_name);
+
+    ret = request_firmware(&fw, fwname, upg->ts_data->dev);
+    if (0 == ret) {
+        FTS_INFO("firmware(%s) request successfully", fwname);
+        tmpbuf = vmalloc(fw->size);
+        if (NULL == tmpbuf) {
+            FTS_ERROR("fw buffer vmalloc fail");
+            ret = -ENOMEM;
+        } else {
+            memcpy(tmpbuf, fw->data, fw->size);
+            upg->fw = tmpbuf;
+            upg->fw_length = fw->size;
+            upg->fw_from_request = 1;
+        }
+    } else {
+        FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret);
+    }
+
+    if (fw != NULL) {
+        release_firmware(fw);
+        fw = NULL;
+    }
+
+    return ret;
+}
+
+static int fts_get_fw_file_via_i(struct fts_upgrade *upg)
+{
+    upg->fw = upg->module_info->fw_file;
+    upg->fw_length = upg->module_info->fw_len;
+    upg->fw_from_request = 0;
+
+    return 0;
+}
+
+/*****************************************************************************
+ *  Name: fts_fwupg_get_fw_file
+ *  Brief: get fw image/file,
+ *         If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_-
+ *         MODULE_ID/FTS_MODULE_NAME;
+ *         If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F-
+ *         TS_MODULE_ID; will use module id to distingwish different modules;
+ *         If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT
+ *         =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt-
+ *         ech_ts_fw_" & FTS_MODULE_NAME;
+ *
+ *         If have flash, module_id=vendor_id, If non-flash,module_id need
+ *         transfer from LCD driver(gpio or lcm_id or ...);
+ *  Input:
+ *  Output:
+ *  Return: return 0 if success, otherwise return error code
+ *****************************************************************************/
+static int fts_fwupg_get_fw_file(struct fts_upgrade *upg)
+{
+    int ret = 0;
+    bool get_fw_i_flag = false;
+
+    FTS_DEBUG("get upgrade fw file");
+    if (!upg || !upg->ts_data) {
+        FTS_ERROR("upg/ts_data is null");
+        return -EINVAL;
+    }
+
+    ret = fts_fwupg_get_module_info(upg);
+    if ((ret < 0) || (!upg->module_info)) {
+        FTS_ERROR("get module info fail");
+        return ret;
+    }
+
+    if (FTS_FW_REQUEST_SUPPORT) {
+        ret = fts_get_fw_file_via_request_firmware(upg);
+        if (ret != 0) {
+            get_fw_i_flag = true;
+        }
+    } else {
+        get_fw_i_flag = true;
+    }
+
+    if (get_fw_i_flag) {
+        ret = fts_get_fw_file_via_i(upg);
+    }
+
+    upg->lic = upg->fw;
+    upg->lic_length = upg->fw_length;
+
+    FTS_INFO("upgrade fw file len:%d", upg->fw_length);
+    if (upg->fw_length < FTS_MIN_LEN) {
+        FTS_ERROR("fw file len(%d) fail", upg->fw_length);
+        return -ENODATA;
+    }
+
+    return ret;
+}
+
+static void fts_fwupg_init_ic_detail(struct fts_upgrade *upg)
+{
+    if (upg && upg->func && upg->func->init) {
+        upg->func->init(upg->fw, upg->fw_length);
+    }
+}
+
+/*****************************************************************************
+ *  Name: fts_fwupg_work
+ *  Brief: 1. get fw image/file
+ *         2. ic init if have
+ *         3. call upgrade main function(fts_fwupg_auto_upgrade)
+ *  Input:
+ *  Output:
+ *  Return:
+ *****************************************************************************/
+static void fts_fwupg_work(struct work_struct *work)
+{
+    int ret = 0;
+    int irq_need_recovery = false;
+    struct fts_upgrade *upg = fwupgrade;
+
+#if !FTS_AUTO_UPGRADE_EN
+    FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on");
+    return ;
+#endif
+
+    FTS_INFO("fw upgrade work function");
+    if (!upg || !upg->ts_data) {
+        FTS_ERROR("upg/ts_data is null");
+        return ;
+    }
+
+    upg->ts_data->fw_loading = 1;
+    if (!upg->ts_data->irq_disabled) {
+        fts_irq_disable();
+        irq_need_recovery = true;
+    }
+    if (fts_esdcheck_is_running(upg->ts_data)) {
+        fts_esdcheck_switch(upg->ts_data, DISABLE);
+    }
+
+    /* get fw */
+    ret = fts_fwupg_get_fw_file(upg);
+    if (ret < 0) {
+        FTS_ERROR("get file fail, can't upgrade");
+    } else {
+        /* ic init if have */
+        fts_fwupg_init_ic_detail(upg);
+        /* run auto upgrade */
+        ret = fts_fwupg_auto_upgrade(upg);
+    }
+
+    if (ret >= 0)
+        fts_esdcheck_switch(upg->ts_data, ENABLE);
+    if (irq_need_recovery)
+        fts_irq_enable();
+    upg->ts_data->fw_loading = 0;
+}
+
+int fts_fwupg_init(struct fts_ts_data *ts_data)
+{
+    int i = 0;
+    int j = 0;
+    u16 ic_stype = 0;
+    struct upgrade_func *func = upgrade_func_list[0];
+    int func_count = sizeof(upgrade_func_list) / sizeof(upgrade_func_list[0]);
+
+    FTS_INFO("fw upgrade init function");
+
+    if (!ts_data || !ts_data->ts_workqueue) {
+        FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function");
+        return -EINVAL;
+    }
+
+    if (0 == func_count) {
+        FTS_ERROR("no upgrade function in tp driver");
+        return -ENODATA;
+    }
+
+    fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL);
+    if (NULL == fwupgrade) {
+        FTS_ERROR("malloc memory for upgrade fail");
+        return -ENOMEM;
+    }
+
+    ic_stype = ts_data->ic_info.ids.type;
+    if (1 == func_count) {
+        fwupgrade->func = func;
+    } else {
+        for (i = 0; i < func_count; i++) {
+            func = upgrade_func_list[i];
+            for (j = 0; j < FTS_MAX_COMPATIBLE_TYPE; j++) {
+                if (0 == func->ctype[j])
+                    break;
+                else if (func->ctype[j] == ic_stype) {
+                    FTS_INFO("match upgrade function,type:%x", (int)func->ctype[j]);
+                    fwupgrade->func = func;
+                }
+            }
+        }
+    }
+
+    if (NULL == fwupgrade->func) {
+        FTS_ERROR("no upgrade function match, can't upgrade");
+        kfree(fwupgrade);
+        fwupgrade = NULL;
+        return -ENODATA;
+    }
+
+    fwupgrade->ts_data = ts_data;
+    INIT_WORK(&fwupgrade->fwupg_work, fts_fwupg_work);
+    queue_work(ts_data->ts_workqueue, &fwupgrade->fwupg_work);
+
+    return 0;
+}
+
+int fts_fwupg_exit(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+    if (fwupgrade) {
+        cancel_work_sync(&fwupgrade->fwupg_work);
+        if (fwupgrade->fw_from_request) {
+            vfree(fwupgrade->fw);
+            fwupgrade->fw = NULL;
+        }
+
+        kfree(fwupgrade);
+        fwupgrade = NULL;
+    }
+    FTS_FUNC_EXIT();
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.h b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.h
new file mode 100755
index 0000000..c3084fb
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash.h
@@ -0,0 +1,235 @@
+/************************************************************************
+* Copyright (c) 2012-2020, Focaltech Systems (R) All Rights Reserved.
+*
+* File Name: focaltech_flash.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-07
+*
+* Abstract:
+*
+************************************************************************/
+#ifndef __LINUX_FOCALTECH_FLASH_H__
+#define __LINUX_FOCALTECH_FLASH_H__
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_CMD_RESET                               0x07
+#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR               0xAD
+#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN           4
+#define FTS_ROMBOOT_CMD_WRITE                       0xAE
+#define FTS_ROMBOOT_CMD_START_APP                   0x08
+#define FTS_DELAY_PRAMBOOT_START                    100
+#define FTS_ROMBOOT_CMD_ECC                         0xCC
+#define FTS_PRAM_SADDR                              0x000000
+#define FTS_DRAM_SADDR                              0xD00000
+
+#define FTS_CMD_READ_FWCFG                          0xA8
+
+#define FTS_CMD_READ                                0x03
+#define FTS_CMD_READ_DELAY                          1
+#define FTS_CMD_READ_LEN                            4
+#define FTS_CMD_READ_LEN_SPI                        6
+#define FTS_CMD_FLASH_TYPE                          0x05
+#define FTS_CMD_FLASH_MODE                          0x09
+#define FLASH_MODE_WRITE_FLASH_VALUE                0x0A
+#define FLASH_MODE_UPGRADE_VALUE                    0x0B
+#define FLASH_MODE_LIC_VALUE                        0x0C
+#define FLASH_MODE_PARAM_VALUE                      0x0D
+#define FTS_CMD_ERASE_APP                           0x61
+#define FTS_REASE_APP_DELAY                         1350
+#define FTS_ERASE_SECTOR_DELAY                      60
+#define FTS_RETRIES_REASE                           50
+#define FTS_RETRIES_DELAY_REASE                     400
+#define FTS_CMD_FLASH_STATUS                        0x6A
+#define FTS_CMD_FLASH_STATUS_LEN                    2
+#define FTS_CMD_FLASH_STATUS_NOP                    0x0000
+#define FTS_CMD_FLASH_STATUS_ECC_OK                 0xF055
+#define FTS_CMD_FLASH_STATUS_ERASE_OK               0xF0AA
+#define FTS_CMD_FLASH_STATUS_WRITE_OK               0x1000
+#define FTS_CMD_ECC_INIT                            0x64
+#define FTS_CMD_ECC_CAL                             0x65
+#define FTS_CMD_ECC_CAL_LEN                         7
+#define FTS_RETRIES_ECC_CAL                         10
+#define FTS_RETRIES_DELAY_ECC_CAL                   50
+#define FTS_CMD_ECC_READ                            0x66
+#define FTS_CMD_DATA_LEN                            0xB0
+#define FTS_CMD_APP_DATA_LEN_INCELL                 0x7A
+#define FTS_CMD_DATA_LEN_LEN                        4
+#define FTS_CMD_SET_WFLASH_ADDR                     0xAB
+#define FTS_CMD_SET_RFLASH_ADDR                     0xAC
+#define FTS_LEN_SET_ADDR                            4
+#define FTS_CMD_WRITE                               0xBF
+#define FTS_RETRIES_WRITE                           100
+#define FTS_RETRIES_DELAY_WRITE                     1
+#define FTS_CMD_WRITE_LEN                           6
+#define FTS_DELAY_READ_ID                           20
+#define FTS_DELAY_UPGRADE_RESET                     80
+#define PRAMBOOT_MIN_SIZE                           0x120
+#define PRAMBOOT_MAX_SIZE                           (64*1024)
+#define FTS_FLASH_PACKET_LENGTH                     32     /* max=128 */
+#define FTS_MAX_LEN_ECC_CALC                        0xFFFE /* must be even */
+#define FTS_MIN_LEN                                 0x120
+#define FTS_MAX_LEN_FILE                            (256 * 1024)
+#define FTS_MAX_LEN_APP                             (64 * 1024)
+#define FTS_MAX_LEN_SECTOR                          (4 * 1024)
+#define FTS_CONIFG_VENDORID_OFF                     0x04
+#define FTS_CONIFG_PANELID_OFF                      0x1E
+#define FTS_CONIFG_PROJECTID_OFF                    0x20
+#define FTS_APPINFO_OFF                             0x100
+#define FTS_APPINFO_APPLEN_OFF                      0x00
+#define FTS_APPINFO_APPLEN2_OFF                     0x12
+#define FTS_REG_UPGRADE                             0xFC
+#define FTS_REG_UPGRADE2                            0xBC
+#define FTS_UPGRADE_AA                              0xAA
+#define FTS_UPGRADE_55                              0x55
+#define FTS_DELAY_UPGRADE_AA                        10
+#define FTS_UPGRADE_LOOP                            30
+#define FTS_UPGRADE_RESET_LOOP                      20
+#define FTS_HEADER_LEN                              32
+#define FTS_FW_BIN_FILEPATH                         "/sdcard/"
+#define FTS_FW_IDE_SIG                              "IDE_"
+#define FTS_FW_IDE_SIG_LEN                          4
+#define MAX_MODULE_VENDOR_NAME_LEN                  16
+
+#define FTS_ROMBOOT_CMD_ECC_NEW_LEN                 7
+#define FTS_ECC_FINISH_TIMEOUT                      100
+#define FTS_ROMBOOT_CMD_ECC_FINISH                  0xCE
+#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5            0xA5
+#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00            0x00
+#define FTS_ROMBOOT_CMD_ECC_READ                    0xCD
+#define AL2_FCS_COEF                ((1 << 15) + (1 << 10) + (1 << 3))
+
+#define FTS_APP_INFO_OFFSET                         0x100
+
+enum FW_STATUS {
+    FTS_RUN_IN_ERROR,
+    FTS_RUN_IN_APP,
+    FTS_RUN_IN_ROM,
+    FTS_RUN_IN_PRAM,
+    FTS_RUN_IN_BOOTLOADER,
+};
+
+enum FW_FLASH_MODE {
+    FLASH_MODE_APP,
+    FLASH_MODE_LIC,
+    FLASH_MODE_PARAM,
+    FLASH_MODE_ALL,
+};
+
+enum ECC_CHECK_MODE {
+    ECC_CHECK_MODE_XOR,
+    ECC_CHECK_MODE_CRC16,
+};
+
+enum UPGRADE_SPEC {
+    UPGRADE_SPEC_V_1_0 = 0x0100,
+    UPGRADE_SPEC_V_1_1 = 0x0101,
+    UPGRADE_SPEC_V_1_2 = 0x0102,
+};
+
+
+enum FW_TYPE {
+    FW_AUTO,
+    FW_GESTURE,
+    FW_NORMAL,
+};
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+/* IC info */
+struct upgrade_func {
+    u16 ctype[FTS_MAX_COMPATIBLE_TYPE];
+    u32 fwveroff;
+    u32 fwcfgoff;
+    u32 appoff;
+    u32 licoff;
+    u32 paramcfgoff;
+    u32 paramcfgveroff;
+    u32 paramcfg2off;
+    int pram_ecc_check_mode;
+    int fw_ecc_check_mode;
+    int upgspec_version;
+    bool new_return_value_from_ic;
+    bool appoff_handle_in_ic;
+    bool is_reset_register_BC;
+    bool read_boot_id_need_reset;
+    bool hid_supported;
+    bool pramboot_supported;
+    u8 *pramboot;
+    u32 pb_length;
+    int (*init)(u8 *, u32);
+    int (*write_pramboot_private)(void);
+    int (*upgrade)(u8 *, u32);
+    int (*get_hlic_ver)(u8 *);
+    int (*lic_upgrade)(u8 *, u32);
+    int (*param_upgrade)(u8 *, u32);
+    int (*force_upgrade)(u8 *, u32);
+};
+
+struct upgrade_setting_nf {
+    u8 rom_idh;
+    u8 rom_idl;
+    u16 reserved;
+    u32 app2_offset;
+    u32 ecclen_max;
+    u8 eccok_val;
+    u8 upgsts_boot;
+    u8 delay_init;
+    u8 spi_pe;
+    u8 length_coefficient;
+    u8 fd_check;
+    u8 drwr_support;
+    u8 ecc_delay;
+};
+
+struct upgrade_module {
+    int id;
+    char vendor_name[MAX_MODULE_VENDOR_NAME_LEN];
+    u8 *fw_file;
+    u32 fw_len;
+};
+
+struct fts_upgrade {
+    struct fts_ts_data *ts_data;
+    struct upgrade_module *module_info;
+    struct upgrade_func *func;
+    struct upgrade_setting_nf *setting_nf;
+    struct work_struct fwupg_work;
+    struct work_struct fwload_work;
+    struct work_struct fwrecover_work;
+    int module_id;
+    bool fw_from_request;
+    u8 *fw;
+    u32 fw_length;
+    u8 *lic;
+    u32 lic_length;
+};
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+extern struct upgrade_func upgrade_func_ft8756;
+
+
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+int fts_fwupg_reset_in_boot(void);
+int fts_fwupg_enter_into_boot(void);
+int fts_fwupg_hwrst_to_boot(void);
+int fts_fwupg_erase(u32 delay);
+int fts_fwupg_ecc_cal(u32 saddr, u32 len);
+int fts_flash_write_buf(u32 saddr, u8 *buf, u32 len, u32 delay);
+int fts_flash_read_buf(u32 saddr, u8 *buf, u32 len);
+int fts_fwupg_upgrade(struct fts_upgrade *upg);
+#endif
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash/focaltech_upgrade_ft8756m.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash/focaltech_upgrade_ft8756m.c
new file mode 100755
index 0000000..f506aad
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_flash/focaltech_upgrade_ft8756m.c
@@ -0,0 +1,446 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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_upgrade_ft8756.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2018-09-01
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "../focaltech_flash.h"
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+u8 pb_file_ft8756[] = {
+#include "../include/pramboot/FT8756_Pramboot_V1.3_20200116.i"
+};
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+#define LIC_FS_H_OFF                0
+#define LIC_FS_L_OFF                1
+#define LIC_CHECKSUM_H_OFF          2
+#define LIC_CHECKSUM_L_OFF          3
+#define LIC_LCD_LEN_H_OFF           4
+#define LIC_LCD_LEN_L_OFF           5
+#define LIC_ECC_H_OFF               6
+#define LIC_ECC_L_OFF               7
+#define LIC_ECC_START_OFF           12
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+static u16 lcd_crc16(u8 *buf, u16 len)
+{
+    int i = 0;
+    u16 crc_in = 0xFFFF;
+    u16 c_poly = 0x8005;
+    u8 ch = 0;
+
+    while (len--) {
+        ch = *(buf++);
+        crc_in ^= (ch << 8);
+        for (i = 0; i < 8; i++) {
+            if (crc_in & 0x8000)
+                crc_in = (crc_in << 1) ^ c_poly;
+            else
+                crc_in = crc_in << 1;
+        }
+    }
+
+    return crc_in;
+}
+
+static int cal_lcdinitcode_ecc(u8 *buf, u16 *ecc_val)
+{
+    u8 bank_len = 0;
+    u16 bank_pos = 0;
+    u16 lcd_len = 0;
+    u16 pos = 0;
+    u16 i = 0;
+    u16 idx = 0;
+    u16 addr_h = 0;
+    u16 addr_l = 0;
+    u8 tmp = 0;
+    u8 *ecc_buf = NULL;
+
+    if ((NULL == buf) || (NULL == ecc_val)) {
+        FTS_ERROR("buf/ecc_val is NULL");
+        return -EINVAL;
+    }
+
+    lcd_len = ((u16)buf[LIC_LCD_LEN_H_OFF] << 8) + buf[LIC_LCD_LEN_L_OFF];
+    if ((lcd_len >= FTS_MAX_LEN_SECTOR) || (lcd_len <= 0)) {
+        FTS_ERROR("host lcd len(0x%x) invalid", lcd_len);
+        return -EINVAL;
+    }
+
+    ecc_buf = kzalloc(lcd_len, GFP_KERNEL);
+    if (NULL == ecc_buf) {
+        FTS_ERROR("initial code ecc buf malloc fail");
+        return -EINVAL;
+    }
+    memset(ecc_buf, 0xFF, lcd_len);
+
+    for (i = 0; i < lcd_len - 4; i++) {
+        tmp = buf[i + LIC_ECC_START_OFF]; /* cal from bank0(offset 12) */
+
+        if (idx == 0) {
+            addr_h = tmp;
+            ecc_buf[pos++] = tmp;
+            idx = 1;
+        } else if (idx == 1) {
+            addr_l = tmp;
+            idx = 2;
+        } else if (idx == 2) {
+            bank_len = tmp;
+            bank_pos = 0;
+            idx = 3;
+        } else if (idx == 3) {
+            ecc_buf[pos++] = tmp + addr_l + bank_pos;
+            if (bank_pos < bank_len - 1) {
+                bank_pos++;
+            } else {
+                idx = 0;
+                addr_h = 0;
+                addr_l = 0;
+            }
+        }
+    }
+    /* abnormal terminal */
+    if ((idx == 1) || (idx == 2)) {
+        pos--;
+    }
+
+    *ecc_val = lcd_crc16(ecc_buf, pos);
+    if (NULL == ecc_buf) {
+        kfree(ecc_buf);
+        ecc_buf = NULL;
+    }
+    return 0;
+}
+
+/* calculate lcd init code checksum */
+static u16 cal_lcdinitcode_checksum(u8 *ptr , int length)
+{
+    /* CRC16 */
+    u16 cfcs = 0;
+    int i = 0;
+    int j = 0;
+
+    if (length % 2) {
+        return 0xFFFF;
+    }
+
+    for (i = 0; i < length; i += 2) {
+        cfcs ^= (((u16)ptr[i] << 8) + ptr[i + 1]);
+        for (j = 0; j < 16; j++) {
+            if (cfcs & 1) {
+                cfcs = (u16)((cfcs >> 1) ^ ((1 << 15) + (1 << 10) + (1 << 3)));
+            } else {
+                cfcs >>= 1;
+            }
+        }
+    }
+    return cfcs;
+}
+
+/*
+ * check_initial_code_valid - check initial code valid or not
+ */
+static int check_initial_code_valid(u8 *buf)
+{
+    int ret = 0;
+    u16 initcode_ecc = 0;
+    u16 buf_ecc = 0;
+    u16 initcode_checksum = 0;
+    u16 buf_checksum = 0;
+    u16 hlic_len = 0;
+
+    hlic_len = (u16)(((u16)buf[LIC_FS_H_OFF]) << 8) + buf[LIC_FS_L_OFF];
+    FTS_INFO("host lcd init code len:0x%x", hlic_len);
+    if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= 0)) {
+        FTS_ERROR("host lcd init code len(0x%x) invalid", hlic_len);
+        return -EINVAL;
+    }
+    initcode_checksum = cal_lcdinitcode_checksum(buf + 4, hlic_len - 4);
+    buf_checksum =
+        ((u16)((u16)buf[LIC_CHECKSUM_H_OFF] << 8) + buf[LIC_CHECKSUM_L_OFF]);
+    FTS_INFO("lcd init code calc checksum:0x%04x,0x%04x",
+             initcode_checksum, buf_checksum);
+    if (initcode_checksum != buf_checksum) {
+        FTS_ERROR("Initial Code checksum fail");
+        return -EINVAL;
+    }
+
+    ret = cal_lcdinitcode_ecc(buf, &initcode_ecc);
+    if (ret < 0) {
+        FTS_ERROR("lcd init code ecc calculate fail");
+        return ret;
+    }
+    buf_ecc = ((u16)((u16)buf[LIC_ECC_H_OFF] << 8) + buf[LIC_ECC_L_OFF]);
+    FTS_INFO("lcd init code cal ecc:%04x, %04x", initcode_ecc, buf_ecc);
+    if (initcode_ecc != buf_ecc) {
+        FTS_ERROR("Initial Code ecc check fail");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int fts_ft8756_get_hlic_ver(u8 *initcode)
+{
+    u8 *hlic_buf = initcode;
+    u16 hlic_len = 0;
+    u8 hlic_ver[2] = { 0 };
+
+    hlic_len =
+        (u16)(((u16)hlic_buf[LIC_FS_H_OFF]) << 8) + hlic_buf[LIC_FS_L_OFF];
+    FTS_INFO("host lcd init code len:0x%x", hlic_len);
+    if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= 0)) {
+        FTS_ERROR("host lcd init code len(0x%x) invalid", hlic_len);
+        return -EINVAL;
+    }
+
+    hlic_ver[0] = hlic_buf[hlic_len];
+    hlic_ver[1] = hlic_buf[hlic_len + 1];
+
+    FTS_INFO("host lcd init code ver:0x%x 0x%x", hlic_ver[0], hlic_ver[1]);
+    if (0xFF != (hlic_ver[0] + hlic_ver[1])) {
+        FTS_ERROR("host lcd init code version check fail");
+        return -EINVAL;
+    }
+
+    return hlic_ver[0];
+}
+
+static int fts_ft8756_upgrade_mode(enum FW_FLASH_MODE mode, u8 *buf, u32 len)
+{
+    int ret = 0;
+    u32 start_addr = 0;
+    u8 cmd[4] = { 0 };
+    u32 delay = 0;
+    int ecc_in_host = 0;
+    int ecc_in_tp = 0;
+
+    if ((NULL == buf) || (len < FTS_MIN_LEN)) {
+        FTS_ERROR("buffer/len(%x) is invalid", len);
+        return -EINVAL;
+    }
+
+    /* enter into upgrade environment */
+    ret = fts_fwupg_enter_into_boot();
+    if (ret < 0) {
+        FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret);
+        goto fw_reset;
+    }
+
+    cmd[0] = FTS_CMD_FLASH_MODE;
+    cmd[1] = FLASH_MODE_UPGRADE_VALUE;
+    if (upgrade_func_ft8756.appoff_handle_in_ic) {
+        start_addr = 0; /* offset handle in pramboot */
+    } else {
+        start_addr = upgrade_func_ft8756.appoff;
+    }
+    if (FLASH_MODE_LIC == mode) {
+        /* lcd initial code upgrade */
+        cmd[1] = FLASH_MODE_LIC_VALUE;
+    } else if (FLASH_MODE_PARAM == mode) {
+        cmd[1] = FLASH_MODE_PARAM_VALUE;
+    }
+    FTS_INFO("flash mode:0x%02x, start addr=0x%04x", cmd[1], start_addr);
+
+    ret = fts_write(cmd, 2);
+    if (ret < 0) {
+        FTS_ERROR("upgrade mode(09) cmd write fail");
+        goto fw_reset;
+    }
+
+    cmd[0] = FTS_CMD_APP_DATA_LEN_INCELL;
+    cmd[1] = BYTE_OFF_16(len);
+    cmd[2] = BYTE_OFF_8(len);
+    cmd[3] = BYTE_OFF_0(len);
+    ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN);
+    if (ret < 0) {
+        FTS_ERROR("data len cmd write fail");
+        goto fw_reset;
+    }
+
+    delay = FTS_ERASE_SECTOR_DELAY * (len / FTS_MAX_LEN_SECTOR);
+    ret = fts_fwupg_erase(delay);
+    if (ret < 0) {
+        FTS_ERROR("erase cmd write fail");
+        goto fw_reset;
+    }
+
+    /* write app */
+    ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1);
+    if (ecc_in_host < 0 ) {
+        FTS_ERROR("flash write fail");
+        goto fw_reset;
+    }
+
+    /* ecc */
+    ecc_in_tp = fts_fwupg_ecc_cal(start_addr, len);
+    if (ecc_in_tp < 0 ) {
+        FTS_ERROR("ecc read fail");
+        goto fw_reset;
+    }
+
+    FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+    if (ecc_in_tp != ecc_in_host) {
+        FTS_ERROR("ecc check fail");
+        goto fw_reset;
+    }
+
+    FTS_INFO("upgrade success, reset to normal boot");
+    ret = fts_fwupg_reset_in_boot();
+    if (ret < 0) {
+        FTS_ERROR("reset to normal boot fail");
+    }
+
+    fts_msleep(400);
+    return 0;
+
+fw_reset:
+    return -EIO;
+}
+
+/************************************************************************
+* Name: fts_ft8756_upgrade
+* Brief:
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_ft8756_upgrade(u8 *buf, u32 len)
+{
+    int ret = 0;
+    u8 *tmpbuf = NULL;
+    u32 app_len = 0;
+
+    FTS_INFO("fw app upgrade...");
+    if (NULL == buf) {
+        FTS_ERROR("fw buf is null");
+        return -EINVAL;
+    }
+
+    if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) {
+        FTS_ERROR("fw buffer len(%x) fail", len);
+        return -EINVAL;
+    }
+
+    app_len = len - upgrade_func_ft8756.appoff;
+    tmpbuf = buf + upgrade_func_ft8756.appoff;
+    ret = fts_ft8756_upgrade_mode(FLASH_MODE_APP, tmpbuf, app_len);
+    if (ret < 0) {
+        FTS_INFO("fw upgrade fail,reset to normal boot");
+        if (fts_fwupg_reset_in_boot() < 0) {
+            FTS_ERROR("reset to normal boot fail");
+        }
+        return ret;
+    }
+
+    return 0;
+}
+
+static int fts_ft8756_lic_upgrade(u8 *buf, u32 len)
+{
+    int ret = 0;
+    u8 *tmpbuf = NULL;
+    u32 lic_len = 0;
+
+    FTS_INFO("lcd initial code upgrade...");
+    if (NULL == buf) {
+        FTS_ERROR("lcd initial code buffer is null");
+        return -EINVAL;
+    }
+
+    if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) {
+        FTS_ERROR("lcd initial code buffer len(%x) fail", len);
+        return -EINVAL;
+    }
+
+    ret = check_initial_code_valid(buf);
+    if (ret < 0) {
+        FTS_ERROR("initial code invalid, not upgrade lcd init code");
+        return -EINVAL;
+    }
+
+    /* remalloc memory for initcode, need change content of initcode afterwise */
+    lic_len = FTS_MAX_LEN_SECTOR;
+    tmpbuf = kzalloc(lic_len, GFP_KERNEL);
+    if (NULL == tmpbuf) {
+        FTS_ERROR("initial code buf malloc fail");
+        return -EINVAL;
+    }
+    memcpy(tmpbuf, buf, lic_len);
+
+    ret = fts_ft8756_upgrade_mode(FLASH_MODE_LIC, tmpbuf, lic_len);
+    if (ret < 0) {
+        FTS_INFO("lcd initial code upgrade fail,reset to normal boot");
+        if (fts_fwupg_reset_in_boot() < 0) {
+            FTS_ERROR("reset to normal boot fail");
+        }
+        if (tmpbuf) {
+            kfree(tmpbuf);
+            tmpbuf = NULL;
+        }
+        return ret;
+    }
+
+    if (tmpbuf) {
+        kfree(tmpbuf);
+        tmpbuf = NULL;
+    }
+    return 0;
+}
+
+struct upgrade_func upgrade_func_ft8756 = {
+    .ctype = {0x15, 0x18, 0x23},
+    .fwveroff = 0x110E,
+    .fwcfgoff = 0xF80,
+    .appoff = 0x1000,
+    .licoff = 0x0000,
+    .appoff_handle_in_ic = true,
+    .pramboot_supported = true,
+    .pramboot = pb_file_ft8756,
+    .pb_length = sizeof(pb_file_ft8756),
+    .pram_ecc_check_mode = ECC_CHECK_MODE_CRC16,
+    .new_return_value_from_ic = true,
+    .hid_supported = false,
+    .upgrade = fts_ft8756_upgrade,
+    .get_hlic_ver = fts_ft8756_get_hlic_ver,
+    .lic_upgrade = fts_ft8756_lic_upgrade,
+};
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_gesture.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_gesture.c
new file mode 100755
index 0000000..29a22e4
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_gesture.c
@@ -0,0 +1,511 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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"
+
+/******************************************************************************
+* 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
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+/*
+* gesture_id    - mean which gesture is recognised
+* point_num     - points number of this gesture
+* coordinate_x  - All gesture point x coordinate
+* coordinate_y  - All gesture point y coordinate
+* mode          - gesture enable/disable, need enable by host
+*               - 1:enable gesture function(default)  0:disable
+* active        - gesture work flag,
+*                 always set 1 when suspend, set 0 when resume
+*/
+struct fts_gesture_st {
+    u8 gesture_id;
+    u8 point_num;
+    u16 coordinate_x[FTS_GESTURE_POINTS_MAX];
+    u16 coordinate_y[FTS_GESTURE_POINTS_MAX];
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_gesture_st fts_gesture_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)
+{
+    int count = 0;
+    u8 val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    fts_read_reg(FTS_REG_GESTURE_EN, &val);
+    count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n",
+                     ts_data->gesture_support ? "On" : "Off");
+    count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_gesture_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    if (ts_data->suspended) {
+        FTS_INFO("In suspend,not operation gesture mode!");
+        return count;
+    }
+    mutex_lock(&ts_data->input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_DEBUG("enable gesture");
+        ts_data->gesture_support = ENABLE;
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("disable gesture");
+        ts_data->gesture_support = DISABLE;
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_gesture_buf_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    int i = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+    struct fts_gesture_st *gesture = &fts_gesture_data;
+
+    mutex_lock(&input_dev->mutex);
+    count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n", gesture->gesture_id);
+    count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n",
+                      gesture->point_num);
+    count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n");
+
+    /* save point data,max:6 */
+    for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
+        count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i,
+                          gesture->coordinate_x[i], gesture->coordinate_y[i]);
+        if ((i + 1) % 4 == 0)
+            count += snprintf(buf + count, PAGE_SIZE, "\n");
+    }
+    count += snprintf(buf + count, PAGE_SIZE, "\n");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_gesture_buf_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    return -EPERM;
+}
+
+static ssize_t fts_gesture_bm_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    count = snprintf(buf, PAGE_SIZE, "gesture bmode:%d\n",
+                     ts_data->gesture_bmode);
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_gesture_bm_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    int value = 0xFF;
+    int ret = 0;
+
+    mutex_lock(&ts_data->input_dev->mutex);
+    ret = sscanf(buf, "%d", &value);
+    if (ret == 1) {
+        FTS_DEBUG("gesture bmode:%d->%d", ts_data->gesture_bmode, value);
+        ts_data->gesture_bmode = value;
+    }
+    mutex_unlock(&ts_data->input_dev->mutex);
+
+    return count;
+}
+
+/* sysfs gesture node
+ *   read example: cat  fts_gesture_mode       ---read gesture mode
+ *   write example:echo 1 > fts_gesture_mode   --- write gesture mode to 1
+ *
+ */
+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 DEVICE_ATTR(fts_gesture_bm, S_IRUGO | S_IWUSR,
+                   fts_gesture_bm_show, fts_gesture_bm_store);
+
+static struct attribute *fts_gesture_mode_attrs[] = {
+    &dev_attr_fts_gesture_mode.attr,
+    &dev_attr_fts_gesture_buf.attr,
+    &dev_attr_fts_gesture_bm.attr,
+    NULL,
+};
+
+static struct attribute_group fts_gesture_group = {
+    .attrs = fts_gesture_mode_attrs,
+};
+
+static int fts_create_gesture_sysfs(struct device *dev)
+{
+    int ret = 0;
+
+    ret = sysfs_create_group(&dev->kobj, &fts_gesture_group);
+    if (ret) {
+        FTS_ERROR("gesture sys node create fail");
+        sysfs_remove_group(&dev->kobj, &fts_gesture_group);
+        return ret;
+    }
+
+    return 0;
+}
+
+static void fts_gesture_report(struct input_dev *input_dev, int gesture_id)
+{
+    int gesture;
+
+    FTS_DEBUG("gesture_id:0x%x", gesture_id);
+    switch (gesture_id) {
+    case GESTURE_LEFT:
+        gesture = KEY_GESTURE_LEFT;
+        break;
+    case GESTURE_RIGHT:
+        gesture = KEY_GESTURE_RIGHT;
+        break;
+    case GESTURE_UP:
+        gesture = KEY_GESTURE_UP;
+        break;
+    case GESTURE_DOWN:
+        gesture = KEY_GESTURE_DOWN;
+        break;
+    case GESTURE_DOUBLECLICK:
+        gesture = KEY_GESTURE_U;
+        break;
+    case GESTURE_O:
+        gesture = KEY_GESTURE_O;
+        break;
+    case GESTURE_W:
+        gesture = KEY_GESTURE_W;
+        break;
+    case GESTURE_M:
+        gesture = KEY_GESTURE_M;
+        break;
+    case GESTURE_E:
+        gesture = KEY_GESTURE_E;
+        break;
+    case GESTURE_L:
+        gesture = KEY_GESTURE_L;
+        break;
+    case GESTURE_S:
+        gesture = KEY_GESTURE_S;
+        break;
+    case GESTURE_V:
+        gesture = KEY_GESTURE_V;
+        break;
+    case GESTURE_Z:
+        gesture = KEY_GESTURE_Z;
+        break;
+    case  GESTURE_C:
+        gesture = KEY_GESTURE_C;
+        break;
+    default:
+        gesture = -1;
+        break;
+    }
+    /* report event key */
+    if (gesture != -1) {
+        FTS_DEBUG("Gesture Code=%d", gesture);
+        input_report_key(input_dev, gesture, 1);
+        input_sync(input_dev);
+        input_report_key(input_dev, gesture, 0);
+        input_sync(input_dev);
+    }
+}
+
+/*****************************************************************************
+* Name: fts_gesture_readdata
+* Brief: Read information about gesture: enable flag/gesture points..., if ges-
+*        ture enable, save gesture points' information, and report to OS.
+*        It will be called this function every intrrupt when gesture is supported.
+*
+*        gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4
+* Input: ts_data - global struct data
+*        data    - gesture data buffer
+* Output:
+* Return: 0 - read gesture data successfully, the report data is gesture data
+*         1 - tp not in suspend/gesture not enable in TP FW
+*         -Exx - error
+*****************************************************************************/
+int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *touch_buf)
+{
+    int ret = 0;
+    int i = 0;
+    int index = 0;
+    u8 buf[FTS_GESTURE_DATA_LEN] = { 0 };
+    u8 gesture_en = 0xFF;
+    struct input_dev *input_dev = ts_data->input_dev;
+    struct fts_gesture_st *gesture = &fts_gesture_data;
+
+    ret = fts_read_reg(FTS_REG_GESTURE_EN, &gesture_en);
+    if (gesture_en != ENABLE) {
+        FTS_DEBUG("gesture not enable in fw, don't process gesture");
+        return 0;
+    }
+
+    if ((ts_data->gesture_bmode == GESTURE_BM_TOUCH) && touch_buf &&
+        (TOUCH_DEFAULT == ((touch_buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F))) {
+        memcpy(buf, touch_buf + FTS_TOUCH_DATA_LEN, FTS_GESTURE_DATA_LEN);
+    } else {
+        buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+        ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2);
+        if (ret < 0) {
+            FTS_ERROR("read gesture header data fail");
+            return ret;
+        }
+    }
+
+    /* init variable before read gesture point */
+    memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
+    memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
+    gesture->gesture_id = buf[2];
+    gesture->point_num = buf[3];
+    FTS_DEBUG("gesture_id=%d, point_num=%d",
+              gesture->gesture_id, gesture->point_num);
+
+    /* save point data,max:6 */
+    for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
+        index = 4 * i + 4;
+        gesture->coordinate_x[i] = (u16)(((buf[0 + index] & 0x0F) << 8)
+                                         + buf[1 + index]);
+        gesture->coordinate_y[i] = (u16)(((buf[2 + index] & 0x0F) << 8)
+                                         + buf[3 + index]);
+    }
+
+    /* report gesture to OS */
+    fts_gesture_report(input_dev, gesture->gesture_id);
+    return FTS_RETVAL_IGNORE_TOUCHES;
+}
+
+void fts_gesture_recovery(struct fts_ts_data *ts_data)
+{
+    u8 state = 0xFF;
+    if (ts_data->gesture_support && ts_data->suspended) {
+        fts_write_reg(0xD1, 0xFF);
+        fts_write_reg(0xD2, 0xFF);
+        fts_write_reg(0xD5, 0xFF);
+        fts_write_reg(0xD6, 0xFF);
+        fts_write_reg(0xD7, 0xFF);
+        fts_write_reg(0xD8, 0xFF);
+        fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
+        fts_msleep(1);
+        fts_read_reg(FTS_REG_GESTURE_EN, &state);
+        if (state != ENABLE) {
+            FTS_ERROR("set gesture mode failed");
+        }
+    }
+}
+
+int fts_gesture_suspend(struct fts_ts_data *ts_data)
+{
+    int i = 0;
+    u8 state = 0xFF;
+
+    FTS_FUNC_ENTER();
+
+    for (i = 0; i < FTS_MAX_RETRIES_WRITEREG; i++) {
+        fts_write_reg(0xD1, 0xFF);
+        fts_write_reg(0xD2, 0xFF);
+        fts_write_reg(0xD5, 0xFF);
+        fts_write_reg(0xD6, 0xFF);
+        fts_write_reg(0xD7, 0xFF);
+        fts_write_reg(0xD8, 0xFF);
+        fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
+        fts_msleep(1);
+        fts_read_reg(FTS_REG_GESTURE_EN, &state);
+        if (state == ENABLE)
+            break;
+    }
+
+    if (i >= FTS_MAX_RETRIES_WRITEREG)
+        FTS_ERROR("make IC enter into gesture(suspend) fail,state:%x", state);
+    else
+        FTS_INFO("Enter into gesture(suspend) successfully");
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+int fts_gesture_resume(struct fts_ts_data *ts_data)
+{
+    int i = 0;
+    u8 state = 0xFF;
+
+    FTS_FUNC_ENTER();
+    for (i = 0; i < FTS_MAX_RETRIES_WRITEREG; i++) {
+        fts_write_reg(FTS_REG_GESTURE_EN, DISABLE);
+        fts_msleep(1);
+        fts_read_reg(FTS_REG_GESTURE_EN, &state);
+        if (state == DISABLE)
+            break;
+    }
+
+    if (i >= FTS_MAX_RETRIES_WRITEREG)
+        FTS_ERROR("make IC exit gesture(resume) fail,state:%x", state);
+    else
+        FTS_INFO("resume from gesture successfully");
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+int fts_gesture_init(struct fts_ts_data *ts_data)
+{
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    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(ts_data->dev);
+
+    memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st));
+    ts_data->gesture_bmode = GESTURE_BM_REG;
+    ts_data->gesture_support = DISABLE;
+
+    if (ts_data->bus_type == BUS_TYPE_SPI) {
+        if ((ts_data->ic_info.ids.type <= 0x25)
+            || (ts_data->ic_info.ids.type == 0x87)
+            || (ts_data->ic_info.ids.type == 0x88)) {
+            FTS_INFO("ic type:0x%02x,GESTURE_BM_TOUCH", ts_data->ic_info.ids.type);
+            ts_data->touch_size += FTS_GESTURE_DATA_LEN;
+            ts_data->gesture_bmode = GESTURE_BM_TOUCH;
+        }
+    }
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+int fts_gesture_exit(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+    sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group);
+    FTS_FUNC_EXIT();
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_i2c.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_i2c.c
new file mode 100755
index 0000000..f38f754
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_i2c.c
@@ -0,0 +1,335 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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: Focaltech Driver Team
+*
+* Created: 2016-08-04
+*
+* Abstract: i2c communication with TP
+*
+* Version: v2.0
+*
+* Revision History:
+*
+************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define I2C_RETRY_NUMBER                    3
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
+{
+    int ret = 0;
+    int i = 0;
+    struct fts_ts_data *ts_data = fts_data;
+    struct i2c_msg msg_list[2];
+    struct i2c_msg *msg = NULL;
+    int msg_num = 0;
+
+    /* must have data when read */
+    if (!ts_data || !ts_data->client || !data || !datalen
+        || (datalen > FTS_MAX_BUS_BUF) || (cmdlen > FTS_MAX_BUS_BUF)) {
+        FTS_ERROR("fts_data/client/cmdlen(%d)/data/datalen(%d) is invalid",
+                  cmdlen, datalen);
+        return -EINVAL;
+    }
+
+    mutex_lock(&ts_data->bus_lock);
+    memset(&msg_list[0], 0, sizeof(struct i2c_msg));
+    memset(&msg_list[1], 0, sizeof(struct i2c_msg));
+    memcpy(ts_data->bus_tx_buf, cmd, cmdlen);
+    msg_list[0].addr = ts_data->client->addr;
+    msg_list[0].flags = 0;
+    msg_list[0].len = cmdlen;
+    msg_list[0].buf = ts_data->bus_tx_buf;
+    msg_list[1].addr = ts_data->client->addr;
+    msg_list[1].flags = I2C_M_RD;
+    msg_list[1].len = datalen;
+    msg_list[1].buf = ts_data->bus_rx_buf;
+    if (cmd && cmdlen) {
+        msg = &msg_list[0];
+        msg_num = 2;
+    } else {
+        msg = &msg_list[1];
+        msg_num = 1;
+    }
+
+    for (i = 0; i < I2C_RETRY_NUMBER; i++) {
+        ret = i2c_transfer(ts_data->client->adapter, msg, msg_num);
+        if (ret < 0) {
+            FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret);
+        } else {
+            memcpy(data, ts_data->bus_rx_buf, datalen);
+            break;
+        }
+    }
+
+    mutex_unlock(&ts_data->bus_lock);
+    return ret;
+}
+
+int fts_write(u8 *writebuf, u32 writelen)
+{
+    int ret = 0;
+    int i = 0;
+    struct fts_ts_data *ts_data = fts_data;
+    struct i2c_msg msgs;
+
+    if (!ts_data || !ts_data->client || !writebuf || !writelen
+        || (writelen > FTS_MAX_BUS_BUF)) {
+        FTS_ERROR("fts_data/client/data/datalen(%d) is invalid", writelen);
+        return -EINVAL;
+    }
+
+    mutex_lock(&ts_data->bus_lock);
+    memset(&msgs, 0, sizeof(struct i2c_msg));
+    memcpy(ts_data->bus_tx_buf, writebuf, writelen);
+    msgs.addr = ts_data->client->addr;
+    msgs.flags = 0;
+    msgs.len = writelen;
+    msgs.buf = ts_data->bus_tx_buf;
+    for (i = 0; i < I2C_RETRY_NUMBER; i++) {
+        ret = i2c_transfer(ts_data->client->adapter, &msgs, 1);
+        if (ret < 0) {
+            FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret);
+        } else {
+            break;
+        }
+    }
+    mutex_unlock(&ts_data->bus_lock);
+    return ret;
+}
+
+int fts_read_reg(u8 addr, u8 *value)
+{
+    return fts_read(&addr, 1, value, 1);
+}
+
+int fts_write_reg(u8 addr, u8 value)
+{
+    u8 buf[2] = { 0 };
+
+    buf[0] = addr;
+    buf[1] = value;
+    return fts_write(buf, sizeof(buf));
+}
+
+int fts_bus_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen)
+{
+    return 0;
+}
+
+int fts_bus_configure(struct fts_ts_data *ts_data, u8 *buf, u32 size)
+{
+    FTS_FUNC_ENTER();
+    if (ts_data->client && buf && size && (buf[0] != ts_data->client->addr)) {
+        ts_data->client->addr = buf[0];
+        FTS_INFO("Change i2c addr 0x%x to 0x%x", (ts_data->client->addr << 1), (buf[0] << 1));
+    }
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+int fts_bus_set_speed(struct fts_ts_data *ts_data, u32 speed)
+{
+    return 0;
+}
+
+
+/*****************************************************************************
+* TP Driver
+*****************************************************************************/
+static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+    int ret = 0;
+    struct fts_ts_data *ts_data = NULL;
+
+    FTS_INFO("Touch Screen(I2C BUS) driver prboe...");
+    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+        FTS_ERROR("I2C not supported");
+        return -ENODEV;
+    }
+
+    /* malloc memory for global struct variable */
+    ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL);
+    if (!ts_data) {
+        FTS_ERROR("allocate memory for fts_data fail");
+        return -ENOMEM;
+    }
+
+    ts_data->client = client;
+    ts_data->dev = &client->dev;
+    ts_data->log_level = 1;
+    ts_data->fw_is_running = 0;
+    ts_data->bus_type = BUS_TYPE_I2C;
+    ts_data->bus_ver = BUS_VER_DEFAULT;
+    i2c_set_clientdata(client, ts_data);
+
+    ret = fts_ts_probe_entry(ts_data);
+    if (ret) {
+        FTS_ERROR("Touch Screen(I2C BUS) driver probe fail");
+        i2c_set_clientdata(client, NULL);
+        kfree_safe(ts_data);
+        return ret;
+    }
+
+    FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully");
+    return 0;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
+static void fts_ts_remove(struct i2c_client *client)
+{
+    struct fts_ts_data *ts_data = i2c_get_clientdata(client);
+    FTS_INFO("Touch Screen(I2C BUS) driver remove...");
+    if (ts_data) {
+        fts_ts_remove_entry(ts_data);
+        i2c_set_clientdata(client, NULL);
+        kfree_safe(ts_data);
+    }
+}
+#else
+static int fts_ts_remove(struct i2c_client *client)
+{
+    struct fts_ts_data *ts_data = i2c_get_clientdata(client);
+    FTS_INFO("Touch Screen(I2C BUS) driver remove...");
+    if (ts_data) {
+        fts_ts_remove_entry(ts_data);
+        i2c_set_clientdata(client, NULL);
+        kfree_safe(ts_data);
+    }
+    return 0;
+}
+#endif
+
+static void fts_ts_shutdown(struct i2c_client *client)
+{
+    struct fts_ts_data *ts_data = i2c_get_clientdata(client);
+    FTS_FUNC_ENTER();
+    if (ts_data) {
+        fts_ts_remove_entry(ts_data);
+        i2c_set_clientdata(client, NULL);
+        kfree_safe(ts_data);
+    }
+    FTS_FUNC_EXIT();
+}
+
+#if IS_ENABLED(CONFIG_PM) && FTS_PATCH_COMERR_PM
+static int fts_pm_suspend(struct device *dev)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    FTS_INFO("system enters into pm_suspend");
+    ts_data->pm_suspend = true;
+    reinit_completion(&ts_data->pm_completion);
+    return 0;
+}
+
+static int fts_pm_resume(struct device *dev)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+
+    FTS_INFO("system resumes from pm_suspend");
+    ts_data->pm_suspend = false;
+    complete(&ts_data->pm_completion);
+    return 0;
+}
+
+static const struct dev_pm_ops fts_dev_pm_ops = {
+    .suspend = fts_pm_suspend,
+    .resume = fts_pm_resume,
+};
+#endif
+
+static const struct i2c_device_id fts_ts_id[] = {
+    {FTS_DRIVER_NAME, 0},
+    {},
+};
+static const struct of_device_id fts_dt_match[] = {
+    {.compatible = "focaltech,fts", },
+    {},
+};
+MODULE_DEVICE_TABLE(of, fts_dt_match);
+
+static struct i2c_driver fts_ts_i2c_driver = {
+    .probe = fts_ts_probe,
+    .remove = fts_ts_remove,
+    .shutdown = fts_ts_shutdown,
+    .driver = {
+        .name = FTS_DRIVER_NAME,
+        .owner = THIS_MODULE,
+#if IS_ENABLED(CONFIG_PM) && FTS_PATCH_COMERR_PM
+        .pm = &fts_dev_pm_ops,
+#endif
+        .of_match_table = of_match_ptr(fts_dt_match),
+    },
+    .id_table = fts_ts_id,
+};
+
+static int __init fts_ts_i2c_init(void)
+{
+    int ret = 0;
+
+    FTS_FUNC_ENTER();
+    ret = i2c_add_driver(&fts_ts_i2c_driver);
+    if ( ret != 0 ) {
+        FTS_ERROR("Focaltech touch screen driver(I2C) init failed!");
+    }
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+static void __exit fts_ts_i2c_exit(void)
+{
+    i2c_del_driver(&fts_ts_i2c_driver);
+}
+
+module_init(fts_ts_i2c_init);
+module_exit(fts_ts_i2c_exit);
+
+MODULE_AUTHOR("FocalTech Driver Team");
+MODULE_DESCRIPTION("FocalTech Touchscreen Driver(I2C)");
+MODULE_LICENSE("GPL v2");
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_point_report_check.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_point_report_check.c
new file mode 100755
index 0000000..8b117a8
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_point_report_check.c
@@ -0,0 +1,185 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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: Focaltech Driver Team
+*
+* Created: 2016-11-16
+*
+* Abstract: point report check function
+*
+* Version: v1.0
+*
+* Revision History:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define POINT_REPORT_CHECK_WAIT_TIME                200    /* unit:ms */
+#define PRC_INTR_INTERVALS                          100    /* unit:ms */
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+*  Name: fts_prc_func
+*  Brief: fts point report check work func, report whole up of points
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_prc_func(struct work_struct *work)
+{
+    struct fts_ts_data *ts_data = container_of(work,
+                                  struct fts_ts_data, prc_work.work);
+    unsigned long cur_jiffies = jiffies;
+    unsigned long intr_timeout = msecs_to_jiffies(PRC_INTR_INTERVALS);
+
+    if (ts_data->prc_support && !ts_data->suspended) {
+        intr_timeout += ts_data->intr_jiffies;
+        if (time_after(cur_jiffies, intr_timeout)) {
+            if (ts_data->touch_points) {
+                fts_release_all_finger();
+                if (ts_data->log_level >= 3)
+                    FTS_DEBUG("prc trigger interval:%dms",
+                              jiffies_to_msecs(cur_jiffies - ts_data->intr_jiffies));
+            }
+            ts_data->prc_mode = 0;
+        } else {
+            queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work,
+                               msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
+            ts_data->prc_mode = 1;
+        }
+    } else {
+        ts_data->prc_mode = 0;
+    }
+}
+
+/*****************************************************************************
+*  Name: fts_prc_queue_work
+*  Brief: fts point report check queue work, call it when interrupt comes
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_prc_queue_work(struct fts_ts_data *ts_data)
+{
+    if (ts_data->prc_support && !ts_data->prc_mode && !ts_data->suspended) {
+        queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work,
+                           msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
+        ts_data->prc_mode = 1;
+    }
+}
+
+
+static ssize_t fts_prc_store(
+    struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        FTS_DEBUG("enable prc");
+        ts_data->prc_support = ENABLE;
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("disable prc");
+        cancel_delayed_work_sync(&ts_data->prc_work);
+        ts_data->prc_support = DISABLE;
+    }
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static ssize_t fts_prc_show(
+    struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct input_dev *input_dev = ts_data->input_dev;
+
+    mutex_lock(&input_dev->mutex);
+    count = snprintf(buf, PAGE_SIZE, "PRC: %s\n", \
+                     ts_data->prc_support ? "Enable" : "Disable");
+    mutex_unlock(&input_dev->mutex);
+
+    return count;
+}
+
+static DEVICE_ATTR(fts_prc, S_IRUGO | S_IWUSR, fts_prc_show, fts_prc_store);
+
+/*****************************************************************************
+*  Name: fts_point_report_check_init
+*  Brief:
+*  Input:
+*  Output:
+*  Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_point_report_check_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+
+    FTS_FUNC_ENTER();
+
+    if (ts_data->ts_workqueue) {
+        INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
+    } else {
+        FTS_ERROR("fts workqueue is NULL, can't run point report check function");
+        return -EINVAL;
+    }
+
+    ret = sysfs_create_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr);
+    if ( ret < 0) {
+        FTS_ERROR("create prc sysfs fail");
+    }
+
+    ts_data->prc_support = FTS_POINT_REPORT_CHECK_EN;
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_point_report_check_exit
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_point_report_check_exit(struct fts_ts_data *ts_data)
+{
+    FTS_FUNC_ENTER();
+    cancel_delayed_work_sync(&ts_data->prc_work);
+    sysfs_remove_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr);
+    FTS_FUNC_EXIT();
+    return 0;
+}
diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_proximity.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_proximity.c
new file mode 100755
index 0000000..b656e0a
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/focaltech_touch_ft8756/focaltech_proximity.c
@@ -0,0 +1,827 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2020, 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_proximity.c
+*
+*    Author: Focaltech Driver Team
+*
+*   Created: 2016-09-19
+*
+*  Abstract: close proximity function
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release based on xiaguobin's solution. By luougojin 2016-08-19
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#include "focaltech_common.h"
+
+#if FTS_PSENSOR_EN
+/*****************************************************************************
+ * Private constant and macro definitions using #define
+ *****************************************************************************/
+/* proximity register address*/
+#define FTS_REG_PSENSOR_ENABLE                  0xB0
+#define FTS_REG_PSENSOR_STATUS                  0xB5
+#define FTS_REG_PSENSOR_CLEAR_STATUS            0xB6
+
+/* proximity register value read from TP */
+#define PROXIMITY_TP_VAL_NEAR                   0xC0
+#define PROXIMITY_TP_VAL_FAR                    0xE0
+#define PROXIMITY_TP_VAL_ERROR                  0xEE
+#define PROXIMITY_TP_VAL_DEFAULT                0xFF
+
+/* host state : far or near */
+#define PROXIMITY_HOST_STATE_NEAR               0
+#define PROXIMITY_HOST_STATE_FAR                1
+#define PROXIMITY_HOST_STATE_DEFAULT            PROXIMITY_HOST_STATE_FAR
+
+/* proximity solutions */
+#define PROXIMITY_SOLUTION_SAMPLE               0
+#define PROXIMITY_SOLUTION_QCOM                 1
+#define PROXIMITY_SOLUTION_MTK                  2
+#define PROXIMITY_SOLUTION_SAMPLE_1             3
+#define PROXIMITY_SOLUTION                      PROXIMITY_SOLUTION_SAMPLE
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_proximity_ops;
+
+/*
+ * @tp_val, proximity value read from TP register, the value is:
+ *          PROXIMITY_TP_VAL_NEAR/PROXIMITY_TP_VAL_FAR and so on.
+ *
+ * @tp_val_last, the backup proximity value
+ * @host_state, the proximity state of host, tp driver will report the
+ *      value to Android, the value is:
+ *          PROXIMITY_HOST_STATE_NEAR
+ *          PROXIMITY_HOST_STATE_FAR
+ */
+struct fts_proximity {
+    struct fts_ts_data *ts_data;
+    struct input_dev *proximity_input_dev;
+    struct fts_proximity_ops *ops;
+    u8 tp_val;
+    u8 tp_val_last;
+    int host_state;
+};
+
+struct fts_proximity_ops {
+    int (*init)(struct fts_proximity *proximity_data);
+    int (*exit)(struct fts_proximity *proximity_data);
+    int (*report)(struct fts_proximity *proximity_data);
+};
+
+/*****************************************************************************
+* variables or functions
+*****************************************************************************/
+static struct fts_proximity fts_proximity_data;
+
+
+static void fts_proximity_set_reg(int value)
+{
+    int i = 0;
+    u8 enable_value = value ? 0x01 : 0x00;
+    u8 regval = 0xFF;
+
+    for (i = 0; i < FTS_MAX_RETRIES_WRITEREG; i++) {
+        fts_read_reg(FTS_REG_PSENSOR_ENABLE, &regval);
+        if (regval == enable_value)
+            break;
+        fts_write_reg(FTS_REG_PSENSOR_ENABLE, enable_value);
+        fts_msleep(1);
+    }
+
+    if (i >= FTS_MAX_RETRIES_WRITEREG)
+        FTS_ERROR("set proximity mode to %x failed,reg_val:%x", enable_value, regval);
+    else if (i > 0)
+        FTS_INFO("set proximity mode to %x successfully", value);
+}
+
+/************************************************************************
+* Name: fts_proximity_enable
+* Brief:  enable or disable proximity function, set variable and write it
+*         to TP FW.
+*
+* Input:  proximity_data, global structure variable.
+*         @enable, 0 is to disable proximity, 1 to enable.
+* Output:
+*
+* Return: 0 for success
+***********************************************************************/
+static int fts_proximity_enable(struct fts_proximity *proximity_data, int enable)
+{
+    int ret = 0;
+    if (!proximity_data || !proximity_data->ts_data || !proximity_data->proximity_input_dev) {
+        FTS_ERROR("proximity/ts/input is null");
+        return -EINVAL;
+    }
+
+    FTS_INFO("set proximity mode to %s", !!enable ? "enable" : "disable");
+    mutex_lock(&proximity_data->proximity_input_dev->mutex);
+    proximity_data->ts_data->proximity_mode = !!enable;
+    fts_proximity_set_reg(enable);
+    proximity_data->tp_val = PROXIMITY_TP_VAL_DEFAULT;
+    proximity_data->tp_val_last = PROXIMITY_TP_VAL_DEFAULT;
+    proximity_data->host_state = PROXIMITY_HOST_STATE_DEFAULT;
+    mutex_unlock(&proximity_data->proximity_input_dev->mutex);
+    return ret;
+}
+
+
+
+
+#if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_MTK)
+#include <hwmsensor.h>
+#include <sensors_io.h>
+#include <alsps.h>
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+/*
+ * FTS_ALSPS_SUPPORT is choose structure hwmsen_object or control_path, data_path
+ * FTS_ALSPS_SUPPORT = 1, is control_path, data_path
+ * FTS_ALSPS_SUPPORT = 0, hwmsen_object
+ */
+#define FTS_ALSPS_SUPPORT            1
+/*
+ * FTS_OPEN_DATA_HAL_SUPPORT is choose structure ps_control_path or batch, flush
+ * FTS_ALSPS_SUPPORT = 1, is batch, flush
+ * FTS_ALSPS_SUPPORT = 0, NULL
+ */
+#define FTS_OPEN_DATA_HAL_SUPPORT    1
+
+#if !FTS_ALSPS_SUPPORT
+#include <hwmsen_dev.h>
+#endif
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+#if FTS_ALSPS_SUPPORT
+/* if use  this typ of enable , Gsensor should report inputEvent(x, y, z ,stats, div) to HAL */
+static int ps_open_report_data(int open)
+{
+    /* should queue work to report event if  is_report_input_direct=true */
+    return 0;
+}
+
+/* if use  this type of enable , Psensor only enabled but not report inputEvent to HAL */
+static int ps_enable_nodata(int en)
+{
+    int ret = 0;
+    FTS_DEBUG("[PROXIMITY]SENSOR_ENABLE value = %d", en);
+    /* Enable proximity */
+    ret = fts_proximity_enable(fts_proximity_data, en);
+    return ret;
+}
+
+static int ps_set_delay(u64 ns)
+{
+    return 0;
+}
+
+#if FTS_OPEN_DATA_HAL_SUPPORT
+static int ps_batch(int flag, int64_t sampling_period_ns, int64_t max_batch_report_ns)
+{
+    return 0;
+}
+
+static int ps_flush(void)
+{
+    return 0;
+}
+#endif
+
+static int ps_get_data(int *value, int *status)
+{
+    *value = (int)fts_proximity_data.host_state;
+    FTS_DEBUG("proximity status = %d\n", *value);
+    *status = SENSOR_STATUS_ACCURACY_MEDIUM;
+    return 0;
+}
+
+static int ps_local_init(void)
+{
+    int err = 0;
+    struct ps_control_path ps_ctl = { 0 };
+    struct ps_data_path ps_data = { 0 };
+
+    ps_ctl.is_use_common_factory = false;
+    ps_ctl.open_report_data = ps_open_report_data;
+    ps_ctl.enable_nodata = ps_enable_nodata;
+    ps_ctl.set_delay = ps_set_delay;
+#if FTS_OPEN_DATA_HAL_SUPPORT
+    ps_ctl.batch = ps_batch;
+    ps_ctl.flush = ps_flush;
+#endif
+    ps_ctl.is_report_input_direct = false;
+    ps_ctl.is_support_batch = false;
+
+    err = ps_register_control_path(&ps_ctl);
+    if (err) {
+        FTS_ERROR("register fail = %d\n", err);
+    }
+    ps_data.get_data = ps_get_data;
+    ps_data.vender_div = 100;
+    err = ps_register_data_path(&ps_data);
+    if (err) {
+        FTS_ERROR("tregister fail = %d\n", err);
+    }
+
+    return err;
+}
+int ps_local_uninit(void)
+{
+    return 0;
+}
+
+struct alsps_init_info ps_init_info = {
+    .name = "fts_ts",
+    .init = ps_local_init,
+    .uninit = ps_local_uninit,
+};
+
+#else
+
+static int mtk_ps_operate(void *self, uint32_t command, void *buff_in,
+                          int size_in, void *buff_out, int size_out, int *actualout)
+{
+    int err = 0;
+    int value;
+    struct hwm_sensor_data *sensor_data;
+    struct fts_proximity *proximity_data = &fts_proximity_data;
+
+    if (!proximity_data || !proximity_data->ts_data) {
+        FTS_ERROR("proximity_data/ts_data" is null);
+        return -EINVAL;
+    }
+
+    FTS_DEBUG("[PROXIMITY]COMMAND = %d", command);
+    switch (command) {
+    case SENSOR_DELAY:
+        if ((buff_in == NULL) || (size_in < sizeof(int))) {
+            FTS_ERROR("[PROXIMITY]Set delay parameter error!");
+            err = -EINVAL;
+        }
+        break;
+
+    case SENSOR_ENABLE:
+        if ((buff_in == NULL) || (size_in < sizeof(int))) {
+            FTS_ERROR("[PROXIMITY]Enable sensor parameter error!");
+            err = -EINVAL;
+        } else {
+            value = *(int *)buff_in;
+            FTS_DEBUG("[PROXIMITY]SENSOR_ENABLE value = %d", value);
+            /* Enable proximity */
+            err = fts_proximity_enable(proximity_data, value);
+        }
+        break;
+
+    case SENSOR_GET_DATA:
+        if ((buff_out == NULL) || (size_out < sizeof(struct hwm_sensor_data))) {
+            FTS_ERROR("[PROXIMITY]get sensor data parameter error!");
+            err = -EINVAL;
+        } else {
+            sensor_data = (struct hwm_sensor_data *)buff_out;
+            sensor_data->values[0] = (int)proximity_data->host_state;
+            FTS_DEBUG("sensor_data->values[0] = %d", sensor_data->values[0]);
+            sensor_data->value_divide = 1;
+            sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
+        }
+        break;
+    default:
+        FTS_ERROR("[PROXIMITY]ps has no operate function:%d!", command);
+        err = -EPERM;
+        break;
+    }
+
+    return err;
+}
+#endif
+
+
+static int mtk_proximity_report(struct fts_proximity *proximity_data)
+{
+    int ret = 0;
+    int proximity_state = PROXIMITY_HOST_STATE_DEFAULT;
+#if !FTS_ALSPS_SUPPORT
+    struct hwm_sensor_data sensor_data;
+#endif
+
+    if (proximity_data->tp_val == PROXIMITY_TP_VAL_NEAR) {
+        /* close. need lcd off */
+        proximity_state = PROXIMITY_HOST_STATE_NEAR;
+    } else if (proximity_data->tp_val == PROXIMITY_TP_VAL_FAR) {
+        /* far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+    } else if (proximity_data->tp_val == PROXIMITY_TP_VAL_ERROR) {
+        /* error, need report far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+    }
+
+    if (proximity_state != proximity_data->host_state) {
+        FTS_INFO("report proximity state:%s", proximity_state ? "AWAY" : "NEAR");
+        proximity_data->host_state = proximity_state;
+#if FTS_ALSPS_SUPPORT
+        ret = ps_report_interrupt_data(proximity_state);
+#else
+        sensor_data.values[0] = proximity_state;
+        sensor_data.value_divide = 1;
+        sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
+        ret = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data);
+        if (ret) {
+            FTS_ERROR("[PROXIMITY] Call hwmsen_get_interrupt_data failed, ret=%d", ret);
+            return ret;
+        }
+#endif
+        return FTS_RETVAL_IGNORE_TOUCHES;
+    }
+
+    return 0;
+}
+
+static int mtk_proximity_init(struct fts_proximity *proximity_data)
+{
+#if !FTS_ALSPS_SUPPORT
+    int err = 0;
+    struct hwmsen_object obj_ps;
+#endif
+
+    FTS_FUNC_ENTER();
+#if FTS_ALSPS_SUPPORT
+    alsps_driver_add(&ps_init_info);
+#else
+    obj_ps.polling = 0; /* interrupt mode */
+    obj_ps.sensor_operate = mtk_ps_operate;
+    err = hwmsen_attach(ID_PROXIMITY, &obj_ps);
+    if (err)
+        FTS_ERROR("[PROXIMITY]fts proximity attach fail = %d!", err);
+    else
+        FTS_INFO("[PROXIMITY]fts proximity attach ok = %d\n", err);
+#endif
+
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+struct fts_proximity_ops mtk_proximity_ops = {
+    .init = mtk_proximity_init,
+    .report = mtk_proximity_report,
+};
+#endif
+
+#if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_QCOM)
+#include <linux/sensors.h>
+
+struct qcom_proximity {
+    struct fts_proximity *proximity_data;
+    struct sensors_classdev ps_cdev;
+};
+
+static struct qcom_proximity qcom_proximity_data;
+
+static struct sensors_classdev __maybe_unused qcom_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,
+};
+
+static int qcom_proximity_enable(struct sensors_classdev *sensors_cdev, unsigned int enable)
+{
+    struct qcom_proximity *qps = container_of(sensors_cdev, struct qcom_proximity, ps_cdev);
+    if (qps && qps->proximity_data) {
+        fts_proximity_enable(qps->proximity_data, enable);
+    }
+    return enable;
+}
+
+static int qcom_proximity_report(struct fts_proximity *proximity_data)
+{
+    int proximity_state = PROXIMITY_HOST_STATE_DEFAULT;
+
+    if (!proximity_data || !proximity_data->proximity_input_dev) {
+        FTS_ERROR("proximity/input is null");
+        return -EINVAL;
+    }
+
+    if (proximity_data->tp_val == PROXIMITY_TP_VAL_NEAR) {
+        /* close. need lcd off */
+        proximity_state = PROXIMITY_HOST_STATE_NEAR;
+    } else if (proximity_data->tp_val == PROXIMITY_TP_VAL_FAR) {
+        /* far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+    } else if (proximity_data->tp_val == PROXIMITY_TP_VAL_ERROR) {
+        /* error, need report far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+    }
+
+    if (proximity_state != proximity_data->host_state) {
+        FTS_INFO("report proximity state:%s", proximity_state ? "AWAY" : "NEAR");
+        proximity_data->host_state = proximity_state;
+        input_report_abs(proximity_data->proximity_input_dev, ABS_DISTANCE,
+                         (proximity_state == PROXIMITY_HOST_STATE_NEAR) ? 0 : 1;
+                         input_sync(proximity_data->proximity_input_dev);
+                         return FTS_RETVAL_IGNORE_TOUCHES;
+    }
+
+    return 0;
+}
+
+static int qcom_proximity_init(struct fts_proximity *proximity_data)
+{
+    int ret = 0;
+    struct qcom_proximity *qps = &qcom_proximity_data;
+    FTS_FUNC_ENTER();
+    if (proximity_data && proximity_data->ts_data && proximity_data->ts_data->dev) {
+        memset(qps, 0, sizeof(struct qcom_proximity));
+        qps->proximity_data = proximity_data;
+        qps->ps_cdev = qcom_proximity_cdev;
+        qps->ps_cdev.sensors_enable = qcom_proximity_enable;
+        ret = sensors_classdev_register(proximity_data->ts_data->dev, &qps->ps_cdev);
+        if (ret) FTS_ERROR("sensors_classdev_register failed,ret=%d", ret);
+    } else {
+        FTS_ERROR("proximity/ts/device is null");
+        ret = -EINVAL;
+    }
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+static int qcom_proximity_exit(struct fts_proximity *proximity_data)
+{
+    FTS_FUNC_ENTER();
+    sensors_classdev_unregister(qcom_proximity_data.ps_cdev);
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+struct fts_proximity_ops qcom_proximity_ops = {
+    .init = qcom_proximity_init,
+    .exit = qcom_proximity_exit,
+    .report = qcom_proximity_report,
+};
+#endif // #if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_QCOM)
+
+
+#if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_SAMPLE_1)
+static int sample_1_proximity_report(struct fts_proximity *proximity_data)
+{
+    u8 clear_status_value = 0xFF;
+    int proximity_state = PROXIMITY_HOST_STATE_DEFAULT;
+
+    if (!proximity_data || !proximity_data->proximity_input_dev) {
+        FTS_ERROR("proximity/input is null");
+        return -EINVAL;
+    }
+
+    if ((proximity_data->tp_val > 0x00) && (proximity_data->tp_val < 0x04)) {
+        /* close. need lcd off */
+        proximity_state = PROXIMITY_HOST_STATE_NEAR;
+        clear_status_value = 1;
+    } else if (proximity_data->tp_val == 0x00) {
+        /* far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+        clear_status_value = 0;
+    } else {
+        /* error, need report far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+        clear_status_value = 0xEE;
+    }
+
+    if (proximity_data->tp_val != proximity_data->tp_val_last) {
+        FTS_DEBUG("tp proximity status:%x->%x", proximity_data->tp_val_last, proximity_data->tp_val);
+        tpd_notifier_call_chain(proximity_data->host_state, NULL);
+        fts_write_reg(FTS_REG_PSENSOR_CLEAR_STATUS, clear_status_value);
+    }
+
+    if (proximity_state != proximity_data->host_state) {
+        FTS_INFO("report proximity state:%s", proximity_state ? "AWAY" : "NEAR");
+        proximity_data->host_state = proximity_state;
+        return FTS_RETVAL_IGNORE_TOUCHES;
+    }
+
+    return 0;
+}
+
+static struct fts_proximity_ops sample_1_proximity_ops = {
+    .report = sample_1_proximity_report,
+};
+#endif // #if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_SAMPLE_1)
+
+
+#if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_SAMPLE)
+static int sample_proximity_report(struct fts_proximity *proximity_data)
+{
+    int proximity_state = PROXIMITY_HOST_STATE_DEFAULT;
+    if (!proximity_data || !proximity_data->proximity_input_dev) {
+        FTS_ERROR("proximity/input is null");
+        return -EINVAL;
+    }
+
+    if (proximity_data->tp_val == PROXIMITY_TP_VAL_NEAR) {
+        /* close. need lcd off */
+        proximity_state = PROXIMITY_HOST_STATE_NEAR;
+    } else if (proximity_data->tp_val == PROXIMITY_TP_VAL_FAR) {
+        /* far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+    } else if (proximity_data->tp_val == PROXIMITY_TP_VAL_ERROR) {
+        /* error, need report far away */
+        proximity_state = PROXIMITY_HOST_STATE_FAR;
+    }
+
+    if (proximity_data->ts_data->log_level >= 3) {
+        FTS_DEBUG("tp proximity status, now:%x,before:%x", proximity_data->tp_val, proximity_data->tp_val_last);
+        FTS_DEBUG("proximity state, now:%x,before:%d", proximity_state, proximity_data->host_state);
+    }
+    if (proximity_state != proximity_data->host_state) {
+        FTS_INFO("report proximity state:%s", proximity_state ? "AWAY" : "NEAR");
+        proximity_data->host_state = proximity_state;
+        /* TODO: Report proximity state to host */
+
+
+        return FTS_RETVAL_IGNORE_TOUCHES;
+    }
+
+    return 0;
+}
+
+static int sample_proximity_init(struct fts_proximity *proximity_data)
+{
+    FTS_FUNC_ENTER();
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+static int sample_proximity_exit(struct fts_proximity *proximity_data)
+{
+    FTS_FUNC_ENTER();
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+static struct fts_proximity_ops sample_proximity_ops = {
+    .init = sample_proximity_init,
+    .exit = sample_proximity_exit,
+    .report = sample_proximity_report,
+};
+#endif // #if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_SAMPLE)
+
+
+static ssize_t fts_proximity_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+    int count = 0;
+    u8 val = 0;
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct fts_proximity *proximity_data = &fts_proximity_data;
+
+    if (proximity_data->proximity_input_dev) {
+        mutex_lock(&proximity_data->proximity_input_dev->mutex);
+        fts_read_reg(FTS_REG_PSENSOR_ENABLE, &val);
+        count = snprintf(buf, PAGE_SIZE, "Proximity Mode:%s\n", ts_data->proximity_mode ? "On" : "Off");
+        count += snprintf(buf + count, PAGE_SIZE, "Reg(0xB0)=%d\n", val);
+        mutex_unlock(&proximity_data->proximity_input_dev->mutex);
+    }
+
+    return count;
+}
+
+static ssize_t fts_proximity_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct fts_ts_data *ts_data = dev_get_drvdata(dev);
+    struct fts_proximity *proximity_data = &fts_proximity_data;
+
+    if (FTS_SYSFS_ECHO_ON(buf)) {
+        if (ts_data->suspended) {
+            FTS_INFO("In suspend,not allowed to enable proximity mode");
+        } else {
+            FTS_DEBUG("enable proximity");
+            fts_proximity_enable(proximity_data, ENABLE);
+        }
+    } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+        FTS_DEBUG("disable proximity");
+        fts_proximity_enable(proximity_data, DISABLE);
+    }
+
+    return count;
+}
+
+/* sysfs node of proximity_mode, maybe not used */
+static DEVICE_ATTR(fts_proximity_mode, S_IRUGO | S_IWUSR, fts_proximity_show, fts_proximity_store);
+static struct attribute *fts_proximity_mode_attrs[] = { &dev_attr_fts_proximity_mode.attr, NULL, };
+static struct attribute_group fts_proximity_group = {.attrs = fts_proximity_mode_attrs,};
+
+
+static fts_proximity_input_init(struct fts_proximity *proximity_data)
+{
+    int ret = 0;
+    struct input_dev *proximity_input_dev;
+
+    FTS_FUNC_ENTER();
+    if (!proximity_data || !proximity_data->ts_data || !proximity_data->ts_data->dev) {
+        FTS_ERROR("proximity_data/ts_data/device is null");
+        return -EINVAL;
+    }
+
+    proximity_input_dev = input_allocate_device();
+    if (!proximity_input_dev) {
+        FTS_ERROR("Failed to allocate memory for input_proximity device");
+        return -ENOMEM;
+    }
+
+    proximity_input_dev->dev.parent = proximity_data->ts_data->dev;
+    proximity_input_dev->name = "proximity";
+    __set_bit(EV_ABS, proximity_input_dev->evbit);
+    input_set_abs_params(proximity_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+    ret = input_register_device(proximity_input_dev);
+    if (ret) {
+        FTS_ERROR("proximity input device registration failed");
+        input_free_device(proximity_input_dev);
+        proximity_input_dev = NULL;
+        return ret;
+    }
+
+    proximity_data->proximity_input_dev = proximity_input_dev;
+    FTS_FUNC_EXIT();
+    return 0;
+}
+
+int fts_proximity_recovery(struct fts_ts_data *ts_data)
+{
+    if (ts_data->proximity_mode) {
+        fts_proximity_set_reg(ENABLE);
+    }
+    return 0;
+}
+
+/*****************************************************************************
+* Name: fts_proximity_readdata
+* Brief: read proximity value from TP, check whether tp is near or far away,
+*        and report the state to host if need.
+*
+* Input: ts_data
+* Output:
+* Return: return negative code if error occurs,return 0 or 1 if success.
+*         return 0 if continue report finger touches.
+*         return 1(FTS_RETVAL_IGNORE_TOUCHES) if you want to ingore this
+*         finger reporting, As default, the following situation will report 1:
+*               a.proximity state changed
+*               b.System in suspend state
+*****************************************************************************/
+int fts_proximity_readdata(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    struct fts_proximity *proximity_data = &fts_proximity_data;
+    u8 psensor_status = 0xFF;
+    u8 psensor_enable = 0xFF;
+
+    ret = fts_read_reg(FTS_REG_PSENSOR_ENABLE, &psensor_enable);
+    if (psensor_enable != ENABLE) {
+        FTS_DEBUG("proximity not enable in FW, don't process proximity");
+        return 0;
+    }
+
+    ret = fts_read_reg(FTS_REG_PSENSOR_STATUS, &psensor_status);
+    if (ret < 0) {
+        FTS_ERROR("read proximity value failed,ret=%d", ret);
+        proximity_data->tp_val = PROXIMITY_TP_VAL_ERROR;
+    } else {
+        if (ts_data->log_level >= 3)
+            FTS_INFO("read proximity status:0x%x", psensor_status);
+        else if (proximity_data->tp_val != psensor_status)
+            FTS_INFO("read proximity status:0x%x[%x]", psensor_status, proximity_data->tp_val);
+        proximity_data->tp_val = psensor_status;
+    }
+
+    if (proximity_data->ops->report) {
+        ret = proximity_data->ops->report(proximity_data);
+    }
+
+    proximity_data->tp_val_last = proximity_data->tp_val;
+    if (ts_data->suspended) ret = FTS_RETVAL_IGNORE_TOUCHES;
+    return ret;
+}
+
+int fts_proximity_suspend(struct fts_ts_data *ts_data)
+{
+    if (enable_irq_wake(ts_data->irq)) {
+        FTS_ERROR("enable_irq_wake(irq:%d) fail", ts_data->irq);
+    }
+    FTS_INFO("proximity mode in suspend.");
+    return 0;
+}
+
+int fts_proximity_resume(struct fts_ts_data *ts_data)
+{
+    if (disable_irq_wake(ts_data->irq)) {
+        FTS_ERROR("disable_irq_wake(irq:%d) fail", ts_data->irq);
+    }
+    fts_proximity_recovery(ts_data);
+    return 0;
+}
+
+int fts_proximity_init(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    struct fts_proximity *proximity_data = &fts_proximity_data;
+
+    FTS_FUNC_ENTER();
+    memset((u8 *)proximity_data, 0, sizeof(struct fts_proximity));
+    proximity_data->ts_data = ts_data;
+    proximity_data->tp_val = PROXIMITY_TP_VAL_DEFAULT;
+    proximity_data->tp_val_last = PROXIMITY_TP_VAL_DEFAULT;
+    proximity_data->host_state = PROXIMITY_HOST_STATE_DEFAULT;
+
+    /* TODO: initialize following platform implementation  */
+#if (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_SAMPLE)
+    proximity_data->ops = &sample_proximity_ops;
+#elif (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_QCOM)
+    proximity_data->ops = &qcom_proximity_ops;
+#elif (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_MTK)
+    proximity_data->ops = &mtk_proximity_ops;
+#elif (PROXIMITY_SOLUTION == PROXIMITY_SOLUTION_SAMPLE_1)
+    proximity_data->ops = &sample_1_proximity_ops;
+#endif
+
+    ret = fts_proximity_input_init(proximity_data);
+    if (ret) {
+        FTS_ERROR("proximity input init failed");
+        return ret;
+    }
+
+    ret = sysfs_create_group(&ts_data->dev->kobj, &fts_proximity_group);
+    if (ret) {
+        FTS_ERROR("proximity sys node create failed");
+        sysfs_remove_group(&ts_data->dev->kobj, &fts_proximity_group);
+    }
+
+    if (proximity_data->ops && proximity_data->ops->init) {
+        ret = proximity_data->ops->init(proximity_data);
+        if (ret) FTS_ERROR("proximity init failed,ret=%d", ret);
+    }
+    FTS_FUNC_EXIT();
+    return ret;
+}
+
+int fts_proximity_exit(struct fts_ts_data *ts_data)
+{
+    int ret = 0;
+    struct fts_proximity *proximity_data = &fts_proximity_data;
+    FTS_FUNC_ENTER();
+    sysfs_remove_group(&ts_data->dev->kobj, &fts_proximity_group);
+    input_unregister_device(proximity_data->proximity_input_dev);
+    if (proximity_data->ops && proximity_data->ops->exit) {
+        ret = proximity_data->ops->exit(proximity_data);
+        if (ret) FTS_ERROR("proximity exit failed,ret=%d", ret);
+    }
+    FTS_FUNC_EXIT();
+    return ret;
+}
+#endif  /* FTS_PSENSOR_EN */
+
+
diff --git a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/Makefile b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/Makefile
index f666e4f..5ee479b 100644
--- a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/Makefile
+++ b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/Makefile
@@ -29,3 +29,5 @@
 		lcd/bp101wx1-206.o lcd/m133x56-105.o lcd/m133x56-105-two.o lcd/k101im2qa04.o lcd/k101im2byl02l.o \
 		conver_chips/lt8911ex.o lcd/K101_IM2BYL02_L_800X1280.o lcd/mipi_8_800x1280.o lcd/mipi_10_800x1280.o
 disp-objs += $(obj_low)
+
+disp-objs += lcd/mipi_5_720x1280.o 
diff --git a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.c b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.c
new file mode 100755
index 0000000..c93f70e
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.c
@@ -0,0 +1,392 @@
+/* drivers/video/sunxi/disp2/disp/lcd/k101im2qa04.c
+ *
+ * Copyright (c) 2017 Allwinnertech Co., Ltd.
+ * Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#include "mipi_5_720x1280.h"
+
+static void lcd_power_on(u32 sel);
+static void lcd_power_off(u32 sel);
+static void lcd_bl_open(u32 sel);
+static void lcd_bl_close(u32 sel);
+
+static void lcd_panel_init1(u32 sel);
+static void lcd_panel_init2(u32 sel);
+static void lcd_panel_exit(u32 sel);
+
+#define panel_reset(sel, val) sunxi_lcd_gpio_set_value(sel, 0, val)
+
+static void lcd_cfg_panel_info(struct panel_extend_para *info)
+{
+	u32 i = 0, j = 0;
+	u32 items;
+	u8 lcd_gamma_tbl[][2] = {
+		{0, 0},
+		{15, 15},
+		{30, 30},
+		{45, 45},
+		{60, 60},
+		{75, 75},
+		{90, 90},
+		{105, 105},
+		{120, 120},
+		{135, 135},
+		{150, 150},
+		{165, 165},
+		{180, 180},
+		{195, 195},
+		{210, 210},
+		{225, 225},
+		{240, 240},
+		{255, 255},
+	};
+
+	u32 lcd_cmap_tbl[2][3][4] = {
+	{
+		{LCD_CMAP_G0, LCD_CMAP_B1, LCD_CMAP_G2, LCD_CMAP_B3},
+		{LCD_CMAP_B0, LCD_CMAP_R1, LCD_CMAP_B2, LCD_CMAP_R3},
+		{LCD_CMAP_R0, LCD_CMAP_G1, LCD_CMAP_R2, LCD_CMAP_G3},
+		},
+		{
+		{LCD_CMAP_B3, LCD_CMAP_G2, LCD_CMAP_B1, LCD_CMAP_G0},
+		{LCD_CMAP_R3, LCD_CMAP_B2, LCD_CMAP_R1, LCD_CMAP_B0},
+		{LCD_CMAP_G3, LCD_CMAP_R2, LCD_CMAP_G1, LCD_CMAP_R0},
+		},
+	};
+
+	items = sizeof(lcd_gamma_tbl) / 2;
+	for (i = 0; i < items - 1; i++) {
+		u32 num = lcd_gamma_tbl[i+1][0] - lcd_gamma_tbl[i][0];
+
+		for (j = 0; j < num; j++) {
+			u32 value = 0;
+
+			value = lcd_gamma_tbl[i][1] +
+				((lcd_gamma_tbl[i+1][1] - lcd_gamma_tbl[i][1])
+				* j) / num;
+			info->lcd_gamma_tbl[lcd_gamma_tbl[i][0] + j] =
+							(value<<16)
+							+ (value<<8) + value;
+		}
+	}
+	info->lcd_gamma_tbl[255] = (lcd_gamma_tbl[items-1][1]<<16) +
+					(lcd_gamma_tbl[items-1][1]<<8)
+					+ lcd_gamma_tbl[items-1][1];
+
+	memcpy(info->lcd_cmap_tbl, lcd_cmap_tbl, sizeof(lcd_cmap_tbl));
+
+}
+
+static s32 lcd_open_flow(u32 sel)
+{
+	LCD_OPEN_FUNC(sel, lcd_power_on, 50);
+	LCD_OPEN_FUNC(sel, lcd_panel_init1, 50);
+	LCD_OPEN_FUNC(sel, lcd_panel_init2, 50);
+	LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 100);
+	LCD_OPEN_FUNC(sel, lcd_bl_open, 0);
+
+	return 0;
+}
+
+static s32 lcd_close_flow(u32 sel)
+{
+	LCD_CLOSE_FUNC(sel, lcd_bl_close, 0);
+	LCD_CLOSE_FUNC(sel, lcd_panel_exit, 1);
+	LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 10);
+	LCD_CLOSE_FUNC(sel, lcd_power_off, 0);
+
+	return 0;
+}
+
+static void lcd_power_on(u32 sel)
+{
+	
+
+	panel_reset(sel, 0);
+	sunxi_lcd_power_enable(sel, 0);
+	sunxi_lcd_pin_cfg(sel, 1);
+	//sunxi_lcd_power_enable(sel, 1);
+	sunxi_lcd_delay_ms(50);
+	panel_reset(sel, 1);
+	sunxi_lcd_delay_ms(10);
+	panel_reset(sel, 0);
+	sunxi_lcd_delay_ms(20);
+	panel_reset(sel, 1);
+	sunxi_lcd_delay_ms(120);
+}
+
+static void lcd_power_off(u32 sel)
+{
+	panel_reset(sel, 0);
+	sunxi_lcd_delay_ms(1);
+	sunxi_lcd_power_disable(sel, 1);
+	sunxi_lcd_delay_ms(1);
+	sunxi_lcd_power_disable(sel, 0);
+	sunxi_lcd_pin_cfg(sel, 0);
+}
+
+static void lcd_bl_open(u32 sel)
+{
+	sunxi_lcd_pwm_enable(sel);
+	sunxi_lcd_backlight_enable(sel);
+}
+
+static void lcd_bl_close(u32 sel)
+{
+	sunxi_lcd_backlight_disable(sel);
+	sunxi_lcd_pwm_disable(sel);
+}
+
+#define ROWS 171
+#define COLS 44
+
+u8 data[ROWS][COLS] = {
+    // CMD2 ENABLE
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x04, 0xFF, 0x87, 0x56, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x03, 0xFF, 0x87, 0x56},
+    // Panel Resolution 720x1280
+    {0x15, 0x00, 0x02, 0x00, 0xA1},
+    {0x39, 0x00, 0x07, 0xB3, 0x02, 0xD0, 0x05, 0x00, 0x20, 0xFC},
+    // TCON
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xF6, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xF6, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x07, 0xC0, 0x02, 0x14, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xF6, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xC1},
+    {0x39, 0x00, 0x09, 0xC0, 0x01, 0x27, 0x00, 0xEA, 0x00, 0xC5, 0x01, 0x5F},
+    {0x15, 0x00, 0x02, 0x00, 0xD7},
+    {0x39, 0x00, 0x07, 0xC0, 0x00, 0xC5, 0x00, 0x10, 0x00, 0x24},
+    {0x15, 0x00, 0x02, 0x00, 0xA3},
+    {0x39, 0x00, 0x07, 0xC1, 0x00, 0x20, 0x00, 0x20, 0x00, 0x02},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCE, 0x01, 0x81, 0x09, 0x13, 0x00, 0x78, 0x00, 0x78, 0x00, 0x50, 0x00, 0x50, 0x00, 0x78, 0x00, 0x78},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x10, 0xCE, 0x00, 0x8E, 0x0E, 0xB6, 0x00, 0x8E, 0x80, 0x09, 0x13, 0x00, 0x04, 0x00, 0x1C, 0x1F, 0x16},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x04, 0xCE, 0x20, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x04, 0xCE, 0x22, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xD1},
+    {0x39, 0x00, 0x08, 0xCE, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xE1},
+    {0x39, 0x00, 0x0C, 0xCE, 0x08, 0x02, 0x4D, 0x02, 0x4D, 0x02, 0x4D, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xF1},
+    {0x39, 0x00, 0x0A, 0xCE, 0x0C, 0x06, 0x0A, 0x00, 0xE9, 0x00, 0xE9, 0x00, 0xF8},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x05, 0xCF, 0x00, 0x00, 0x6E, 0x72},
+    {0x15, 0x00, 0x02, 0x00, 0xB5},
+    {0x39, 0x00, 0x05, 0xCF, 0x02, 0x02, 0x94, 0x98},
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x05, 0xCF, 0x04, 0x04, 0xD8, 0xDC},
+    {0x15, 0x00, 0x02, 0x00, 0xC5},
+    {0x39, 0x00, 0x05, 0xCF, 0x00, 0x00, 0x08, 0x0C},
+    // Scan Mode
+    {0x15, 0x00, 0x02, 0x00, 0xE8},
+    {0x15, 0x00, 0x02, 0xC0, 0x40},
+    // VST1&VST2
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x09, 0xC2, 0x84, 0x00, 0x05, 0x89, 0x83, 0x00, 0x05, 0x89},
+    // CKV1-3
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x10, 0xC2, 0x82, 0x04, 0x00, 0x05, 0x89, 0x81, 0x04, 0x00, 0x05, 0x89, 0x00, 0x04, 0x00, 0x05, 0x89},
+    // CKV4-6
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x10, 0xC2, 0x01, 0x04, 0x00, 0x05, 0x89, 0x02, 0x04, 0x00, 0x05, 0x89, 0x03, 0x04, 0x00, 0x05, 0x89},
+    // CKV7-8
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x0B, 0xC2, 0x04, 0x04, 0x00, 0x05, 0x89, 0x05, 0x04, 0x00, 0x05, 0x89},
+    {0x15, 0x00, 0x02, 0x00, 0xE0},
+    {0x39, 0x00, 0x05, 0xC2, 0x77, 0x77, 0x77, 0x77},
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x05, 0xC3, 0x99, 0x99, 0x99, 0x99},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCB, 0x00, 0xC5, 0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x0A, 0x05, 0xC5, 0x00, 0x05, 0x05, 0x00, 0xC0},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x11, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x05, 0xCB, 0x00, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x05, 0xCB, 0x10, 0x51, 0x94, 0x50},
+    {0x15, 0x00, 0x02, 0x00, 0xC0},
+    {0x39, 0x00, 0x05, 0xCB, 0x10, 0x51, 0x94, 0x50},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCC, 0x2D, 0x2D, 0x2D, 0x2D, 0x07, 0x09, 0x0B, 0x0D, 0x25, 0x25, 0x03, 0x2D, 0x22, 0x2D, 0x24, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x09, 0xCC, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x11, 0xCD, 0x2D, 0x2D, 0x2D, 0x2D, 0x06, 0x08, 0x0A, 0x0C, 0x25, 0x00, 0x02, 0x2D, 0x22, 0x2D, 0x24, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x90},
+    {0x39, 0x00, 0x09, 0xCD, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x11, 0xCC, 0x2D, 0x2D, 0x2D, 0x2D, 0x08, 0x06, 0x0C, 0x0A, 0x25, 0x25, 0x02, 0x2D, 0x24, 0x2D, 0x23, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x09, 0xCC, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x11, 0xCD, 0x2D, 0x2D, 0x2D, 0x2D, 0x09, 0x07, 0x0D, 0x0B, 0x25, 0x00, 0x03, 0x2D, 0x24, 0x2D, 0x23, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0xB0},
+    {0x39, 0x00, 0x09, 0xCD, 0x29, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x26},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x15, 0x00, 0x02, 0xA7, 0x13},
+    {0x15, 0x00, 0x02, 0x00, 0x82},
+    {0x39, 0x00, 0x03, 0xA7, 0x22, 0x02},
+    {0x15, 0x00, 0x02, 0x00, 0x85},
+    {0x15, 0x00, 0x02, 0xC4, 0x1C},
+    {0x15, 0x00, 0x02, 0x00, 0xA0},
+    {0x39, 0x00, 0x04, 0xC4, 0x8D, 0xD8, 0x8D},
+    {0x15, 0x00, 0x02, 0x00, 0x93},
+    {0x15, 0x00, 0x02, 0xC5, 0x37},
+    {0x15, 0x00, 0x02, 0x00, 0x97},
+    {0x15, 0x00, 0x02, 0xC5, 0x37},
+    {0x15, 0x00, 0x02, 0x00, 0xB6},
+    {0x39, 0x00, 0x03, 0xC5, 0x2D, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x9A},
+    {0x15, 0x00, 0x02, 0xC5, 0x19},
+    {0x15, 0x00, 0x02, 0x00, 0x9C},
+    {0x15, 0x00, 0x02, 0xC5, 0x19},
+    {0x15, 0x00, 0x02, 0x00, 0x97},
+    {0x15, 0x00, 0x02, 0xC4, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x9B},
+    {0x15, 0x00, 0x02, 0xF5, 0x4B},
+    {0x15, 0x00, 0x02, 0x00, 0x93},
+    {0x39, 0x00, 0x03, 0xF5, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x9D},
+    {0x15, 0x00, 0x02, 0xF5, 0x49},
+    {0x15, 0x00, 0x02, 0x00, 0x82},
+    {0x39, 0x00, 0x03, 0xF5, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x8C},
+    {0x39, 0x00, 0x04, 0xC3, 0x00, 0x00, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x84},
+    {0x39, 0x00, 0x03, 0xC5, 0x28, 0x28},
+    {0x15, 0x00, 0x02, 0x00, 0xA4},
+    {0x15, 0x00, 0x02, 0xD7, 0x00},
+    {0x15, 0x00, 0x02, 0x00, 0x80},
+    {0x39, 0x00, 0x03, 0xF5, 0x59, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0x84},
+    {0x39, 0x00, 0x04, 0xF5, 0x59, 0x59, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0x96},
+    {0x15, 0x00, 0x02, 0xF5, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0xA6},
+    {0x15, 0x00, 0x02, 0xF5, 0x59},
+    {0x15, 0x00, 0x02, 0x00, 0xCA},
+    {0x15, 0x00, 0x02, 0xC0, 0x80},
+    {0x15, 0x00, 0x02, 0x00, 0xB1},
+    {0x15, 0x00, 0x02, 0xF5, 0x1F},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x03, 0xD8, 0x29, 0x2D},
+    {0x15, 0x00, 0x02, 0x00, 0x86},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x22, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0x96},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xA6},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x3E, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xE9},
+    {0x39, 0x00, 0x07, 0xC0, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xA3},
+    {0x39, 0x00, 0x07, 0xCE, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    {0x15, 0x00, 0x02, 0x00, 0xB3},
+    {0x39, 0x00, 0x07, 0xCE, 0x01, 0x04, 0x01, 0x01, 0x1B, 0x03},
+    // gamma2.2
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE1, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE2, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE3, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE4, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE5, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    {0x15, 0x00, 0x02, 0x00, 0x00},
+    {0x39, 0x00, 0x29, 0xE6, 0x05, 0x06, 0x09, 0x10, 0x69, 0x1A, 0x22, 0x28, 0x33, 0x98, 0x3A, 0x41, 0x47, 0x4C, 0xE5, 0x51, 0x59, 0x60, 0x67, 0x05, 0x6E, 0x75, 0x7C, 0x85, 0x08, 0x8F, 0x94, 0x9B, 0xA2, 0x22, 0xAB, 0xB6, 0xC4, 0xCE, 0x9E, 0xDA, 0xEA, 0xF7, 0xFF, 0x7B},
+    // EN GND 4 frame
+    {0x15, 0x00, 0x02, 0x00, 0xCC},
+    {0x15, 0x00, 0x02, 0xC0, 0x13},
+    // LVD detect Voltage
+    {0x15, 0x00, 0x02, 0x00, 0x82},
+    {0x15, 0x00, 0x02, 0xC5, 0x35},
+    {0x15, 0x00, 0x02, 0x00, 0x94},
+    {0x39, 0x00, 0x04, 0xC5, 0x02, 0x01, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x98},
+    {0x39, 0x00, 0x03, 0xC5, 0x25, 0x01},
+    {0x15, 0x00, 0x02, 0x00, 0x9B},
+    {0x15, 0x00, 0x02, 0xC5, 0x21},
+    {0x15, 0x00, 0x02, 0x00, 0x9D},
+    {0x15, 0x00, 0x02, 0xC5, 0x25},
+    // Write ID
+    {0x15, 0x00, 0x02, 0x00, 0xD0},
+    {0x15, 0x00, 0x02, 0xD0, 0x56},
+    //LCD initial code End
+    {0x05, 0x78, 0x01, 0x11},
+    {0x05, 0x00, 0x01, 0x29},
+    {0x15, 0x00, 0x02, 0x35, 0x00}
+};
+
+
+static void lcd_panel_init1(u32 sel)
+{
+    int i;
+    u8 command; 
+    u8 *para;
+    u32 para_num;
+    u8 *tmp;
+    int j;
+	printk(KERN_ERR"--kernel!! mipi 720x1280 init\n");
+	sunxi_lcd_dsi_clk_enable(sel);
+    sunxi_lcd_delay_ms(5);
+    for(i = 0; i < ROWS; i++) {
+        tmp = data[i];
+        if (tmp[0] == 0x05) {
+            sunxi_lcd_dsi_dcs_write_0para(sel, tmp[3]);
+        } else {
+            command = tmp[3];
+            para_num = tmp[2] - 1;
+            para = tmp + 4;
+            sunxi_lcd_dsi_dcs_write(sel, command, para, para_num);
+        }
+        if (tmp[1] != 0x0)
+            sunxi_lcd_delay_ms(tmp[1]);
+    }
+	
+}
+
+static void lcd_panel_init2(u32 sel)
+{
+}
+
+
+static void lcd_panel_exit(u32 sel)
+{
+	sunxi_lcd_dsi_dcs_write_0para(sel, 0x28);
+	sunxi_lcd_delay_ms(1);
+	sunxi_lcd_dsi_dcs_write_0para(sel, 0x10);
+	sunxi_lcd_delay_ms(1);
+}
+
+/*sel: 0:lcd0; 1:lcd1*/
+static s32 lcd_user_defined_func(u32 sel, u32 para1, u32 para2, u32 para3)
+{
+	return 0;
+}
+
+__lcd_panel_t mipi_5_720x1280_panel = {
+	/* panel driver name, must mach the name of
+	 * lcd_drv_name in sys_config.fex
+	 */
+	.name = "mipi_5_720x1280",
+	.func = {
+		.cfg_panel_info = lcd_cfg_panel_info,
+			.cfg_open_flow = lcd_open_flow,
+			.cfg_close_flow = lcd_close_flow,
+			.lcd_user_defined_func = lcd_user_defined_func,
+	},
+};
diff --git a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.h b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.h
new file mode 100755
index 0000000..071e552
--- /dev/null
+++ b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/mipi_5_720x1280.h
@@ -0,0 +1,23 @@
+/* drivers/video/sunxi/disp2/disp/lcd/k101im2qa04.h
+ *
+ * Copyright (c) 2017 Allwinnertech Co., Ltd.
+ * Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
+ *
+ * k101im2qa04 panel driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _MIPI_5_720X1280_H
+#define _MIPI_5_720X1280_H
+
+#include "panels.h"
+
+typedef struct __lcd_panel __lcd_panel_t;
+
+extern struct __lcd_panel mipi_5_720x1280_panel;
+extern s32 bsp_disp_get_panel_info(u32 screen_id, struct disp_panel_para *info);
+
+#endif /*End of file*/
diff --git a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.c b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.c
index 9302a3d..c24899f 100644
--- a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.c
+++ b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.c
@@ -41,6 +41,7 @@
 	&K101_IM2BYL02_L_800X1280_mipi_panel,
 	&mipi_8_800x1280_panel,
 	&mipi_10_800x1280_panel,
+    &mipi_5_720x1280_panel,
 	NULL,
 };
 
diff --git a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.h b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.h
index b213082..4d8f0ac 100644
--- a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.h
+++ b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/panels.h
@@ -100,4 +100,5 @@
 extern struct __lcd_panel K101_IM2BYL02_L_800X1280_mipi_panel;
 extern struct __lcd_panel mipi_8_800x1280_panel;
 extern struct __lcd_panel mipi_10_800x1280_panel;
+extern struct __lcd_panel mipi_5_720x1280_panel;
 #endif

--
Gitblit v1.6.2