From 28bc9f4c00f2a0d9c8e1904b230cab013474d1b8 Mon Sep 17 00:00:00 2001 From: hujing3 Date: Thu, 14 Apr 2022 14:26:16 +0800 Subject: [PATCH] focaltech_0flash_v2_mmi:ft8726 init codes on new version 3.4 focaltech_0flash_v2_mmi devon: support for ft8726, add init codes Change-Id: I55968366435fa8f918d889af7cd56cbe6b228326 Reviewed-on: https://gerrit.mot.com/2245682 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wei Deng Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- .../focaltech_0flash_v2_mmi/Android.mk | 10 + .../focaltech_0flash_v2_mmi/Kbuild | 16 + .../focaltech_0flash_v2_mmi/Kconfig | 16 + .../focaltech_0flash_v2_mmi/Makefile | 10 + .../focaltech_0flash_v2_mmi/Makefile.kernel | 14 + .../focaltech_common.h | 178 ++ .../focaltech_config.h | 302 +++ .../focaltech_0flash_v2_mmi/focaltech_core.c | 2176 +++++++++++++++++ .../focaltech_0flash_v2_mmi/focaltech_core.h | 336 +++ .../focaltech_esdcheck.c | 449 ++++ .../focaltech_ex_fun.c | 1469 +++++++++++ .../focaltech_ex_mode.c | 307 +++ .../focaltech_0flash_v2_mmi/focaltech_flash.c | 1269 ++++++++++ .../focaltech_0flash_v2_mmi/focaltech_flash.h | 214 ++ .../focaltech_gesture.c | 504 ++++ .../focaltech_point_report_check.c | 185 ++ .../focaltech_0flash_v2_mmi/focaltech_spi.c | 452 ++++ .../include/firmware/fw_sample.h | 0 18 files changed, 7907 insertions(+) create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild create mode 100755 drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile create mode 100755 drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.h create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c create mode 100644 drivers/input/touchscreen/focaltech_0flash_v2_mmi/include/firmware/fw_sample.h diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk new file mode 100644 index 000000000000..321de3a8dedd --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_0flash_v2_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild new file mode 100644 index 000000000000..8b25509a75a7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_v2_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_v2_mmi/include + +obj-m := focaltech_0flash_v2_mmi.o +focaltech_0flash_v2_mmi-objs += focaltech_core.o +focaltech_0flash_v2_mmi-objs += focaltech_esdcheck.o +focaltech_0flash_v2_mmi-objs += focaltech_ex_fun.o +focaltech_0flash_v2_mmi-objs += focaltech_ex_mode.o +focaltech_0flash_v2_mmi-objs += focaltech_flash.o +focaltech_0flash_v2_mmi-objs += focaltech_gesture.o +focaltech_0flash_v2_mmi-objs += focaltech_point_report_check.o +focaltech_0flash_v2_mmi-objs += focaltech_spi.o + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig new file mode 100755 index 000000000000..e2bf1085bbf0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig @@ -0,0 +1,16 @@ +# +# Focaltech Touchscreen driver configuration +# + +config TOUCHSCREEN_FTS + bool "Focaltech Touchscreen" + default n + help + Say Y here if you have Focaltech touch panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_DIRECTORY + string "Focaltech ts directory name" + default "focaltech_touch" + depends on TOUCHSCREEN_FTS + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel new file mode 100755 index 000000000000..e48fa8a073f2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel @@ -0,0 +1,14 @@ +# Makefile for the focaltech touchscreen drivers. + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_spi.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash.o + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h new file mode 100644 index 000000000000..108ab385dd25 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h @@ -0,0 +1,178 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_common.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-16 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_COMMON_H__ +#define __LINUX_FOCALTECH_COMMON_H__ + +#include "focaltech_config.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_VERSION "Focaltech V3.4 20211214" + +#define BYTE_OFF_0(x) (u8)((x) & 0xFF) +#define BYTE_OFF_8(x) (u8)(((x) >> 8) & 0xFF) +#define BYTE_OFF_16(x) (u8)(((x) >> 16) & 0xFF) +#define BYTE_OFF_24(x) (u8)(((x) >> 24) & 0xFF) +#define FLAGBIT(x) (0x00000001 << (x)) +#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x))) + +#define FLAG_ICSERIALS_LEN 8 +#define FLAG_HID_BIT 10 +#define FLAG_IDC_BIT 11 + +#define IC_SERIALS (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FTS_MAX_CHIP_IDS 8 + +#define FTS_CHIP_TYPE_MAPPING {{0x1C, 0x87, 0x20, 0x87, 0x20, 0x87, 0xA0, 0x00, 0x00}} + +#define FTS_CHIP_ID_MAPPING {{0x1C, {0x8726, 0x872A, 0x872B}}} + +#define FILE_NAME_LENGTH 128 +#define ENABLE 1 +#define DISABLE 0 +#define VALID 1 +#define INVALID 0 +#define FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_START_DELAY 12 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 +#define FTS_CMD_READ_INFO 0x5E + +/*register address*/ +#define FTS_REG_INT_CNT 0x8F +#define FTS_REG_FLOW_WORK_CNT 0x91 +#define FTS_REG_WORKMODE 0x00 +#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40 +#define FTS_REG_WORKMODE_WORK_VALUE 0x00 +#define FTS_REG_ESDCHECK_DISABLE 0x8D +#define FTS_REG_CHIP_ID 0xA3 +#define FTS_REG_CHIP_ID2 0x9F +#define FTS_REG_POWER_MODE 0xA5 +#define FTS_REG_POWER_MODE_SLEEP 0x03 +#define FTS_REG_FW_VER 0xA6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_LCD_BUSY_NUM 0xAB +#define FTS_REG_FACE_DEC_MODE_EN 0xB0 +#define FTS_REG_FACTORY_MODE_DETACH_FLAG 0xB4 +#define FTS_REG_FACE_DEC_MODE_STATUS 0x01 +#define FTS_REG_IDE_PARA_VER_ID 0xB5 +#define FTS_REG_IDE_PARA_STATUS 0xB6 +#define FTS_REG_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 { + u16 type; + u8 chip_idh; + u8 chip_idl; + u8 rom_idh; + u8 rom_idl; + u8 pb_idh; + u8 pb_idl; + u8 bl_idh; + u8 bl_idl; +}; + +struct ft_chip_id_t { + u16 type; + u16 chip_ids[FTS_MAX_CHIP_IDS]; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; + struct ft_chip_id_t cid; +}; + +/***************************************************************************** +* DEBUG function define here +*****************************************************************************/ +#if FTS_DEBUG_EN +#define FTS_DEBUG(fmt, args...) do { \ + printk("[FTS_TS]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_FUNC_ENTER() do { \ + printk("[FTS_TS]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_FUNC_EXIT() do { \ + printk("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) +#else /* #if FTS_DEBUG_EN*/ +#define FTS_DEBUG(fmt, args...) +#define FTS_FUNC_ENTER() +#define FTS_FUNC_EXIT() +#endif + +#define FTS_INFO(fmt, args...) do { \ + printk(KERN_INFO "[FTS_TS/I]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_ERROR(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/E]%s:"fmt"\n", __func__, ##args); \ +} while (0) +#endif /* __LINUX_FOCALTECH_COMMON_H__ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h new file mode 100644 index 000000000000..e0d0d53adc55 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h @@ -0,0 +1,302 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/************************************************************************ +* +* File Name: focaltech_config.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: global configurations +* +* Version: v1.0 +* +************************************************************************/ +#ifndef _LINUX_FOCLATECH_CONFIG_H_ +#define _LINUX_FOCLATECH_CONFIG_H_ + +/**************************************************/ +/****** G: A, I: B, S: C, U: D ******************/ +/****** chip type defines, do not modify *********/ +#define _FT8716 0x87160805 +#define _FT8736 0x87360806 +#define _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8201AA 0x8201A810 +#define _FT8006P 0x86220811 +#define _FT8613S 0x8613C814 +#define _FT8756 0x87560815 +#define _FT8656 0x86560815 +#define _FT8756M 0x8756A815 +#define _FT8302 0x83020816 +#define _FT8009 0x80090817 +#define _FT8006S_AA 0x86320819 +#define _FT7250 0x7250081A +#define _FT7120 0x7120081B +#define _FT8720 0x8720081C +#define _FT8726 0x8726081C +#define _FT8720H 0x8720E81C +#define _FT8720M 0x8720F81C +#define _FT8016 0x8016081D +#define _FT2388 0x2388081E +#define _FT8006S_AB 0x8642081F +#define _FT8722 0x87220820 +#define _FT8201AB 0x8201B821 +#define _FT8203 0x82030821 +#define _FT8201M 0x8201C821 +#define _FT8006S_AN 0x8006B822 +#define _FT7131 0x71310823 +#define _FT7250AB 0x7250B824 +#define _FT7130 0x71300825 +#define _FT8205 0x82050826 + + + +#define _FT5426 0x54260402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 +#define _FT5526_V00 0x5526C402 + +#define _FT5726 0x57260401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 +#define _FT6436T 0x6436E003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT5446_Q03 0x5446C482 +#define _FT5446_P03 0x5446A481 +#define _FT5446_N03 0x5446A489 +#define _FT5426_003 0x5426D482 +#define _FT5526_003 0x5526D482 +#define _FT3437_003 0x34370482 + +#define _FT3518 0x35180481 +#define _FT3518U 0x3518D481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 +#define _FT3418 0x34180481 + +#define _FT5446U 0x5446D083 +#define _FT5456U 0x5456D083 +#define _FT3417U 0x3417D083 +#define _FT5426U 0x5426D083 +#define _FT3428 0x34280083 + +#define _FT7302 0x73020084 +#define _FT7202 0x72020084 +#define _FT3308 0x33080084 +#define _FT6446 0x64460084 + +#define _FT6346U 0x6346D085 +#define _FT6346G 0x6346A085 +#define _FT3067 0x30670085 +#define _FT3068 0x30680085 +#define _FT3168 0x31680085 +#define _FT3268 0x32680085 +#define _FT6146 0x61460085 +#define _FT3168G 0x3168A085 + +#define _FT5726_003 0x5726D486 +#define _FT5726_V03 0x5726C486 +#define _FT3617_003 0x3617D486 + +#define _FT3618 0x36180487 +#define _FT5646 0x56460487 +#define _FT3A58 0x3A580487 +#define _FT3B58 0x3B580487 +#define _FT3D58 0x3D580487 +#define _FT3D59 0x3D590487 +#define _FT5936 0x59360487 +#define _FT5A36 0x5A360487 +#define _FT5B36 0x5B360487 +#define _FT5D36 0x5D360487 +#define _FT5946 0x59460487 +#define _FT5A46 0x5A460487 +#define _FT5B46 0x5B460487 +#define _FT5D46 0x5D460487 + +#define _FT3658U 0x3658D488 +#define _FT3658G 0x3658A488 + +#define _FT3519 0x35190489 +#define _FT3519T 0x3519E489 +#define _FT3419 0x34190489 +#define _FT5536U_003 0x5536D489 +#define _FT5426G 0x5426A489 +#define _FT3437_N03 0x34370489 + +#define _FT3680 0x3680008A +#define _FT368A 0x368A008A +#define _FT3681 0x3681008A +#define _FT3881 0x3881008A + +#define _FT3169 0x3169008B +#define _FT3269 0x3269008B + + + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#define FTS_CHIP_TYPE _FT8726 + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#define FTS_DEBUG_EN 1 + +/* + * Linux MultiTouch Protocol + * 1: Protocol B(default), 0: Protocol A + */ +#define FTS_MT_PROTOCOL_B_EN 1 + +/* + * Report Pressure in multitouch + * 1:enable(default),0:disable +*/ +#define FTS_REPORT_PRESSURE_EN 1 + +/* + * Stylus PEN enable + * 1:enable(default),0:disable +*/ +#define FTS_PEN_EN 0 + +/* + * Gesture function enable + * default: disable + */ +#define FTS_GESTURE_EN 0 + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + + +/* + * 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 + */ +#define FTS_AUTO_UPGRADE_EN 0 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * module_id: mean vendor_id generally, also maybe gpio or lcm_id... + * If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID + * FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them + * FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID + * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID + */ +#define FTS_MODULE_ID 0x0000 +#define FTS_MODULE2_ID 0x0000 +#define FTS_MODULE3_ID 0x0000 + +/* + * Need set the following when get firmware via firmware_request() + * For example: if module'vendor is tianma, + * #define FTS_MODULE_NAME "tianma" + * then file_name will be "focaltech_ts_fw_tianma" + * You should rename fw to "focaltech_ts_fw_tianma", and push it into + * etc/firmware or by customers + */ +#define FTS_MODULE_NAME "" +#define FTS_MODULE2_NAME "" +#define FTS_MODULE3_NAME "" + +/* + * FW.i file for auto upgrade, you must replace it with your own + * define your own fw_file, the sample one to be replaced is invalid + * NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID + */ +#define FTS_UPGRADE_FW_FILE "include/firmware/fw_sample.h" + +/* + * if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_VENDOR_ID2 + * define your own fw_file, the sample one is invalid + */ +#define FTS_UPGRADE_FW2_FILE "include/firmware/fw_sample.h" + +/* + * if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_VENDOR_ID3 + * define your own fw_file, the sample one is invalid + */ +#define FTS_UPGRADE_FW3_FILE "include/firmware/fw_sample.h" + +/*********************************************************/ + +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c new file mode 100644 index 000000000000..d42c85f38743 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c @@ -0,0 +1,2176 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: entrance for focaltech ts driver +* +* Version: V1.0 +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +#include +#else +#include +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define FTS_DRIVER_PEN_NAME "fts_ts,pen" +#define INTERVAL_READ_REG 200 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#if FTS_POWER_SOURCE_CUST_EN +#define FTS_VTG_MIN_UV 2800000 +#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; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); + +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h) +{ + int i = 0; + struct ft_chip_id_t *cid = &ts_data->ic_info.cid; + u8 cid_h = 0x0; + + if (cid->type == 0) + return -ENODATA; + + for (i = 0; i < FTS_MAX_CHIP_IDS; i++) { + cid_h = ((cid->chip_ids[i] >> 8) & 0x00FF); + if (cid_h && (id_h == cid_h)) { + return 0; + } + } + + return -ENODATA; +} + +/***************************************************************************** +* Name: fts_wait_tp_to_valid +* Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG), +* need call when reset/power on/resume... +* Input: +* Output: +* Return: return 0 if tp valid, otherwise return error code +*****************************************************************************/ +int fts_wait_tp_to_valid(void) +{ + int ret = 0; + int cnt = 0; + u8 idh = 0; + struct fts_ts_data *ts_data = fts_data; + u8 chip_idh = ts_data->ic_info.ids.chip_idh; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if ((idh == chip_idh) || (fts_check_cid(ts_data, idh) == 0)) { + FTS_INFO("TP Ready,Device ID:0x%02x", idh); + return 0; + } else + FTS_DEBUG("TP Not Ready,ReadData:0x%02x,ret:%d", idh, ret); + + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} + +/***************************************************************************** +* Name: fts_tp_state_recovery +* Brief: Need execute this function when reset +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_tp_state_recovery(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + /* wait tp stable */ + fts_wait_tp_to_valid(); + /* recover TP charger state 0x8B */ + /* recover TP glove state 0xC0 */ + /* recover TP cover state 0xC1 */ + fts_ex_mode_recovery(ts_data); + /* recover TP gesture state 0xD0 */ + fts_gesture_recovery(ts_data); + FTS_FUNC_EXIT(); +} + +int fts_reset_proc(int hdelayms) +{ + FTS_DEBUG("tp reset"); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(1); + gpio_direction_output(fts_data->pdata->reset_gpio, 1); + if (hdelayms) { + msleep(hdelayms); + } + + return 0; +} + +void fts_irq_disable(void) +{ + unsigned long irqflags; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (!fts_data->irq_disabled) { + disable_irq_nosync(fts_data->irq); + fts_data->irq_disabled = true; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +void fts_irq_enable(void) +{ + unsigned long irqflags = 0; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (fts_data->irq_disabled) { + enable_irq(fts_data->irq); + fts_data->irq_disabled = false; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +void fts_hid2std(void) +{ + int ret = 0; + u8 buf[3] = {0xEB, 0xAA, 0x09}; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + ret = fts_write(buf, 3); + if (ret < 0) { + FTS_ERROR("hid2std cmd write fail"); + } else { + msleep(10); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_read(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_DEBUG("hidi2c change to stdi2c not support or fail"); + } + } +} + +static int fts_match_cid(struct fts_ts_data *ts_data, + u16 type, u8 id_h, u8 id_l, bool force) +{ +#ifdef FTS_CHIP_ID_MAPPING + u32 i = 0; + u32 j = 0; + struct ft_chip_id_t chip_id_list[] = FTS_CHIP_ID_MAPPING; + u32 cid_entries = sizeof(chip_id_list) / sizeof(struct ft_chip_id_t); + u16 id = (id_h << 8) + id_l; + + memset(&ts_data->ic_info.cid, 0, sizeof(struct ft_chip_id_t)); + for (i = 0; i < cid_entries; i++) { + if (!force && (type == chip_id_list[i].type)) { + break; + } else if (force && (type == chip_id_list[i].type)) { + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + if (i >= cid_entries) { + return -ENODATA; + } + + for (j = 0; j < FTS_MAX_CHIP_IDS; j++) { + if (id == chip_id_list[i].chip_ids[j]) { + FTS_DEBUG("cid:%x==%x", id, chip_id_list[i].chip_ids[j]); + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + return -ENODATA; +#else + return -EINVAL; +#endif +} + + +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + u32 i = 0; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t); + + if ((0x0 == id_h) || (0x0 == id_l)) { + FTS_ERROR("id_h/id_l is 0"); + return -EINVAL; + } + + FTS_INFO("verify id:0x%02x%02x", id_h, id_l); + for (i = 0; i < ctype_entries; i++) { + if (VALID == fw_valid) { + if (((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl)) + || (!fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 0))) + break; + } else { + if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl)) + || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl)) + || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl))) { + break; + } + } + } + + if (i >= ctype_entries) { + return -ENODATA; + } + + fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 1); + ts_data->ic_info.ids = ctype[i]; + return 0; +} + +static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id) +{ + int ret = 0; + u8 chip_id[2] = { 0 }; + u8 id_cmd[4] = { 0 }; + u32 id_cmd_len = 0; + + id_cmd[0] = FTS_CMD_START1; + id_cmd[1] = FTS_CMD_START2; + ret = fts_write(id_cmd, 2); + if (ret < 0) { + FTS_ERROR("start cmd write fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + id_cmd[0] = FTS_CMD_READ_ID; + id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; + if (ts_data->ic_info.is_incell) + id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + id_cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_read(id_cmd, id_cmd_len, chip_id, 2); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]); + return -EIO; + } + + id[0] = chip_id[0]; + id[1] = chip_id[1]; + return 0; +} + +/***************************************************************************** +* Name: fts_get_ic_information +* Brief: read chip id to get ic information, after run the function, driver w- +* ill know which IC is it. +* If cant get the ic information, maybe not focaltech's touch IC, need +* unregister the driver +* Input: +* Output: +* Return: return 0 if get correct ic information, otherwise return error code +*****************************************************************************/ +static int fts_get_ic_information(struct fts_ts_data *ts_data) +{ + int ret = 0; + int cnt = 0; + u8 chip_id[2] = { 0 }; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + for (cnt = 0; cnt < 3; cnt++) { + fts_reset_proc(0); + mdelay(FTS_CMD_START_DELAY + (cnt * 8)); + + ret = fts_read_bootid(ts_data, &chip_id[0]); + if (ret < 0) { + FTS_DEBUG("read boot id fail,retry:%d", cnt); + continue; + } + + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_DEBUG("can't get ic informaton,retry:%d", cnt); + continue; + } + + break; + } + + if (cnt >= 3) { + FTS_ERROR("get ic informaton fail"); + return -EIO; + } + + + FTS_INFO("get ic information, chip id = 0x%02x%02x(cid type=0x%x)", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl, + ts_data->ic_info.cid.type); + + return 0; +} + +/***************************************************************************** +* Reprot related +*****************************************************************************/ +static void fts_show_touch_buffer(u8 *data, u32 datalen) +{ + u32 i = 0; + u32 count = 0; + char *tmpbuf = NULL; + + tmpbuf = kzalloc(1024, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("tmpbuf zalloc fail"); + return; + } + + for (i = 0; i < datalen; i++) { + count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]); + if (count >= 1024) + break; + } + FTS_DEBUG("touch_buf:%s", tmpbuf); + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +void fts_release_all_finger(void) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = ts_data->pdata->max_touch_number; +#endif + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < max_touches; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + +#if FTS_PEN_EN + input_report_key(ts_data->pen_dev, BTN_TOOL_PEN, 0); + input_report_key(ts_data->pen_dev, BTN_TOUCH, 0); + input_sync(ts_data->pen_dev); +#endif + + ts_data->touch_points = 0; + ts_data->key_state = 0; + mutex_unlock(&ts_data->report_mutex); +} + +/***************************************************************************** +* Name: fts_input_report_key +* Brief: process key events,need report key-event if key enable. +* if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50), +* need report it to key event. +* x_dim: parse from dts, means key x_coordinate, dimension:+-50 +* y_dim: parse from dts, means key y_coordinate, dimension:+-50 +* Input: +* Output: +* Return: return 0 if it's key event, otherwise return error code +*****************************************************************************/ +static int fts_input_report_key(struct fts_ts_data *ts_data, struct ts_event *kevent) +{ + int i = 0; + int x = kevent->x; + int y = kevent->y; + int *x_dim = &ts_data->pdata->key_x_coords[0]; + int *y_dim = &ts_data->pdata->key_y_coords[0]; + + if (!ts_data->pdata->have_key) { + return -EINVAL; + } + for (i = 0; i < ts_data->pdata->key_number; i++) { + if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) && + (y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) { + if (EVENT_DOWN(kevent->flag) + && !(ts_data->key_state & (1 << i))) { + input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 1); + ts_data->key_state |= (1 << i); + FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y); + } else if (EVENT_UP(kevent->flag) + && (ts_data->key_state & (1 << i))) { + input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 0); + ts_data->key_state &= ~(1 << i); + FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y); + } + return 0; + } + } + return -EINVAL; +} + +#if FTS_MT_PROTOCOL_B_EN +static int fts_input_report_b(struct fts_ts_data *ts_data, struct ts_event *events) +{ + int i = 0; + int touch_down_point_cur = 0; + int touch_point_pre = ts_data->touch_points; + u32 max_touch_num = ts_data->pdata->max_touch_number; + bool touch_event_coordinate = false; + struct input_dev *input_dev = ts_data->input_dev; + + for (i = 0; i < ts_data->touch_event_num; i++) { + if (fts_input_report_key(ts_data, &events[i]) == 0) { + continue; + } + + touch_event_coordinate = true; + if (EVENT_DOWN(events[i].flag)) { + input_mt_slot(input_dev, events[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true); +#if FTS_REPORT_PRESSURE_EN + input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y); + + touch_down_point_cur |= (1 << events[i].id); + touch_point_pre |= (1 << events[i].id); + + if ((ts_data->log_level >= 2) || + ((1 == ts_data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { + FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!", + events[i].id, events[i].x, events[i].y, + events[i].p, events[i].area); + } + } else { + input_mt_slot(input_dev, events[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + touch_point_pre &= ~(1 << events[i].id); + if (ts_data->log_level >= 1) FTS_DEBUG("[B]P%d UP!", events[i].id); + } + } + + if (unlikely(touch_point_pre ^ touch_down_point_cur)) { + for (i = 0; i < max_touch_num; i++) { + if ((1 << i) & (touch_point_pre ^ touch_down_point_cur)) { + if (ts_data->log_level >= 1) FTS_DEBUG("[B]P%d UP!", i); + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + } + } + + if (touch_down_point_cur) + input_report_key(input_dev, BTN_TOUCH, 1); + else if (touch_event_coordinate || ts_data->touch_points) { + if (ts_data->touch_points && (ts_data->log_level >= 1)) + FTS_DEBUG("[B]Points All Up!"); + input_report_key(input_dev, BTN_TOUCH, 0); + } + + ts_data->touch_points = touch_down_point_cur; + input_sync(input_dev); + return 0; +} +#else +static int fts_input_report_a(struct fts_ts_data *ts_data, struct ts_event *events) +{ + int i = 0; + int touch_down_point_num_cur = 0; + bool touch_event_coordinate = false; + struct input_dev *input_dev = ts_data->input_dev; + + for (i = 0; i < ts_data->touch_event_num; i++) { + if (fts_input_report_key(ts_data, &events[i]) == 0) { + continue; + } + + touch_event_coordinate = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y); + input_mt_sync(input_dev); + + touch_down_point_num_cur++; + if ((ts_data->log_level >= 2) || + ((1 == ts_data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { + FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!", + events[i].id, events[i].x, events[i].y, + events[i].p, events[i].area); + } + } + } + + if (touch_down_point_num_cur) + input_report_key(input_dev, BTN_TOUCH, 1); + else if (touch_event_coordinate || ts_data->touch_points) { + if (ts_data->touch_points && (ts_data->log_level >= 1)) + FTS_DEBUG("[A]Points All Up!"); + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } + + ts_data->touch_points = touch_down_point_num_cur; + input_sync(input_dev); + return 0; +} +#endif + +#if FTS_PEN_EN +static int fts_input_pen_report(struct fts_ts_data *ts_data, u8 *pen_buf) +{ + struct input_dev *pen_dev = ts_data->pen_dev; + struct pen_event *pevt = &ts_data->pevent; + + /*get information of stylus*/ + pevt->inrange = (pen_buf[2] & 0x20) ? 1 : 0; + pevt->tip = (pen_buf[2] & 0x01) ? 1 : 0; + pevt->flag = pen_buf[3] >> 6; + pevt->id = pen_buf[5] >> 4; + pevt->x = ((pen_buf[3] & 0x0F) << 8) + pen_buf[4]; + pevt->y = ((pen_buf[5] & 0x0F) << 8) + pen_buf[6]; + pevt->p = ((pen_buf[7] & 0x0F) << 8) + pen_buf[8]; + pevt->tilt_x = (short)((pen_buf[9] << 8) + pen_buf[10]); + pevt->tilt_y = (short)((pen_buf[11] << 8) + pen_buf[12]); + pevt->azimuth = ((pen_buf[13] << 8) + pen_buf[14]); + pevt->tool_type = BTN_TOOL_PEN; + + input_report_key(pen_dev, BTN_STYLUS, !!(pen_buf[2] & 0x02)); + input_report_key(pen_dev, BTN_STYLUS2, !!(pen_buf[2] & 0x08)); + + switch (ts_data->pen_etype) { + case STYLUS_DEFAULT: + if (pevt->tip && pevt->p) { + if ((ts_data->log_level >= 2) || (!pevt->down)) + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d DOWN", + pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag, + pevt->tilt_x, pevt->tilt_y); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + input_report_key(pen_dev, BTN_TOUCH, 1); + input_report_key(pen_dev, BTN_TOOL_PEN, 1); + pevt->down = 1; + } else if (!pevt->tip && pevt->down) { + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d UP", + pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag, + pevt->tilt_x, pevt->tilt_y); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_key(pen_dev, BTN_TOUCH, 0); + input_report_key(pen_dev, BTN_TOOL_PEN, 0); + pevt->down = 0; + } + input_sync(pen_dev); + break; + case STYLUS_HOVER: + if (ts_data->log_level >= 1) + FTS_DEBUG("[PEN][%02X]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d,%d", + pen_buf[2], pevt->x, pevt->y, pevt->p, pevt->tip, + pevt->flag, pevt->tilt_x, pevt->tilt_y, pevt->azimuth); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_Z, pevt->azimuth); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + input_report_key(pen_dev, BTN_TOOL_PEN, EVENT_DOWN(pevt->flag)); + input_report_key(pen_dev, BTN_TOUCH, pevt->tip); + input_sync(pen_dev); + break; + default: + FTS_ERROR("Unknown stylus event"); + break; + } + + return 0; +} +#endif + +static int fts_read_touchdata(struct fts_ts_data *ts_data, u8 *buf) +{ + int ret = 0; + + ts_data->touch_addr = 0x01; + ret = fts_read(&ts_data->touch_addr, 1, buf, ts_data->touch_size); + + if (((0xEF == buf[1]) && (0xEF == buf[2]) && (0xEF == buf[3])) + || ((ret < 0) && (0xEF == buf[0]))) { + fts_release_all_finger(); + /* check if need recovery fw */ + fts_fw_recovery(); + ts_data->fw_is_running = true; + return 1; + } else if (ret < 0) { + FTS_ERROR("touch data(%x) abnormal,ret:%d", buf[1], ret); + return ret; + } + + + return 0; +} + + +static int fts_read_parse_touchdata(struct fts_ts_data *ts_data, u8 *touch_buf) +{ + int ret = 0; + u8 gesture_en = 0xFF; + + memset(touch_buf, 0xFF, FTS_MAX_TOUCH_BUF); + ts_data->ta_size = ts_data->touch_size; + + /*read touch data*/ + ret = fts_read_touchdata(ts_data, touch_buf); + if (ret < 0) { + FTS_ERROR("read touch data fails"); + return TOUCH_ERROR; + } + + if (ts_data->log_level >= 3) + fts_show_touch_buffer(touch_buf, ts_data->ta_size); + + if (ret) + return TOUCH_IGNORE; + + /*gesture*/ + if (ts_data->suspended && ts_data->gesture_support) { + ret = fts_read_reg(FTS_REG_GESTURE_EN, &gesture_en); + if ((ret >= 0) && (gesture_en == ENABLE)) + return TOUCH_GESTURE; + else + FTS_DEBUG("gesture not enable in fw, don't process gesture"); + } + + if ((touch_buf[1] == 0xFF) && (touch_buf[2] == 0xFF) + && (touch_buf[3] == 0xFF) && (touch_buf[4] == 0xFF)) { + FTS_INFO("touch buff is 0xff, need recovery state"); + return TOUCH_FW_INIT; + } + + return ((touch_buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F); +} + +static int fts_irq_read_report(struct fts_ts_data *ts_data) +{ + int i = 0; + int max_touch_num = ts_data->pdata->max_touch_number; + int touch_etype = 0; + u8 event_num = 0; + u8 finger_num = 0; + u8 pointid = 0; + u8 base = 0; + u8 *touch_buf = ts_data->touch_buf; + struct ts_event *events = ts_data->events; + + touch_etype = fts_read_parse_touchdata(ts_data, touch_buf); + switch (touch_etype) { + case TOUCH_DEFAULT: + finger_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (finger_num > max_touch_num) { + FTS_ERROR("invalid point_num(%d)", finger_num); + return -EIO; + } + + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= FTS_MAX_ID) + break; + else if (pointid >= max_touch_num) { + FTS_ERROR("ID(%d) beyond max_touch_number", pointid); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + + event_num++; + if (EVENT_DOWN(events[i].flag) && (finger_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } + + if (event_num == 0) { + FTS_INFO("no touch point information(%02x)", touch_buf[2]); + return -EIO; + } + ts_data->touch_event_num = event_num; + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + +#if FTS_PEN_EN + case TOUCH_PEN: + mutex_lock(&ts_data->report_mutex); + fts_input_pen_report(ts_data, touch_buf); + mutex_unlock(&ts_data->report_mutex); + break; +#endif + + case TOUCH_EVENT_NUM: + event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (!event_num || (event_num > max_touch_num)) { + FTS_ERROR("invalid touch event num(%d)", event_num); + return -EIO; + } + + ts_data->touch_event_num = event_num; + for (i = 0; i < event_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= max_touch_num) { + FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)", + pointid, max_touch_num); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + } + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + + case TOUCH_EXTRA_MSG: + if (!ts_data->touch_analysis_support) { + FTS_ERROR("touch_analysis is disabled"); + return -EINVAL; + } + + event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (!event_num || (event_num > max_touch_num)) { + FTS_ERROR("invalid touch event num(%d)", event_num); + return -EIO; + } + + ts_data->touch_event_num = event_num; + for (i = 0; i < event_num; i++) { + base = FTS_ONE_TCH_LEN * i + 4; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= max_touch_num) { + FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)", + pointid, max_touch_num); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + } + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + + case TOUCH_GESTURE: + if (0 == fts_gesture_readdata(ts_data, touch_buf)) { + FTS_INFO("succuss to get gesture data in irq handler"); + } + break; + + case TOUCH_FW_INIT: + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + break; + + case TOUCH_IGNORE: + case TOUCH_ERROR: + break; + + default: + FTS_INFO("unknown touch event(%d)", touch_etype); + break; + } + + return 0; +} + +static irqreturn_t fts_irq_handler(int irq, void *data) +{ + struct fts_ts_data *ts_data = fts_data; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + int ret = 0; + + if ((ts_data->suspended) && (ts_data->pm_suspend)) { + ret = wait_for_completion_timeout( + &ts_data->pm_completion, + msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM)); + if (!ret) { + FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq"); + return IRQ_HANDLED; + } + } +#endif + + ts_data->intr_jiffies = jiffies; + fts_prc_queue_work(ts_data); + fts_irq_read_report(ts_data); + if (ts_data->touch_analysis_support && ts_data->ta_flag) { + ts_data->ta_flag = 0; + if (ts_data->ta_buf && ts_data->ta_size) + memcpy(ts_data->ta_buf, ts_data->touch_buf, ts_data->ta_size); + wake_up_interruptible(&ts_data->ts_waitqueue); + } + + return IRQ_HANDLED; +} + +static int fts_irq_registration(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + + ts_data->irq = gpio_to_irq(pdata->irq_gpio); + pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler, + pdata->irq_gpio_flags, + FTS_DRIVER_NAME, ts_data); + + return ret; +} + +#if FTS_PEN_EN +static int fts_input_pen_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct input_dev *pen_dev; + struct fts_ts_platform_data *pdata = ts_data->pdata; + + FTS_FUNC_ENTER(); + pen_dev = input_allocate_device(); + if (!pen_dev) { + FTS_ERROR("Failed to allocate memory for input_pen device"); + return -ENOMEM; + } + + pen_dev->dev.parent = ts_data->dev; + pen_dev->name = FTS_DRIVER_PEN_NAME; + pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(ABS_X, pen_dev->absbit); + __set_bit(ABS_Y, pen_dev->absbit); + __set_bit(BTN_STYLUS, pen_dev->keybit); + __set_bit(BTN_STYLUS2, pen_dev->keybit); + __set_bit(BTN_TOUCH, pen_dev->keybit); + __set_bit(BTN_TOOL_PEN, pen_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit); + input_set_abs_params(pen_dev, ABS_X, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_X, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_Y, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_Z, 0, 36000, 0, 0); + + ret = input_register_device(pen_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_free_device(pen_dev); + pen_dev = NULL; + return ret; + } + + ts_data->pen_dev = pen_dev; + ts_data->pen_etype = STYLUS_DEFAULT; + FTS_FUNC_EXIT(); + return 0; +} +#endif + +static int fts_input_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + int key_num = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev; + + FTS_FUNC_ENTER(); + input_dev = input_allocate_device(); + if (!input_dev) { + FTS_ERROR("Failed to allocate memory for input device"); + return -ENOMEM; + } + + /* Init and register Input device */ + input_dev->name = FTS_DRIVER_NAME; + if (ts_data->bus_type == BUS_TYPE_I2C) + input_dev->id.bustype = BUS_I2C; + else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = ts_data->dev; + + input_set_drvdata(input_dev, ts_data); + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + if (pdata->have_key) { + FTS_INFO("set key capabilities"); + for (key_num = 0; key_num < pdata->key_number; key_num++) + input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]); + } + +#if FTS_MT_PROTOCOL_B_EN + input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT); +#else + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0); +#endif + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0); +#if FTS_REPORT_PRESSURE_EN + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); +#endif + + ret = input_register_device(input_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } + +#if FTS_PEN_EN + ret = fts_input_pen_init(ts_data); + if (ret) { + FTS_ERROR("Input-pen device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } +#endif + + ts_data->input_dev = input_dev; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_buffer_init(struct fts_ts_data *ts_data) +{ + ts_data->touch_buf = (u8 *)kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL); + if (!ts_data->touch_buf) { + FTS_ERROR("failed to alloc memory for touch buf"); + return -ENOMEM; + } + + ts_data->touch_size = FTS_TOUCH_DATA_LEN; + + + ts_data->touch_analysis_support = 0; + ts_data->ta_flag = 0; + ts_data->ta_size = 0; + + return 0; +} + +#if FTS_POWER_SOURCE_CUST_EN +/***************************************************************************** +* Power Control +*****************************************************************************/ +#if FTS_PINCTRL_EN +static int fts_pinctrl_init(struct fts_ts_data *ts) +{ + int ret = 0; + + ts->pinctrl = devm_pinctrl_get(ts->dev); + if (IS_ERR_OR_NULL(ts->pinctrl)) { + FTS_ERROR("Failed to get pinctrl, please check dts"); + ret = PTR_ERR(ts->pinctrl); + goto err_pinctrl_get; + } + + ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(ts->pins_active)) { + FTS_ERROR("Pin state[active] not found"); + ret = PTR_ERR(ts->pins_active); + goto err_pinctrl_lookup; + } + + ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(ts->pins_suspend)) { + FTS_ERROR("Pin state[suspend] not found"); + ret = PTR_ERR(ts->pins_suspend); + goto err_pinctrl_lookup; + } + + ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(ts->pins_release)) { + FTS_ERROR("Pin state[release] not found"); + ret = PTR_ERR(ts->pins_release); + } + + return 0; +err_pinctrl_lookup: + if (ts->pinctrl) { + devm_pinctrl_put(ts->pinctrl); + } +err_pinctrl_get: + ts->pinctrl = NULL; + ts->pins_release = NULL; + ts->pins_suspend = NULL; + ts->pins_active = NULL; + return ret; +} + +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 */ + +static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(ts_data->vdd)) { + FTS_ERROR("vdd is invalid"); + return -EINVAL; + } + + FTS_FUNC_ENTER(); + if (enable) { + if (ts_data->power_disabled) { + FTS_DEBUG("regulator enable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + msleep(1); + ret = regulator_enable(ts_data->vdd); + if (ret) { + FTS_ERROR("enable vdd regulator failed,ret=%d", ret); + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + ret = regulator_enable(ts_data->vcc_i2c); + if (ret) { + FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret); + } + } + ts_data->power_disabled = false; + } + } else { + if (!ts_data->power_disabled) { + FTS_DEBUG("regulator disable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + msleep(1); + ret = regulator_disable(ts_data->vdd); + if (ret) { + FTS_ERROR("disable vdd regulator failed,ret=%d", ret); + } + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + ret = regulator_disable(ts_data->vcc_i2c); + if (ret) { + FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret); + } + } + ts_data->power_disabled = true; + } + } + + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_power_source_init +* Brief: Init regulator power:vdd/vcc_io(if have), generally, no vcc_io +* vdd---->vdd-supply in dts, kernel will auto add "-supply" to parse +* Must be call after fts_gpio_configure() execute,because this function +* will operate reset-gpio which request gpio in fts_gpio_configure() +* Input: +* Output: +* Return: return 0 if init power successfully, otherwise return error code +*****************************************************************************/ +static int fts_power_source_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ts_data->vdd = regulator_get(ts_data->dev, "vdd"); + if (IS_ERR_OR_NULL(ts_data->vdd)) { + ret = PTR_ERR(ts_data->vdd); + FTS_ERROR("get vdd regulator failed,ret=%d", ret); + return ret; + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV, + FTS_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret); + regulator_put(ts_data->vdd); + return ret; + } + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + ret = regulator_set_voltage(ts_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); + regulator_put(ts_data->vcc_i2c); + } + } + } + +#if FTS_PINCTRL_EN + fts_pinctrl_init(ts_data); + fts_pinctrl_select_normal(ts_data); +#endif + + ts_data->power_disabled = true; + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret) { + FTS_ERROR("fail to enable power(regulator)"); + } + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_power_source_exit(struct fts_ts_data *ts_data) +{ +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + + fts_power_source_ctrl(ts_data, DISABLE); + + if (!IS_ERR_OR_NULL(ts_data->vdd)) { + if (regulator_count_voltages(ts_data->vdd) > 0) + regulator_set_voltage(ts_data->vdd, 0, FTS_VTG_MAX_UV); + regulator_put(ts_data->vdd); + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) + regulator_set_voltage(ts_data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV); + regulator_put(ts_data->vcc_i2c); + } + + return 0; +} + +static int fts_power_source_suspend(struct fts_ts_data *ts_data) +{ + int ret = 0; + +#if FTS_PINCTRL_EN + fts_pinctrl_select_suspend(ts_data); +#endif + + ret = fts_power_source_ctrl(ts_data, DISABLE); + if (ret < 0) { + FTS_ERROR("power off fail, ret=%d", ret); + } + + return ret; +} + +static int fts_power_source_resume(struct fts_ts_data *ts_data) +{ + int ret = 0; + +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif + + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret < 0) { + FTS_ERROR("power on fail, ret=%d", ret); + } + + return ret; +} +#endif /* FTS_POWER_SOURCE_CUST_EN */ + +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; +} + +static int fts_get_dt_coords(struct device *dev, char *name, + struct fts_ts_platform_data *pdata) +{ + int ret = 0; + u32 coords[FTS_COORDS_ARR_SIZE] = { 0 }; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != FTS_COORDS_ARR_SIZE) { + FTS_ERROR("invalid:%s, size:%d", name, coords_size); + return -EINVAL; + } + + ret = of_property_read_u32_array(np, name, coords, coords_size); + if (ret < 0) { + FTS_ERROR("Unable to read %s, please check dts", name); + pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT; + pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT; + pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT; + pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT; + return -ENODATA; + } else { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } + + FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, + pdata->y_min, pdata->y_max); + return 0; +} + +static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) +{ + int ret = 0; + struct device_node *np = dev->of_node; + u32 temp_val = 0; + + FTS_FUNC_ENTER(); + + ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata); + if (ret < 0) + FTS_ERROR("Unable to get display-coords"); + + /* key */ + pdata->have_key = of_property_read_bool(np, "focaltech,have-key"); + if (pdata->have_key) { + ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number); + if (ret < 0) + FTS_ERROR("Key number undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,keys", + pdata->keys, pdata->key_number); + if (ret < 0) + FTS_ERROR("Keys undefined!"); + else if (pdata->key_number > FTS_MAX_KEYS) + pdata->key_number = FTS_MAX_KEYS; + + ret = of_property_read_u32_array(np, "focaltech,key-x-coords", + pdata->key_x_coords, + pdata->key_number); + if (ret < 0) + FTS_ERROR("Key Y Coords undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,key-y-coords", + pdata->key_y_coords, + pdata->key_number); + if (ret < 0) + FTS_ERROR("Key X Coords undefined!"); + + FTS_INFO("VK Number:%d, key:(%d,%d,%d), " + "coords:(%d,%d),(%d,%d),(%d,%d)", + pdata->key_number, + pdata->keys[0], pdata->keys[1], pdata->keys[2], + pdata->key_x_coords[0], pdata->key_y_coords[0], + pdata->key_x_coords[1], pdata->key_y_coords[1], + pdata->key_x_coords[2], pdata->key_y_coords[2]); + } + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", + 0, &pdata->reset_gpio_flags); + if (pdata->reset_gpio < 0) + FTS_ERROR("Unable to get reset_gpio"); + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", + 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + FTS_ERROR("Unable to get irq_gpio"); + + ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val); + if (ret < 0) { + FTS_ERROR("Unable to get max-touch-number, please check dts"); + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + } else { + if (temp_val < 2) + pdata->max_touch_number = 2; /* max_touch_number must >= 2 */ + else if (temp_val > FTS_MAX_POINTS_SUPPORT) + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + else + pdata->max_touch_number = temp_val; + } + + FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d", + pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); + + FTS_FUNC_EXIT(); + return 0; +} + +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, + resume_work); + + fts_ts_resume(ts_data->dev); +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("FB event:%lu,blank:%d", event, *blank); + switch (*blank) { + case FB_BLANK_UNBLANK: + if (FB_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (FB_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case FB_BLANK_POWERDOWN: + if (FB_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (FB_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("FB BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +static struct drm_panel *active_panel; + +static int drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FTS_ERROR("find drm_panel count(%d) fail", count); + return -ENODEV; + } + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + FTS_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + FTS_ERROR("no find drm_panel"); + return -ENODEV; +} + +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (DRM_PANEL_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) + || (event == MSM_DRM_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (MSM_DRM_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void fts_ts_early_suspend(struct early_suspend *handler) +{ + struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, + early_suspend); + + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); +} + +static void fts_ts_late_resume(struct early_suspend *handler) +{ + struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, + early_suspend); + + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); +} +#endif + +static int fts_ts_probe_entry(struct fts_ts_data *ts_data) +{ + int ret = 0; + int pdata_size = sizeof(struct fts_ts_platform_data); + + FTS_FUNC_ENTER(); + FTS_INFO("%s", FTS_DRIVER_VERSION); + ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL); + if (!ts_data->pdata) { + FTS_ERROR("allocate memory for platform_data fail"); + return -ENOMEM; + } + + if (ts_data->dev->of_node) { + ret = fts_parse_dt(ts_data->dev, ts_data->pdata); + if (ret) + FTS_ERROR("device-tree parse fail"); + +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + ret = drm_check_dt(ts_data->dev->of_node); + if (ret) { + FTS_ERROR("parse drm-panel fail"); + } +#endif +#endif + } else { + if (ts_data->dev->platform_data) { + memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size); + } else { + FTS_ERROR("platform_data is null"); + return -ENODEV; + } + } + + ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); + if (!ts_data->ts_workqueue) { + FTS_ERROR("create fts workqueue fail"); + } + + spin_lock_init(&ts_data->irq_lock); + mutex_init(&ts_data->report_mutex); + mutex_init(&ts_data->bus_lock); + init_waitqueue_head(&ts_data->ts_waitqueue); + + /* Init communication interface */ + ret = fts_bus_init(ts_data); + if (ret) { + FTS_ERROR("bus initialize fail"); + goto err_bus_init; + } + + ret = fts_input_init(ts_data); + if (ret) { + FTS_ERROR("input initialize fail"); + goto err_input_init; + } + + ret = fts_buffer_init(ts_data); + if (ret) { + FTS_ERROR("buffer init fail"); + goto err_buffer_init; + } + + ret = fts_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + +#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; + } + + ret = fts_create_apk_debug_channel(ts_data); + if (ret) { + FTS_ERROR("create apk debug node fail"); + } + + ret = fts_create_sysfs(ts_data); + if (ret) { + FTS_ERROR("create sysfs node fail"); + } + + ret = fts_point_report_check_init(ts_data); + if (ret) { + FTS_ERROR("init point report check fail"); + } + + ret = fts_ex_mode_init(ts_data); + if (ret) { + FTS_ERROR("init glove/cover/charger fail"); + } + + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } + + + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } + + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + init_completion(&ts_data->pm_completion); + ts_data->pm_suspend = false; +#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_DRM) + ts_data->fb_notif.notifier_call = drm_notifier_callback; +#if defined(CONFIG_DRM_PANEL) + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); + if (ret) + FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } +#else + ret = msm_drm_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); + } +#endif +#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 FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(ts_data); +#endif + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); +err_gpio_config: + kfree_safe(ts_data->touch_buf); +err_buffer_init: + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif +err_input_init: + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); +err_bus_init: + kfree_safe(ts_data->bus_tx_buf); + kfree_safe(ts_data->bus_rx_buf); + kfree_safe(ts_data->pdata); + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_ts_remove_entry(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + cancel_work_sync(&fts_data->resume_work); + fts_point_report_check_exit(ts_data); + fts_release_apk_debug_channel(ts_data); + fts_remove_sysfs(ts_data); + fts_ex_mode_exit(ts_data); + + fts_fwupg_exit(ts_data); + + + fts_esdcheck_exit(ts_data); + + fts_gesture_exit(ts_data); + + free_irq(ts_data->irq, ts_data); + + fts_bus_exit(ts_data); + + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[FB]Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif); +#else + if (msm_drm_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif + + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); + + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_exit(ts_data); +#endif + + kfree_safe(ts_data->touch_buf); + kfree_safe(ts_data->pdata); + kfree_safe(ts_data); + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_ts_suspend(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (ts_data->suspended) { + FTS_INFO("Already in suspend state"); + return 0; + } + + if (ts_data->fw_loading) { + FTS_INFO("fw upgrade in process, can't suspend"); + return 0; + } + + fts_esdcheck_suspend(ts_data); + + if (ts_data->gesture_support) { + fts_gesture_suspend(ts_data); + } else { + + FTS_INFO("make TP enter into sleep mode"); + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_suspend(ts_data); + if (ret < 0) { + FTS_ERROR("power enter suspend fail"); + } +#endif + } + } + + fts_release_all_finger(); + ts_data->suspended = true; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + + ts_data->suspended = false; + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(ts_data); +#endif + fts_reset_proc(200); + } + + fts_wait_tp_to_valid(); + fts_ex_mode_recovery(ts_data); + + fts_esdcheck_resume(ts_data); + + if (ts_data->gesture_support) { + fts_gesture_resume(ts_data); + } + + FTS_FUNC_EXIT(); + return 0; +} + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM +static int fts_pm_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system enters into pm_suspend"); + ts_data->pm_suspend = true; + reinit_completion(&ts_data->pm_completion); + return 0; +} + +static int fts_pm_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system resumes from pm_suspend"); + ts_data->pm_suspend = false; + complete(&ts_data->pm_completion); + return 0; +} + +static const struct dev_pm_ops fts_dev_pm_ops = { + .suspend = fts_pm_suspend, + .resume = fts_pm_resume, +}; +#endif + +/***************************************************************************** +* TP Driver +*****************************************************************************/ +static int fts_ts_probe(struct spi_device *spi) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + + FTS_INFO("Touch Screen(SPI BUS) driver prboe..."); + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) { + FTS_ERROR("spi setup fail"); + return ret; + } + + /* malloc memory for global struct variable */ + ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("allocate memory for fts_data fail"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->spi = spi; + ts_data->dev = &spi->dev; + ts_data->log_level = 1; + + ts_data->bus_type = BUS_TYPE_SPI_V2; + spi_set_drvdata(spi, ts_data); + + ret = fts_ts_probe_entry(ts_data); + if (ret) { + FTS_ERROR("Touch Screen(SPI BUS) driver probe fail"); + kfree_safe(ts_data); + return ret; + } + + FTS_INFO("Touch Screen(SPI BUS) driver prboe successfully"); + return 0; +} + +static int fts_ts_remove(struct spi_device *spi) +{ + return fts_ts_remove_entry(spi_get_drvdata(spi)); +} + +static const struct spi_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct spi_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + .pm = &fts_dev_pm_ops, +#endif + .of_match_table = of_match_ptr(fts_dt_match), + }, + .id_table = fts_ts_id, +}; + +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = spi_register_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_exit(void) +{ + spi_unregister_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_0flash_v2_mmi/focaltech_core.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.h new file mode 100644 index 000000000000..cb9805a11533 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.h @@ -0,0 +1,336 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.h + +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_CORE_H__ +#define __LINUX_FOCALTECH_CORE_H__ +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#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_DIM 10 +#define FTS_COORDS_ARR_SIZE 4 +#define FTS_ONE_TCH_LEN 6 +#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 2) + +#define FTS_GESTURE_POINTS_MAX 6 +#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4) + +#define FTS_SIZE_PEN 15 +#define FTS_SIZE_DEFAULT 15 + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_OFF_E_XH 0 +#define FTS_TOUCH_OFF_XL 1 +#define FTS_TOUCH_OFF_ID_YH 2 +#define FTS_TOUCH_OFF_YL 3 +#define FTS_TOUCH_OFF_PRE 4 +#define FTS_TOUCH_OFF_AREA 5 +#define FTS_TOUCH_E_NUM 1 +#define FTS_X_MIN_DISPLAY_DEFAULT 0 +#define FTS_Y_MIN_DISPLAY_DEFAULT 0 +#define FTS_X_MAX_DISPLAY_DEFAULT 720 +#define FTS_Y_MAX_DISPLAY_DEFAULT 1280 + +#define FTS_TOUCH_DOWN 0 +#define FTS_TOUCH_UP 1 +#define FTS_TOUCH_CONTACT 2 +#define EVENT_DOWN(flag) ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag)) +#define EVENT_UP(flag) (FTS_TOUCH_UP == flag) + +#define FTS_MAX_COMPATIBLE_TYPE 4 +#define FTS_MAX_COMMMAND_LENGTH 16 + +#define FTS_MAX_TOUCH_BUF 4096 + + +/***************************************************************************** +* Alternative mode (When something goes wrong, the modules may be able to solve the problem.) +*****************************************************************************/ +/* + * For commnication error in PM(deep sleep) state + */ +#define FTS_PATCH_COMERR_PM 0 +#define FTS_TIMEOUT_COMERR_PM 700 + + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct ftxxxx_proc { + struct proc_dir_entry *proc_entry; + u8 opmode; + u8 cmd_len; + u8 cmd[FTS_MAX_COMMMAND_LENGTH]; +}; + +struct fts_ts_platform_data { + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool have_key; + u32 key_number; + u32 keys[FTS_MAX_KEYS]; + u32 key_y_coords[FTS_MAX_KEYS]; + u32 key_x_coords[FTS_MAX_KEYS]; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 max_touch_number; +}; + +struct ts_event { + int x; /*x coordinate */ + int y; /*y coordinate */ + int p; /* pressure */ + int flag; /* touch event flag: 0 -- down; 1-- up; 2 -- contact */ + int id; /*touch ID */ + int area; +}; + +struct pen_event { + int down; + int inrange; + int tip; + int x; /*x coordinate */ + int y; /*y coordinate */ + int p; /* pressure */ + int flag; /* touch event flag: 0 -- down; 1-- up; 2 -- contact */ + int id; /*touch ID */ + int tilt_x; + int tilt_y; + int azimuth; + int tool_type; +}; + +struct fts_ts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct input_dev *input_dev; + struct input_dev *pen_dev; + struct fts_ts_platform_data *pdata; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct work_struct fwupg_work; + struct delayed_work esdcheck_work; + struct delayed_work prc_work; + struct work_struct resume_work; + wait_queue_head_t ts_waitqueue; + struct ftxxxx_proc proc; + struct ftxxxx_proc proc_ta; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + unsigned long intr_jiffies; + int irq; + int log_level; + int fw_is_running; /* confirm fw is running when using spi:default 0 */ + int dummy_byte; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + struct completion pm_completion; + bool pm_suspend; +#endif + bool suspended; + bool fw_loading; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool touch_analysis_support; + bool prc_support; + bool prc_mode; + bool esd_support; + + bool gesture_support; /* gesture enable or disable, default: disable */ + u8 gesture_bmode; /*gesture buffer mode*/ + + u8 pen_etype; + struct pen_event pevent; + struct ts_event events[FTS_MAX_POINTS_SUPPORT]; /* multi-touch */ + u8 touch_addr; + u32 touch_size; + u8 *touch_buf; + int touch_event_num; + int touch_points; + int key_state; + int ta_flag; + u32 ta_size; + u8 *ta_buf; + + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + struct regulator *vdd; + struct regulator *vcc_i2c; +#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) || defined(CONFIG_DRM) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +}; + +enum _FTS_BUS_TYPE { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_SPI_V2, +}; + +enum _FTS_TOUCH_ETYPE { + TOUCH_DEFAULT = 0x00, + TOUCH_EVENT_NUM = 0x02, + TOUCH_EXTRA_MSG = 0x08, + TOUCH_PEN = 0x0B, + TOUCH_GESTURE = 0x80, + TOUCH_FW_INIT = 0x81, + TOUCH_IGNORE = 0xFE, + TOUCH_ERROR = 0xFF, +}; + +enum _FTS_STYLUS_ETYPE { + STYLUS_DEFAULT, + STYLUS_HOVER, +}; + +enum _FTS_GESTURE_BMODE { + GESTURE_BM_REG, + GESTURE_BM_TOUCH, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_ts_data *fts_data; + +/* communication interface */ +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen); +int fts_read_reg(u8 addr, u8 *value); +int fts_write(u8 *writebuf, u32 writelen); +int fts_write_reg(u8 addr, u8 value); +void fts_hid2std(void); +int fts_bus_init(struct fts_ts_data *ts_data); +int fts_bus_exit(struct fts_ts_data *ts_data); +int fts_spi_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen); + +/* Gesture functions */ +int fts_gesture_init(struct fts_ts_data *ts_data); +int fts_gesture_exit(struct fts_ts_data *ts_data); +void fts_gesture_recovery(struct fts_ts_data *ts_data); +int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data); +int fts_gesture_suspend(struct fts_ts_data *ts_data); +int fts_gesture_resume(struct fts_ts_data *ts_data); + +/* Apk and functions */ +int fts_create_apk_debug_channel(struct fts_ts_data *); +void fts_release_apk_debug_channel(struct fts_ts_data *); + +/* ADB functions */ +int fts_create_sysfs(struct fts_ts_data *ts_data); +int fts_remove_sysfs(struct fts_ts_data *ts_data); + +/* ESD */ +int fts_esdcheck_init(struct fts_ts_data *ts_data); +int fts_esdcheck_exit(struct fts_ts_data *ts_data); +void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable); +void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug); +void fts_esdcheck_suspend(struct fts_ts_data *ts_data); +void fts_esdcheck_resume(struct fts_ts_data *ts_data); + +/* Host test */ + +/* Point Report Check*/ +int fts_point_report_check_init(struct fts_ts_data *ts_data); +int fts_point_report_check_exit(struct fts_ts_data *ts_data); +void fts_prc_queue_work(struct fts_ts_data *ts_data); + +/* FW upgrade */ +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); +int fts_fw_resume(bool need_reset); +int fts_fw_recovery(void); +int fts_upgrade_bin(char *fw_name, bool force); +int fts_enter_test_environment(bool test_state); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h); +int fts_wait_tp_to_valid(void); +void fts_release_all_finger(void); +void fts_tp_state_recovery(struct fts_ts_data *ts_data); +int fts_ex_mode_init(struct fts_ts_data *ts_data); +int fts_ex_mode_exit(struct fts_ts_data *ts_data); +int fts_ex_mode_recovery(struct fts_ts_data *ts_data); + +void fts_irq_disable(void); +void fts_irq_enable(void); +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c new file mode 100644 index 000000000000..ce404346f744 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c @@ -0,0 +1,449 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_esdcheck.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-03 +* +* Abstract: ESD check function +* +* Version: v1.0 +* +* Revision History: +* v1.0: +* First release. By luougojin 2016-08-03 +* v1.1: By luougojin 2017-02-15 +* 1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define ESDCHECK_WAIT_TIME 1000 /* ms */ +#define LCD_ESD_PATCH 0 +#define ESDCHECK_INTRCNT_MAX 2 +#define ESD_INTR_INTERVALS 200 /* unit:ms */ + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_esdcheck_st { + u8 mode : 1; /* 1- need check esd 0- no esd check */ + u8 suspend : 1; + u8 proc_debug : 1; /* apk or adb use */ + u8 intr : 1; /* 1- Interrupt trigger */ + u8 unused : 4; + u8 intr_cnt; + u8 flow_work_hold_cnt; /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */ + u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */ + u32 hardware_reset_cnt; + u32 nack_cnt; + u32 dataerror_cnt; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_esdcheck_st fts_esdcheck_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +#if LCD_ESD_PATCH +int lcd_need_reset; +static int tp_need_recovery; /* LCD reset cause Tp reset */ +int idc_esdcheck_lcderror(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 val = 0; + + FTS_DEBUG("check LCD ESD"); + if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) { + tp_need_recovery = 0; + /* LCD reset, need recover TP state */ + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + } + + ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val); + if ( ret < 0) { + FTS_ERROR("read reg0xED fail,ret:%d", ret); + return -EIO; + } + + if (val == 0xAA) { + /* + * 1. Set flag lcd_need_reset = 1; + * 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0 + * 3. recover TP state + */ + FTS_INFO("LCD ESD, need execute LCD reset"); + lcd_need_reset = 1; + tp_need_recovery = 1; + } + + return 0; +} +#endif + +static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.hardware_reset_cnt++; + + fts_reset_proc(200); + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + + FTS_FUNC_EXIT(); + return 0; +} + +static bool get_chip_id(struct fts_ts_data *ts_data) +{ + int ret = 0; + int i = 0; + u8 idh = 0; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if (ret < 0) { + FTS_ERROR("read chip id fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ((idh == chip_id) || (fts_check_cid(ts_data, idh) == 0)) { + break; + } else { + FTS_DEBUG("read chip_id:%x,retry:%d", idh, i); + fts_esdcheck_data.dataerror_cnt++; + } + } + msleep(10); + } + + /* if can't get correct data in 3 times, then need hardware reset */ + if (i >= 3) { + FTS_ERROR("read chip id 3 times fail, need execute TP reset"); + return true; + } + + return false; +} + +/***************************************************************************** +* Name: get_flow_cnt +* Brief: Read flow cnt(0x91) +* Input: +* Output: +* Return: 1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times +* 0(false) - Reg 0x91(flow cnt) normal +*****************************************************************************/ +static bool get_flow_cnt(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + + reg_addr = FTS_REG_FLOW_WORK_CNT; + ret = fts_read(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("read reg0x91 fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) { + FTS_DEBUG("reg0x91,val:%x,last:%x", reg_value, + fts_esdcheck_data.flow_work_cnt_last); + fts_esdcheck_data.flow_work_hold_cnt++; + } else { + fts_esdcheck_data.flow_work_hold_cnt = 0; + } + + fts_esdcheck_data.flow_work_cnt_last = reg_value; + } + + /* Flow Work Cnt keep a value for 5 times, need execute TP reset */ + if (fts_esdcheck_data.flow_work_hold_cnt >= 5) { + FTS_DEBUG("reg0x91 keep a value for 5 times, need execute TP reset"); + return true; + } + + return false; +} + +static int esdcheck_algorithm(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + bool hardware_reset = 0; + unsigned long intr_timeout = msecs_to_jiffies(ESD_INTR_INTERVALS); + + /* 1. esdcheck is interrupt, then return */ + intr_timeout += ts_data->intr_jiffies; + if (time_before(jiffies, intr_timeout)) { + return 0; + } + + /* 2. check power state, if suspend, no need check esd */ + if (fts_esdcheck_data.suspend == 1) { + FTS_DEBUG("In suspend, not check esd"); + /* because in suspend state, adb can be used, when upgrade FW, will + * active ESD check(active = 1); But in suspend, then will don't + * queue_delayed_work, when resume, don't check ESD again + */ + return 0; + } + + /* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/ + if (fts_esdcheck_data.proc_debug == 1) { + FTS_INFO("In apk/adb command mode, not check esd"); + return 0; + } + + /* 4. In factory mode, can't check esd */ + reg_addr = FTS_REG_WORKMODE; + ret = fts_read_reg(reg_addr, ®_value); + if ( ret < 0 ) { + fts_esdcheck_data.nack_cnt++; + } else if ( (reg_value & 0x70) == FTS_REG_WORKMODE_FACTORY_VALUE) { + FTS_DEBUG("in factory mode(%x), no check esd", reg_value); + return 0; + } + + /* 5. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 6. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */ + if (!hardware_reset) { + hardware_reset = get_flow_cnt(ts_data); + } + + /* 7. If need hardware reset, then handle it here */ + if (hardware_reset == 1) { + FTS_DEBUG("NoACK=%d, Error Data=%d, Hardware Reset=%d", + fts_esdcheck_data.nack_cnt, + fts_esdcheck_data.dataerror_cnt, + fts_esdcheck_data.hardware_reset_cnt); + fts_esdcheck_tp_reset(ts_data); + } + + return 0; +} + +static void esdcheck_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, esdcheck_work.work); + + if (ts_data->esd_support && fts_esdcheck_data.mode) { +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } +} + +/***************************************************************************** +* Name: fts_esdcheck_proc_busy +* Brief: When APK or ADB command access TP via driver, then need set proc_debug, +* then will not check ESD. +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug) +{ + if (ts_data->esd_support) { + fts_esdcheck_data.proc_debug = proc_debug; + } +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + if (fts_esdcheck_data.mode == ENABLE) { + if (enable) { + FTS_DEBUG("ESD check start"); + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.flow_work_cnt_last = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + queue_delayed_work(ts_data->ts_workqueue, + &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } else { + FTS_DEBUG("ESD check stop"); + cancel_delayed_work_sync(&ts_data->esdcheck_work); + } + } + FTS_FUNC_EXIT(); + } +} + +void fts_esdcheck_suspend(struct fts_ts_data *ts_data) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ts_data, DISABLE); + fts_esdcheck_data.suspend = 1; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + } +} + +void fts_esdcheck_resume(struct fts_ts_data *ts_data) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ts_data, ENABLE); + fts_esdcheck_data.suspend = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + } +} + +static ssize_t fts_esdcheck_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + ts_data->esd_support = ENABLE; + fts_esdcheck_switch(ts_data, ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_switch(ts_data, DISABLE); + ts_data->esd_support = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_esdcheck_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \ + ts_data->esd_support ? "On" : "Off"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* sysfs esd node + * read example: cat fts_esd_mode ---read esd mode + * write example:echo 01 > fts_esd_mode ---make esdcheck enable + * + */ +static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store); + +static struct attribute *fts_esd_mode_attrs[] = { + + &dev_attr_fts_esd_mode.attr, + NULL, +}; + +static struct attribute_group fts_esd_group = { + .attrs = fts_esd_mode_attrs, +}; + +static int fts_create_esd_sysfs(struct device *dev) +{ + int ret = 0; + + ret = sysfs_create_group(&dev->kobj, &fts_esd_group); + if ( ret != 0) { + FTS_ERROR("fts_create_esd_sysfs(sysfs) create fail"); + sysfs_remove_group(&dev->kobj, &fts_esd_group); + return ret; + } + return 0; +} + +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_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + fts_create_esd_sysfs(ts_data->dev); + ts_data->esd_support = FTS_ESDCHECK_EN; + fts_esdcheck_switch(ts_data, ENABLE); + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + fts_esdcheck_data.mode = DISABLE; + ts_data->esd_support = DISABLE; + cancel_delayed_work_sync(&ts_data->esdcheck_work); + sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c new file mode 100644 index 000000000000..7e83b5932449 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c @@ -0,0 +1,1469 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: Focaltech_ex_fun.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define PROC_UPGRADE 0 +#define PROC_READ_REGISTER 1 +#define PROC_WRITE_REGISTER 2 +#define PROC_AUTOCLB 4 +#define PROC_UPGRADE_INFO 5 +#define PROC_WRITE_DATA 6 +#define PROC_READ_DATA 7 +#define PROC_SET_TEST_FLAG 8 +#define PROC_SET_SLAVE_ADDR 10 +#define PROC_HW_RESET 11 +#define PROC_READ_STATUS 12 +#define PROC_SET_BOOT_MODE 13 +#define PROC_ENTER_TEST_ENVIRONMENT 14 +#define PROC_WRITE_DATA_DIRECT 16 +#define PROC_READ_DATA_DIRECT 17 +#define PROC_CONFIGURE 18 +#define PROC_CONFIGURE_INTR 20 +#define PROC_GET_DRIVER_INFO 21 +#define PROC_NAME "ftxxxx-debug" +#define PROC_BUF_SIZE 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +enum { + RWREG_OP_READ = 0, + RWREG_OP_WRITE = 1, +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct rwreg_operation_t { + int type; /* 0: read, 1: write */ + int reg; /* register */ + int len; /* read/write length */ + int val; /* length = 1; read: return value, write: op return */ + int res; /* 0: success, otherwise: fail */ + char *opbuf; /* length >= 1, read return value, write: op return */ +} rw_op; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +static ssize_t fts_debug_write( + struct file *filp, const char __user *buff, size_t count, loff_t *ppos) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[PROC_BUF_SIZE]; + struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp)); + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen < 1) { + FTS_ERROR("apk proc count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + if (buflen == 1) { + ret = buflen; + goto proc_write_err; + } + + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); + if (writebuf[1] == 0) { + fts_esdcheck_switch(ts_data, ENABLE); + } else { + fts_esdcheck_switch(ts_data, DISABLE); + } + break; + + case PROC_READ_REGISTER: + proc->cmd[0] = writebuf[1]; + break; + + case PROC_WRITE_REGISTER: + ret = fts_write_reg(writebuf[1], writebuf[2]); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_REGISTER write error"); + goto proc_write_err; + } + break; + + case PROC_READ_DATA: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA: + writelen = buflen - 1; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA write error"); + goto proc_write_err; + } + break; + + + case PROC_HW_RESET: + if (buflen < PROC_BUF_SIZE) { + snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + } + break; + + case PROC_SET_BOOT_MODE: + FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); + if (0 == writebuf[1]) { + ts_data->fw_is_running = true; + } else { + ts_data->fw_is_running = false; + } + break; + case PROC_ENTER_TEST_ENVIRONMENT: + FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); + if (0 == writebuf[1]) { + fts_enter_test_environment(0); + } else { + fts_enter_test_environment(1); + } + break; + + case PROC_READ_DATA_DIRECT: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA_DIRECT: + writelen = buflen - 1; + ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE: + ts_data->spi->mode = writebuf[1]; + ts_data->spi->bits_per_word = writebuf[2]; + ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); + FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, + ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); + ret = spi_setup(ts_data->spi); + if (ret) { + FTS_ERROR("spi setup fail"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + break; + + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +static ssize_t fts_debug_read( + struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *readbuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp)); + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen <= 0) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + + switch (proc->opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_read_reg(proc->cmd[0], &readbuf[0]); + if (ret < 0) { + FTS_ERROR("PROC_READ_REGISTER read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA_DIRECT: + num_read_chars = buflen; + ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA_DIRECT read error"); + goto proc_read_err; + } + break; + + case PROC_GET_DRIVER_INFO: + if (buflen >= 64) { + num_read_chars = buflen; + readbuf[0] = ts_data->bus_type; + snprintf(&readbuf[32], buflen - 32, "%s", FTS_DRIVER_VERSION); + } + break; + + default: + break; + } + + ret = num_read_chars; +proc_read_err: + if ((num_read_chars > 0) && copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops fts_proc_fops = { + .proc_read = fts_debug_read, + .proc_write = fts_debug_write, +}; +#else +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .write = fts_debug_write, +}; +#endif +#else +static int fts_debug_write( + struct file *filp, const char __user *buff, unsigned long count, void *data) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[PROC_BUF_SIZE]; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen < 1) { + FTS_ERROR("apk proc wirte count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); + if (writebuf[1] == 0) { + fts_esdcheck_switch(ts_data, ENABLE); + } else { + fts_esdcheck_switch(ts_data, DISABLE); + } + break; + + case PROC_READ_REGISTER: + proc->cmd[0] = writebuf[1]; + break; + + case PROC_WRITE_REGISTER: + ret = fts_write_reg(writebuf[1], writebuf[2]); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_REGISTER write error"); + goto proc_write_err; + } + break; + + case PROC_READ_DATA: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA: + writelen = buflen - 1; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA write error"); + goto proc_write_err; + } + break; + + + case PROC_HW_RESET: + if (buflen < PROC_BUF_SIZE) { + snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + } + break; + + case PROC_SET_BOOT_MODE: + FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); + if (0 == writebuf[1]) { + ts_data->fw_is_running = true; + } else { + ts_data->fw_is_running = false; + } + break; + case PROC_ENTER_TEST_ENVIRONMENT: + FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); + if (0 == writebuf[1]) { + fts_enter_test_environment(0); + } else { + fts_enter_test_environment(1); + } + break; + + case PROC_READ_DATA_DIRECT: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA_DIRECT: + writelen = buflen - 1; + ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE: + ts_data->spi->mode = writebuf[1]; + ts_data->spi->bits_per_word = writebuf[2]; + ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); + FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, + ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); + ret = spi_setup(ts_data->spi); + if (ret) { + FTS_ERROR("spi setup fail"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + break; + + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +static int fts_debug_read( + char *page, char **start, off_t off, int count, int *eof, void *data ) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *readbuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen <= 0) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + + switch (proc->opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_read_reg(proc->cmd[0], &readbuf[0]); + if (ret < 0) { + FTS_ERROR("PROC_READ_REGISTER read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA_DIRECT: + num_read_chars = buflen; + ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA_DIRECT read error"); + goto proc_read_err; + } + break; + + case PROC_GET_DRIVER_INFO: + if (buflen >= 64) { + num_read_chars = buflen; + readbuf[0] = ts_data->bus_type; + snprintf(&readbuf[16], buflen - 16, "%s", FTS_DRIVER_VERSION); + } + break; + + default: + break; + } + + ret = num_read_chars; +proc_read_err: + if (!memcpy(page, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +static int fts_ta_open(struct inode *inode, struct file *file) +{ + struct fts_ts_data *ts_data = PDE_DATA(inode); + + if (ts_data->touch_analysis_support) { + FTS_INFO("fts_ta open"); + ts_data->ta_buf = kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL); + if (!ts_data->ta_buf) { + FTS_ERROR("kzalloc for ta_buf fails"); + return -ENOMEM; + } + } + return 0; +} + +static int fts_ta_release(struct inode *inode, struct file *file) +{ + struct fts_ts_data *ts_data = PDE_DATA(inode); + + if (ts_data->touch_analysis_support) { + FTS_INFO("fts_ta close"); + ts_data->ta_flag = 0; + if (ts_data->ta_buf) { + kfree(ts_data->ta_buf); + ts_data->ta_buf = NULL; + } + } + return 0; +} + +static ssize_t fts_ta_read( + struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int read_num = (int)count; + struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp)); + + if (!ts_data->touch_analysis_support || !ts_data->ta_buf) { + FTS_ERROR("touch_analysis is disabled, or ta_buf is NULL"); + return -EINVAL; + } + + if (!(filp->f_flags & O_NONBLOCK)) { + ts_data->ta_flag = 1; + wait_event_interruptible(ts_data->ts_waitqueue, !ts_data->ta_flag); + } + + read_num = (ts_data->ta_size < read_num) ? ts_data->ta_size : read_num; + if ((read_num > 0) && (copy_to_user(buff, ts_data->ta_buf, read_num))) { + FTS_ERROR("copy to user error"); + return -EFAULT; + } + + return read_num; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops fts_procta_fops = { + .proc_open = fts_ta_open, + .proc_release = fts_ta_release, + .proc_read = fts_ta_read, +}; +#else +static const struct file_operations fts_procta_fops = { + .open = fts_ta_open, + .release = fts_ta_release, + .read = fts_ta_read, +}; +#endif +#endif + +int fts_create_apk_debug_channel(struct fts_ts_data *ts_data) +{ + struct ftxxxx_proc *proc = &ts_data->proc; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc->proc_entry = proc_create_data(PROC_NAME, 0777, NULL, &fts_proc_fops, ts_data); + if (NULL == proc->proc_entry) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } +#else + proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL); + if (NULL == proc->proc_entry) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } + proc->proc_entry->write_proc = fts_debug_write; + proc->proc_entry->read_proc = fts_debug_read; +#endif + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + ts_data->proc_ta.proc_entry = proc_create_data("fts_ta", 0777, NULL, \ + &fts_procta_fops, ts_data); + if (!ts_data->proc_ta.proc_entry) { + FTS_ERROR("create proc_ta entry fail"); + return -ENOMEM; + } +#endif + + FTS_INFO("Create proc entry success!"); + return 0; +} + +void fts_release_apk_debug_channel(struct fts_ts_data *ts_data) +{ + struct ftxxxx_proc *proc = &ts_data->proc; + + if (proc->proc_entry) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc_remove(proc->proc_entry); +#else + remove_proc_entry(PROC_NAME, NULL); +#endif + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + if (ts_data->proc_ta.proc_entry) + proc_remove(ts_data->proc_ta.proc_entry); +#endif +} + +/************************************************************************ + * sysfs interface + ***********************************************************************/ +/* fts_hw_reset interface */ +static ssize_t fts_hw_reset_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + ssize_t count = 0; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + count = snprintf(buf, PAGE_SIZE, "hw reset executed\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_hw_reset_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_irq interface */ +static ssize_t fts_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct irq_desc *desc = irq_to_desc(ts_data->irq); + + count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth); + + return count; +} + +static ssize_t fts_irq_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_boot_mode interface */ +static ssize_t fts_bootmode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[EX-FUN]set to boot mode"); + ts_data->fw_is_running = false; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]set to fw mode"); + ts_data->fw_is_running = true; + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +static ssize_t fts_bootmode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (true == ts_data->fw_is_running) { + count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n"); + } else { + count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n"); + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_tpfwver interface */ +static ssize_t fts_tpfwver_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + mutex_lock(&input_dev->mutex); + + ret = fts_read_reg(FTS_REG_FW_VER, &fwver); + if ((ret < 0) || (fwver == 0xFF) || (fwver == 0x00)) + num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n"); + else + num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +static ssize_t fts_tpfwver_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_rw_reg */ +static ssize_t fts_tprwreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (rw_op.len < 0) { + count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n"); + } else if (rw_op.len == 1) { + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } else { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } + } else { + if (RWREG_OP_READ == rw_op.type) { + count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len); + count += snprintf(buf + count, PAGE_SIZE, "Result: "); + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res); + } else { + if (rw_op.opbuf) { + for (i = 0; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + } + } else { + ; + count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1); + count += snprintf(buf + count, PAGE_SIZE, "Write Data: "); + if (rw_op.opbuf) { + for (i = 1; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res); + } else { + count += snprintf(buf + count, PAGE_SIZE, "Result: success\n"); + } + } + /*if (rw_op.opbuf) { + kfree(rw_op.opbuf); + rw_op.opbuf = NULL; + }*/ + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static int shex_to_int(const char *hex_buf, int size) +{ + int i; + int base = 1; + int value = 0; + char single; + + for (i = size - 1; i >= 0; i--) { + single = hex_buf[i]; + + if ((single >= '0') && (single <= '9')) { + value += (single - '0') * base; + } else if ((single >= 'a') && (single <= 'z')) { + value += (single - 'a' + 10) * base; + } else if ((single >= 'A') && (single <= 'Z')) { + value += (single - 'A' + 10) * base; + } else { + return -EINVAL; + } + + base *= 16; + } + + return value; +} + + +static u8 shex_to_u8(const char *hex_buf, int size) +{ + return (u8)shex_to_int(hex_buf, size); +} +/* + * Format buf: + * [0]: '0' write, '1' read(reserved) + * [1-2]: addr, hex + * [3-4]: length, hex + * [5-6]...[n-(n+1)]: data, hex + */ +static int fts_parse_buf(const char *buf, size_t cmd_len) +{ + int length; + int i; + char *tmpbuf; + + rw_op.reg = shex_to_u8(buf + 1, 2); + length = shex_to_int(buf + 3, 2); + + if (buf[0] == '1') { + rw_op.len = length; + rw_op.type = RWREG_OP_READ; + FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len); + } else { + if (cmd_len < (length * 2 + 5)) { + pr_err("data invalided!\n"); + return -EINVAL; + } + FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length); + + /* first byte is the register addr */ + rw_op.type = RWREG_OP_WRITE; + rw_op.len = length + 1; + } + + if (rw_op.len > 0) { + tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("allocate memory failed!\n"); + return -ENOMEM; + } + + if (RWREG_OP_WRITE == rw_op.type) { + tmpbuf[0] = rw_op.reg & 0xFF; + FTS_DEBUG("write buffer: "); + for (i = 1; i < rw_op.len; i++) { + tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2); + FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF); + } + } + rw_op.opbuf = tmpbuf; + } + + return rw_op.len; +} + +static ssize_t fts_tprwreg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + ssize_t cmd_length = 0; + + mutex_lock(&input_dev->mutex); + cmd_length = count - 1; + + if (rw_op.opbuf) { + kfree(rw_op.opbuf); + rw_op.opbuf = NULL; + } + + FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf); + /* compatible old ops */ + if (2 == cmd_length) { + rw_op.type = RWREG_OP_READ; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + } else if (4 == cmd_length) { + rw_op.type = RWREG_OP_WRITE; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + rw_op.val = shex_to_int(buf + 2, 2); + } else if (cmd_length < 5) { + FTS_ERROR("Invalid cmd buffer"); + mutex_unlock(&input_dev->mutex); + return -EINVAL; + } else { + rw_op.len = fts_parse_buf(buf, cmd_length); + } + + if (rw_op.len < 0) { + FTS_ERROR("cmd buffer error!"); + + } else { + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + rw_op.res = fts_read_reg(reg, &val); + rw_op.val = val; + } else { + char reg; + reg = rw_op.reg & 0xFF; + + rw_op.res = fts_read(®, 1, rw_op.opbuf, rw_op.len); + } + + if (rw_op.res < 0) { + FTS_ERROR("Could not read 0x%02x", rw_op.reg); + } else { + FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len); + rw_op.res = 0; + } + + } else { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + val = rw_op.val & 0xFF; + rw_op.res = fts_write_reg(reg, val); + } else { + rw_op.res = fts_write(rw_op.opbuf, rw_op.len); + } + if (rw_op.res < 0) { + FTS_ERROR("Could not write 0x%02x", rw_op.reg); + + } else { + FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len); + rw_op.res = 0; + } + } + } + + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_upgrade_bin interface */ +static ssize_t fts_fwupgradebin_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_force_upgrade interface */ +static ssize_t fts_fwforceupg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 1); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_driver_info interface */ +static ssize_t fts_driverinfo_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", + FTS_DRIVER_VERSION); + + count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n", + pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max); + + count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", + pdata->max_touch_number); + + count += snprintf(buf + count, PAGE_SIZE, + "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n", + ts_data->ic_info.ids.chip_idh, + ts_data->ic_info.ids.chip_idl); + + if (ts_data->bus_type == BUS_TYPE_I2C) { + count += snprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + } else { + count += snprintf(buf + count, PAGE_SIZE, + "BUS:%s,mode:%d,max_freq:%d\n", "SPI", + ts_data->spi->mode, ts_data->spi->max_speed_hz); + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_driverinfo_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_dump_reg interface */ +static ssize_t fts_dumpreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + fts_read_reg(FTS_REG_POWER_MODE, &val); + count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val); + + fts_read_reg(FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_LIC_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val); + + fts_read_reg(FTS_REG_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val); + + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val); + + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val); + + fts_read_reg(FTS_REG_INT_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val); + + fts_read_reg(FTS_REG_FLOW_WORK_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val); + + mutex_unlock(&input_dev->mutex); + return count; +} + +static ssize_t fts_dumpreg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_dump_reg interface */ +static ssize_t fts_tpbuf_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + int i = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n"); + for (i = 0; i < FTS_TOUCH_DATA_LEN; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02x ", ts_data->touch_buf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_tpbuf_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_log_level node */ +static ssize_t fts_log_level_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n", + ts_data->log_level); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_log_level_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("log level:%d->%d", ts_data->log_level, value); + ts_data->log_level = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_pen node */ +static ssize_t fts_pen_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "pen event:%s\n", + ts_data->pen_etype ? "hover" : "default"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_pen_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("pen event:%d->%d", ts_data->pen_etype, value); + ts_data->pen_etype = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_touch_size node */ +static ssize_t fts_touchsize_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch size:%d\n", ts_data->touch_size); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touchsize_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + if ((value > 2) && (value < FTS_MAX_TOUCH_BUF)) { + FTS_DEBUG("touch size:%d->%d", ts_data->touch_size, value); + ts_data->touch_size = value; + } else + FTS_DEBUG("touch size:%d invalid", value); + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_ta_mode node */ +static ssize_t fts_tamode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch analysis:%s\n", \ + ts_data->touch_analysis_support ? "Enable" : "Disable"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_tamode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + ts_data->touch_analysis_support = !!value; + FTS_DEBUG("set touch analysis:%d", ts_data->touch_analysis_support); + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store); + +/* read and write register(s) +* All data type is **HEX** +* Single Byte: +* read: echo 88 > rw_reg ---read register 0x88 +* write: echo 8807 > rw_reg ---write 0x07 into register 0x88 +* Multi-bytes: +* [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex] +* rw-flag: 0, write; 1, read +* read: echo 10005 > rw_reg ---read reg 0x00-0x05 +* write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05 +* Get result: +* cat rw_reg +*/ +static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store); +/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */ +static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store); +static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_store); +static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store); +static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store); +static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store); +static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store); +static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store); +static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store); +static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store); +static DEVICE_ATTR(fts_pen, S_IRUGO | S_IWUSR, fts_pen_show, fts_pen_store); +static DEVICE_ATTR(fts_touch_size, S_IRUGO | S_IWUSR, fts_touchsize_show, fts_touchsize_store); +static DEVICE_ATTR(fts_ta_mode, S_IRUGO | S_IWUSR, fts_tamode_show, fts_tamode_store); + +/* add your attr in here*/ +static struct attribute *fts_attributes[] = { + &dev_attr_fts_fw_version.attr, + &dev_attr_fts_rw_reg.attr, + &dev_attr_fts_dump_reg.attr, + &dev_attr_fts_upgrade_bin.attr, + &dev_attr_fts_force_upgrade.attr, + &dev_attr_fts_driver_info.attr, + &dev_attr_fts_hw_reset.attr, + &dev_attr_fts_irq.attr, + &dev_attr_fts_boot_mode.attr, + &dev_attr_fts_touch_point.attr, + &dev_attr_fts_log_level.attr, + &dev_attr_fts_pen.attr, + &dev_attr_fts_touch_size.attr, + &dev_attr_fts_ta_mode.attr, + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +int fts_create_sysfs(struct fts_ts_data *ts_data) +{ + int ret = 0; + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group); + if (ret) { + FTS_ERROR("[EX]: sysfs_create_group() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return -ENOMEM; + } else { + FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); + } + + return ret; +} + +int fts_remove_sysfs(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c new file mode 100644 index 000000000000..38157ffa0677 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c @@ -0,0 +1,307 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2020, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_ex_mode.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-31 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* 2.Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* 3.Private enumerations, structures and unions using typedef +*****************************************************************************/ +enum _ex_mode { + MODE_GLOVE = 0, + MODE_COVER, + MODE_CHARGER, +}; + +/***************************************************************************** +* 4.Static variables +*****************************************************************************/ + +/***************************************************************************** +* 5.Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* 6.Static function prototypes +*******************************************************************************/ +static int fts_ex_mode_switch(enum _ex_mode mode, u8 value) +{ + int ret = 0; + u8 m_val = 0; + + if (value) + m_val = 0x01; + else + m_val = 0x00; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_GLOVE switch to %d fail", m_val); + } + break; + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_COVER switch to %d fail", m_val); + } + break; + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_CHARGER switch to %d fail", m_val); + } + break; + default: + FTS_ERROR("mode(%d) unsupport", mode); + ret = -EINVAL; + break; + } + + return ret; +} + +static ssize_t fts_glove_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Glove Reg(0xC0):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_glove_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->glove_mode) { + FTS_DEBUG("enter glove mode"); + ret = fts_ex_mode_switch(MODE_GLOVE, ENABLE); + if (ret >= 0) { + ts_data->glove_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->glove_mode) { + FTS_DEBUG("exit glove mode"); + ret = fts_ex_mode_switch(MODE_GLOVE, DISABLE); + if (ret >= 0) { + ts_data->glove_mode = DISABLE; + } + } + } + + FTS_DEBUG("glove mode:%d", ts_data->glove_mode); + return count; +} + + +static ssize_t fts_cover_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_COVER_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Cover Reg(0xC1):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_cover_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->cover_mode) { + FTS_DEBUG("enter cover mode"); + ret = fts_ex_mode_switch(MODE_COVER, ENABLE); + if (ret >= 0) { + ts_data->cover_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->cover_mode) { + FTS_DEBUG("exit cover mode"); + ret = fts_ex_mode_switch(MODE_COVER, DISABLE); + if (ret >= 0) { + ts_data->cover_mode = DISABLE; + } + } + } + + FTS_DEBUG("cover mode:%d", ts_data->cover_mode); + return count; +} + +static ssize_t fts_charger_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Charger Reg(0x8B):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_charger_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->charger_mode) { + FTS_DEBUG("enter charger mode"); + ret = fts_ex_mode_switch(MODE_CHARGER, ENABLE); + if (ret >= 0) { + ts_data->charger_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->charger_mode) { + FTS_DEBUG("exit charger mode"); + ret = fts_ex_mode_switch(MODE_CHARGER, DISABLE); + if (ret >= 0) { + ts_data->charger_mode = DISABLE; + } + } + } + + FTS_DEBUG("charger mode:%d", ts_data->glove_mode); + return count; +} + + +/* read and write charger mode + * read example: cat fts_glove_mode ---read glove mode + * write example:echo 1 > fts_glove_mode ---write glove mode to 01 + */ +static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR, + fts_glove_mode_show, fts_glove_mode_store); + +static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR, + fts_cover_mode_show, fts_cover_mode_store); + +static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR, + fts_charger_mode_show, fts_charger_mode_store); + +static struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.attr, + NULL, +}; + +static struct attribute_group fts_touch_mode_group = { + .attrs = fts_touch_mode_attrs, +}; + +int fts_ex_mode_recovery(struct fts_ts_data *ts_data) +{ + if (ts_data->glove_mode) { + fts_ex_mode_switch(MODE_GLOVE, ENABLE); + } + + if (ts_data->cover_mode) { + fts_ex_mode_switch(MODE_COVER, ENABLE); + } + + if (ts_data->charger_mode) { + fts_ex_mode_switch(MODE_CHARGER, ENABLE); + } + + return 0; +} + +int fts_ex_mode_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + ts_data->glove_mode = DISABLE; + ts_data->cover_mode = DISABLE; + ts_data->charger_mode = DISABLE; + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group); + if (ret < 0) { + FTS_ERROR("create sysfs(ex_mode) fail"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group); + return ret; + } else { + FTS_DEBUG("create sysfs(ex_mode) successfully"); + } + + return 0; +} + +int fts_ex_mode_exit(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c new file mode 100644 index 000000000000..3f4048af8900 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c @@ -0,0 +1,1269 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_flash.c +* +* Author: Focaltech Driver Team +* +* Created: 2017-12-06 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_FW_REQUEST_SUPPORT 0 +/* Example: focaltech_ts_fw_tianma.bin */ +#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_" +#define FTS_READ_BOOT_ID_TIMEOUT 3 +#define FTS_FLASH_PACKET_LENGTH_SPI_LOW (4 * 1024 - 4) +#define FTS_FLASH_PACKET_LENGTH_SPI (32 * 1024 - 16) + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[] = { +#include FTS_UPGRADE_FW_FILE +}; + +u8 fw_file2[] = { +#include FTS_UPGRADE_FW2_FILE +}; + +u8 fw_file3[] = { +#include FTS_UPGRADE_FW3_FILE +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +struct upgrade_setting_nf upgrade_setting_list[] = { + {0x87, 0x19, 0, (64 * 1024), (128 * 1024), 0x00, 0x02, 8, 1, 1, 1, 0, 0}, + {0x86, 0x22, 0, (64 * 1024), (128 * 1024), 0x00, 0x02, 8, 1, 1, 0, 0, 0}, + {0x87, 0x56, 0, (88 * 1024), 32766, 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x80, 0x09, 0, (88 * 1024), 32766, 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x86, 0x32, 0, (64 * 1024), (128 * 1024), 0xA5, 0x01, 12, 0, 1, 0, 0, 0}, + {0x86, 0x42, 0, (64 * 1024), (128 * 1024), 0xA5, 0x01, 12, 0, 1, 0, 0, 0}, + {0x87, 0x20, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x87, 0x22, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x82, 0x01, 0, (96 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0}, + {0xF0, 0xC6, 0, (84 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x56, 0x62, 0, (128 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 4, 0, 0, 5}, + {0x82, 0x05, 0, (120 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0}, +}; + +struct fts_upgrade *fwupgrade; + +static int fts_check_bootid(void) +{ + int ret = 0; + u8 cmd = 0; + u8 id[2] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + struct ft_chip_t *chip_id; + + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + chip_id = &upg->ts_data->ic_info.ids; + + cmd = FTS_CMD_READ_ID; + ret = fts_read(&cmd, 1, id, 2); + if (ret < 0) { + FTS_ERROR("read boot id(0x%02x 0x%02x) fail", id[0], id[1]); + return ret; + } + + FTS_INFO("read boot id:0x%02x 0x%02x", id[0], id[1]); + if ((chip_id->rom_idh == id[0]) && (chip_id->rom_idl == id[1])) { + return 0; + } + + return -EIO; +} + +static int fts_fwupg_hardware_reset_to_boot(void) +{ + fts_reset_proc(0); + return 0; +} + +static int fts_enter_into_boot(void) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 cmd[2] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + FTS_INFO("enter into boot environment"); + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + /* hardware tp reset to boot */ + fts_fwupg_hardware_reset_to_boot(); + mdelay(upg->setting_nf->delay_init + i); + + /* enter into boot & check boot id*/ + for (j = 0; j < FTS_READ_BOOT_ID_TIMEOUT; j++) { + cmd[0] = FTS_CMD_START1; + ret = fts_write(cmd, 1); + if (ret >= 0) { + mdelay(upg->setting_nf->delay_init); + ret = fts_check_bootid(); + if (0 == ret) { + FTS_INFO("boot id check pass, retry=%d", i); + return 0; + } + } + } + } + + return -EIO; +} + +static bool fts_check_fast_download(void) +{ + int ret = 0; + u8 cmd[6] = {0xF2, 0x00, 0x78, 0x0A, 0x00, 0x02}; + u8 value = 0; + u8 value2[2] = { 0 }; + + ret = fts_read_reg(0xdb, &value); + if (ret < 0) { + FTS_ERROR("read 0xdb fail"); + goto read_err; + } + + ret = fts_read(cmd, 6, value2, 2); + if (ret < 0) { + FTS_ERROR("read f2 fail"); + goto read_err; + } + + FTS_INFO("0xdb = 0x%x, 0xF2 = 0x%x", value, value2[0]); + if ((value >= 0x18) && (value2[0] == 0x55)) { + FTS_INFO("IC support fast-download"); + return true; + } + +read_err: + FTS_INFO("IC not support fast-download"); + return false; +} + +static int fts_dpram_write_pe(u32 saddr, const u8 *buf, u32 len, bool wpram) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd = NULL; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + bool fd_support = true; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("dpram write"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (!buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > upg->setting_nf->app2_offset)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + if (upg->setting_nf->fd_check) { + fd_support = fts_check_fast_download(); + if (!fd_support) + packet_size = FTS_FLASH_PACKET_LENGTH_SPI_LOW; + } + + cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN + 1); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN + 1); + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + cmd[4] = BYTE_OFF_8(packet_len); + cmd[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + cmd[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + } + + ret = fts_write(&cmd[0], FTS_CMD_WRITE_LEN + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + + if (!fd_support) + mdelay(3); + } + +write_pram_err: + if (cmd) { + vfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_dpram_write(u32 saddr, const u8 *buf, u32 len, bool wpram) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd = NULL; + u32 addr = 0; + u32 baseaddr = wpram ? FTS_PRAM_SADDR : FTS_DRAM_SADDR; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("dpram write"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (!buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > upg->setting_nf->app2_offset)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN + 1); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN + 1); + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset + baseaddr; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + /* set pram address */ + cmd[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + ret = fts_write(&cmd[0], FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN); + if (ret < 0) { + FTS_ERROR("set pram(%d) addr(%d) fail", i, addr); + goto write_pram_err; + } + + /* write pram data */ + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (j = 0; j < packet_len; j++) { + cmd[1 + j] = buf[offset + j]; + } + ret = fts_write(&cmd[0], 1 + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + } + +write_pram_err: + if (cmd) { + vfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_ecc_cal_tp(u32 ecc_saddr, u32 ecc_len, u16 *ecc_value) +{ + int ret = 0; + int i = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + u8 value[2] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("ecc calc in tp"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(ecc_saddr); + cmd[2] = BYTE_OFF_8(ecc_saddr); + cmd[3] = BYTE_OFF_0(ecc_saddr); + cmd[4] = BYTE_OFF_16(ecc_len); + cmd[5] = BYTE_OFF_8(ecc_len); + cmd[6] = BYTE_OFF_0(ecc_len); + + /* make boot to calculate ecc in pram */ + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd fail"); + return ret; + } + mdelay(2); + + /* wait boot calculate ecc finish */ + if (upg->setting_nf->ecc_delay) { + mdelay(upg->setting_nf->ecc_delay); + } else { + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + ret = fts_read(cmd, 1, value, 1); + if (ret < 0) { + FTS_ERROR("ecc finish cmd fail"); + return ret; + } + if (upg->setting_nf->eccok_val == value[0]) + break; + mdelay(1); + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish timeout,ecc_finish=%x", value[0]); + return -EIO; + } + } + + /* get ecc value calculate in boot */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, value, 2); + if (ret < 0) { + FTS_ERROR("ecc read cmd fail"); + return ret; + } + + *ecc_value = ((u16)(value[0] << 8) + value[1]) & 0x0000FFFF; + return 0; +} + +static int fts_ecc_cal_host(const u8 *data, u32 data_len, u16 *ecc_value) +{ + u16 ecc = 0; + u32 i = 0; + u32 j = 0; + u16 al2_fcs_coef = AL2_FCS_COEF; + + for (i = 0; i < data_len; i += 2 ) { + ecc ^= ((data[i] << 8) | (data[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ al2_fcs_coef); + else + ecc >>= 1; + } + } + + *ecc_value = ecc & 0x0000FFFF; + return 0; +} + +static int fts_ecc_check(const u8 *buf, u32 len, u32 ecc_saddr) +{ + int ret = 0; + int i = 0; + u16 ecc_in_host = 0; + u16 ecc_in_tp = 0; + int packet_length = 0; + int packet_number = 0; + int packet_remainder = 0; + int offset = 0; + u32 packet_size = FTS_MAX_LEN_FILE; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("ecc check"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->setting_nf->ecclen_max) { + packet_size = upg->setting_nf->ecclen_max; + } + + packet_number = len / packet_size; + packet_remainder = len % packet_size; + if (packet_remainder) + packet_number++; + packet_length = packet_size; + + for (i = 0; i < packet_number; i++) { + /* last packet */ + if ((i == (packet_number - 1)) && packet_remainder) + packet_length = packet_remainder; + + ret = fts_ecc_cal_host(buf + offset, packet_length, &ecc_in_host); + if (ret < 0) { + FTS_ERROR("ecc in host calc fail"); + return ret; + } + + ret = fts_ecc_cal_tp(ecc_saddr + offset, packet_length, &ecc_in_tp); + if (ret < 0) { + FTS_ERROR("ecc in tp calc fail"); + return ret; + } + + FTS_DEBUG("ecc in tp:%04x,host:%04x,i:%d", ecc_in_tp, ecc_in_host, i); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc_in_tp(%x) != ecc_in_host(%x), ecc check fail", + ecc_in_tp, ecc_in_host); + return -EIO; + } + + offset += packet_length; + } + + return 0; +} + +static int fts_pram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 pram_app_size = 0; + u16 code_len = 0; + u16 code_len_n = 0; + u32 pram_start_addr = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("begin to write pram app(bin len:%d)", len); + if (!upg || !upg->setting_nf) { + FTS_ERROR("upgrade/setting_nf is null"); + return -EINVAL; + } + + /* get pram app length */ + code_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1]; + code_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 2] << 8) + + buf[FTS_APP_INFO_OFFSET + 3]; + if ((code_len + code_len_n) != 0xFFFF) { + FTS_ERROR("pram code len(%x %x) fail", code_len, code_len_n); + return -EINVAL; + } + + pram_app_size = ((u32)code_len) * upg->setting_nf->length_coefficient; + FTS_INFO("pram app length in fact:%d", pram_app_size); + + /* write pram */ + if (upg->setting_nf->spi_pe) + ret = fts_dpram_write_pe(pram_start_addr, buf, pram_app_size, true); + else + ret = fts_dpram_write(pram_start_addr, buf, pram_app_size, true); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(buf, pram_app_size, pram_start_addr); + if (ret < 0) { + FTS_ERROR("pram ecc check fail"); + return ret; + } + + FTS_INFO("pram app write successfully"); + return 0; +} + +static int fts_dram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 dram_size = 0; + u32 pram_app_size = 0; + u32 dram_start_addr = 0; + u16 const_len = 0; + u16 const_len_n = 0; + const u8 *dram_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("begin to write dram data(bin len:%d)", len); + if (!upg || !upg->setting_nf) { + FTS_ERROR("upgrade/setting_nf is null"); + return -EINVAL; + } + + /* get dram data length */ + const_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0x8] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x9]; + const_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 0x0A] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x0B]; + if (((const_len + const_len_n) != 0xFFFF) || (const_len == 0)) { + FTS_INFO("no support dram,const len(%x %x)", const_len, const_len_n); + return 0; + } + + dram_size = ((u32)const_len) * upg->setting_nf->length_coefficient; + pram_app_size = ((u32)(((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1])); + pram_app_size = pram_app_size * upg->setting_nf->length_coefficient; + + dram_buf = buf + pram_app_size; + FTS_INFO("dram buf length in fact:%d,offset:%d", dram_size, pram_app_size); + /* write pram */ + ret = fts_dpram_write(dram_start_addr, dram_buf, dram_size, false); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(dram_buf, dram_size, dram_start_addr); + if (ret < 0) { + FTS_ERROR("dram ecc check fail"); + return ret; + } + + FTS_INFO("dram data write successfully"); + return 0; +} + +static int fts_pram_start(void) +{ + int ret = 0; + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + + FTS_INFO("remap to start pram"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + + msleep(10); + return 0; +} + +/* + * description: download fw to IC and run + * + * param - buf: const, fw data buffer + * len: length of fw + * + * return 0 if success, otherwise return error code + */ +static int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("begin to write and start fw(bin len:%d)", len); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + upg->ts_data->fw_is_running = false; + + if (need_reset) { + /* enter into boot environment */ + ret = fts_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into boot environment fail"); + return ret; + } + } + + /* write pram */ + ret = fts_pram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + if (upg->setting_nf->drwr_support) { + /* write dram */ + ret = fts_dram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + } + + /* remap pram and run fw */ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + upg->ts_data->fw_is_running = true; + FTS_INFO("fw download successfully"); + return 0; +} + +static int fts_fw_download(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + int i = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade download function"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (!buf || (len < FTS_MIN_LEN)) { + FTS_ERROR("fw/len(%d) is invalid", len); + return -EINVAL; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); + fts_esdcheck_switch(upg->ts_data, DISABLE); + + for (i = 0; i < 3; i++) { + FTS_INFO("fw download times:%d", i + 1); + ret = fts_fw_write_start(buf, len, need_reset); + if (0 == ret) + break; + } + if (i >= 3) { + FTS_ERROR("fw download fail"); + ret = -EIO; + goto err_fw_download; + } + + fts_esdcheck_switch(upg->ts_data, ENABLE); + ret = 0; +err_fw_download: + fts_irq_enable(); + upg->ts_data->fw_loading = 0; + + return ret; +} + + +static int fts_read_file_default(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:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +static int fts_read_file_request_firmware(char *file_name, u8 **file_buf) +{ + int ret = 0; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + +#if !FTS_FW_REQUEST_SUPPORT + return -EINVAL; +#endif + + snprintf(fwname, FILE_NAME_LENGTH, "%s", file_name); + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (0 == ret) { + FTS_INFO("firmware(%s) request successfully", fwname); + *file_buf = vmalloc(fw->size); + if (NULL == *file_buf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(*file_buf, fw->data, fw->size); + ret = fw->size; + } + } else { + FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret); + ret = -EIO; + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + + ret = fts_read_file_request_firmware(file_name, file_buf); + if (ret < 0) { + ret = fts_read_file_default(file_name, file_buf); + if (ret < 0) { + FTS_ERROR("get fw file(default) fail"); + return ret; + } + } + + return ret; +} + +int fts_upgrade_bin(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 (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN)) { + FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + ret = fts_fw_download(fw_file_buf, fw_file_len, true); + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + 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; +} + + +int fts_enter_test_environment(bool test_state) +{ + int ret = 0; + int i = 0; + u8 detach_flag = 0; + u32 app_offset = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw test download function"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + if (!upg->fw || (upg->fw_length <= upg->setting_nf->app2_offset)) { + FTS_INFO("not multi-app"); + return 0; + } + + if (test_state) { + app_offset = upg->setting_nf->app2_offset; + } + + /*download firmware*/ + upg->ts_data->fw_loading = 1; + for (i = 0; i < 3; i++) { + FTS_INFO("fw download times:%d", i + 1); + ret = fts_fw_write_start(upg->fw + app_offset, upg->fw_length, true); + if (0 == ret) + break; + } + upg->ts_data->fw_loading = 0; + + if (i >= 3) { + FTS_ERROR("fw(addr:%x) download fail", app_offset); + return -EIO; + } + + msleep(50); + ret = fts_read_reg(FTS_REG_FACTORY_MODE_DETACH_FLAG, &detach_flag); + FTS_INFO("regb4:0x%02x", detach_flag); + + return 0; +} + +int fts_fw_resume(bool need_reset) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + bool get_fw_i_flag = true; + const u8 *fw_buf = NULL; + u32 fwlen = 0; + + FTS_INFO("fw upgrade resume function"); + if (!upg || !upg->fw) { + FTS_ERROR("upg/fw is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + if (FTS_FW_REQUEST_SUPPORT) { + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \ + FTS_FW_NAME_PREX_WITH_REQUEST, upg->module_info->vendor_name); + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (ret == 0) { + FTS_INFO("firmware(%s) request successfully", fwname); + fw_buf = fw->data; + fwlen = fw->size; + get_fw_i_flag = false; + } else { + FTS_ERROR("%s:firmware(%s) request fail,ret=%d\n", + __func__, fwname, ret); + } + } + + if (get_fw_i_flag) { + FTS_INFO("download fw from bootimage"); + fw_buf = upg->fw; + fwlen = upg->fw_length; + } + + ret = fts_fw_download(fw_buf, fwlen, need_reset); + if (ret < 0) { + FTS_ERROR("fw resume download failed"); + } + + if (FTS_FW_REQUEST_SUPPORT) { + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + } + + return ret; +} + +int fts_fw_recovery(void) +{ + int ret = 0; + u8 boot_state = 0; + u8 chip_id = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("check if boot recovery"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upg/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + upg->ts_data->fw_is_running = false; + ret = fts_check_bootid(); + if (ret < 0) { + FTS_ERROR("check boot id fail"); + upg->ts_data->fw_is_running = true; + return ret; + } + + ret = fts_read_reg(0xD0, &boot_state); + if (ret < 0) { + FTS_ERROR("read boot state failed, ret=%d", ret); + upg->ts_data->fw_is_running = true; + return ret; + } + + if (boot_state != upg->setting_nf->upgsts_boot) { + FTS_INFO("not in boot mode(0x%x),exit", boot_state); + upg->ts_data->fw_is_running = true; + return -EIO; + } + + FTS_INFO("abnormal situation,need download fw"); + ret = fts_fw_resume(false); + if (ret < 0) { + FTS_ERROR("fts_fw_resume fail"); + return ret; + } + + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id); + FTS_INFO("read chip id:0x%02x", chip_id); + + fts_tp_state_recovery(upg->ts_data); + + FTS_INFO("boot recovery pass"); + return ret; +} + +static int fts_fwupg_get_module_info(struct fts_upgrade *upg) +{ + int i = 0; + struct upgrade_module *info = &module_list[0]; + + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + if (FTS_GET_MODULE_NUM > 1) { + FTS_INFO("module id:%04x", upg->module_id); + for (i = 0; i < FTS_GET_MODULE_NUM; i++) { + info = &module_list[i]; + if (upg->module_id == info->id) { + FTS_INFO("module id match, get fw file successfully"); + break; + } + } + if (i >= FTS_GET_MODULE_NUM) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg) +{ + int ret = 0; + const struct firmware *fw = NULL; + u8 *tmpbuf = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \ + FTS_FW_NAME_PREX_WITH_REQUEST, \ + upg->module_info->vendor_name); + + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (0 == ret) { + FTS_INFO("firmware(%s) request successfully", fwname); + tmpbuf = vmalloc(fw->size); + if (NULL == tmpbuf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(tmpbuf, fw->data, fw->size); + upg->fw = tmpbuf; + upg->fw_length = fw->size; + upg->fw_from_request = 1; + } + } else { + FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret); + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int fts_get_fw_file_via_i(struct fts_upgrade *upg) +{ + upg->fw = upg->module_info->fw_file; + upg->fw_length = upg->module_info->fw_len; + upg->fw_from_request = 0; + + return 0; +} + +/***************************************************************************** + * Name: fts_fwupg_get_fw_file + * Brief: get fw image/file, + * If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_- + * MODULE_ID/FTS_MODULE_NAME; + * If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F- + * TS_MODULE_ID; will use module id to distingwish different modules; + * If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT + * =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt- + * ech_ts_fw_" & FTS_VENDOR_NAME; + * + * If have flash, module_id=vendor_id, If non-flash,module_id need + * transfer from LCD driver(gpio or lcm_id or ...); + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + *****************************************************************************/ +static int fts_fwupg_get_fw_file(struct fts_upgrade *upg) +{ + int ret = 0; + bool get_fw_i_flag = false; + + FTS_DEBUG("get upgrade fw file"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + ret = fts_fwupg_get_module_info(upg); + if ((ret < 0) || (!upg->module_info)) { + FTS_ERROR("get module info fail"); + return ret; + } + + if (FTS_FW_REQUEST_SUPPORT) { + msleep(500); + ret = fts_get_fw_file_via_request_firmware(upg); + if (ret != 0) { + get_fw_i_flag = true; + } + } else { + get_fw_i_flag = true; + } + + if (get_fw_i_flag) { + ret = fts_get_fw_file_via_i(upg); + } + + FTS_INFO("upgrade fw file len:%d", upg->fw_length); + if (upg->fw_length < FTS_MIN_LEN) { + FTS_ERROR("fw file len(%d) fail", upg->fw_length); + return -ENODATA; + } + + return ret; +} + +static void fts_fwupg_work(struct work_struct *work) +{ + int ret = 0; + u8 chip_id = 0; + struct fts_upgrade *upg = fwupgrade; + +#if !FTS_AUTO_UPGRADE_EN + FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on"); + return ; +#endif + + FTS_INFO("fw upgrade work function"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + /* get fw */ + ret = fts_fwupg_get_fw_file(upg); + if (ret < 0) { + FTS_ERROR("get file fail, can't upgrade"); + return ; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return ; + } + + ret = fts_fw_download(upg->fw, upg->fw_length, true); + if (ret < 0) { + FTS_ERROR("fw auto download failed"); + } else { + msleep(50); + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id); + FTS_INFO("read chip id:0x%02x", chip_id); + } +} + +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + int i = 0; + struct upgrade_setting_nf *setting = &upgrade_setting_list[0]; + int setting_count = + sizeof(upgrade_setting_list) / sizeof(upgrade_setting_list[0]); + + FTS_INFO("fw upgrade init function"); + if (!ts_data || !ts_data->ts_workqueue) { + FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function"); + return -EINVAL; + } + + if (0 == setting_count) { + FTS_ERROR("no upgrade settings in tp driver, init fail"); + 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 == setting_count) { + fwupgrade->setting_nf = setting; + } else { + for (i = 0; i < setting_count; i++) { + setting = &upgrade_setting_list[i]; + if ((setting->rom_idh == ts_data->ic_info.ids.rom_idh) + && (setting->rom_idl == ts_data->ic_info.ids.rom_idl)) { + FTS_INFO("match upgrade setting,type(ID):0x%02x%02x", + setting->rom_idh, setting->rom_idl); + fwupgrade->setting_nf = setting; + } + } + } + + if (NULL == fwupgrade->setting_nf) { + FTS_ERROR("no upgrade settings match, can't upgrade"); + kfree(fwupgrade); + fwupgrade = NULL; + return -ENODATA; + } + + fts_esdcheck_switch(ts_data, DISABLE); + fwupgrade->ts_data = ts_data; + INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work); + queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work); + + return 0; +} + +int fts_fwupg_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + cancel_work_sync(&ts_data->fwupg_work); + + if (fwupgrade) { + if (fwupgrade->fw_from_request) { + vfree(fwupgrade->fw); + fwupgrade->fw = NULL; + } + + kfree(fwupgrade); + fwupgrade = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h new file mode 100644 index 000000000000..547f0bbca523 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h @@ -0,0 +1,214 @@ +/************************************************************************ +* Copyright (c) 2012-2020, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_flash.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-07 +* +* Abstract: +* +************************************************************************/ +#ifndef __LINUX_FOCALTECH_FLASH_H__ +#define __LINUX_FOCALTECH_FLASH_H__ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_CMD_RESET 0x07 +#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR 0xAD +#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN 4 +#define FTS_ROMBOOT_CMD_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 100 +#define FTS_ROMBOOT_CMD_ECC 0xCC +#define FTS_PRAM_SADDR 0x000000 +#define FTS_DRAM_SADDR 0xD00000 + +#define FTS_CMD_READ_FWCFG 0xA8 + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#define FTS_CMD_READ_LEN_SPI 6 +#define FTS_CMD_FLASH_TYPE 0x05 +#define FTS_CMD_FLASH_MODE 0x09 +#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A +#define FLASH_MODE_UPGRADE_VALUE 0x0B +#define FLASH_MODE_LIC_VALUE 0x0C +#define FLASH_MODE_PARAM_VALUE 0x0D +#define FTS_CMD_ERASE_APP 0x61 +#define FTS_REASE_APP_DELAY 1350 +#define FTS_ERASE_SECTOR_DELAY 60 +#define FTS_RETRIES_REASE 50 +#define FTS_RETRIES_DELAY_REASE 400 +#define FTS_CMD_FLASH_STATUS 0x6A +#define FTS_CMD_FLASH_STATUS_LEN 2 +#define FTS_CMD_FLASH_STATUS_NOP 0x0000 +#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055 +#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA +#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000 +#define FTS_CMD_ECC_INIT 0x64 +#define FTS_CMD_ECC_CAL 0x65 +#define FTS_CMD_ECC_CAL_LEN 7 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#define FTS_CMD_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 4 +#define FTS_CMD_WRITE 0xBF +#define FTS_RETRIES_WRITE 100 +#define FTS_RETRIES_DELAY_WRITE 1 +#define FTS_CMD_WRITE_LEN 6 +#define FTS_DELAY_READ_ID 20 +#define FTS_DELAY_UPGRADE_RESET 80 +#define PRAMBOOT_MIN_SIZE 0x120 +#define PRAMBOOT_MAX_SIZE (64*1024) +#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */ +#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */ +#define FTS_MIN_LEN 0x120 +#define FTS_MAX_LEN_FILE (256 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_SECTOR (4 * 1024) +#define FTS_CONIFG_VENDORID_OFF 0x04 +#define FTS_CONIFG_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_REG_UPGRADE2 0xBC +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_UPGRADE_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/sdcard/" +#define FTS_FW_IDE_SIG "IDE_" +#define FTS_FW_IDE_SIG_LEN 4 +#define MAX_MODULE_VENDOR_NAME_LEN 16 + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ECC_FINISH_TIMEOUT 100 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5 0xA5 +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00 0x00 +#define FTS_ROMBOOT_CMD_ECC_READ 0xCD +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +#define FTS_APP_INFO_OFFSET 0x100 + +enum FW_STATUS { + FTS_RUN_IN_ERROR, + FTS_RUN_IN_APP, + FTS_RUN_IN_ROM, + FTS_RUN_IN_PRAM, + FTS_RUN_IN_BOOTLOADER, +}; + +enum FW_FLASH_MODE { + FLASH_MODE_APP, + FLASH_MODE_LIC, + FLASH_MODE_PARAM, + FLASH_MODE_ALL, +}; + +enum ECC_CHECK_MODE { + ECC_CHECK_MODE_XOR, + ECC_CHECK_MODE_CRC16, +}; + +enum UPGRADE_SPEC { + UPGRADE_SPEC_V_1_0 = 0x0100, + UPGRADE_SPEC_V_1_1 = 0x0101, + UPGRADE_SPEC_V_1_2 = 0x0102, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u16 ctype[FTS_MAX_COMPATIBLE_TYPE]; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + int pram_ecc_check_mode; + int fw_ecc_check_mode; + int upgspec_version; + bool new_return_value_from_ic; + bool appoff_handle_in_ic; + bool is_reset_register_BC; + bool read_boot_id_need_reset; + bool hid_supported; + bool pramboot_supported; + u8 *pramboot; + u32 pb_length; + int (*init)(u8 *, u32); + int (*write_pramboot_private)(void); + int (*upgrade)(u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(u8 *, u32); + int (*param_upgrade)(u8 *, u32); + int (*force_upgrade)(u8 *, u32); +}; + +struct upgrade_setting_nf { + u8 rom_idh; + u8 rom_idl; + u16 reserved; + u32 app2_offset; + u32 ecclen_max; + u8 eccok_val; + u8 upgsts_boot; + u8 delay_init; + u8 spi_pe; + u8 length_coefficient; + u8 fd_check; + u8 drwr_support; + u8 ecc_delay; +}; + +struct upgrade_module { + int id; + char vendor_name[MAX_MODULE_VENDOR_NAME_LEN]; + u8 *fw_file; + u32 fw_len; +}; + +struct fts_upgrade { + struct fts_ts_data *ts_data; + struct upgrade_module *module_info; + struct upgrade_func *func; + struct upgrade_setting_nf *setting_nf; + int module_id; + bool fw_from_request; + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#endif diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c new file mode 100644 index 000000000000..43cbcb8ee0ec --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c @@ -0,0 +1,504 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_gestrue.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/****************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define KEY_GESTURE_U KEY_U +#define KEY_GESTURE_UP KEY_UP +#define KEY_GESTURE_DOWN KEY_DOWN +#define KEY_GESTURE_LEFT KEY_LEFT +#define KEY_GESTURE_RIGHT KEY_RIGHT +#define KEY_GESTURE_O KEY_O +#define KEY_GESTURE_E KEY_E +#define KEY_GESTURE_M KEY_M +#define KEY_GESTURE_L KEY_L +#define KEY_GESTURE_W KEY_W +#define KEY_GESTURE_S KEY_S +#define KEY_GESTURE_V KEY_V +#define KEY_GESTURE_C KEY_C +#define KEY_GESTURE_Z KEY_Z + +#define GESTURE_LEFT 0x20 +#define GESTURE_RIGHT 0x21 +#define GESTURE_UP 0x22 +#define GESTURE_DOWN 0x23 +#define GESTURE_DOUBLECLICK 0x24 +#define GESTURE_O 0x30 +#define GESTURE_W 0x31 +#define GESTURE_M 0x32 +#define GESTURE_E 0x33 +#define GESTURE_L 0x44 +#define GESTURE_S 0x46 +#define GESTURE_V 0x54 +#define GESTURE_Z 0x41 +#define GESTURE_C 0x34 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* +* gesture_id - mean which gesture is recognised +* point_num - points number of this gesture +* coordinate_x - All gesture point x coordinate +* coordinate_y - All gesture point y coordinate +* mode - gesture enable/disable, need enable by host +* - 1:enable gesture function(default) 0:disable +* active - gesture work flag, +* always set 1 when suspend, set 0 when resume +*/ +struct fts_gesture_st { + u8 gesture_id; + u8 point_num; + u16 coordinate_x[FTS_GESTURE_POINTS_MAX]; + u16 coordinate_y[FTS_GESTURE_POINTS_MAX]; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_gesture_st fts_gesture_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static ssize_t fts_gesture_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n", + ts_data->gesture_support ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val); + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable gesture"); + ts_data->gesture_support = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable gesture"); + ts_data->gesture_support = DISABLE; + } + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_buf_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + int i = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + struct fts_gesture_st *gesture = &fts_gesture_data; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n", gesture->gesture_id); + count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n", + gesture->point_num); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n"); + + /* save point data,max:6 */ + for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i, + gesture->coordinate_x[i], gesture->coordinate_y[i]); + if ((i + 1) % 4 == 0) + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_buf_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t fts_gesture_bm_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "gesture bmode:%d\n", + ts_data->gesture_bmode); + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_bm_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + int value = 0xFF; + int ret = 0; + + mutex_lock(&ts_data->input_dev->mutex); + ret = sscanf(buf, "%d", &value); + if (ret == 1) { + FTS_DEBUG("gesture bmode:%d->%d", ts_data->gesture_bmode, value); + ts_data->gesture_bmode = value; + } + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +/* sysfs gesture node + * read example: cat fts_gesture_mode ---read gesture mode + * write example:echo 1 > fts_gesture_mode --- write gesture mode to 1 + * + */ +static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show, + fts_gesture_store); +/* + * read example: cat fts_gesture_buf --- read gesture buf + */ +static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR, + fts_gesture_buf_show, fts_gesture_buf_store); + +static DEVICE_ATTR(fts_gesture_bm, S_IRUGO | S_IWUSR, + fts_gesture_bm_show, fts_gesture_bm_store); + +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + &dev_attr_fts_gesture_bm.attr, + NULL, +}; + +static struct attribute_group fts_gesture_group = { + .attrs = fts_gesture_mode_attrs, +}; + +static int fts_create_gesture_sysfs(struct device *dev) +{ + int ret = 0; + + ret = sysfs_create_group(&dev->kobj, &fts_gesture_group); + if (ret) { + FTS_ERROR("gesture sys node create fail"); + sysfs_remove_group(&dev->kobj, &fts_gesture_group); + return ret; + } + + return 0; +} + +static void fts_gesture_report(struct input_dev *input_dev, int gesture_id) +{ + int gesture; + + FTS_DEBUG("gesture_id:0x%x", gesture_id); + switch (gesture_id) { + case GESTURE_LEFT: + gesture = KEY_GESTURE_LEFT; + break; + case GESTURE_RIGHT: + gesture = KEY_GESTURE_RIGHT; + break; + case GESTURE_UP: + gesture = KEY_GESTURE_UP; + break; + case GESTURE_DOWN: + gesture = KEY_GESTURE_DOWN; + break; + case GESTURE_DOUBLECLICK: + gesture = KEY_GESTURE_U; + break; + case GESTURE_O: + gesture = KEY_GESTURE_O; + break; + case GESTURE_W: + gesture = KEY_GESTURE_W; + break; + case GESTURE_M: + gesture = KEY_GESTURE_M; + break; + case GESTURE_E: + gesture = KEY_GESTURE_E; + break; + case GESTURE_L: + gesture = KEY_GESTURE_L; + break; + case GESTURE_S: + gesture = KEY_GESTURE_S; + break; + case GESTURE_V: + gesture = KEY_GESTURE_V; + break; + case GESTURE_Z: + gesture = KEY_GESTURE_Z; + break; + case GESTURE_C: + gesture = KEY_GESTURE_C; + break; + default: + gesture = -1; + break; + } + /* report event key */ + if (gesture != -1) { + FTS_DEBUG("Gesture Code=%d", gesture); + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); + } +} + +/***************************************************************************** +* Name: fts_gesture_readdata +* Brief: Read information about gesture: enable flag/gesture points..., if ges- +* ture enable, save gesture points' information, and report to OS. +* It will be called this function every intrrupt when FTS_GESTURE_EN = 1 +* +* gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4 +* Input: ts_data - global struct data +* data - gesture data buffer +* Output: +* Return: 0 - read gesture data successfully, the report data is gesture data +* 1 - tp not in suspend/gesture not enable in TP FW +* -Exx - error +*****************************************************************************/ +int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *touch_buf) +{ + int ret = 0; + int i = 0; + int index = 0; + u8 buf[FTS_GESTURE_DATA_LEN] = { 0 }; + struct input_dev *input_dev = ts_data->input_dev; + struct fts_gesture_st *gesture = &fts_gesture_data; + + if (!ts_data->gesture_support) { + FTS_ERROR("gesture no support"); + return -EINVAL; + } + + if (ts_data->gesture_bmode == GESTURE_BM_TOUCH) { + memcpy(buf, touch_buf + FTS_TOUCH_DATA_LEN, FTS_GESTURE_DATA_LEN); + } else { + buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2); + if (ret < 0) { + FTS_ERROR("read gesture header data fail"); + return ret; + } + } + + /* init variable before read gesture point */ + memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16)); + memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16)); + gesture->gesture_id = buf[2]; + gesture->point_num = buf[3]; + FTS_DEBUG("gesture_id=%d, point_num=%d", + gesture->gesture_id, gesture->point_num); + + /* save point data,max:6 */ + for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) { + index = 4 * i + 4; + gesture->coordinate_x[i] = (u16)(((buf[0 + index] & 0x0F) << 8) + + buf[1 + index]); + gesture->coordinate_y[i] = (u16)(((buf[2 + index] & 0x0F) << 8) + + buf[3 + index]); + } + + /* report gesture to OS */ + fts_gesture_report(input_dev, gesture->gesture_id); + return 0; +} + +void fts_gesture_recovery(struct fts_ts_data *ts_data) +{ + if (ts_data->gesture_support && ts_data->suspended) { + FTS_DEBUG("gesture recovery..."); + fts_write_reg(0xD1, 0xFF); + fts_write_reg(0xD2, 0xFF); + fts_write_reg(0xD5, 0xFF); + fts_write_reg(0xD6, 0xFF); + fts_write_reg(0xD7, 0xFF); + fts_write_reg(0xD8, 0xFF); + fts_write_reg(FTS_REG_GESTURE_EN, ENABLE); + } +} + +int fts_gesture_suspend(struct fts_ts_data *ts_data) +{ + int i = 0; + u8 state = 0xFF; + + FTS_FUNC_ENTER(); + if (enable_irq_wake(ts_data->irq)) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + + for (i = 0; i < 5; i++) { + fts_write_reg(0xD1, 0xFF); + fts_write_reg(0xD2, 0xFF); + fts_write_reg(0xD5, 0xFF); + fts_write_reg(0xD6, 0xFF); + fts_write_reg(0xD7, 0xFF); + fts_write_reg(0xD8, 0xFF); + fts_write_reg(FTS_REG_GESTURE_EN, ENABLE); + msleep(1); + fts_read_reg(FTS_REG_GESTURE_EN, &state); + if (state == ENABLE) + break; + } + + if (i >= 5) + FTS_ERROR("make IC enter into gesture(suspend) fail,state:%x", state); + else + FTS_INFO("Enter into gesture(suspend) successfully"); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_resume(struct fts_ts_data *ts_data) +{ + int i = 0; + u8 state = 0xFF; + + FTS_FUNC_ENTER(); + if (disable_irq_wake(ts_data->irq)) { + FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq); + } + + for (i = 0; i < 5; i++) { + fts_write_reg(FTS_REG_GESTURE_EN, DISABLE); + msleep(1); + fts_read_reg(FTS_REG_GESTURE_EN, &state); + if (state == DISABLE) + break; + } + + if (i >= 5) + FTS_ERROR("make IC exit gesture(resume) fail,state:%x", state); + else + FTS_INFO("resume from gesture successfully"); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_init(struct fts_ts_data *ts_data) +{ + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C); + + __set_bit(KEY_GESTURE_RIGHT, input_dev->keybit); + __set_bit(KEY_GESTURE_LEFT, input_dev->keybit); + __set_bit(KEY_GESTURE_UP, input_dev->keybit); + __set_bit(KEY_GESTURE_DOWN, input_dev->keybit); + __set_bit(KEY_GESTURE_U, input_dev->keybit); + __set_bit(KEY_GESTURE_O, input_dev->keybit); + __set_bit(KEY_GESTURE_E, input_dev->keybit); + __set_bit(KEY_GESTURE_M, input_dev->keybit); + __set_bit(KEY_GESTURE_W, input_dev->keybit); + __set_bit(KEY_GESTURE_L, input_dev->keybit); + __set_bit(KEY_GESTURE_S, input_dev->keybit); + __set_bit(KEY_GESTURE_V, input_dev->keybit); + __set_bit(KEY_GESTURE_C, input_dev->keybit); + __set_bit(KEY_GESTURE_Z, input_dev->keybit); + + fts_create_gesture_sysfs(ts_data->dev); + + memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st)); + ts_data->gesture_bmode = GESTURE_BM_REG; + ts_data->gesture_support = FTS_GESTURE_EN; + + if ((ts_data->ic_info.ids.type <= 0x25) + || (ts_data->ic_info.ids.type == 0x87) + || (ts_data->ic_info.ids.type == 0x88)) { + FTS_INFO("ic type:0x%02x,GESTURE_BM_TOUCH", ts_data->ic_info.ids.type); + ts_data->touch_size += FTS_GESTURE_DATA_LEN; + ts_data->gesture_bmode = GESTURE_BM_TOUCH; + } + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group); + FTS_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c new file mode 100644 index 000000000000..8b117a8bb431 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c @@ -0,0 +1,185 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_point_report_check.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-11-16 +* +* Abstract: point report check function +* +* Version: v1.0 +* +* Revision History: +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */ +#define PRC_INTR_INTERVALS 100 /* unit:ms */ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +/***************************************************************************** +* Name: fts_prc_func +* Brief: fts point report check work func, report whole up of points +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_prc_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, prc_work.work); + unsigned long cur_jiffies = jiffies; + unsigned long intr_timeout = msecs_to_jiffies(PRC_INTR_INTERVALS); + + if (ts_data->prc_support && !ts_data->suspended) { + intr_timeout += ts_data->intr_jiffies; + if (time_after(cur_jiffies, intr_timeout)) { + if (ts_data->touch_points) { + fts_release_all_finger(); + if (ts_data->log_level >= 3) + FTS_DEBUG("prc trigger interval:%dms", + jiffies_to_msecs(cur_jiffies - ts_data->intr_jiffies)); + } + ts_data->prc_mode = 0; + } else { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } + } else { + ts_data->prc_mode = 0; + } +} + +/***************************************************************************** +* Name: fts_prc_queue_work +* Brief: fts point report check queue work, call it when interrupt comes +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_prc_queue_work(struct fts_ts_data *ts_data) +{ + if (ts_data->prc_support && !ts_data->prc_mode && !ts_data->suspended) { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } +} + + +static ssize_t fts_prc_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable prc"); + ts_data->prc_support = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable prc"); + cancel_delayed_work_sync(&ts_data->prc_work); + ts_data->prc_support = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_prc_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "PRC: %s\n", \ + ts_data->prc_support ? "Enable" : "Disable"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static DEVICE_ATTR(fts_prc, S_IRUGO | S_IWUSR, fts_prc_show, fts_prc_store); + +/***************************************************************************** +* Name: fts_point_report_check_init +* Brief: +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_point_report_check_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run point report check function"); + return -EINVAL; + } + + ret = sysfs_create_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr); + if ( ret < 0) { + FTS_ERROR("create prc sysfs fail"); + } + + ts_data->prc_support = FTS_POINT_REPORT_CHECK_EN; + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_point_report_check_exit +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_point_report_check_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + cancel_delayed_work_sync(&ts_data->prc_work); + sysfs_remove_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr); + FTS_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c new file mode 100644 index 000000000000..561ef562487b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c @@ -0,0 +1,452 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/************************************************************************ +* +* File Name: focaltech_spi.c +* +* Author: FocalTech Driver Team +* +* Created: 2019-03-21 +* +* Abstract: new spi protocol communication with TP +* +* Version: v1.0 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define SPI_RETRY_NUMBER 3 +#define CS_HIGH_DELAY 150 /* unit: us */ +#define SPI_BUF_LENGTH 4096 + +#define DATA_CRC_EN 0x20 +#define WRITE_CMD 0x00 +#define READ_CMD (0x80 | DATA_CRC_EN) + +#define SPI_DUMMY_BYTE 3 +#define SPI_HEADER_LENGTH 6 /*CRC*/ +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +/* spi interface */ +static int fts_spi_transfer(u8 *tx_buf, u8 *rx_buf, u32 len) +{ + int ret = 0; + struct spi_device *spi = fts_data->spi; + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = tx_buf, + .rx_buf = rx_buf, + .len = len, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + if (ret) { + FTS_ERROR("spi_sync fail,ret:%d", ret); + return ret; + } + + return ret; +} + +static void fts_spi_buf_show(u8 *data, int datalen) +{ + int i = 0; + int count = 0; + int size = 0; + char *tmpbuf = NULL; + + if (!data || (datalen <= 0)) { + FTS_ERROR("data/datalen is invalid"); + return; + } + + size = (datalen > 256) ? 256 : datalen; + tmpbuf = kzalloc(1024, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("tmpbuf zalloc fail"); + return; + } + + for (i = 0; i < size; i++) + count += snprintf(tmpbuf + count, 1024 - count, "%02X ", data[i]); + + FTS_DEBUG("%s", tmpbuf); + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +static void crckermit(u8 *data, u32 len, u16 *crc_out) +{ + u32 i = 0; + u32 j = 0; + u16 crc = 0xFFFF; + + for ( i = 0; i < len; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8408; + else + crc = (crc >> 1); + } + } + + *crc_out = crc; +} + +static int rdata_check(u8 *rdata, u32 rlen) +{ + u16 crc_calc = 0; + u16 crc_read = 0; + + crckermit(rdata, rlen - 2, &crc_calc); + crc_read = (u16)(rdata[rlen - 1] << 8) + rdata[rlen - 2]; + if (crc_calc != crc_read) { + fts_spi_buf_show(rdata, rlen); + return -EIO; + } + + return 0; +} + +int fts_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u32 txlen_need = writelen + SPI_HEADER_LENGTH + ts_data->dummy_byte; + u32 datalen = writelen - 1; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen_need > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + txbuf[txlen++] = writebuf[0]; + txbuf[txlen++] = WRITE_CMD; + txbuf[txlen++] = (datalen >> 8) & 0xFF; + txbuf[txlen++] = datalen & 0xFF; + if (datalen > 0) { + txlen = txlen + SPI_DUMMY_BYTE; + memcpy(&txbuf[txlen], &writebuf[1], datalen); + txlen = txlen + datalen; + } + + for (i = 0; i < SPI_RETRY_NUMBER; i++) { + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if ((0 == ret) && ((rxbuf[3] & 0xA0) == 0)) { + break; + } else { + FTS_DEBUG("data write(addr:%x),status:%x,retry:%d,ret:%d", + writebuf[0], rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + } + if (ret < 0) { + FTS_ERROR("data write(addr:%x) fail,status:%x,ret:%d", + writebuf[0], rxbuf[3], ret); + } + +err_write: + if (txlen_need > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write_reg(u8 addr, u8 value) +{ + u8 writebuf[2] = { 0 }; + + writebuf[0] = addr; + writebuf[1] = value; + return fts_write(writebuf, 2); +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u32 txlen_need = datalen + SPI_HEADER_LENGTH + ts_data->dummy_byte; + u8 ctrl = READ_CMD; + u32 dp = 0; + + if (!cmd || !cmdlen || !data || !datalen) { + FTS_ERROR("cmd/cmdlen/data/datalen is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen_need > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + txbuf[txlen++] = cmd[0]; + txbuf[txlen++] = ctrl; + txbuf[txlen++] = (datalen >> 8) & 0xFF; + txbuf[txlen++] = datalen & 0xFF; + dp = txlen + SPI_DUMMY_BYTE; + txlen = dp + datalen; + if (ctrl & DATA_CRC_EN) { + txlen = txlen + 2; + } + + for (i = 0; i < SPI_RETRY_NUMBER; i++) { + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if ((0 == ret) && ((rxbuf[3] & 0xA0) == 0)) { + memcpy(data, &rxbuf[dp], datalen); + /* crc check */ + if (ctrl & DATA_CRC_EN) { + ret = rdata_check(&rxbuf[dp], txlen - dp); + if (ret < 0) { + FTS_DEBUG("data read(addr:%x) crc abnormal,retry:%d", + cmd[0], i); + udelay(CS_HIGH_DELAY); + continue; + } + } + break; + } else { + FTS_DEBUG("data read(addr:%x) status:%x,retry:%d,ret:%d", + cmd[0], rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + } + + if (ret < 0) { + FTS_ERROR("data read(addr:%x) %s,status:%x,ret:%d", cmd[0], + (i >= SPI_RETRY_NUMBER) ? "crc abnormal" : "fail", + rxbuf[3], ret); + } + +err_read: + if (txlen_need > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + + +int fts_spi_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + bool read_cmd = (readbuf && readlen) ? 1 : 0; + u32 txlen = (read_cmd) ? readlen : writelen; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_spi_dir; + } + + rxbuf = kzalloc(txlen, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_spi_dir; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + memcpy(txbuf, writebuf, writelen); + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if (ret < 0) { + FTS_ERROR("data read(addr:%x) fail,status:%x,ret:%d", txbuf[0], rxbuf[3], ret); + goto err_spi_dir; + } + + if (read_cmd) { + memcpy(readbuf, rxbuf, txlen); + } + + ret = 0; +err_spi_dir: + if (txlen > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_bus_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + + ts_data->dummy_byte = SPI_DUMMY_BYTE; + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (ts_data && ts_data->bus_tx_buf) { + kfree(ts_data->bus_tx_buf); + ts_data->bus_tx_buf = NULL; + } + + if (ts_data && ts_data->bus_rx_buf) { + kfree(ts_data->bus_rx_buf); + ts_data->bus_rx_buf = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/include/firmware/fw_sample.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/include/firmware/fw_sample.h new file mode 100644 index 000000000000..e69de29bb2d1