From cfc696f078bc655e56b766b96a45c1bbe4a61195 Mon Sep 17 00:00:00 2001 From: "chunlong.cao" Date: Tue, 9 Jun 2020 17:08:53 +0800 Subject: [PATCH] =?UTF-8?q?[Touch][A311D]support=20focaltech=20touch=20pan?= =?UTF-8?q?el=EF=BC=9Afocaltech=20FTS=5F5726=20PD#SWPL-27361=20Problem:=20?= =?UTF-8?q?=09support=20focaltech=20touch=20panel=EF=BC=9Afocaltech=20FTS?= =?UTF-8?q?=5F5726?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Solution: add focaltech FTS_5726 driver Verify: on google newman platform Signed-off-by: chunlong.cao Change-Id: I79ff49e062233c3c7202796be03f79f7fc32e52f --- drivers/input/touchscreen/Kconfig | 2 + drivers/input/touchscreen/Makefile | 1 + .../input/touchscreen/focaltech_touch/Kconfig | 73 + .../touchscreen/focaltech_touch/Makefile | 25 + .../focaltech_touch/focaltech_common.h | 168 ++ .../focaltech_touch/focaltech_config.h | 232 ++ .../focaltech_touch/focaltech_core.c | 1841 +++++++++++++++ .../focaltech_touch/focaltech_core.h | 258 +++ .../focaltech_touch/focaltech_esdcheck.c | 552 +++++ .../focaltech_touch/focaltech_ex_fun.c | 1319 +++++++++++ .../focaltech_touch/focaltech_ex_mode.c | 382 ++++ .../focaltech_touch/focaltech_flash.c | 2013 +++++++++++++++++ .../focaltech_touch/focaltech_flash.h | 172 ++ .../focaltech_touch/focaltech_flash/Makefile | 9 + .../focaltech_upgrade_ft3427.c | 162 ++ .../focaltech_upgrade_ft5726.c | 145 ++ .../focaltech_touch/focaltech_gesture.c | 591 +++++ .../focaltech_touch/focaltech_i2c.c | 243 ++ .../focaltech_point_report_check.c | 136 ++ 19 files changed, 8324 insertions(+) create mode 100644 drivers/input/touchscreen/focaltech_touch/Kconfig create mode 100644 drivers/input/touchscreen/focaltech_touch/Makefile create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_common.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_config.h create mode 100755 drivers/input/touchscreen/focaltech_touch/focaltech_core.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_core.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c create mode 100755 drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c create mode 100755 drivers/input/touchscreen/focaltech_touch/focaltech_flash.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3427.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft5726.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b36af44e74a6..764b8db5f359 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1214,6 +1214,8 @@ config TOUCHSCREEN_ROHM_BU21023 To compile this driver as a module, choose M here: the module will be called bu21023_ts. +source "drivers/input/touchscreen/focaltech_touch/Kconfig" + config TOUCHSCREEN_DWAV_USB_MT tristate "D-WAV Scientific USB MultiTouch" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index be22a22973d4..7682209cb118 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -99,4 +99,5 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_touch/ obj-$(CONFIG_TOUCHSCREEN_DWAV_USB_MT) += dwav-usb-mt.o diff --git a/drivers/input/touchscreen/focaltech_touch/Kconfig b/drivers/input/touchscreen/focaltech_touch/Kconfig new file mode 100644 index 000000000000..935393899637 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/Kconfig @@ -0,0 +1,73 @@ +# +# Focaltech Touchscreen driver configuration +# + +config TOUCHSCREEN_FTS_3427 + bool "Focaltech Touchscreen FT3427" + depends on I2C + default n + help + Say Y here if you have Focaltech 3427 IC. + If unsure, say N. + +config TOUCHSCREEN_FTS_5726 + bool "Focaltech Touchscreen FT5726" + depends on I2C + default n + help + Say Y here if you have FocalTech 5726 IC. + If unsure, say N. + +if TOUCHSCREEN_FTS_5726 + +config TOUCHSCREEN_FTS_5726_P1 + bool "Focaltech Touchscreen FT5726 for P1 Build Only" + depends on I2C + default n + help + Say Y here if you have FocalTech 5726 IC for P1 build. + If unsure, say N. + +config TOUCHSCREEN_FTS_5726_MP_POR + bool "Focaltech Touchscreen FT5726 for EVT Build Onwards" + depends on I2C + default n + help + Say Y here if you have FocalTech 5726 IC with the POR + touch configuration and HW. This should only be used + for devices from the EVT build onwards. + If unsure, say N. + +config TOUCHSCREEN_FTS_5726_UPDATE_TOPTOUCH_FW + bool "Focaltech Touchscreen Update TopTouch FW" + depends on I2C + default n + help + Say Y here if you have FocalTech 5726 IC, + and want to update FW for a TopTouch panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_5726_UPDATE_TOPGROUP_FW + bool "Focaltech Touchscreen Update TopGroup FW" + depends on I2C + default n + help + Say Y here if you have FocalTech 5726 IC, + and want to update FW for a TopGroup panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_5726_UPDATE_LENSONE_FW + bool "Focaltech Touchscreen Update Lensone FW" + depends on I2C + default n + help + Say Y here if you have FocalTech 5726 IC, + and want to update FW for a Lensone panel. + If unsure, say N. + +endif # TOUCHSCREEN_FTS_5726 + +config TOUCHSCREEN_FTS_DIRECTORY + string "Focaltech Touchscreen Directory" + default "focaltech_touch" + depends on TOUCHSCREEN_FTS_3427 || TOUCHSCREEN_FTS_5726 diff --git a/drivers/input/touchscreen/focaltech_touch/Makefile b/drivers/input/touchscreen/focaltech_touch/Makefile new file mode 100644 index 000000000000..d7de5c1edaca --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/Makefile @@ -0,0 +1,25 @@ +# +# Makefile for the focaltech touchscreen drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_flash.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_i2c.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_point_report_check.o +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_flash/ + +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_flash.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_i2c.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_point_report_check.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_flash/ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_common.h b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h new file mode 100644 index 000000000000..78c1b7894225 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h @@ -0,0 +1,168 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2018, 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 V2.2 20180321" + +#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)) << (x)) + +#define FLAG_ICSERIALS_LEN 8 +#define FLAG_HID_BIT 10 +#define FLAG_IDC_BIT 11 + +#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)) + + +#if defined(CONFIG_TOUCHSCREEN_FTS_3427) && !defined(CONFIG_TOUCHSCREEN_FTS_5726) +#define FTS_CHIP_TYPE_MAPPING { { 0x02, 0x54, 0x22, 0x54, 0x22, 0x00, 0x00, 0x54, 0x2C } } +#elif defined(CONFIG_TOUCHSCREEN_FTS_5726) && !defined(CONFIG_TOUCHSCREEN_FTS_3427) +#define FTS_CHIP_TYPE_MAPPING { { 0x01, 0x58, 0x22, 0x58, 0x22, 0x00, 0x00, 0x58, 0x2C } } +#endif + +#define I2C_BUFFER_LENGTH_MAXINUM 256 +#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 10 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 +/*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_MAX_REPORT_RATE 0x88 +#define FTS_REG_REPORT_RATE_60HZ 0x06 +#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_VALUE 0x03 +#define FTS_REG_FW_VER 0xA6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_PANEL_ID 0xAC +#define FTS_REG_LCD_BUSY_NUM 0xAB +#define FTS_REG_FACE_DEC_MODE_EN 0xB0 +#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_GLOVE_MODE_EN 0xC0 +#define FTS_REG_COVER_MODE_EN 0xC1 +#define FTS_REG_CHARGER_MODE_EN 0x8B +#define FTS_REG_GESTURE_EN 0xD0 +#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3 +#define FTS_REG_MODULE_ID 0xE3 +#define FTS_REG_LIC_VER 0xE4 +#define FTS_REG_ESD_SATURATE 0xED + +#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 { + u64 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 ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; +}; + +/***************************************************************************** + * DEBUG function define here +*****************************************************************************/ +#if FTS_DEBUG_EN +#define FTS_DEBUG_LEVEL 1 +#if (FTS_DEBUG_LEVEL == 2) +#define FTS_DEBUG(fmt, args...) printk("[FTS][%s]"fmt"\n", __func__, ##args) +#else +#define FTS_DEBUG(fmt, args...) printk("[FTS]"fmt"\n", ##args) +#endif +#define FTS_FUNC_ENTER() printk("[FTS]%s: Enter\n", __func__) +#define FTS_FUNC_EXIT() printk("[FTS]%s: Exit(%d)\n", __func__, __LINE__) +#else /* #if FTS_DEBUG_EN*/ +#define FTS_DEBUG(fmt, args...) +#define FTS_FUNC_ENTER() +#define FTS_FUNC_EXIT() +#endif + +#define FTS_INFO(fmt, args...) printk(KERN_INFO "[FTS][Info]"fmt"\n", ##args) +#define FTS_WARN(fmt, args...) printk(KERN_WARNING "[FTS][Warning]"fmt"\n", ##args) +#define FTS_ERROR(fmt, args...) printk(KERN_ERR "[FTS][Error]"fmt"\n", ##args) + +#endif /* __LINUX_FOCALTECH_COMMON_H__ */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_config.h b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h new file mode 100644 index 000000000000..e19753553d41 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h @@ -0,0 +1,232 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2018, 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 _FT8006M 0x80060807 +#define _FT7250 0x72500807 +#define _FT8606 0x86060808 +#define _FT8607 0x86070809 +#define _FTE716 0xE716080A +#define _FT8006U 0x8006D80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x8201081 + +#define _FT5416 0x54160402 +#define _FT5426 0x54260402 +#define _FT5435 0x54350402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5526I 0x5526B402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT5446I 0x5446B402 +#define _FT5346I 0x5346B402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3C47U 0x3C47D402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 + +#define _FT5626 0x56260401 +#define _FT5726 0x57260401 +#define _FT5826B 0x5826B401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3D47 0x3D470401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 +#define _FT3817B 0x3817B401 +#define _FT3517U 0x3517D401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 +#define _FT5422U 0x5422D482 + +#define _FT3327DQQ_001 0x3327D482 +#define _FT5446DQS_W01 0x5446D482 +#define _FT5446DQS_W02 0x5446A482 +#define _FT5446DQS_002 0x5446B482 +#define _FT5446DQS_Q02 0x5446C482 + +#define _FT3518 0x35180481 +#define _FT3558 0x35580481 + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#if defined(CONFIG_TOUCHSCREEN_FTS_3427) && !defined(CONFIG_TOUCHSCREEN_FTS_5726) +#define FTS_CHIP_TYPE _FT3427 +#elif defined(CONFIG_TOUCHSCREEN_FTS_5726) && !defined(CONFIG_TOUCHSCREEN_FTS_3427) +#define FTS_CHIP_TYPE _FT5726 +#endif + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#define FTS_DEBUG_EN 0 + +/* + * Linux MultiTouch Protocol + * 1: Protocol B(default), 0: Protocol A + */ +#define FTS_MT_PROTOCOL_B_EN 1 + +/* + * Report Pressure in multitouch + * 1:enable(default),0:disable +*/ +#define FTS_REPORT_PRESSURE_EN 1 + +/* + * Gesture function enable + * default: disable + */ +#define FTS_GESTURE_EN 0 + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + +/* + * Production test enable + * 1: enable, 0:disable(default) + */ +#define FTS_TEST_EN 0 + +/* + * Glove mode enable + * 1: enable, 0:disable(default) + */ +#define FTS_GLOVE_EN 0 +/* + * cover enable + * 1: enable, 0:disable(default) + */ +#define FTS_COVER_EN 0 +/* + * Charger enable + * 1: enable, 0:disable(default) + */ +#define FTS_CHARGER_EN 0 + +/* + * Nodes for tools, please keep enable + */ +#define FTS_SYSFS_NODE_EN 1 +#define FTS_APK_NODE_EN 1 + +/* + * Pinctrl enable + * default: disable + */ +#define FTS_PINCTRL_EN 0 + +/* + * Customer power enable + * enable it when customer need control TP power + * default: disable + */ +#define FTS_POWER_SOURCE_CUST_EN 0 + +/****************************************************/ + +/********************** Upgrade ****************************/ +/* + * auto upgrade, please keep enable + */ +#define FTS_AUTO_UPGRADE_EN 1 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Vendor and Panel IDs + */ +#define FTS_VENDOR_ID_DEFAULT 0x0000 +#define FTS_VENDOR_ID_TOPTOUCH 0x00A0 +#define FTS_VENDOR_ID_LENSONE 0x006D +#define FTS_VENDOR_ID_TOPGROUP 0x003E + +#define FTS_PANEL_ID_DEFAULT 0x0000 +#define FTS_PANEL_ID_TOPTOUCH_7MM 0x00C0 +#define FTS_PANEL_ID_TOPTOUCH_11MM 0x00C1 +#define FTS_PANEL_ID_LENSONE_7MM 0x00C2 +#define FTS_PANEL_ID_LENSONE_11MM 0x00C3 +#define FTS_PANEL_ID_TOPGROUP_7MM 0x0050 +#define FTS_PANEL_ID_TOPGROUP_11MM 0x0051 + +/* + * Firmware Update Files + */ +#define FTS_UPGRADE_FW_DEFAULT "firmware/fw_sample.i" + +#define FTS_TOPTOUCH_NUM_FW 2 +#define FTS_UPGRADE_FW_TOPTOUCH_7MM "firmware/7mm/TopTouch_7mm_0x0A.i" +#define FTS_UPGRADE_FW_TOPTOUCH_11MM "firmware/11mm/TopTouch_11mm_0x0F.i" + +#define FTS_LENSONE_NUM_FW 2 +#define FTS_UPGRADE_FW_LENSONE_7MM "firmware/7mm/LensOne_7mm_0x04.i" +#define FTS_UPGRADE_FW_LENSONE_11MM "firmware/11mm/LensOne_11mm_0x0D.i" + +#define FTS_TOPGROUP_NUM_FW 2 +#define FTS_UPGRADE_FW_TOPGROUP_7MM "firmware/7mm/TopGroup_7mm_0x06.i" +#define FTS_UPGRADE_FW_TOPGROUP_11MM "firmware/11mm/TopGroup_11mm_0x0B.i" + +/*********************************************************/ +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c new file mode 100755 index 000000000000..db7a6796685d --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c @@ -0,0 +1,1841 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2018, 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 "focaltech_core.h" +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#if defined(CONFIG_TOUCHSCREEN_FTS_3427) && !defined(CONFIG_TOUCHSCREEN_FTS_5726) +static const int NUM_BOE_PANEL_TYPES __initdata = 2; +static const int NUM_INX_PANEL_TYPES __initdata = 0; +static const char* focaltech_boe_panel_types[] __initdata = + {"lcd_1", "lcd_3"}; +static const char* focaltech_inx_panel_types[] __initdata = {}; +#elif defined(CONFIG_TOUCHSCREEN_FTS_5726) && !defined(CONFIG_TOUCHSCREEN_FTS_3427) +static const int NUM_BOE_PANEL_TYPES __initdata = 2; +static const int NUM_INX_PANEL_TYPES __initdata = 2; +static const char* focaltech_boe_panel_types[] __initdata = + {"boe_nvtk_10", "boe_fiti_10"}; +static const char* focaltech_inx_panel_types[] __initdata = + {"inx_nvtk_10", "inx_fiti_10"}; +#endif + +static enum fts_panel_vendor panel_vendor = FTS_PANEL_VENDOR_NONE; +static int maybe_focaltech __initdata = 1; + +#define FTS_DRIVER_NAME "fts_ts" +#define INTERVAL_READ_REG 100 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#if FTS_POWER_SOURCE_CUST_EN +#define FTS_VTG_MIN_UV 2600000 +#define FTS_VTG_MAX_UV 3300000 +#define FTS_I2C_VTG_MIN_UV 1800000 +#define FTS_I2C_VTG_MAX_UV 1800000 +#endif + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_ts_data *fts_data; +EXPORT_SYMBOL(fts_data); + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static void fts_release_all_finger(void); +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); + + +/***************************************************************************** + * 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(struct i2c_client *client) +{ + int ret = 0; + int cnt = 0; + u8 reg_value = 0; + u8 chip_id = fts_data->ic_info.ids.chip_idh; + + do { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, ®_value); + if ((ret < 0) || (reg_value != chip_id)) { + FTS_DEBUG("TP Not Ready, ReadData = 0x%x", reg_value); + } else if (reg_value == chip_id) { + FTS_INFO("TP Ready, Device ID = 0x%x", reg_value); + return 0; + } + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} + +/************************************************************************ +* Name: fts_get_chip_types +* Brief: verity chip id and get chip type data +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + int 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_DEBUG("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)) + 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; + } + + ts_data->ic_info.ids = ctype[i]; + return 0; +} + +/***************************************************************************** +* Name: fts_get_ic_information +* Brief: +* Input: +* Output: +* Return: return 0 if success, 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 }; + u8 id_cmd[4] = { 0 }; + u32 id_cmd_len = 0; + struct i2c_client *client = ts_data->client; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + do { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id[0]); + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID2, &chip_id[1]); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_DEBUG("i2c 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_DEBUG("TP not ready, read:0x%02x%02x", chip_id[0], chip_id[1]); + } + + cnt++; + 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"); + if (ts_data->ic_info.hid_supported) { + fts_i2c_hid2std(client); + } + + id_cmd[0] = FTS_CMD_START1; + id_cmd[1] = FTS_CMD_START2; + ret = fts_i2c_write(client, id_cmd, 2); + if (ret < 0) { + FTS_ERROR("start cmd write fail"); + return ret; + } + + 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_i2c_read(client, 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"); + return -EIO; + } + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_ERROR("can't get ic informaton"); + return ret; + } + } + + FTS_INFO("get ic information, chip id = 0x%02x%02x", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + + return 0; +} + +/***************************************************************************** +* Name: fts_tp_state_recovery +* Brief: Need execute this function when reset +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_tp_state_recovery(struct i2c_client *client) +{ + FTS_FUNC_ENTER(); + /* wait tp stable */ + fts_wait_tp_to_valid(client); + /* recover TP charger state 0x8B */ + /* recover TP glove state 0xC0 */ + /* recover TP cover state 0xC1 */ + fts_ex_mode_recovery(client); + /* recover TP gesture state 0xD0 */ +#if FTS_GESTURE_EN + fts_gesture_recovery(client); +#endif + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_reset_proc +* Brief: Execute reset operation +* Input: hdelayms - delay time unit:ms +* Output: +* Return: +*****************************************************************************/ +int fts_reset_proc(int hdelayms) +{ + FTS_FUNC_ENTER(); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(20); + gpio_direction_output(fts_data->pdata->reset_gpio, 1); + if (hdelayms) { + msleep(hdelayms); + } + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_irq_disable +* Brief: disable irq +* Input: +* Output: +* Return: +*****************************************************************************/ +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(); +} + +/***************************************************************************** +* Name: fts_irq_enable +* Brief: enable irq +* Input: +* Output: +* Return: +*****************************************************************************/ +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(); +} + +#if FTS_POWER_SOURCE_CUST_EN +/***************************************************************************** +* Power Control +*****************************************************************************/ +static int fts_power_source_init(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + ret = PTR_ERR(data->vdd); + FTS_ERROR("get vdd regulator failed,ret=%d", ret); + return ret; + } + + if (regulator_count_voltages(data->vdd) > 0) { + ret = regulator_set_voltage(data->vdd, FTS_VTG_MIN_UV, FTS_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret); + goto err_set_vtg_vdd; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + ret = PTR_ERR(data->vcc_i2c); + FTS_ERROR("ret vcc_i2c regulator failed,ret=%d", ret); + goto err_get_vcc; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + ret = regulator_set_voltage(data->vcc_i2c, FTS_I2C_VTG_MIN_UV, FTS_I2C_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_vtg failed ret=%d", ret); + goto err_set_vtg_vcc; + } + } + + FTS_FUNC_EXIT(); + return 0; + +err_set_vtg_vcc: + regulator_put(data->vcc_i2c); +err_get_vcc: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV); +err_set_vtg_vdd: + regulator_put(data->vdd); + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_power_source_release(struct fts_ts_data *data) +{ + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV); + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV); + regulator_put(data->vcc_i2c); + + return 0; +} + +static int fts_power_source_ctrl(struct fts_ts_data *data, int enable) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + if (enable) { + if (data->power_disabled) { + ret = regulator_enable(data->vdd); + if (ret) { + FTS_ERROR("enable vdd regulator failed,ret=%d", ret); + } + + ret = regulator_enable(data->vcc_i2c); + if (ret) { + FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret); + } + data->power_disabled = false; + } + } else { + if (!data->power_disabled) { + ret = regulator_disable(data->vdd); + if (ret) { + FTS_ERROR("disable vdd regulator failed,ret=%d", ret); + } + ret = regulator_disable(data->vcc_i2c); + if (ret) { + FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret); + } + data->power_disabled = true; + } + } + + FTS_FUNC_EXIT(); + return ret; +} + +#if FTS_PINCTRL_EN +/***************************************************************************** + * Name: fts_pinctrl_init + * Brief: + * Input: + * Output: + * Return: +*****************************************************************************/ +static int fts_pinctrl_init(struct fts_ts_data *ts) +{ + int ret = 0; + struct i2c_client *client = ts->client; + + ts->pinctrl = devm_pinctrl_get(&client->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; +} + +static int fts_pinctrl_select_normal(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_active) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_active); + if (ret < 0) { + FTS_ERROR("Set normal pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_suspend(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_suspend) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend); + if (ret < 0) { + FTS_ERROR("Set suspend pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_release(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl) { + if (IS_ERR_OR_NULL(ts->pins_release)) { + devm_pinctrl_put(ts->pinctrl); + ts->pinctrl = NULL; + } else { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_release); + if (ret < 0) + FTS_ERROR("Set gesture pin state error:%d", ret); + } + } + + return ret; +} +#endif /* FTS_PINCTRL_EN */ + +#endif /* FTS_POWER_SOURCE_CUST_EN */ + +/***************************************************************************** + * Reprot related +*****************************************************************************/ +#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2)) +char g_sz_debug[1024] = {0}; +static void fts_show_touch_buffer(u8 *buf, int point_num) +{ + int len = point_num * FTS_ONE_TCH_LEN; + int count = 0; + int i; + + memset(g_sz_debug, 0, 1024); + if (len > (fts_data->pnt_buf_size - 3)) { + len = fts_data->pnt_buf_size - 3; + } else if (len == 0) { + len += FTS_ONE_TCH_LEN; + } + count += snprintf(g_sz_debug, PAGE_SIZE, "%02X,%02X,%02X", buf[0], buf[1], buf[2]); + for (i = 0; i < len; i++) { + count += snprintf(g_sz_debug + count, PAGE_SIZE, ",%02X", buf[i + 3]); + } + FTS_DEBUG("buffer: %s", g_sz_debug); +} +#endif + +/***************************************************************************** + * Name: fts_release_all_finger + * Brief: report all points' up events, release touch + * Input: + * Output: + * Return: + *****************************************************************************/ +static void fts_release_all_finger(void) +{ + struct input_dev *input_dev = fts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&fts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < fts_data->pdata->max_touch_number; 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); + + mutex_unlock(&fts_data->report_mutex); + FTS_FUNC_EXIT(); +} + +/************************************************************************ + * Name: fts_input_report_key + * Brief: report key event + * Input: events info + * Output: + * Return: return 0 if success + ***********************************************************************/ +static int fts_input_report_key(struct fts_ts_data *data, int index) +{ + u32 ik; + int id = data->events[index].id; + int x = data->events[index].x; + int y = data->events[index].y; + int flag = data->events[index].flag; + u32 key_num = data->pdata->key_number; + + if (!KEY_EN(data)) { + return -EINVAL; + } + for (ik = 0; ik < key_num; ik++) { + if (TOUCH_IN_KEY(x, data->pdata->key_x_coords[ik])) { + if (EVENT_DOWN(flag)) { + data->key_down = true; + input_report_key(data->input_dev, data->pdata->keys[ik], 1); + FTS_DEBUG("Key%d(%d, %d) DOWN!", ik, x, y); + } else { + data->key_down = false; + input_report_key(data->input_dev, data->pdata->keys[ik], 0); + FTS_DEBUG("Key%d(%d, %d) Up!", ik, x, y); + } + return 0; + } + } + + FTS_ERROR("invalid touch for key, [%d](%d, %d)", id, x, y); + return -EINVAL; +} + +#if FTS_MT_PROTOCOL_B_EN +static int fts_input_report_b(struct fts_ts_data *data) +{ + int i = 0; + int uppoint = 0; + int touchs = 0; + bool va_reported = false; + u32 max_touch_num = data->pdata->max_touch_number; + u32 key_y_coor = data->pdata->key_y_coord; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (KEY_EN(data) && TOUCH_IS_KEY(events[i].y, key_y_coor)) { + fts_input_report_key(data, i); + continue; + } + + if (events[i].id >= max_touch_num) + break; + + va_reported = true; + input_mt_slot(data->input_dev, events[i].id); + + if (EVENT_DOWN(events[i].flag)) { + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); + +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + + if (data->pdata->rotation) { +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_P1) + input_report_abs(data->input_dev, ABS_MT_POSITION_X, data->pdata->x_max - events[i].x); + input_report_abs(data->input_dev, ABS_X, data->pdata->x_max - events[i].x); +#else + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].x); + input_report_abs(data->input_dev, ABS_Y, events[i].x); +#endif + input_report_abs(data->input_dev, ABS_MT_POSITION_X, data->pdata->y_max - events[i].y); + input_report_abs(data->input_dev, ABS_X, data->pdata->y_max - events[i].y); + } else { +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_P1) + input_report_abs(data->input_dev, ABS_MT_POSITION_X, data->pdata->x_max - events[i].x); + input_report_abs(data->input_dev, ABS_X, data->pdata->x_max - events[i].x); +#else + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_X, events[i].x); +#endif + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); + input_report_abs(data->input_dev, ABS_Y, events[i].y); + } + + touchs |= BIT(events[i].id); + data->touchs |= BIT(events[i].id); + + 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 { + uppoint++; + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + data->touchs &= ~BIT(events[i].id); + FTS_DEBUG("[B]P%d UP!", events[i].id); + } + } + + if (unlikely(data->touchs ^ touchs)) { + for (i = 0; i < max_touch_num; i++) { + if (BIT(i) & (data->touchs ^ touchs)) { + FTS_DEBUG("[B]P%d UP!", i); + va_reported = true; + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + } + } + } + data->touchs = touchs; + + if (va_reported) { + /* touchs==0, there's no point but key */ + if (EVENT_NO_DOWN(data) || (!touchs)) { + FTS_DEBUG("[B]Points All Up!"); + input_report_key(data->input_dev, BTN_TOUCH, 0); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} + +#else +static int fts_input_report_a(struct fts_ts_data *data) +{ + int i = 0; + int touchs = 0; + bool va_reported = false; + u32 key_y_coor = data->pdata->key_y_coord; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (KEY_EN(data) && TOUCH_IS_KEY(events[i].y, key_y_coor)) { + fts_input_report_key(data, i); + continue; + } + + va_reported = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); + + input_mt_sync(data->input_dev); + + 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); + touchs++; + } + } + + /* last point down, current no point but key */ + if (data->touchs && !touchs) { + va_reported = true; + } + data->touchs = touchs; + + if (va_reported) { + if (EVENT_NO_DOWN(data)) { + FTS_DEBUG("[A]Points All Up!"); + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_mt_sync(data->input_dev); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} +#endif + +/***************************************************************************** + * Name: fts_read_touchdata + * Brief: + * Input: + * Output: + * Return: return 0 if succuss +*****************************************************************************/ +static int fts_read_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + int i = 0; + u8 pointid; + int base; + struct ts_event *events = data->events; + int max_touch_num = data->pdata->max_touch_number; + u8 *buf = data->point_buf; + struct i2c_client *client = data->client; + +#if FTS_GESTURE_EN + if (0 == fts_gesture_readdata(data)) { + FTS_INFO("succuss to get gesture data in irq handler"); + return 1; + } +#endif + +#if FTS_POINT_REPORT_CHECK_EN + fts_prc_queue_work(data); +#endif + + data->point_num = 0; + data->touch_point = 0; + + memset(buf, 0xFF, data->pnt_buf_size); + buf[0] = 0x00; + + ret = fts_i2c_read(data->client, buf, 1, buf, data->pnt_buf_size); + if (ret < 0) { + FTS_ERROR("read touchdata failed, ret:%d", ret); + return ret; + } + data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; + + if (data->ic_info.is_incell) { + if ((data->point_num == 0x0F) && (buf[1] == 0xFF) && (buf[2] == 0xFF) + && (buf[3] == 0xFF) && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) { + FTS_INFO("touch buff is 0xff, need recovery state"); + fts_tp_state_recovery(client); + return -EIO; + } + } + + if (data->point_num > max_touch_num) { + FTS_INFO("invalid point_num(%d)", data->point_num); + return -EIO; + } + +#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2)) + fts_show_touch_buffer(buf, data->point_num); +#endif + + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i; + + pointid = (buf[FTS_TOUCH_ID_POS + 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; + } + + data->touch_point++; + + events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_X_L_POS + base] & 0xFF); + events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF); + events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6; + events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4; + events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4; + events[i].p = buf[FTS_TOUCH_PRE_POS + base]; + + if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } + if (data->touch_point == 0) { + FTS_INFO("no touch point information"); + return -EIO; + } + + return 0; +} + +/***************************************************************************** +* Name: fts_report_event +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_report_event(struct fts_ts_data *data) +{ +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(data); +#else + fts_input_report_a(data); +#endif +} + +/***************************************************************************** +* Name: fts_ts_interrupt +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static irqreturn_t fts_ts_interrupt(int irq, void *data) +{ + int ret = 0; + struct fts_ts_data *ts_data = (struct fts_ts_data *)data; + + if (!ts_data) { + FTS_ERROR("[INTR]: Invalid fts_ts_data"); + return IRQ_HANDLED; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(1); +#endif + + ret = fts_read_touchdata(ts_data); + if (ret == 0) { + mutex_lock(&ts_data->report_mutex); + fts_report_event(ts_data); + mutex_unlock(&ts_data->report_mutex); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(0); +#endif + + return IRQ_HANDLED; +} + +/***************************************************************************** +* Name: fts_irq_registration +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +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); + FTS_INFO("irq in ts_data:%d irq in client:%d", ts_data->irq, ts_data->client->irq); + if (ts_data->irq != ts_data->client->irq) + FTS_ERROR("IRQs are inconsistent, please check & in DTS"); + + if (0 == pdata->irq_gpio_flags) + pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING; + FTS_INFO("irq flag:%x", pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_ts_interrupt, + pdata->irq_gpio_flags | IRQF_ONESHOT, + ts_data->client->name, ts_data); + + return ret; +} + +/***************************************************************************** +* Name: fts_input_init +* Brief: input device init +* Input: +* Output: +* Return: +*****************************************************************************/ +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; + int point_num; + + 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; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &ts_data->client->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_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 + if (pdata->rotation) { + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_X, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_Y, pdata->x_min, pdata->x_max, 0, 0); + } else { + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_X, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_Y, pdata->y_min, pdata->y_max, 0, 0); + } + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0); +#if FTS_REPORT_PRESSURE_EN + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); +#endif + point_num = pdata->max_touch_number; + ts_data->pnt_buf_size = point_num * FTS_ONE_TCH_LEN + 3; + ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size, GFP_KERNEL); + if (!ts_data->point_buf) { + FTS_ERROR("failed to alloc memory for point buf!"); + ret = -ENOMEM; + goto err_point_buf; + } + + ts_data->events = (struct ts_event *)kzalloc(point_num * sizeof(struct ts_event), GFP_KERNEL); + if (!ts_data->events) { + + FTS_ERROR("failed to alloc memory for point events!"); + ret = -ENOMEM; + goto err_event_buf; + } + + + ret = input_register_device(input_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + goto err_input_reg; + } + + ts_data->input_dev = input_dev; + + FTS_FUNC_EXIT(); + return 0; + +err_input_reg: + kfree_safe(ts_data->events); + +err_event_buf: + kfree_safe(ts_data->point_buf); + +err_point_buf: + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + + FTS_FUNC_EXIT(); + return ret; +} + + +/***************************************************************************** +* Name: fts_gpio_configure +* Brief: Configure IRQ&RESET GPIO +* Input: +* Output: +* Return: return 0 if succuss +*****************************************************************************/ +static int fts_gpio_configure(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + /* request irq gpio */ + if (gpio_is_valid(data->pdata->irq_gpio)) { + ret = gpio_request(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(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(data->pdata->reset_gpio)) { + ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio"); + if (ret) { + FTS_ERROR("[GPIO]reset gpio request failed"); + goto err_irq_gpio_dir; + } + + ret = gpio_direction_output(data->pdata->reset_gpio, 1); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_reset_gpio_dir; + } + } + + FTS_FUNC_EXIT(); + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_get_dt_coords +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +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 && (ret != -EINVAL)) { + FTS_ERROR("Unable to read %s", name); + return -ENODATA; + } + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_P1) + if (!strcmp(name, "focaltech,display-coords,boe,p1") || + !strcmp(name, "focaltech,display-coords,inx,p1")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } +#else + if (!strcmp(name, "focaltech,display-coords")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } +#endif + else { + FTS_ERROR("unsupported property %s", name); + return -EINVAL; + } + + FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, + pdata->y_min, pdata->y_max); + return 0; +} + +/***************************************************************************** +* Name: fts_parse_dt +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) +{ + int ret = 0; + struct device_node *np = dev->of_node; + u32 temp_val; + + FTS_FUNC_ENTER(); + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_P1) + if (panel_vendor == FTS_PANEL_VENDOR_BOE) { + ret = fts_get_dt_coords(dev, "focaltech,display-coords,boe,p1", pdata); + } else if (panel_vendor == FTS_PANEL_VENDOR_INX) { + ret = fts_get_dt_coords(dev, "focaltech,display-coords,inx,p1", pdata); + } else { + ret = -EINVAL; // Unsupported panel type. + } +#else + ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata); +#endif + 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) + FTS_ERROR("Key number undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,keys", + pdata->keys, pdata->key_number); + if (ret) + FTS_ERROR("Keys undefined!"); + else if (pdata->key_number > FTS_MAX_KEYS) + pdata->key_number = FTS_MAX_KEYS; + + ret = of_property_read_u32(np, "focaltech,key-y-coord", &pdata->key_y_coord); + if (ret) + FTS_ERROR("Key Y Coord undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,key-x-coords", + pdata->key_x_coords, pdata->key_number); + if (ret) + FTS_ERROR("Key X Coords undefined!"); + + FTS_INFO("VK(%d): (%d, %d, %d), [%d, %d, %d][%d]", + pdata->key_number, pdata->keys[0], pdata->keys[1], pdata->keys[2], + pdata->key_x_coords[0], pdata->key_x_coords[1], pdata->key_x_coords[2], + pdata->key_y_coord); + } + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0, &pdata->reset_gpio_flags); + if (pdata->reset_gpio < 0) + FTS_ERROR("Unable to get reset_gpio"); + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + FTS_ERROR("Unable to get irq_gpio"); + + ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val); + if (0 == ret) { + if (temp_val < 2) + pdata->max_touch_number = 2; + else if (temp_val > FTS_MAX_POINTS_SUPPORT) + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + else + pdata->max_touch_number = temp_val; + } else { + FTS_ERROR("Unable to get max-touch-number"); + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + } + + ret = of_property_read_u32(np, "rotation", &temp_val); + if (!ret) { + pdata->rotation = temp_val; + FTS_INFO("rotation = %d", pdata->rotation); + } else { + pdata->rotation = 1; + FTS_ERROR("rotation = %d", pdata->rotation); + } + + 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; +} + +#if defined(CONFIG_FB) +/***************************************************************************** +* Name: fb_notifier_callback +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct fts_ts_data *fts_data = + container_of(self, struct fts_ts_data, fb_notif); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && + fts_data && fts_data->client) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + fts_ts_resume(&fts_data->client->dev); + else if (*blank == FB_BLANK_POWERDOWN) + fts_ts_suspend(&fts_data->client->dev); + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +/***************************************************************************** +* Name: fts_ts_early_suspend +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_ts_early_suspend(struct early_suspend *handler) +{ + struct fts_ts_data *data = container_of(handler, + struct fts_ts_data, + early_suspend); + + fts_ts_suspend(&data->client->dev); +} + +/***************************************************************************** +* Name: fts_ts_late_resume +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_ts_late_resume(struct early_suspend *handler) +{ + struct fts_ts_data *data = container_of(handler, + struct fts_ts_data, + early_suspend); + + fts_ts_resume(&data->client->dev); +} +#endif + +/***************************************************************************** +* Name: fts_ts_probe +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + + FTS_FUNC_ENTER(); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FTS_ERROR("I2C not supported"); + return -ENODEV; + } + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + FTS_ERROR("Failed to allocate memory for platform data"); + return -ENOMEM; + } + ret = fts_parse_dt(&client->dev, pdata); + if (ret) + FTS_ERROR("[DTS]DT parsing failed"); + } else { + pdata = client->dev.platform_data; + } + + if (!pdata) { + FTS_ERROR("no ts platform data found"); + return -EINVAL; + } + + ts_data = devm_kzalloc(&client->dev, sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("Failed to allocate memory for fts_data"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->client = client; + ts_data->pdata = pdata; + i2c_set_clientdata(client, ts_data); + + ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); + if (NULL == ts_data->ts_workqueue) { + FTS_ERROR("failed to create fts workqueue"); + } + + spin_lock_init(&ts_data->irq_lock); + mutex_init(&ts_data->report_mutex); + + ts_data->panel_vendor = panel_vendor; + if (ts_data->panel_vendor == FTS_PANEL_VENDOR_NONE) { + FTS_WARN("Unable to determine panel vendor"); + } + + ret = fts_input_init(ts_data); + if (ret) { + FTS_ERROR("fts input initialize fail"); + goto err_input_init; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get vdd/vcc_i2c regulator"); + goto err_power_init; + } + + data->power_disabled = true; + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret) { + FTS_ERROR("fail to enable vdd/vcc_i2c regulator"); + goto err_power_ctrl; + } + +#if FTS_PINCTRL_EN + ret = fts_pinctrl_init(ts_data); + if (0 == ret) { + fts_pinctrl_select_normal(ts_data); + } +#endif +#endif + + ret = fts_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("[GPIO]Failed to configure the gpios"); + goto err_gpio_config; + } + +#if (!FTS_CHIP_IDC) + fts_reset_proc(200); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + +#if FTS_APK_NODE_EN + ret = fts_create_apk_debug_channel(ts_data); + if (ret) { + FTS_ERROR("create apk debug node fail"); + } +#endif + +#if FTS_SYSFS_NODE_EN + ret = fts_create_sysfs(client); + if (ret) { + FTS_ERROR("create sysfs node fail"); + } +#endif + +#if FTS_POINT_REPORT_CHECK_EN + ret = fts_point_report_check_init(ts_data); + if (ret) { + FTS_ERROR("init point report check fail"); + } +#endif + + ret = fts_ex_mode_init(client); + if (ret) { + FTS_ERROR("init glove/cover/charger fail"); + } + +#if FTS_GESTURE_EN + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } +#endif + +#if FTS_TEST_EN + ret = fts_test_init(client); + if (ret) { + FTS_ERROR("init production test fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + +#if FTS_AUTO_UPGRADE_EN + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } +#endif + +#if defined(CONFIG_FB) + 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); + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL; + ts_data->early_suspend.suspend = fts_ts_early_suspend; + ts_data->early_suspend.resume = fts_ts_late_resume; + register_early_suspend(&ts_data->early_suspend); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_irq_req: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); +err_gpio_config: +#if FTS_POWER_SOURCE_CUST_EN +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + fts_power_source_ctrl(ts_data, DISABLE); +err_power_ctrl: + fts_power_source_release(ts_data); +err_power_init: +#endif + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); + input_unregister_device(ts_data->input_dev); +err_input_init: + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + devm_kfree(&client->dev, ts_data); + + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_ts_remove +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_ts_remove(struct i2c_client *client) +{ + struct fts_ts_data *ts_data = i2c_get_clientdata(client); + + FTS_FUNC_ENTER(); + +#if FTS_POINT_REPORT_CHECK_EN + fts_point_report_check_exit(ts_data); +#endif + +#if FTS_APK_NODE_EN + fts_release_apk_debug_channel(ts_data); +#endif + +#if FTS_SYSFS_NODE_EN + fts_remove_sysfs(client); +#endif + + fts_ex_mode_exit(client); + +#if FTS_AUTO_UPGRADE_EN + fts_fwupg_exit(ts_data); +#endif + +#if FTS_TEST_EN + fts_test_exit(client); +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_exit(ts_data); +#endif + +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif + + free_irq(client->irq, ts_data); + input_unregister_device(ts_data->input_dev); + + 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 (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#if FTS_POWER_SOURCE_CUST_EN +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + fts_power_source_ctrl(ts_data, DISABLE); + fts_power_source_release(ts_data); +#endif + + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); + + devm_kfree(&client->dev, ts_data); + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** + * Name: fts_ts_suspend + * Brief: + * Input: + * Output: + * Return: +*****************************************************************************/ +static int fts_ts_suspend(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + 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; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(); +#endif + +#if FTS_GESTURE_EN + if (fts_gesture_suspend(ts_data->client) == 0) { + ts_data->suspended = true; + return 0; + } +#endif + + fts_irq_disable(); + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_ctrl(ts_data, DISABLE); + if (ret < 0) { + FTS_ERROR("power off fail, ret=%d", ret); + } +#if FTS_PINCTRL_EN + fts_pinctrl_select_suspend(ts_data); +#endif +#else + /* TP enter sleep mode */ + ret = fts_i2c_write_reg(ts_data->client, FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP_VALUE); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); +#endif + + ts_data->suspended = true; + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** + * Name: fts_ts_resume + * Brief: + * Input: + * Output: + * Return: +*****************************************************************************/ +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + + fts_release_all_finger(); + +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_ctrl(ts_data, ENABLE); +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif +#endif + + if (!ts_data->ic_info.is_incell) { + fts_reset_proc(200); + } + + fts_tp_state_recovery(ts_data->client); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#if FTS_GESTURE_EN + if (fts_gesture_resume(ts_data->client) == 0) { + ts_data->suspended = false; + return 0; + } +#endif + + ts_data->suspended = false; + fts_irq_enable(); + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** + * Name: get_panel_type + * Brief: Use panel_type command line argument to determine if possibly + using a FocalTech touch IC/sensor. + * Input: panel_type command line argument + * Output: nothing + * Return: 0 +*****************************************************************************/ +static int __init get_panel_type(char *panel_arg) +{ + int i = 0; + FTS_INFO("Panel type command line argument: %s", panel_arg); + + // Maybe a FocalTech IC if no arguments provided + if (*panel_arg == 0) { + FTS_WARN("No panel_type command line argument found"); + maybe_focaltech = 1; + return 0; + } + + // Check if panel_type argument matches expected BOE strings + for (i = 0; i < NUM_BOE_PANEL_TYPES; i++) { + if (strcmp(panel_arg, focaltech_boe_panel_types[i]) == 0) { + FTS_INFO("Using a BOE display"); + panel_vendor = FTS_PANEL_VENDOR_BOE; + maybe_focaltech = 1; + return 0; + } + } + + // Check if panel_type argument matches expected INX strings + // For the FTS_3427, INX panels do not use FocalTech ICs + for (i = 0; i < NUM_INX_PANEL_TYPES; i++) { + if (strcmp(panel_arg, focaltech_inx_panel_types[i]) == 0) { + FTS_INFO("Using an INX display"); + panel_vendor = FTS_PANEL_VENDOR_INX; +#if defined(CONFIG_TOUCHSCREEN_FTS_3427) + // Panels using the FT3427 IC only came from BOE + maybe_focaltech = 1; +#else + // Panels using the FT5726 IC came from both BOE and INX + maybe_focaltech = 1; +#endif // CONFIG_TOUCHSCREEN_FTS_3427 + return 0; + } + } + return 0; +} +__setup("panel_type=", get_panel_type); + +/***************************************************************************** +* I2C Driver +*****************************************************************************/ +static const struct i2c_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, fts_ts_id); + +static struct of_device_id fts_match_table[] = { + { .compatible = "focaltech,fts", }, + { }, +}; + +static struct i2c_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = fts_match_table, + }, + .id_table = fts_ts_id, +}; + +/***************************************************************************** + * Name: fts_ts_init + * Brief: + * Input: + * Output: + * Return: +*****************************************************************************/ +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + if (!maybe_focaltech) { + FTS_INFO("Not using a FocalTech touch IC/sensor"); + return -ENODEV; + } + + ret = i2c_add_driver(&fts_ts_driver); + if (ret != 0) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_ts_exit +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void __exit fts_ts_exit(void) +{ + i2c_del_driver(&fts_ts_driver); +} + +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h new file mode 100644 index 000000000000..3c691f9622a8 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h @@ -0,0 +1,258 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2017, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_MAX_POINTS_SUPPORT 10 /* constant value, can't be changed */ +#define FTS_MAX_KEYS 4 +#define FTS_KEY_WIDTH 50 +#define FTS_ONE_TCH_LEN 6 + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_X_H_POS 3 +#define FTS_TOUCH_X_L_POS 4 +#define FTS_TOUCH_Y_H_POS 5 +#define FTS_TOUCH_Y_L_POS 6 +#define FTS_TOUCH_PRE_POS 7 +#define FTS_TOUCH_AREA_POS 8 +#define FTS_TOUCH_POINT_NUM 2 +#define FTS_TOUCH_EVENT_POS 3 +#define FTS_TOUCH_ID_POS 5 +#define FTS_COORDS_ARR_SIZE 4 + +#define FTS_TOUCH_DOWN 0 +#define FTS_TOUCH_UP 1 +#define FTS_TOUCH_CONTACT 2 +#define EVENT_DOWN(flag) ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag)) +#define EVENT_UP(flag) (FTS_TOUCH_UP == flag) +#define EVENT_NO_DOWN(data) (!data->point_num) +#define KEY_EN(data) (data->pdata->have_key) +#define TOUCH_IS_KEY(y, key_y) (y == key_y) +#define TOUCH_IN_RANGE(val, key_val, half) ((val > (key_val - half)) && (val < (key_val + half))) +#define TOUCH_IN_KEY(x, key_x) TOUCH_IN_RANGE(x, key_x, FTS_KEY_WIDTH) + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +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_coord; + u32 key_x_coords[FTS_MAX_KEYS]; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 max_touch_number; + u32 rotation; +}; + +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; +}; + +enum fts_panel_vendor { + FTS_PANEL_VENDOR_NONE = 0, + FTS_PANEL_VENDOR_BOE = 1, + FTS_PANEL_VENDOR_INX = 2 +}; + +struct fts_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct fts_ts_platform_data *pdata; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct work_struct fwupg_work; + struct delayed_work esdcheck_work; + struct delayed_work prc_work; + struct regulator *vdd; + struct regulator *vcc_i2c; + spinlock_t irq_lock; + struct mutex report_mutex; + int irq; + bool suspended; + bool fw_loading; + bool irq_disabled; + enum fts_panel_vendor panel_vendor; +#if FTS_POWER_SOURCE_CUST_EN + bool power_disabled; +#endif + /* multi-touch */ + struct ts_event *events; + u8 *point_buf; + int pnt_buf_size; + int touchs; + bool key_down; + int touch_point; + int point_num; + struct proc_dir_entry *proc; + u8 proc_opmode; +#if FTS_PINCTRL_EN + struct pinctrl *pinctrl; + struct pinctrl_state *pins_active; + struct pinctrl_state *pins_suspend; + struct pinctrl_state *pins_release; +#endif +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +}; + + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_ts_data *fts_data; + +/* i2c communication*/ +int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue); +int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue); +int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen); +int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen); +void fts_i2c_hid2std(struct i2c_client *client); +int fts_i2c_init(void); +int fts_i2c_exit(void); + +/* Gesture functions */ +#if FTS_GESTURE_EN +int fts_gesture_init(struct fts_ts_data *ts_data); +int fts_gesture_exit(struct i2c_client *client); +void fts_gesture_recovery(struct i2c_client *client); +int fts_gesture_readdata(struct fts_ts_data *ts_data); +int fts_gesture_suspend(struct i2c_client *i2c_client); +int fts_gesture_resume(struct i2c_client *client); +#endif + +/* Apk and functions */ +#if FTS_APK_NODE_EN +int fts_create_apk_debug_channel(struct fts_ts_data *); +void fts_release_apk_debug_channel(struct fts_ts_data *); +#endif + +/* ADB functions */ +#if FTS_SYSFS_NODE_EN +int fts_create_sysfs(struct i2c_client *client); +int fts_remove_sysfs(struct i2c_client *client); +#endif + +/* ESD */ +#if FTS_ESDCHECK_EN +int fts_esdcheck_init(struct fts_ts_data *ts_data); +int fts_esdcheck_exit(struct fts_ts_data *ts_data); +int fts_esdcheck_switch(bool enable); +int fts_esdcheck_proc_busy(bool proc_debug); +int fts_esdcheck_set_intr(bool intr); +int fts_esdcheck_suspend(void); +int fts_esdcheck_resume(void); +#endif + +/* Production test */ +#if FTS_TEST_EN +int fts_test_init(struct i2c_client *client); +int fts_test_exit(struct i2c_client *client); +#endif + +/* Point Report Check*/ +#if FTS_POINT_REPORT_CHECK_EN +int fts_point_report_check_init(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); +#endif + +/* FW upgrade */ +int fts_upgrade_bin(struct i2c_client *client, char *fw_name, bool force); +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_wait_tp_to_valid(struct i2c_client *client); +void fts_tp_state_recovery(struct i2c_client *client); +int fts_ex_mode_init(struct i2c_client *client); +int fts_ex_mode_exit(struct i2c_client *client); +int fts_ex_mode_recovery(struct i2c_client *client); + +void fts_irq_disable(void); +void fts_irq_enable(void); + +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c new file mode 100644 index 000000000000..fb3f3ff10c2e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c @@ -0,0 +1,552 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2017, 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" + +#if FTS_ESDCHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define ESDCHECK_WAIT_TIME 1000 /* ms */ +#define LCD_ESD_PATCH 0 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_esdcheck_st { + u8 mode : 1; /* 1- need check esd 0- no esd check */ + u8 suspend : 1; + u8 proc_debug : 1; /* apk or adb is accessing I2C */ + u8 intr : 1; /* 1- Interrupt trigger */ + u8 unused : 4; + u8 flow_work_hold_cnt; /* 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 i2c_nack_cnt; + u32 i2c_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 +/***************************************************************************** +* Name: lcd_esdcheck +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int lcd_need_reset; +static int tp_need_recovery; /* LCD reset cause Tp reset */ +int idc_esdcheck_lcderror(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 val = 0; + struct i2c_client *client = ts_data->client; + + FTS_DEBUG("[ESD]Check LCD ESD"); + if ((tp_need_recovery == 1) && (lcd_need_reset == 0)) { + tp_need_recovery = 0; + /* LCD reset, need recover TP state */ + fts_tp_state_recovery(client); + } + + ret = fts_i2c_read_reg(client, FTS_REG_ESD_SATURATE, &val); + if (ret < 0) { + FTS_ERROR("[ESD]: Read ESD_SATURATE(0xED) failed ret=%d!", ret); + return -EIO; + } + + if (val == 0xAA) { + /* + * 1. Set flag lcd_need_reset = 1; + * 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0 + * 3. recover TP state + */ + FTS_INFO("LCD ESD, Execute LCD reset!"); + lcd_need_reset = 1; + tp_need_recovery = 1; + } + + return 0; +} +#endif + +/***************************************************************************** +* Name: fts_esdcheck_tp_reset +* Brief: esd check algorithm +* Input: +* Output: +* Return: +*****************************************************************************/ +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(200); + fts_tp_state_recovery(ts_data->client); + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: get_chip_id +* Brief: Read Chip Id 3 times +* Input: +* Output: +* Return: 1 - Read Chip Id 3 times failed +* 0 - Read Chip Id pass +*****************************************************************************/ +static bool get_chip_id(struct fts_ts_data *ts_data) +{ + int ret = 0; + int i = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + struct i2c_client *client = ts_data->client; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + reg_addr = FTS_REG_CHIP_ID; + ret = fts_i2c_read(client, ®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!", ret); + fts_esdcheck_data.i2c_nack_cnt++; + } else { + if (reg_value == chip_id) { + break; + } else { + fts_esdcheck_data.i2c_dataerror_cnt++; + } + } + msleep(10); + } + + /* if can't get correct data in 3 times, then need hardware reset */ + if (i >= 3) { + FTS_ERROR("[ESD]: Read Chip id 3 times failed, need execute TP reset!!"); + return 1; + } + + return 0; +} + +/***************************************************************************** +* Name: get_flow_cnt +* Brief: Read flow cnt(0x91) +* Input: +* Output: +* Return: 1 - Reg 0x91(flow cnt) abnormal: hold a value for 5 times +* 0 - Reg 0x91(flow cnt) normal +*****************************************************************************/ +static bool get_flow_cnt(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + struct i2c_client *client = ts_data->client; + + reg_addr = FTS_REG_FLOW_WORK_CNT; + ret = fts_i2c_read(client, ®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", ret); + fts_esdcheck_data.i2c_nack_cnt++; + } else { + if (reg_value == fts_esdcheck_data.flow_work_cnt_last) { + fts_esdcheck_data.flow_work_hold_cnt++; + } else { + fts_esdcheck_data.flow_work_hold_cnt = 0; + } + + fts_esdcheck_data.flow_work_cnt_last = reg_value; + } + + /* if read flow work cnt 5 times and the value are all the same, then need hardware_reset */ + if (fts_esdcheck_data.flow_work_hold_cnt >= 5) { + FTS_DEBUG("[ESD]: Flow Work Cnt(reg0x91) keep a value for 5 times, need execute TP reset!!"); + return 1; + } + + return 0; +} + +/***************************************************************************** +* Name: esdcheck_algorithm +* Brief: esd check algorithm +* Input: +* Output: +* Return: +*****************************************************************************/ +static int esdcheck_algorithm(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + bool hardware_reset = 0; + struct i2c_client *client = ts_data->client; + + /* 1. esdcheck is interrupt, then return */ + if (fts_esdcheck_data.intr == 1) { + FTS_DEBUG("[ESD]: In interrupt state, not check esd, return immediately!!"); + return 0; + } + + /* 2. check power state, if suspend, no need check esd */ + if (fts_esdcheck_data.suspend == 1) { + FTS_DEBUG("[ESD]: In suspend, not check esd, return immediately!!"); + /* because in suspend state, adb can be used, when upgrade FW, will active ESD check(active = 1) + * But in suspend, then will don't queue_delayed_work, when resume, don't check ESD again + */ + return 0; + } + + /* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/ + if (fts_esdcheck_data.proc_debug == 1) { + FTS_INFO("[ESD]: In apk or adb command mode, not check esd, return immediately!!"); + return 0; + } + + /* 4. In factory mode, can't check esd */ + reg_addr = FTS_REG_WORKMODE; + ret = fts_i2c_read(client, ®_addr, 1, ®_value, 1); + if (ret < 0) { + fts_esdcheck_data.i2c_nack_cnt++; + } else if ((reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) { + FTS_DEBUG("[ESD]: not in work mode, no check esd, return immediately!!"); + return 0; + } + + /* 5. IDC esd check lcd default:close */ +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + + /* 6. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 7. 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); + } + + /* 8. If need hardware reset, then handle it here */ + if (hardware_reset == 1) { + fts_esdcheck_tp_reset(ts_data); + } + + FTS_DEBUG("[ESD]: NoACK=%d, Error Data=%d, Hardware Reset=%d", fts_esdcheck_data.i2c_nack_cnt, fts_esdcheck_data.i2c_dataerror_cnt, fts_esdcheck_data.hardware_reset_cnt); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_func +* Brief: fts_esdcheck_func +* Input: +* Output: +* Return: +*****************************************************************************/ +static void esdcheck_func(struct work_struct *work) +{ + u8 val = 0; + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, esdcheck_work.work); + + FTS_FUNC_ENTER(); + if (ENABLE == fts_esdcheck_data.mode) { + if (ts_data->ic_info.is_incell) { + fts_i2c_read_reg(ts_data->client, FTS_REG_ESDCHECK_DISABLE, &val); + if (0xA5 == val) { + fts_esdcheck_data.mode = DISABLE; + return; + } + } + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_esdcheck_set_intr +* Brief: interrupt flag (main used in interrupt tp report) +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_set_intr(bool intr) +{ + /* interrupt don't add debug message */ + fts_esdcheck_data.intr = intr; + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_get_status(void) +* Brief: get current status +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_get_status(void) +{ + /* interrupt don't add debug message */ + return fts_esdcheck_data.mode; +} + +/***************************************************************************** +* Name: fts_esdcheck_proc_busy +* Brief: When APK or ADB command access TP via driver, then need set proc_debug, +* then will not check ESD. +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_proc_busy(bool proc_debug) +{ + fts_esdcheck_data.proc_debug = proc_debug; + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_switch(bool enable) +{ + struct fts_ts_data *ts_data = fts_data; + FTS_FUNC_ENTER(); + if (fts_esdcheck_data.mode == ENABLE) { + if (enable) { + FTS_DEBUG("[ESD]: ESD check start!!"); + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.flow_work_cnt_last = 0; + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } else { + FTS_DEBUG("[ESD]: ESD check stop!!"); + cancel_delayed_work(&ts_data->esdcheck_work); + } + } else { + FTS_DEBUG("[ESD]: ESD should disable!!"); + cancel_delayed_work(&ts_data->esdcheck_work); + } + + FTS_FUNC_EXIT(); + return 0; +} +EXPORT_SYMBOL(fts_esdcheck_switch); +/***************************************************************************** +* Name: fts_esdcheck_suspend +* Brief: Run when tp enter into suspend +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_suspend(void) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(DISABLE); + fts_esdcheck_data.suspend = 1; + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_resume +* Brief: Run when tp resume +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_resume(void) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ENABLE); + fts_esdcheck_data.suspend = 0; + FTS_FUNC_EXIT(); + return 0; +} + +/************************************************************************ +* Name: fts_esdcheck_store +* Brief: no +* Input: device, device attribute, char buf, char count +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_esdcheck_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_switch(ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_data.mode = DISABLE; + fts_esdcheck_switch(DISABLE); + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +/************************************************************************ +* Name: fts_esdcheck_show +* Brief: no +* Input: device, device attribute, char buf +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_esdcheck_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", fts_esdcheck_get_status() ? "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_esd_group = { + .attrs = fts_esd_mode_attrs, +}; +/***************************************************************************** +* Name: fts_create_gesture_sysfs +* Brief: +* Input: +* Output: +* Return: 0-success or others-error +*****************************************************************************/ +int fts_create_esd_sysfs(struct i2c_client *client) +{ + int ret = 0; + + ret = sysfs_create_group(&client->dev.kobj, &fts_esd_group); + if (ret != 0) { + FTS_ERROR("fts_create_esd_sysfs(sysfs) create failed!"); + sysfs_remove_group(&client->dev.kobj, &fts_esd_group); + return ret; + } + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_init +* Brief: Init and create a queue work to check esd +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_esdcheck_init(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 = ENABLE; + fts_esdcheck_switch(ENABLE); + fts_create_esd_sysfs(ts_data->client); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_exit +* Brief: When FTS TP driver is removed, then call this function to destory work queue +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_ESDCHECK_EN */ + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c new file mode 100755 index 000000000000..45c88825eec7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c @@ -0,0 +1,1319 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2018, 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 +*****************************************************************************/ +/*create apk debug channel*/ +#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_NAME "ftxxxx-debug" +#define PROC_WRITE_BUF_SIZE 256 +#define PROC_READ_BUF_SIZE 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ +enum { + RWREG_OP_READ = 0, + RWREG_OP_WRITE = 1, +}; +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 +*****************************************************************************/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +/************************************************************************ +* Name: fts_debug_write +* Brief:interface of write proc +* Input: file point, data buf, data len, no use +* Output: no +* Return: data len +***********************************************************************/ +static ssize_t fts_debug_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos) +{ + u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[25]; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) { + FTS_ERROR("apk proc wirte count(%d) fail", (int)count); + return -EINVAL; + } + + if (copy_from_user(&writebuf, buff, count)) { + FTS_ERROR("[APK]: copy from user error!!"); + return -EFAULT; + } + + ts_data->proc_opmode = writebuf[0]; + + switch (ts_data->proc_opmode) { + case PROC_SET_TEST_FLAG: + FTS_INFO("[APK]: PROC_SET_TEST_FLAG = %x!!", writebuf[1]); +#if FTS_ESDCHECK_EN + if (writebuf[1] == 0) { + fts_esdcheck_switch(ENABLE); + } else { + fts_esdcheck_switch(DISABLE); + } +#endif + break; + case PROC_READ_REGISTER: + writelen = 1; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + } + break; + case PROC_WRITE_REGISTER: + writelen = 2; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + } + break; + case PROC_SET_SLAVE_ADDR: +#if (FTS_CHIP_TYPE == _FT8201) + FTS_INFO("Original i2c addr 0x%x", client->addr << 1); + if (writebuf[1] != client->addr) { + client->addr = writebuf[1]; + FTS_INFO("Change i2c addr 0x%x to 0x%x", client->addr << 1, writebuf[1] << 1); + } +#endif + break; + + case PROC_HW_RESET: + snprintf(tmp, PAGE_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(1); + } + break; + + case PROC_READ_DATA: + case PROC_WRITE_DATA: + writelen = buflen - 1; + if (writelen > 0) { + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + } + } + break; + default: + break; + } + + if (ret < 0) { + return ret; + } else { + return count; + } +} + +/************************************************************************ +* Name: fts_debug_read +* Brief:interface of read proc +* Input: point to the data, no use, no use, read len, no use, no use +* Output: page point to data +* Return: read char number +***********************************************************************/ +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 readlen = 0; + u8 buf[PROC_READ_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((count == 0) || (count > PROC_READ_BUF_SIZE)) { + FTS_ERROR("apk proc read count(%d) fail", (int)count); + return -EINVAL; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + switch (ts_data->proc_opmode) { + case PROC_READ_REGISTER: + readlen = 1; + ret = fts_i2c_read(client, NULL, 0, buf, readlen); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + return ret; + } + num_read_chars = 1; + break; + case PROC_READ_DATA: + readlen = count; + ret = fts_i2c_read(client, NULL, 0, buf, readlen); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + return ret; + } + + num_read_chars = readlen; + break; + case PROC_WRITE_DATA: + break; + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + if (copy_to_user(buff, buf, num_read_chars)) { + FTS_ERROR("[APK]: copy to user error!!"); + return -EFAULT; + } + + return num_read_chars; +} + +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .write = fts_debug_write, +}; +#else +/* interface of write proc */ +/************************************************************************ +* Name: fts_debug_write +* Brief:interface of write proc +* Input: file point, data buf, data len, no use +* Output: no +* Return: data len +***********************************************************************/ +static int fts_debug_write(struct file *filp, + const char __user *buff, unsigned long len, void *data) +{ + int ret = 0; + u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 }; + int buflen = len; + int writelen = 0; + char tmp[25]; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) { + FTS_ERROR("apk proc wirte count(%d) fail", (int)count); + return -EINVAL; + } + + if (copy_from_user(&writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + return -EFAULT; + } + + ts_data->proc_opmode = writebuf[0]; + + switch (ts_data->proc_opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x!!", writebuf[1]); +#if FTS_ESDCHECK_EN + if (writebuf[1] == 0) { + fts_esdcheck_switch(ENABLE); + } else { + fts_esdcheck_switch(DISABLE); + } +#endif + break; + case PROC_READ_REGISTER: + writelen = 1; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!n"); + } + break; + case PROC_WRITE_REGISTER: + writelen = 2; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + } + break; + case PROC_SET_SLAVE_ADDR: +#if (FTS_CHIP_TYPE == _FT8201) + ret = client->addr; + FTS_DEBUG("Original i2c addr 0x%x ", ret << 1); + if (writebuf[1] != client->addr) { + client->addr = writebuf[1]; + FTS_DEBUG("Change i2c addr 0x%x to 0x%x", ret << 1, writebuf[1] << 1); + } +#endif + break; + + case PROC_HW_RESET: + snprintf(tmp, PAGE_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("Begin HW Reset"); + fts_reset_proc(1); + } + break; + + case PROC_READ_DATA: + case PROC_WRITE_DATA: + writelen = len - 1; + if (writelen > 0) { + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + } + } + break; + default: + break; + } + + if (ret < 0) { + return ret; + } else { + return len; + } +} + +/* interface of read proc */ +/************************************************************************ +* Name: fts_debug_read +* Brief:interface of read proc +* Input: point to the data, no use, no use, read len, no use, no use +* Output: page point to data +* Return: read char number +***********************************************************************/ +static int fts_debug_read(char *page, char **start, + off_t off, int count, int *eof, void *data) +{ + int ret = 0; + u8 buf[PROC_READ_BUF_SIZE] = { 0 }; + int num_read_chars = 0; + int readlen = 0; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((count == 0) || (count > PROC_READ_BUF_SIZE)) { + FTS_ERROR("apk proc read count(%d) fail", (int)count); + return -EINVAL; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + switch (ts_data->proc_opmode) { + case PROC_READ_REGISTER: + readlen = 1; + ret = fts_i2c_read(client, NULL, 0, buf, readlen); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + return ret; + } + num_read_chars = 1; + break; + case PROC_READ_DATA: + readlen = count; + ret = fts_i2c_read(client, NULL, 0, buf, readlen); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + return ret; + } + + num_read_chars = readlen; + break; + case PROC_WRITE_DATA: + break; + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + memcpy(page, buf, num_read_chars); + return num_read_chars; +} +#endif + +/************************************************************************ +* Name: fts_create_apk_debug_channel +* Brief: create apk debug channel +* Input: i2c info +* Output: +* Return: return 0 if success +***********************************************************************/ +int fts_create_apk_debug_channel(struct fts_ts_data *ts_data) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + ts_data->proc = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops); +#else + ts_data->proc = create_proc_entry(PROC_NAME, 0777, NULL); +#endif + if (NULL == ts_data->proc) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } else { + FTS_INFO("Create proc entry success!"); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) + ts_data->proc->write_proc = fts_debug_write; + ts_data->proc->read_proc = fts_debug_read; +#endif + } + + return 0; +} + +/************************************************************************ +* Name: fts_release_apk_debug_channel +* Brief: release apk debug channel +* Input: +* Output: +* Return: +***********************************************************************/ +void fts_release_apk_debug_channel(struct fts_ts_data *ts_data) +{ + + if (ts_data->proc) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc_remove(ts_data->proc); +#else + remove_proc_entry(PROC_NAME, NULL); +#endif + } +} + +/************************************************************************ + * sysfs interface + ***********************************************************************/ + +/* + * fts_hw_reset interface + */ +static ssize_t fts_hw_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t fts_hw_reset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_dev = fts_data->input_dev; + ssize_t count = 0; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(1); + count = snprintf(buf, PAGE_SIZE, "hw reset executed\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* + * fts_toggle_hw_reset interface + * This grants userspace access to toggle reset pin so caller can manage timing + */ +static ssize_t fts_toggle_reset_pin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (FTS_SYSFS_ECHO_ON(buf)) { + gpio_direction_output(fts_data->pdata->reset_gpio, 1); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + } + msleep(20); + + return count; +} +static ssize_t fts_toggle_reset_pin_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +/* + * fts_irq interface + */ +static ssize_t fts_irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[EX-FUN]enable irq"); + fts_irq_enable(); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]disable irq"); + fts_irq_disable(); + } + mutex_unlock(&input_dev->mutex); + return count; +} + +static ssize_t fts_irq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +/* + * fts_tpfwver interface + */ +static ssize_t fts_tpfwver_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + struct i2c_client *client = ts_data->client; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + mutex_lock(&input_dev->mutex); + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + if (fts_i2c_read_reg(client, FTS_REG_FW_VER, &fwver) < 0) { + num_read_chars = snprintf(buf, PAGE_SIZE, "I2c transfer error!\n"); + } +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + if ((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; +} + +/************************************************************************ +* Name: fts_tprwreg_show +* Brief: no +* Input: device, device attribute, char buf +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_tprwreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i; + struct input_dev *input_dev = fts_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; +} + + + +/************************************************************************ +* Name: fts_tprwreg_store +* Brief: read/write register +* Input: device, device attribute, char buf, char count +* Output: print register value +* Return: char count +***********************************************************************/ +static ssize_t fts_tprwreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + ssize_t cmd_length = 0; + + mutex_lock(&input_dev->mutex); + cmd_length = count - 1; + + 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 FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + 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_i2c_read_reg(client, reg, &val); + rw_op.val = val; + } else { + char reg; + reg = rw_op.reg & 0xFF; + + rw_op.res = fts_i2c_read(client, ®, 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_i2c_write_reg(client, reg, val); + } else { + rw_op.res = fts_i2c_write(client, 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; + } + } + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + 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]; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + struct i2c_client *client = ts_data->client; + + 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, PAGE_SIZE, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + fts_upgrade_bin(client, fwname, 0); + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + ts_data->fw_loading = 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 = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + struct i2c_client *client = ts_data->client; + + 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, PAGE_SIZE, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + fts_upgrade_bin(client, fwname, 1); + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + ts_data->fw_loading = 0; + 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 = fts_data; + 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_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t fts_dumpreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + fts_i2c_read_reg(client, FTS_REG_POWER_MODE, &val); + count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_LIC_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_IDE_PARA_VER_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_LCD_BUSY_NUM, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_CHARGER_MODE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_INT_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_FLOW_WORK_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + mutex_unlock(&input_dev->mutex); + return count; +} + +#define GET_FRAME_NUM 20 +static int data_flag; +static int get_frame_num = 10; +/* + * fts_test_save_test_data - Save test data to /tmp/ + */ +static int fts_save_data(char *file_name, char *data_buf, int len) +{ + struct file *pfile = NULL; + char filepath[128]; + loff_t pos; + mm_segment_t old_fs; + + FTS_FUNC_ENTER(); + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", "/tmp/", file_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0); + } + if (IS_ERR(pfile)) { + FTS_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(pfile, data_buf, len, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_read_differ(struct i2c_client *client) +{ + int count = 0; + int i = 0; + int j = 0; + int k = 0; + u8 tx_num = 0; + u8 rx_num = 0; + u8 regvalue = 0x00; + u8 regaddr = 0x00; + char *read_buffer = NULL; + u8 *tmp_buffer = NULL; + int frame_num = 0; + short rawdata = 0; + u8 regval = 0x00; + + /*1 enter factory mode */ + fts_i2c_write_reg(client, 0x00, 0x40); + msleep(200); + + /*2 get channel num*/ + fts_i2c_read_reg(client, 0x02, &tx_num); + fts_i2c_read_reg(client, 0x03, &rx_num); + + /*3 switch differ mode */ + fts_i2c_read_reg(client, 0x06, ®val); + fts_i2c_write_reg(client, 0x06, 0x01); + msleep(100); + + read_buffer = vmalloc(tx_num*rx_num*2*sizeof(char)*get_frame_num*10); + if (read_buffer == NULL) + return -1; + memset(read_buffer, 0x00, tx_num*rx_num*2*sizeof(char)*get_frame_num*10); + + tmp_buffer = vmalloc(tx_num*rx_num*2*sizeof(u8)); + if (tmp_buffer == NULL) + goto release_read_buffer; + memset(tmp_buffer, 0x00, tx_num*rx_num*2*sizeof(u8)); + + count += snprintf(read_buffer + count, PAGE_SIZE, "tp channel: tx = %d, rx = %d\n", tx_num, rx_num); + /*4 read rawdata*/ + for (frame_num = 0; frame_num < (get_frame_num+1); frame_num++) { + + /*scan*/ + fts_i2c_read_reg(client, 0x00, ®value); + regvalue |= 0x80; + fts_i2c_write_reg(client, 0x00, regvalue); + for (i = 0; i < 50; i++) { + msleep(16); + fts_i2c_read_reg(client, 0x00, ®value); + if (0x40 == regvalue) + break; + } + + /*get rawdata*/ + fts_i2c_write_reg(client, 0x01, 0xAA); + regaddr = 0x36; + fts_i2c_read(client, ®addr, 1, tmp_buffer, tx_num*rx_num*2); + if (frame_num == 0) + continue; + + FTS_DEBUG("Frame_num : %d \n", frame_num); + count += snprintf(read_buffer + count, PAGE_SIZE, "Frame_num : %d \n", frame_num); + for (i = 0; i < tx_num; i++) { + for (j = 0; j < rx_num; j++) { + k = rx_num * i + j; + rawdata = ((short)(tmp_buffer[k * 2] << 8) + tmp_buffer[k * 2 + 1]); + count += snprintf(read_buffer + count, PAGE_SIZE, "%5d ", rawdata); + } + count += snprintf(read_buffer + count, PAGE_SIZE, "\n"); + } + } + + fts_save_data("differ.txt", read_buffer, count); + + vfree(tmp_buffer); +release_read_buffer: + vfree(read_buffer); + /*switch rawdata*/ + fts_i2c_write_reg(client, 0x06, regval); + /*5 enter work mode*/ + fts_i2c_write_reg(client, 0x00, 0x00); + msleep(200); + return 0; +} + +static int fts_read_rawdata(struct i2c_client *client) +{ + int count = 0; + int i = 0; + int j = 0; + int k = 0; + u8 tx_num = 0; + u8 rx_num = 0; + u8 regvalue = 0x00; + u8 regaddr = 0x00; + char *read_buffer = NULL; + u8 *tmp_buffer = NULL; + int frame_num = 0; + int rawdata = 0; + + /*1 enter factory mode */ + fts_i2c_write_reg(client, 0x00, 0x40); + msleep(200); + + /*2 get channel num*/ + fts_i2c_read_reg(client, 0x02, &tx_num); + fts_i2c_read_reg(client, 0x03, &rx_num); + + read_buffer = vmalloc(tx_num*rx_num*2*sizeof(char)*get_frame_num*10); + if (read_buffer == NULL) + return -1; + memset(read_buffer, 0x00, tx_num*rx_num*2*sizeof(char)*get_frame_num*10); + + tmp_buffer = vmalloc(tx_num*rx_num*2*sizeof(u8)); + if (tmp_buffer == NULL) + goto release_read_buffer; + memset(tmp_buffer, 0x00, tx_num*rx_num*2*sizeof(u8)); + + count += snprintf(read_buffer + count, PAGE_SIZE, "tp channel: tx = %d, rx = %d\n", tx_num, rx_num); + /*4 read rawdata*/ + for (frame_num = 0; frame_num < (get_frame_num+1); frame_num++) { + + /*scan*/ + fts_i2c_read_reg(client, 0x00, ®value); + regvalue |= 0x80; + fts_i2c_write_reg(client, 0x00, regvalue); + for (i = 0; i < 50; i++) { + msleep(16); + fts_i2c_read_reg(client, 0x00, ®value); + if (0x40 == regvalue) + break; + } + + /*get rawdata*/ + fts_i2c_write_reg(client, 0x01, 0xAA); + regaddr = 0x36; + fts_i2c_read(client, ®addr, 1, tmp_buffer, tx_num*rx_num*2); + if (frame_num == 0) + continue; + + FTS_INFO("Frame_num : %d \n", frame_num); + count += snprintf(read_buffer + count, PAGE_SIZE, "Frame_num : %d \n", frame_num); + for (i = 0; i < tx_num; i++) { + for (j = 0; j < rx_num; j++) { + k = rx_num * i + j; + rawdata = (tmp_buffer[k * 2] << 8) + tmp_buffer[k * 2 + 1]; + count += snprintf(read_buffer + count, PAGE_SIZE, "%5d ", rawdata); + } + count += snprintf(read_buffer + count, PAGE_SIZE, "\n"); + } + } + + fts_save_data("rawdata.txt", read_buffer, count); + + vfree(tmp_buffer); +release_read_buffer: + vfree(read_buffer); + /*5 enter work mode*/ + fts_i2c_write_reg(client, 0x00, 0x00); + msleep(200); + return 0; +} + +/* + * fts_rawdata interface + */ +static ssize_t fts_rawdata_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + sscanf(buf, "%d %d", &data_flag, &get_frame_num); + FTS_INFO("data_flag = %d, read frame num = %d", data_flag, get_frame_num); + + mutex_unlock(&input_dev->mutex); + return count; +} + +static ssize_t fts_rawdata_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + if (data_flag == 1) { + FTS_INFO("read differ frame num = %d", get_frame_num); + fts_read_differ(client); + } else { + FTS_INFO("read rawdata frame num = %d", get_frame_num); + fts_read_rawdata(client); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + count = snprintf(buf, PAGE_SIZE, "cat data finish\n"); + mutex_unlock(&input_dev->mutex); + return count; +} + +/* 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_toggle_reset_pin, S_IRUGO|S_IWUSR, fts_toggle_reset_pin_show, fts_toggle_reset_pin_store); +static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store); +static DEVICE_ATTR(fts_rawdata, S_IRUGO | S_IWUSR, fts_rawdata_show, fts_rawdata_store); + +/* 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_toggle_reset_pin.attr, + &dev_attr_fts_irq.attr, + &dev_attr_fts_rawdata.attr, + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +/************************************************************************ +* Name: fts_create_sysfs +* Brief: create sysfs interface +* Input: +* Output: +* Return: return 0 if success +***********************************************************************/ +int fts_create_sysfs(struct i2c_client *client) +{ + int ret = 0; + + ret = sysfs_create_group(&client->dev.kobj, &fts_attribute_group); + if (ret) { + FTS_ERROR("[EX]: sysfs_create_group() failed!!"); + sysfs_remove_group(&client->dev.kobj, &fts_attribute_group); + return -ENOMEM; + } else { + FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); + } + + return ret; +} +/************************************************************************ +* Name: fts_remove_sysfs +* Brief: remove sysfs interface +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_remove_sysfs(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &fts_attribute_group); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c new file mode 100644 index 000000000000..c8d7efcc7bfc --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c @@ -0,0 +1,382 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2010-2017, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_ex_mode.c +* +* Author: Liu WeiGuang +* +* Created: 2016-08-31 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* 2.Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* 3.Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_mode_flag { + int fts_glove_mode_flag; + int fts_cover_mode_flag; + int fts_charger_mode_flag; +}; + +struct fts_mode_flag g_fts_mode_flag; + +/***************************************************************************** +* 4.Static variables +*****************************************************************************/ + +/***************************************************************************** +* 5.Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_enter_glove_mode(struct i2c_client *client, int mode); +int fts_enter_cover_mode(struct i2c_client *client, int mode); +int fts_enter_charger_mode(struct i2c_client *client, int mode); + +/***************************************************************************** +* 6.Static function prototypes +*******************************************************************************/ + +#if FTS_GLOVE_EN +static ssize_t fts_touch_glove_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_GLOVE_MODE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Glove Mode: %s\n", g_fts_mode_flag.fts_glove_mode_flag ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Glove Reg(0xC0) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touch_glove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client; + + + client = ts_data->client; + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!g_fts_mode_flag.fts_glove_mode_flag) { + FTS_INFO("[Mode]enter glove mode"); + ret = fts_enter_glove_mode(client, true); + if (ret >= 0) { + g_fts_mode_flag.fts_glove_mode_flag = true; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (g_fts_mode_flag.fts_glove_mode_flag) { + FTS_INFO("[Mode]exit glove mode"); + ret = fts_enter_glove_mode(client, false); + if (ret >= 0) { + g_fts_mode_flag.fts_glove_mode_flag = false; + } + } + } + FTS_INFO("[Mode]glove mode status: %d", g_fts_mode_flag.fts_glove_mode_flag); + return count; +} + +/************************************************************************ +* Name: fts_enter_glove_mode +* Brief: change glove mode +* Input: glove mode +* Output: no +* Return: success >=0, otherwise failed +***********************************************************************/ +int fts_enter_glove_mode(struct i2c_client *client, int mode) +{ + int ret = 0; + static u8 buf_addr[2] = { 0 }; + static u8 buf_value[2] = { 0 }; + buf_addr[0] = FTS_REG_GLOVE_MODE_EN; /* glove control */ + + if (mode) + buf_value[0] = 0x01; + else + buf_value[0] = 0x00; + + ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]); + if (ret < 0) { + FTS_ERROR("[Mode]fts_enter_glove_mode write value fail"); + } + + return ret ; + +} + +/* read and write glove mode +* read example: cat fts_touch_glove_mode---read glove mode +* write example:echo 01 > fts_touch_glove_mode ---write glove mode to 01 +* +*/ +static DEVICE_ATTR (fts_glove_mode, S_IRUGO | S_IWUSR, fts_touch_glove_show, fts_touch_glove_store); + +#endif + +#if FTS_COVER_EN +static ssize_t fts_touch_cover_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_COVER_MODE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Cover Mode: %s\n", g_fts_mode_flag.fts_cover_mode_flag ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Cover Reg(0xC1) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touch_cover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client; + + client = ts_data->client; + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!g_fts_mode_flag.fts_cover_mode_flag) { + FTS_INFO("[Mode]enter cover mode"); + ret = fts_enter_cover_mode(client, true); + if (ret >= 0) { + g_fts_mode_flag.fts_cover_mode_flag = true; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (g_fts_mode_flag.fts_cover_mode_flag) { + FTS_INFO("[Mode]exit cover mode"); + ret = fts_enter_cover_mode(client, false); + if (ret >= 0) { + g_fts_mode_flag.fts_cover_mode_flag = false; + } + } + } + FTS_INFO("[Mode]cover mode status: %d", g_fts_mode_flag.fts_cover_mode_flag); + return count; +} + +/************************************************************************ +* Name: fts_enter_cover_mode +* Brief: change cover mode +* Input: cover mode +* Output: no +* Return: success >=0, otherwise failed +***********************************************************************/ +int fts_enter_cover_mode(struct i2c_client *client, int mode) +{ + int ret = 0; + static u8 buf_addr[2] = { 0 }; + static u8 buf_value[2] = { 0 }; + buf_addr[0] = FTS_REG_COVER_MODE_EN; /* cover control */ + + if (mode) + buf_value[0] = 0x01; + else + buf_value[0] = 0x00; + + ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]); + if (ret < 0) { + FTS_ERROR("[Mode] fts_enter_cover_mode write value fail \n"); + } + + return ret ; + +} + +/* read and write cover mode +* read example: cat fts_touch_cover_mode---read cover mode +* write example:echo 01 > fts_touch_cover_mode ---write cover mode to 01 +* +*/ +static DEVICE_ATTR (fts_cover_mode, S_IRUGO | S_IWUSR, fts_touch_cover_show, fts_touch_cover_store); + +#endif + +#if FTS_CHARGER_EN +static ssize_t fts_touch_charger_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_CHARGER_MODE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Charge Mode: %s\n", g_fts_mode_flag.fts_charger_mode_flag ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Charge Reg(0x8B) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touch_charger_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client; + + client = ts_data->client; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!g_fts_mode_flag.fts_charger_mode_flag) { + FTS_INFO("[Mode]enter charger mode"); + ret = fts_enter_charger_mode(client, true); + if (ret >= 0) { + g_fts_mode_flag.fts_charger_mode_flag = true; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (g_fts_mode_flag.fts_charger_mode_flag) { + FTS_INFO("[Mode]exit charger mode"); + ret = fts_enter_charger_mode(client, false); + if (ret >= 0) { + g_fts_mode_flag.fts_charger_mode_flag = false; + } + } + } + FTS_INFO("[Mode]charger mode status: %d", g_fts_mode_flag.fts_charger_mode_flag); + return count; +} + +/************************************************************************ +* Name: fts_enter_charger_mode +* Brief: change charger mode +* Input: charger mode +* Output: no +* Return: success >=0, otherwise failed +***********************************************************************/ +int fts_enter_charger_mode(struct i2c_client *client, int mode) +{ + int ret = 0; + static u8 buf_addr[2] = { 0 }; + static u8 buf_value[2] = { 0 }; + buf_addr[0] = FTS_REG_CHARGER_MODE_EN; /* charger control */ + + if (mode) + buf_value[0] = 0x01; + else + buf_value[0] = 0x00; + + ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]); + if (ret < 0) { + FTS_DEBUG("[Mode]fts_enter_charger_mode write value fail"); + } + + return ret ; + +} + +/* read and write charger mode +* read example: cat fts_touch_charger_mode---read charger mode +* write example:echo 01 > fts_touch_charger_mode ---write charger mode to 01 +* +*/ +static DEVICE_ATTR (fts_charger_mode, S_IRUGO | S_IWUSR, fts_touch_charger_show, fts_touch_charger_store); + +#endif + +static struct attribute *fts_touch_mode_attrs[] = { +#if FTS_GLOVE_EN + &dev_attr_fts_glove_mode.attr, +#endif + +#if FTS_COVER_EN + &dev_attr_fts_cover_mode.attr, +#endif + +#if FTS_CHARGER_EN + &dev_attr_fts_charger_mode.attr, +#endif + + NULL, +}; + +static struct attribute_group fts_touch_mode_group = { + .attrs = fts_touch_mode_attrs, +}; + +int fts_ex_mode_init(struct i2c_client *client) +{ + int err = 0; + + g_fts_mode_flag.fts_glove_mode_flag = false; + g_fts_mode_flag.fts_cover_mode_flag = false; + g_fts_mode_flag.fts_charger_mode_flag = false; + + err = sysfs_create_group(&client->dev.kobj, &fts_touch_mode_group); + if (0 != err) { + FTS_ERROR("[Mode]create sysfs failed."); + sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group); + return -EIO; + } else { + FTS_DEBUG("[Mode]create sysfs succeeded"); + } + + return err; + +} + +int fts_ex_mode_exit(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group); + return 0; +} + +int fts_ex_mode_recovery(struct i2c_client *client) +{ + int ret = 0; +#if FTS_GLOVE_EN + if (g_fts_mode_flag.fts_glove_mode_flag) + ret = fts_enter_glove_mode(client, true); +#endif + +#if FTS_COVER_EN + if (g_fts_mode_flag.fts_cover_mode_flag) + ret = fts_enter_cover_mode(client, true); +#endif + +#if FTS_CHARGER_EN + if (g_fts_mode_flag.fts_charger_mode_flag) + ret = fts_enter_charger_mode(client, true); +#endif + + return ret; +} + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c new file mode 100755 index 000000000000..a0f1c882f5df --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c @@ -0,0 +1,2013 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2018, 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" + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +/* FW Upgrade Files */ +u8 fw_file_default[] = { +#include FTS_UPGRADE_FW_DEFAULT +}; + +struct upgrade_fw fw_list_default[] = { + {FTS_VENDOR_ID_DEFAULT, FTS_PANEL_ID_DEFAULT, + fw_file_default, sizeof(fw_file_default)}, +}; + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_TOPTOUCH_FW) +u8 fw_file_toptouch_7mm[] = { +#include FTS_UPGRADE_FW_TOPTOUCH_7MM +}; + +u8 fw_file_toptouch_11mm[] = { +#include FTS_UPGRADE_FW_TOPTOUCH_11MM +}; + +struct upgrade_fw fw_list_toptouch[] = { + {FTS_VENDOR_ID_TOPTOUCH, FTS_PANEL_ID_TOPTOUCH_7MM, + fw_file_toptouch_7mm, sizeof(fw_file_toptouch_7mm)}, + {FTS_VENDOR_ID_TOPTOUCH, FTS_PANEL_ID_TOPTOUCH_11MM, + fw_file_toptouch_11mm, sizeof(fw_file_toptouch_11mm)}, +}; +#endif + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_LENSONE_FW) +u8 fw_file_lensone_7mm[] = { +#include FTS_UPGRADE_FW_LENSONE_7MM +}; + +u8 fw_file_lensone_11mm[] = { +#include FTS_UPGRADE_FW_LENSONE_11MM +}; + +struct upgrade_fw fw_list_lensone[] = { + {FTS_VENDOR_ID_LENSONE, FTS_PANEL_ID_LENSONE_7MM, + fw_file_lensone_7mm, sizeof(fw_file_lensone_7mm)}, + {FTS_VENDOR_ID_LENSONE, FTS_PANEL_ID_LENSONE_11MM, + fw_file_lensone_11mm, sizeof(fw_file_lensone_11mm)}, +}; +#endif + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_TOPGROUP_FW) +u8 fw_file_topgroup_7mm[] = { +#include FTS_UPGRADE_FW_TOPGROUP_7MM +}; + +u8 fw_file_topgroup_11mm[] = { +#include FTS_UPGRADE_FW_TOPGROUP_11MM +}; + +struct upgrade_fw fw_list_topgroup[] = { + {FTS_VENDOR_ID_TOPGROUP, FTS_PANEL_ID_TOPGROUP_7MM, + fw_file_topgroup_7mm, sizeof(fw_file_topgroup_7mm)}, + {FTS_VENDOR_ID_TOPGROUP, FTS_PANEL_ID_TOPGROUP_11MM, + fw_file_topgroup_11mm, sizeof(fw_file_topgroup_11mm)}, +}; +#endif + +#if defined(CONFIG_TOUCHSCREEN_FTS_3427) && !defined(CONFIG_TOUCHSCREEN_FTS_5726) +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5x46, +}; +#elif defined(CONFIG_TOUCHSCREEN_FTS_5726) && !defined(CONFIG_TOUCHSCREEN_FTS_3427) +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5822, +}; +#endif + +struct fts_upgrade *fwupgrade; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static u16 fts_pram_ecc_calc_host(u8 *pbuf, u16 length) +{ + u16 ecc = 0; + u16 i = 0; + u16 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; +} + +/************************************************************************ + * fts_pram_ecc_cal - Calculate and get pramboot ecc + * + * return pramboot ecc of tp if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_ecc_cal_algo( + struct i2c_client *client, + u32 start_addr, + u32 ecc_length) +{ + int ret = 0; + int i = 0; + int ecc = 0; + u8 val[2] = { 0 }; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_INFO("read out pramboot checksum"); + 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_i2c_write(client, 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 < 100; i++) { + msleep(1); + ret = fts_i2c_read(client, cmd, 1, val, 1); + if (ret < 0) { + FTS_ERROR("ecc_finish read cmd fail"); + return ret; + } + if (0 == val[0]) + break; + } + if (i >= 100) { + FTS_ERROR("wait ecc finish fail"); + return -EIO; + } + + cmd[0] = FTS_CMD_READ_ECC; + ret = fts_i2c_read(client, 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(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + + FTS_INFO("read out pramboot checksum"); + + ret = fts_i2c_read_reg(client, 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 i2c_client *client, u32 saddr, u32 len) +{ + if ((NULL == fwupgrade) && (NULL == fwupgrade->func)) { + FTS_ERROR("fwupgrade/func is null"); + return -EINVAL; + } + + if (fwupgrade->func->newmode) { + return fts_pram_ecc_cal_algo(client, saddr, len); + } else { + return fts_pram_ecc_cal_xor(client); + } +} + +/************************************************************************ + * fts_pram_write_buf - write pramboot data and calculate ecc + * + * return pramboot ecc of host if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_write_buf(struct i2c_client *client, 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; + + FTS_INFO("write pramboot to pram"); + if ((NULL == fwupgrade) && (NULL == fwupgrade->func)) { + FTS_ERROR("fwupgrade/func is null"); + return -EINVAL; + } + + if (NULL == buf) { + FTS_ERROR("pramboot 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; + + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + if (!fwupgrade->func->newmode) { + ecc_tmp ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("pramboot write data(%d) fail", i); + return ret; + } + } + + if (fwupgrade->func->newmode) { + ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len); + } else { + ecc_in_host = (int)ecc_tmp; + } + + return ecc_in_host; +} + +/************************************************************************ + * fts_pram_start - remap to start pramboot + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_start(struct i2c_client *client) +{ + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + int ret = 0; + + FTS_INFO("remap to start pramboot"); + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + msleep(FTS_DELAY_PRAMBOOT_START); + + return 0; +} + +/************************************************************************ + * fts_pram_write_remap - write pramboot to pram and start pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_write_remap(struct i2c_client *client) +{ + int ret = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + u8 *pb_buf = NULL; + u32 pb_len = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("write pram and remap"); + + if (!upg || !upg->func || !upg->func->pramboot) { + FTS_ERROR("upgrade/pramboot is null"); + return -EINVAL; + } + + if (upg->func->pb_length < FTS_MIN_LEN) { + FTS_ERROR("pramboot length(%x) 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(client, 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(client, 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(client); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_pram_init - initialize pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_init(struct i2c_client *client) +{ + 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_i2c_read(client, 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_i2c_write(client, wbuf, 3); + if (ret < 0) { + FTS_ERROR("write flash type fail"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_pram_write_init +* Brief: wirte pramboot to pram and initialize +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_pram_write_init(struct i2c_client *client) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + struct fts_upgrade *upg = fwupgrade; + + 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(client, &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(client); + 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(client); + if (ret < 0) { + FTS_ERROR("reset to romboot fail"); + return ret; + } + } + + /* check the length of the pramboot */ + ret = fts_pram_write_remap(client); + 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(client, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("not in pramboot"); + return -EIO; + } + + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("pramboot init fail"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_check_fw_valid +* Brief: check fw in tp is valid or not +* Input: +* Output: +* Return: return true if fw is valid, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client) +{ + int ret = 0; + + ret = fts_wait_tp_to_valid(client); + if (ret < 0) { + FTS_INFO("tp fw invaild"); + return false; + } + + FTS_INFO("tp fw vaild"); + return true; +} + +/************************************************************************ +* 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 +***********************************************************************/ +int fts_fwupg_get_boot_state(struct i2c_client *client, 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 = fts_data->ic_info.ids; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("**********read boot id**********"); + if ((NULL == fw_sts) || (NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func/fw_sts is null"); + return -EINVAL; + } + + if (upg->func->hid_supported) + fts_i2c_hid2std(client); + + cmd[0] = FTS_CMD_START1; + cmd[1] = FTS_CMD_START2; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("write 55 aa cmd fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + cmd[0] = FTS_CMD_READ_ID; + cmd[1] = cmd[2] = cmd[3] = 0x00; + if (fts_data->ic_info.is_incell) + cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_i2c_read(client, 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]); + 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; +} + +/************************************************************************ +* Name: fts_fwupg_check_state +* Brief: confirm tp run in romboot/pramboot/bootloader +* Input: +* Output: +* Return: return true if state is match, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_state(struct i2c_client *client, 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(client, &cstate); + /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */ + if (cstate == rstate) + return true; + msleep(FTS_DELAY_READ_ID); + } + + 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(struct i2c_client *client) +{ + int ret = 0; + u8 cmd = FTS_CMD_RESET; + + FTS_INFO("reset in boot environment"); + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_boot +* Brief: reset to boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_to_boot(struct i2c_client *client) +{ + int ret = 0; + + FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment"); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_AA); + if (ret < 0) { + FTS_ERROR("write FC=0xAA fail"); + return ret; + } + msleep(FTS_DELAY_FC_AA); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_55); + if (ret < 0) { + FTS_ERROR("write FC=0x55 fail"); + return ret; + } + + 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 +***********************************************************************/ +int fts_fwupg_reset_to_romboot(struct i2c_client *client) +{ + int ret = 0; + int i = 0; + u8 cmd = FTS_CMD_RESET; + enum FW_STATUS state = FTS_RUN_IN_ERROR; + + ret = fts_i2c_write(client, &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(client, &state); + if (FTS_RUN_IN_ROM == state) + break; + mdelay(5); + } + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("reset to romboot fail"); + return -EIO; + } + + 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(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("***********enter into pramboot/bootloader***********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_reset_to_boot(client); + if (ret < 0) { + FTS_ERROR("enter into romboot/bootloader fail"); + return ret; + } + } + + if (upg->func->pramboot_supported) { + FTS_INFO("pram supported, write pramboot and init"); + /* pramboot */ + ret = fts_pram_write_init(client); + 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(client, 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( + struct i2c_client *client, + 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_i2c_read(client, &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); */ + msleep(retries_delay); + } + + 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(struct i2c_client *client, 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_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("erase cmd fail"); + return ret; + } + msleep(delay); + + /* read status 0xF0AA: success */ + flag = fts_fwupg_check_flash_status(client, 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(struct i2c_client *client, u32 saddr, u32 len) +{ + int ret = 0; + u32 i = 0; + u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 }; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u32 packet_num = 0; + u32 packet_len = 0; + u32 remainder = 0; + u32 addr = 0; + u32 offset = 0; + + FTS_INFO("**********read out checksum**********"); + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_i2c_write(client, wbuf, 1); + if (ret < 0) { + FTS_ERROR("ecc init cmd write fail"); + return ret; + } + + 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 ((i == (packet_num - 1)) && remainder) { + packet_len = remainder; + } + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + + FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len); + ret = fts_i2c_write(client, wbuf, FTS_CMD_ECC_CAL_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd write fail"); + return ret; + } + + msleep(packet_len / 256); + + /* read status if check sum is finished */ + ret = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ECC_OK, + FTS_RETRIES_ECC_CAL, FTS_RETRIES_DELAY_ECC_CAL); + if (ret < 0) { + FTS_ERROR("ecc flash status read fail"); + return ret; + } + } + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_i2c_read(client, wbuf, 1, val, 1); + if (ret < 0) { + FTS_ERROR("ecc read cmd write fail"); + return ret; + } + + return val[0]; +} + +/************************************************************************ + * 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( + struct i2c_client *client, + 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; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_in_host = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + u16 wr_ok = 0; + + FTS_INFO("**********write data to flash**********"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("buf is NULL or len is 0"); + 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); + + packet_buf[0] = FTS_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + ecc_in_host ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + 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_i2c_read(client, &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); + } + } + + return (int)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(struct i2c_client *client, 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]; + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("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); + + wbuf[0] = FTS_CMD_READ; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + ret = fts_i2c_write(client, wbuf, FTS_CMD_READ_LEN); + if (ret < 0) { + FTS_ERROR("pram/bootloader write 03 command fail"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */ + ret = fts_i2c_read(client, NULL, 0, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03 command fail"); + return ret; + } + } + + 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 + ***********************************************************************/ +int fts_flash_read(struct i2c_client *client, 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(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail"); + goto read_flash_err; + } + + ret = fts_flash_read_buf(client, 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(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return ret; +} + +/************************************************************************ + * Name: fts_read_file + * Brief: read file + * Input: file name + * Output: + * Return: return file len if succuss, otherwise return error code + ***********************************************************************/ +int fts_read_file(char *file_name, u8 **file_buf) +{ + 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:%x read len:%x pos:%x", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +/************************************************************************ +* Name: fts_upgrade_bin +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_upgrade_bin(struct i2c_client *client, char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN) || (ret > FTS_MAX_LEN_FILE)) { + FTS_ERROR("read fw bin file(sdcard) fail, len:%d", ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%x", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(client, 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(client, 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(client, 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(client); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + +err_bin: + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + return ret; +} + +#if FTS_AUTO_LIC_UPGRADE_EN +static int fts_lic_get_vid_in_tp(struct i2c_client *client, u16 *vid) +{ + int ret = 0; + u8 val[2] = { 0 }; + + if (NULL == vid) { + FTS_ERROR("vid is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val[0]); + if (fts_data->ic_info.is_incell) + ret = fts_i2c_read_reg(client, FTS_REG_MODULE_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(u16 *vid) +{ + u8 val[2] = { 0 }; + u8 *licbuf = NULL; + u32 conf_saddr = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->lic || !vid) { + FTS_ERROR("upgrade/func/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 (fts_data->ic_info.is_incell) + val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF]; + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, 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(u8 *ver) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + 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; +} + +/* check if lcd init code need upgrade +* true-need false-no need +*/ +static bool fts_lic_need_upgrade(struct i2c_client *client) +{ + 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(client); + if (!fwvalid) { + FTS_INFO("fw is invalid, no upgrade lcd init code"); + return false; + } + + ret = fts_lic_get_vid_in_host(&vid_in_host); + if (ret < 0) { + FTS_ERROR("vendor id in host invalid"); + return false; + } + + ret = fts_lic_get_vid_in_tp(client, &vid_in_tp); + if (ret < 0) { + FTS_ERROR("vendor id in tp invalid"); + return false; + } + + FTS_DEBUG("vid in tp:%x, host:%x", 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(&initcode_ver_in_host); + if (ret < 0) { + FTS_ERROR("init code in host invalid"); + return false; + } + + ret = fts_lic_get_ver_in_tp(client, &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; +} + +int fts_lic_upgrade(struct i2c_client *client, 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(client); + 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(client, upg->lic, upg->lic_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_lic_get_ver_in_tp(client, &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(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, 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(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/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; +} + +/************************************************************************ + * fts_param_need_upgrade - check fw paramcfg need upgrade or not + * + * Return: return true if paramcfg need upgrade + ***********************************************************************/ +static bool fts_param_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + u8 val = 0; + u8 ver_in_host = 0; + u8 ver_in_tp = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(client); + if (!fwvalid) { + FTS_INFO("fw is invalid, no upgrade paramcfg"); + return false; + } + + ret = fts_param_get_ver_in_host(&ver_in_host); + if (ret < 0) { + FTS_ERROR("param version in host invalid"); + return false; + } + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, &val); + if (ret < 0) { + FTS_ERROR("read IDE PARAM STATUS in tp fail"); + return false; + } + + if ((val & 0x80) != 0x80) { + FTS_INFO("no IDE VER in tp"); + return false; + } else if ((val & 0x7F) != 0x00) { + FTS_INFO("IDE VER, param invalid, need upgrade param"); + return true; + } + + ret = fts_param_get_ver_in_tp(client, &ver_in_tp); + if (ret < 0) { + FTS_ERROR("get IDE param ver in tp fail"); + return false; + } + + FTS_INFO("fw paramcfg version in tp:%x, host:%x", ver_in_tp, ver_in_host); + if (ver_in_tp < ver_in_host) { + return true; + } + + return false; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_tp - read fw ver from tp register + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_FW_VER, ver); + if (ret < 0) { + FTS_ERROR("read fw ver from tp fail"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_host - read fw ver in host fw image + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->fwveroff) { + FTS_ERROR("fw len(%x) < fw ver offset(%x)", + upg->fw_length, upg->func->fwveroff); + return -EINVAL; + } + + FTS_INFO("fw version offset:%x", upg->func->fwveroff); + *ver = upg->fw[upg->func->fwveroff]; + return 0; +} + +/************************************************************************ + * fts_fwupg_need_upgrade - check fw need upgrade or not + * + * Return: return true if fw need upgrade + ***********************************************************************/ +static bool fts_fwupg_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + u8 fw_ver_in_host = 0; + u8 fw_ver_in_tp = 0; + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_get_ver_in_host(&fw_ver_in_host); + if (ret < 0) { + FTS_ERROR("get fw ver in host fail"); + return false; + } + + ret = fts_fwupg_get_ver_in_tp(client, &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 + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_upgrade(struct i2c_client *client, 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(client); + 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(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_fwupg_get_ver_in_tp(client, &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 { + FTS_INFO("fw don't need upgrade"); + if (upg->func->param_upgrade) { + if (fts_param_need_upgrade(client)) { + FTS_INFO("upgrade param area(times:%d)", upgrade_count); + ret = upg->func->param_upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_param_get_ver_in_tp(client, &ver); + FTS_INFO("success upgrade to fw param version %02x", ver); + break; + } + } else { + FTS_INFO("param don't need upgrade"); + break; + } + } else { + break; + } + } + } while (upgrade_count < 2); + + return ret; +} + +#if FTS_AUTO_UPGRADE_EN +/************************************************************************ + * fts_fwupg_auto_upgrade - upgrade main entry + ***********************************************************************/ +void fts_fwupg_auto_upgrade(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct i2c_client *client = ts_data->client; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("********************FTS enter upgrade********************"); + + ret = fts_fwupg_upgrade(client, 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(client, 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********************"); +} + +/* + * fts_fwupg_get_vendor_id - Get the Vendor ID from the TP + * + * Return 0 if success, otherwise return error code + */ +int fts_fwupg_get_vendor_id(struct fts_ts_data *ts_data, u16 *vid) +{ + int ret = 0; + bool fwvalid = false; + u8 vendor_id = 0; + u8 module_id = 0; + u32 fwcfg_addr = 0; + u8 cfgbuf[FTS_HEADER_LEN] = { 0 }; + struct i2c_client *client = ts_data->client; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("Read Vendor ID from TP"); + if ((!upg) || (!upg->func) || (!vid)) { + FTS_ERROR("upgrade/func/vid is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &vendor_id); + if (ts_data->ic_info.is_incell) + ret = fts_i2c_read_reg(client, FTS_REG_MODULE_ID, &module_id); + } else { + fwcfg_addr = upg->func->fwcfgoff; + ret = fts_flash_read(client, fwcfg_addr, cfgbuf, FTS_HEADER_LEN); + vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF]; + if (ts_data->ic_info.is_incell) { + if ((cfgbuf[FTS_CONIFG_MODULEID_OFF] + + cfgbuf[FTS_CONIFG_MODULEID_OFF + 1]) == 0xFF) + module_id = cfgbuf[FTS_CONIFG_MODULEID_OFF]; + } + } + + if (ret < 0) { + FTS_ERROR("fail to get vendor id from tp"); + return ret; + } + + *vid = ((u16)module_id << 8) + vendor_id; + return 0; +} + +/* + * fts_fwupg_get_panel_id - Get the Panel ID from the TP + * + * Return 0 if success, otherwise return the error code + */ + +int fts_fwupg_get_panel_id(struct fts_ts_data *ts_data, u16 *id) +{ + int ret = 0; + u8 panel_id; + struct i2c_client *client = ts_data->client; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("Read Panel ID from TP"); + if ((!upg) || (!upg->func) || (!id)) { + FTS_ERROR("Unable to get Panel ID"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_PANEL_ID, &panel_id); + if (ret < 0) { + FTS_ERROR("Failed to read the Panel ID register"); + return ret; + } + + *id = (u16)panel_id; + return ret; +} + +/* + * fts_fwupg_get_fw_from_ids + * - Get the proper FW corresponding to the supplied panel + * and vendor ids + * Return 0 if success, otherwise return -ENODATA + */ + +static int fts_fwupg_get_fw_from_ids(u16 vendor_id, u16 panel_id, struct upgrade_fw **fw) +{ + int i; + i = 0; + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_TOPTOUCH_FW) + for (i = 0; i < FTS_TOPTOUCH_NUM_FW; i++) { + if (vendor_id == fw_list_toptouch[i].vendor_id && + panel_id == fw_list_toptouch[i].panel_id) { + *fw = &fw_list_toptouch[i]; + return 0; + } + } +#endif + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_LENSONE_FW) + for (i = 0; i < FTS_LENSONE_NUM_FW; i++) { + if (vendor_id == fw_list_lensone[i].vendor_id && + panel_id == fw_list_lensone[i].panel_id) { + *fw = &fw_list_lensone[i]; + return 0; + } + } +#endif + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_TOPGROUP_FW) + for (i = 0; i < FTS_TOPGROUP_NUM_FW; i++) { + if (vendor_id == fw_list_topgroup[i].vendor_id && + panel_id == fw_list_topgroup[i].panel_id) { + *fw = &fw_list_topgroup[i]; + return 0; + } + } +#endif + + return -ENODATA; +} + +/* + * fts_fwupg_get_fw_from_display_vendor + * - Get the proper FW corresponding to the display vendor. This function + * - is intended to be used as a backup if the panel and vendor ID registers + * - are unable to determine the proper FW package. This function instead + * - uses the display vendor and defaults to the 1.1mm CG-thickness FW package + * - since this is the POR configuration. + * Return 0 if success, otherwise return -ENODATA + */ +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_MP_POR) +static int fts_fwupg_get_fw_from_display_vendor(struct fts_ts_data *ts_data, + struct upgrade_fw **fw) +{ + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_LENSONE_FW) + if (ts_data->panel_vendor == FTS_PANEL_VENDOR_BOE) { + *fw = &fw_list_lensone[1]; // Select 1.1mm CG thickness FW + return 0; + } +#endif + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_UPDATE_TOPGROUP_FW) + if (ts_data->panel_vendor == FTS_PANEL_VENDOR_INX) { + *fw = &fw_list_topgroup[1]; // Select 1.1mm CG thickness FW + return 0; + } +#endif + + return -ENODATA; +} +#endif + +/* + * fts_fwupg_get_fw_file - get upgrade fw file in host driver + * + * return 0 if success, otherwise return error code + */ +static int fts_fwupg_get_fw_file(struct fts_ts_data *ts_data) +{ + struct upgrade_fw *fw = &fw_list_default[0]; + struct fts_upgrade *upg = fwupgrade; + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726) && !defined(CONFIG_TOUCHSCREEN_FTS_5726_P1) + int ret = 0; + u16 vendor_id = 0; + u16 panel_id = 0; + + ret = fts_fwupg_get_vendor_id(ts_data, &vendor_id); + if (ret < 0) { + FTS_ERROR("Failed to get the Vendor ID"); + return ret; + } + ret = fts_fwupg_get_panel_id(ts_data, &panel_id); + if (ret < 0) { + FTS_ERROR("Failed to get the Panel ID"); + return ret; + } + FTS_INFO("Touch IC Vendor ID:0x%02X, Panel ID:0x%02X", vendor_id, panel_id); + + ret = fts_fwupg_get_fw_from_ids(vendor_id, panel_id, &fw); + if (ret < 0) { + FTS_ERROR("Vendor ID and Panel ID don't correspond to a FW file"); + +#if defined(CONFIG_TOUCHSCREEN_FTS_5726_MP_POR) + ret = fts_fwupg_get_fw_from_display_vendor(ts_data, &fw); + if (ret < 0) { + FTS_ERROR("No FW corresponding to the display vendor"); + return -ENODATA; + } +#else + return -ENODATA; +#endif // CONFIG_TOUCHSCREEN_FTS_5726_MP_POR + + } +#endif + + if (upg) { + upg->fw = fw->fw_file; + upg->fw_length = fw->fw_len; + upg->lic = fw->fw_file; + upg->lic_length = fw->fw_len; + + FTS_INFO("upgrade fw file len:%x", upg->fw_length); + if ((upg->fw_length < FTS_MIN_LEN) + || (upg->fw_length > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file len(%x) fail", upg->fw_length); + return -ENODATA; + } + } + return 0; +} + +/* + * fts_fwupg_init_ic_detail - for ic detail initialaztion + */ +static void fts_fwupg_init_ic_detail(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (upg && upg->func && upg->func->init) { + upg->func->init(); + } +} + +/* + * fts_fwupg_work - fw upgrade work function + * 1. get fw image/file + * 2. call upgrade main function(fts_fwupg_auto_upgrade) + */ +static void fts_fwupg_work(struct work_struct *work) +{ + int ret = 0; + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, fwupg_work); + + FTS_INFO("fw upgrade work function"); + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + FTS_INFO("get upgrade fw file"); + ret = fts_fwupg_get_fw_file(ts_data); + fts_fwupg_init_ic_detail(); + if (ret < 0) { + FTS_ERROR("get file fail, can't upgrade"); + } else { + /* run auto upgrade */ + fts_fwupg_auto_upgrade(ts_data); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + ts_data->fw_loading = 0; +} + +/***************************************************************************** + * Name: fts_fwupg_init + * Brief: upgrade function initialization + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + *****************************************************************************/ +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + int ic_stype = ts_data->ic_info.ids.type; + 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 (NULL == ts_data->ts_workqueue) { + FTS_ERROR("fts 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; + } + + if (1 == func_count) { + fwupgrade->func = func; + } else { + for (i = 0; i < func_count; i++) { + func = upgrade_func_list[i]; + for (j = 0; j < FTX_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 functin match, can't upgrade"); + return -ENODATA; + } + + INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work); + queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work); + + return 0; +} + +/***************************************************************************** + * Name: fts_fwupg_exit + * Brief: + * Input: + * Output: + * Return: + *****************************************************************************/ +int fts_fwupg_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (fwupgrade) { + kfree(fwupgrade); + } + FTS_FUNC_EXIT(); + + return 0; +} + +#endif /* #if FTS_AUTO_UPGRADE_EN */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h new file mode 100644 index 000000000000..301427cd7905 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h @@ -0,0 +1,172 @@ +/************************************************************************ +* Copyright (C) 2010-2018, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_flash.h +* +* Author: fupeipei +* +* 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_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 10 +#define FTS_ROMBOOT_CMD_ECC 0xCC + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#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 200 +#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 6 +#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_DATA_LEN_LEN 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 (128 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_SECTOR (4 * 1024) +#define FTS_CONIFG_VENDORID_OFF 0x04 +#define FTS_CONIFG_MODULEID_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_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_FC_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/data/" + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_CMD_READ_ECC 0xCD +#define FTX_MAX_COMPATIBLE_TYPE 4 +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +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, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + int newmode; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + bool hid_supported; + bool pramboot_supported; + u8 *pramboot; + u32 pb_length; + int (*init)(void); + int (*upgrade)(struct i2c_client *, u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(struct i2c_client *, u8 *, u32); + int (*param_upgrade)(struct i2c_client *, u8 *, u32); + int (*force_upgrade)(struct i2c_client *, u8 *, u32); +}; + +struct fts_upgrade { + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; + struct upgrade_func *func; +}; + +struct upgrade_fw { + u16 vendor_id; + u16 panel_id; + u8 *fw_file; + u32 fw_len; +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_upgrade *fwupgrade; +#if defined(CONFIG_TOUCHSCREEN_FTS_3427) && !defined(CONFIG_TOUCHSCREEN_FTS_5726) +extern struct upgrade_func upgrade_func_ft5x46; +#elif defined(CONFIG_TOUCHSCREEN_FTS_5726) && !defined(CONFIG_TOUCHSCREEN_FTS_3427) +extern struct upgrade_func upgrade_func_ft5822; +#endif + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client); +int fts_fwupg_get_boot_state(struct i2c_client *client, enum FW_STATUS *fw_sts); +bool fts_fwupg_check_state(struct i2c_client *client, enum FW_STATUS rstate); +int fts_fwupg_reset_in_boot(struct i2c_client *client); +int fts_fwupg_reset_to_boot(struct i2c_client *client); +int fts_fwupg_reset_to_romboot(struct i2c_client *client); +int fts_fwupg_enter_into_boot(struct i2c_client *client); +int fts_fwupg_erase(struct i2c_client *client, u32 delay); +int fts_fwupg_ecc_cal(struct i2c_client *client, u32 saddr, u32 len); +int fts_flash_write_buf(struct i2c_client *client, u32 saddr, u8 *buf, u32 len, u32 delay); +int fts_fwupg_upgrade(struct i2c_client *client, struct fts_upgrade *upg); +#endif diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile new file mode 100644 index 000000000000..a0057f587ad2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the focaltech touchscreen drivers. +# + +# Each configuration option enables a list of files. + + +obj-$(CONFIG_TOUCHSCREEN_FTS_3427) += focaltech_upgrade_ft3427.o +obj-$(CONFIG_TOUCHSCREEN_FTS_5726) += focaltech_upgrade_ft5726.o diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3427.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3427.c new file mode 100644 index 000000000000..95aad6c408a4 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3427.c @@ -0,0 +1,162 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2018, 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_ft5x46.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-15 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "../focaltech_core.h" +#include "../focaltech_flash.h" + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static int fts_ft5x46_init(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (NULL == upg) { + FTS_ERROR("upg is null"); + return -EINVAL; + } + + if (upg->fw_length < FTS_MIN_LEN) { + FTS_ERROR("fw length fail"); + return -EINVAL; + } + + upgrade_func_ft5x46.fwveroff = upg->fw_length - 2; + return 0; +} + +/************************************************************************ +* Name: fts_ft5x46_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5x46_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + 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; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + cmd[0] = FTS_CMD_DATA_LEN; + cmd[1] = BYTE_OFF_16(len); + cmd[2] = BYTE_OFF_8(len); + cmd[3] = BYTE_OFF_0(len); + ret = fts_i2c_write(client, cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + ret = fts_fwupg_erase(client, FTS_REASE_APP_DELAY); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5x46.appoff; + ecc_in_host = fts_flash_write_buf(client, start_addr, buf, len, 1); + if (ecc_in_host < 0) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(client, 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(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + msleep(200); + return 0; + +fw_reset: + FTS_INFO("upgrade fail, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return -EIO; +} + +struct upgrade_func upgrade_func_ft5x46 = { + .ctype = {0x02}, + .fwveroff = 0x0000, + .fwcfgoff = 0xD780, + .appoff = 0x0000, + .pramboot_supported = false, + .hid_supported = true, + .init = fts_ft5x46_init, + .upgrade = fts_ft5x46_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft5726.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft5726.c new file mode 100644 index 000000000000..3417405ff66e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft5726.c @@ -0,0 +1,145 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2018, 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_ft5822.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-15 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "../focaltech_core.h" +#include "../focaltech_flash.h" + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +/************************************************************************ +* Name: fts_ft5822_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5822_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + 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; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + cmd[0] = FTS_CMD_DATA_LEN; + cmd[1] = BYTE_OFF_16(len); + cmd[2] = BYTE_OFF_8(len); + cmd[3] = BYTE_OFF_0(len); + ret = fts_i2c_write(client, cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + ret = fts_fwupg_erase(client, FTS_REASE_APP_DELAY); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5822.appoff; + ecc_in_host = fts_flash_write_buf(client, start_addr, buf, len, 1); + if (ecc_in_host < 0) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(client, 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(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + msleep(200); + return 0; + +fw_reset: + FTS_INFO("upgrade fail, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return -EIO; +} + + +struct upgrade_func upgrade_func_ft5822 = { + .ctype = {0x01}, + .fwveroff = 0x10A, + .fwcfgoff = 0xFFB0, + .appoff = 0x0000, + .pramboot_supported = false, + .hid_supported = true, + .upgrade = fts_ft5822_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c new file mode 100644 index 000000000000..95464a00b26a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c @@ -0,0 +1,591 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2017, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_gestrue.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#if FTS_GESTURE_EN +/****************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define KEY_GESTURE_U KEY_U +#define KEY_GESTURE_UP KEY_UP +#define KEY_GESTURE_DOWN KEY_DOWN +#define KEY_GESTURE_LEFT KEY_LEFT +#define KEY_GESTURE_RIGHT KEY_RIGHT +#define KEY_GESTURE_O KEY_O +#define KEY_GESTURE_E KEY_E +#define KEY_GESTURE_M KEY_M +#define KEY_GESTURE_L KEY_L +#define KEY_GESTURE_W KEY_W +#define KEY_GESTURE_S KEY_S +#define KEY_GESTURE_V KEY_V +#define KEY_GESTURE_C KEY_C +#define KEY_GESTURE_Z KEY_Z + +#define GESTURE_LEFT 0x20 +#define GESTURE_RIGHT 0x21 +#define GESTURE_UP 0x22 +#define GESTURE_DOWN 0x23 +#define GESTURE_DOUBLECLICK 0x24 +#define GESTURE_O 0x30 +#define GESTURE_W 0x31 +#define GESTURE_M 0x32 +#define GESTURE_E 0x33 +#define GESTURE_L 0x44 +#define GESTURE_S 0x46 +#define GESTURE_V 0x54 +#define GESTURE_Z 0x41 +#define GESTURE_C 0x34 +#define FTS_GESTRUE_POINTS 255 +#define FTS_GESTRUE_POINTS_HEADER 8 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* +* header - byte0:gesture id +* byte1:pointnum +* byte2~7:reserved +* coordinate_x - All gesture point x coordinate +* coordinate_y - All gesture point y coordinate +* mode - 1:enable gesture function(default) +* - 0:disable +* active - 1:enter into gesture(suspend) +* 0:gesture disable or resume +*/ +struct fts_gesture_st { + u8 header[FTS_GESTRUE_POINTS_HEADER]; + u16 coordinate_x[FTS_GESTRUE_POINTS]; + u16 coordinate_y[FTS_GESTRUE_POINTS]; + u8 mode; /*host driver enable gesture flag*/ + u8 active; /*gesture actutally work*/ +}; + +/***************************************************************************** +* 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); +static ssize_t fts_gesture_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t fts_gesture_buf_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t fts_gesture_buf_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); + +/* sysfs gesture node + * read example: cat fts_gesture_mode ---read gesture mode + * write example:echo 01 > fts_gesture_mode ---write gesture mode to 01 + * + */ +static DEVICE_ATTR (fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show, fts_gesture_store); +/* + * read example: cat fts_gesture_buf ---read gesture buf + */ +static DEVICE_ATTR (fts_gesture_buf, S_IRUGO | S_IWUSR, fts_gesture_buf_show, fts_gesture_buf_store); +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + NULL, +}; + +static struct attribute_group fts_gesture_group = { + .attrs = fts_gesture_mode_attrs, +}; + +/************************************************************************ +* Name: fts_gesture_show +* Brief: +* Input: device, device attribute, char buf +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Gesture Mode: %s\n", fts_gesture_data.mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/************************************************************************ +* Name: fts_gesture_store +* Brief: +* Input: device, device attribute, char buf, char count +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[GESTURE]enable gesture"); + fts_gesture_data.mode = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[GESTURE]disable gesture"); + fts_gesture_data.mode = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} +/************************************************************************ +* Name: fts_gesture_buf_show +* Brief: +* Input: device, device attribute, char buf +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_buf_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Gesture ID: 0x%x\n", fts_gesture_data.header[0]); + count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum: %d\n", fts_gesture_data.header[1]); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Point Buf:\n"); + for (i = 0; i < fts_gesture_data.header[1]; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i, fts_gesture_data.coordinate_x[i], fts_gesture_data.coordinate_y[i]); + if ((i + 1) % 4 == 0) + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/************************************************************************ +* Name: fts_gesture_buf_store +* Brief: +* Input: device, device attribute, char buf, char count +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_buf_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + /* place holder for future use */ + return -EPERM; +} + +/***************************************************************************** +* Name: fts_create_gesture_sysfs +* Brief: +* Input: +* Output: +* Return: 0-success or others-error +*****************************************************************************/ +int fts_create_gesture_sysfs(struct i2c_client *client) +{ + int ret = 0; + + ret = sysfs_create_group(&client->dev.kobj, &fts_gesture_group); + if (ret != 0) { + FTS_ERROR("[GESTURE]fts_gesture_mode_group(sysfs) create failed!"); + sysfs_remove_group(&client->dev.kobj, &fts_gesture_group); + return ret; + } + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_report +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_gesture_report(struct input_dev *input_dev, int gesture_id) +{ + int gesture; + + FTS_FUNC_ENTER(); + FTS_INFO("fts 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); + } + + FTS_FUNC_EXIT(); +} + +/************************************************************************ +* Name: fts_gesture_read_buffer +* Brief: read data from TP register +* Input: +* Output: +* Return: fail <0 +***********************************************************************/ +static int fts_gesture_read_buffer(struct i2c_client *client, u8 *buf, int read_bytes) +{ + int remain_bytes; + int ret; + int i; + + if (read_bytes <= I2C_BUFFER_LENGTH_MAXINUM) { + ret = fts_i2c_read(client, buf, 1, buf, read_bytes); + } else { + ret = fts_i2c_read(client, buf, 1, buf, I2C_BUFFER_LENGTH_MAXINUM); + remain_bytes = read_bytes - I2C_BUFFER_LENGTH_MAXINUM; + for (i = 1; remain_bytes > 0; i++) { + if (remain_bytes <= I2C_BUFFER_LENGTH_MAXINUM) + ret = fts_i2c_read(client, buf, 0, buf + I2C_BUFFER_LENGTH_MAXINUM * i, remain_bytes); + else + ret = fts_i2c_read(client, buf, 0, buf + I2C_BUFFER_LENGTH_MAXINUM * i, I2C_BUFFER_LENGTH_MAXINUM); + remain_bytes -= I2C_BUFFER_LENGTH_MAXINUM; + } + } + + return ret; +} + +/************************************************************************ +* Name: fts_gesture_readdata +* Brief: read data from TP register +* Input: +* Output: +* Return: return 0 if succuss, otherwise reture error code +***********************************************************************/ +int fts_gesture_readdata(struct fts_ts_data *ts_data) +{ + u8 buf[FTS_GESTRUE_POINTS * 4] = { 0 }; + int ret = 0; + int i = 0; + int gestrue_id = 0; + int read_bytes = 0; + u8 pointnum; + u8 state; + struct i2c_client *client = ts_data->client; + struct input_dev *input_dev = ts_data->input_dev; + + if (!ts_data->suspended) { + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state); + if ((ret < 0) || (state != ENABLE)) { + FTS_DEBUG("gesture not enable, don't process gesture"); + return -EIO; + } + + /* init variable before read gesture point */ + memset(fts_gesture_data.header, 0, FTS_GESTRUE_POINTS_HEADER); + memset(fts_gesture_data.coordinate_x, 0, FTS_GESTRUE_POINTS * sizeof(u16)); + memset(fts_gesture_data.coordinate_y, 0, FTS_GESTRUE_POINTS * sizeof(u16)); + + buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + ret = fts_i2c_read(client, buf, 1, buf, FTS_GESTRUE_POINTS_HEADER); + if (ret < 0) { + FTS_ERROR("[GESTURE]Read gesture header data failed!!"); + FTS_FUNC_EXIT(); + return ret; + } + + memcpy(fts_gesture_data.header, buf, FTS_GESTRUE_POINTS_HEADER); + gestrue_id = buf[0]; + pointnum = buf[1]; + read_bytes = ((int)pointnum) * 4 + 2; + buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + FTS_DEBUG("[GESTURE]PointNum=%d", pointnum); + ret = fts_gesture_read_buffer(client, buf, read_bytes); + if (ret < 0) { + FTS_ERROR("[GESTURE]Read gesture touch data failed!!"); + FTS_FUNC_EXIT(); + return ret; + } + + fts_gesture_report(input_dev, gestrue_id); + for (i = 0; i < pointnum; i++) { + fts_gesture_data.coordinate_x[i] = (((s16) buf[0 + (4 * i + 2)]) & 0x0F) << 8 + | (((s16) buf[1 + (4 * i + 2)]) & 0xFF); + fts_gesture_data.coordinate_y[i] = (((s16) buf[2 + (4 * i + 2)]) & 0x0F) << 8 + | (((s16) buf[3 + (4 * i + 2)]) & 0xFF); + } + + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_recovery +* Brief: recovery gesture state when reset or power on +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_gesture_recovery(struct i2c_client *client) +{ + if ((ENABLE == fts_gesture_data.mode) && (ENABLE == fts_gesture_data.active)) { + FTS_INFO("enter fts_gesture_recovery"); + fts_i2c_write_reg(client, 0xD1, 0xff); + fts_i2c_write_reg(client, 0xD2, 0xff); + fts_i2c_write_reg(client, 0xD5, 0xff); + fts_i2c_write_reg(client, 0xD6, 0xff); + fts_i2c_write_reg(client, 0xD7, 0xff); + fts_i2c_write_reg(client, 0xD8, 0xff); + fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, ENABLE); + } +} + +/***************************************************************************** +* Name: fts_gesture_suspend +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +int fts_gesture_suspend(struct i2c_client *client) +{ + int ret; + int i; + u8 state; + + FTS_INFO("gesture suspend..."); + /* gesture not enable, return immediately */ + if (fts_gesture_data.mode == DISABLE) { + FTS_INFO("gesture is disabled"); + return -EINVAL; + } + + for (i = 0; i < 5; i++) { + fts_i2c_write_reg(client, 0xd1, 0xff); + fts_i2c_write_reg(client, 0xd2, 0xff); + fts_i2c_write_reg(client, 0xd5, 0xff); + fts_i2c_write_reg(client, 0xd6, 0xff); + fts_i2c_write_reg(client, 0xd7, 0xff); + fts_i2c_write_reg(client, 0xd8, 0xff); + fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, ENABLE); + msleep(1); + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state); + if (state == ENABLE) + break; + } + + if (i >= 5) { + FTS_ERROR("[GESTURE]Enter into gesture(suspend) failed!\n"); + fts_gesture_data.active = DISABLE; + return -EIO; + } + + ret = enable_irq_wake(fts_data->irq); + if (ret) { + FTS_INFO("enable_irq_wake(irq:%d) failed", fts_data->irq); + } + + fts_gesture_data.active = ENABLE; + FTS_INFO("[GESTURE]Enter into gesture(suspend) successfully!"); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_resume +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +int fts_gesture_resume(struct i2c_client *client) +{ + int ret; + int i; + u8 state; + + FTS_INFO("gesture resume..."); + /* gesture not enable, return immediately */ + if (fts_gesture_data.mode == DISABLE) { + FTS_DEBUG("gesture is disabled"); + return -EINVAL; + } + + if (fts_gesture_data.active == DISABLE) { + FTS_DEBUG("gesture in suspend is failed, no running fts_gesture_resume"); + return -EINVAL; + } + + for (i = 0; i < 5; i++) { + fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, DISABLE); + msleep(1); + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state); + if (state == DISABLE) + break; + } + + if (i >= 5) { + FTS_ERROR("[GESTURE]Clear gesture(resume) failed!\n"); + return -EIO; + } + + ret = disable_irq_wake(fts_data->irq); + if (ret) { + FTS_INFO("disable_irq_wake(irq:%d) failed", fts_data->irq); + } + + FTS_INFO("[GESTURE]resume from gesture successfully!"); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_init +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_gesture_init(struct fts_ts_data *ts_data) +{ + struct i2c_client *client = ts_data->client; + 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(client); + fts_gesture_data.mode = ENABLE; + fts_gesture_data.active = DISABLE; + + FTS_FUNC_EXIT(); + return 0; +} + +/************************************************************************ +* Name: fts_gesture_exit +* Brief: call when driver removed +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_gesture_exit(struct i2c_client *client) +{ + FTS_FUNC_ENTER(); + sysfs_remove_group(&client->dev.kobj, &fts_gesture_group); + FTS_FUNC_EXIT(); + return 0; +} +#endif diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c new file mode 100644 index 000000000000..369f4930b123 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c @@ -0,0 +1,243 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/************************************************************************ +* +* File Name: focaltech_i2c.c +* +* Author: fupeipei +* +* Created: 2016-08-04 +* +* Abstract: i2c communication with TP +* +* Version: v1.0 +* +* Revision History: +* v1.0: +* First release. By fupeipei 2016-08-04 +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define I2C_RETRY_NUMBER 3 +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static DEFINE_MUTEX(i2c_rw_access); + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ + +/************************************************************************ +* Name: fts_i2c_read +* Brief: i2c read +* Input: i2c info, write buf, write len, read buf, read len +* Output: get data in the 3rd buf +* Return: fail <0 +***********************************************************************/ +int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) +{ + int ret = 0; + int i = 0; + + mutex_lock(&i2c_rw_access); + + if (readlen > 0) { + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) { + FTS_ERROR("[IIC]: i2c_transfer(write) error, ret=%d!!", ret); + } else + break; + } + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) { + FTS_ERROR("[IIC]: i2c_transfer(read) error, ret=%d!!", ret); + } else + break; + } + } + } + + mutex_unlock(&i2c_rw_access); + return ret; +} +EXPORT_SYMBOL(fts_i2c_read); + +/************************************************************************ +* Name: fts_i2c_write +* Brief: i2c write +* Input: i2c info, write buf, write len +* Output: no +* Return: fail <0 +***********************************************************************/ +int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen) +{ + int ret = 0; + int i = 0; + + mutex_lock(&i2c_rw_access); + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) { + FTS_ERROR("%s: i2c_transfer(write) error, ret=%d", __func__, ret); + } else + break; + } + } + mutex_unlock(&i2c_rw_access); + + return ret; +} +EXPORT_SYMBOL(fts_i2c_write); + +/************************************************************************ +* Name: fts_i2c_write_reg +* Brief: write register +* Input: i2c info, reg address, reg value +* Output: no +* Return: fail <0 +***********************************************************************/ +int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue) +{ + u8 buf[2] = {0}; + + buf[0] = regaddr; + buf[1] = regvalue; + return fts_i2c_write(client, buf, sizeof(buf)); +} + +/************************************************************************ +* Name: fts_i2c_read_reg +* Brief: read register +* Input: i2c info, reg address, reg value +* Output: get reg value +* Return: fail <0 +***********************************************************************/ +int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue) +{ + return fts_i2c_read(client, ®addr, 1, regvalue, 1); +} + +/************************************************************************ +* HID to standard I2C +***********************************************************************/ +void fts_i2c_hid2std(struct i2c_client *client) +{ + int ret = 0; + u8 buf[3] = {0xeb, 0xaa, 0x09}; + + ret = fts_i2c_write(client, buf, 3); + if (ret < 0) + FTS_ERROR("hid2std cmd write fail"); + else { + msleep(10); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_i2c_read(client, NULL, 0, buf, 3); + if (ret < 0) + FTS_ERROR("hid2std cmd read fail"); + else if ((0xeb == buf[0]) && (0xaa == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("hidi2c change to stdi2c successful"); + } else { + FTS_ERROR("hidi2c change to stdi2c fail"); + } + } +} + +/************************************************************************ +* Name: fts_i2c_init +* Brief: fts i2c init +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_i2c_init(void) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +/************************************************************************ +* Name: fts_i2c_exit +* Brief: fts i2c exit +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_i2c_exit(void) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c new file mode 100644 index 000000000000..8a146b131923 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c @@ -0,0 +1,136 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_point_report_check.c +* +* Author: WangTao +* +* Created: 2016-11-16 +* +* Abstract: point report check function +* +* Version: v1.0 +* +* Revision History: +* v1.0: +* First release. By WangTao 2016-11-16 +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_POINT_REPORT_CHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */ + +/***************************************************************************** +* 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); + struct input_dev *input_dev = ts_data->input_dev; + +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&ts_data->report_mutex); + +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < ts_data->pdata->max_touch_number; 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); + + mutex_unlock(&ts_data->report_mutex); + + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* 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) +{ + cancel_delayed_work(&ts_data->prc_work); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); +} + +/***************************************************************************** +* Name: fts_point_report_check_init +* Brief: +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_point_report_check_init(struct fts_ts_data *ts_data) +{ + 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; + } + + 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(); + + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_POINT_REPORT_CHECK_EN */ +