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 = <®_dc1sw>; + eldo3-supply = <®_eldo3>; + dldo2-supply = <®_dldo2>; + dcdc1-supply = <®_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, ®val); + 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(®_addr, 1, ®_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, ®_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(®, 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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®val); + 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