From 3161226167df0ccf1f4f2224efae6f34261455ba Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 28 Oct 2020 18:41:37 +0800 Subject: [PATCH] driver: input: screentouch: support focaltech touch. Signed-off-by: Jason Song Change-Id: Idd0d772225ed11a5080649d8e962e5cb4c2b89b7 --- drivers/input/touchscreen/Kconfig | 8 + drivers/input/touchscreen/Makefile | 1 + .../touchscreen/focaltech_touch/Makefile | 18 + .../focaltech_touch/focaltech_common.h | 159 ++ .../focaltech_touch/focaltech_config.h | 244 ++ .../focaltech_touch/focaltech_core.c | 1784 +++++++++++++++ .../focaltech_touch/focaltech_core.h | 254 +++ .../focaltech_touch/focaltech_esdcheck.c | 552 +++++ .../focaltech_touch/focaltech_ex_fun.c | 1130 ++++++++++ .../focaltech_touch/focaltech_ex_mode.c | 382 ++++ .../focaltech_touch/focaltech_flash.c | 1971 +++++++++++++++++ .../focaltech_touch/focaltech_flash.h | 182 ++ .../focaltech_touch/focaltech_flash/Makefile | 8 + .../focaltech_upgrade_ft8201.c | 1033 +++++++++ .../focaltech_touch/focaltech_gesture.c | 597 +++++ .../focaltech_touch/focaltech_i2c.c | 240 ++ .../focaltech_point_report_check.c | 135 ++ .../focaltech_touch/focaltech_test/Makefile | 11 + .../focaltech_test/focaltech_test.c | 1948 ++++++++++++++++ .../focaltech_test/focaltech_test.h | 589 +++++ .../focaltech_test/focaltech_test_ini.c | 1220 ++++++++++ .../focaltech_test/focaltech_test_ini.h | 144 ++ .../focaltech_test/supported_ic/Makefile | 6 + .../supported_ic/focaltech_test_ft8201.c | 1034 +++++++++ .../include/firmware/fw_sample.h | 0 .../FT8006M_Pramboot_V1.6_20180426_le.h | 299 +++ 26 files changed, 13949 insertions(+) create mode 100644 drivers/input/touchscreen/focaltech_touch/Makefile create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_common.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_config.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_core.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_core.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft8201.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/Makefile create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.c create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.h create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/Makefile create mode 100644 drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/focaltech_test_ft8201.c create mode 100644 drivers/input/touchscreen/focaltech_touch/include/firmware/fw_sample.h create mode 100644 drivers/input/touchscreen/focaltech_touch/include/pramboot/FT8006M_Pramboot_V1.6_20180426_le.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3548f56325e4..3b9f2a394a90 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1380,4 +1380,12 @@ config TOUCHSCREEN_VTL_CT36X config TOUCHSCREEN_GT1X tristate "GT1X touchscreens support" +config TOUCHSCREEN_FTS + bool "Focaltech Touchscreen" + depends on I2C + default n + help + Say Y here if you have Focaltech touch panel. + If unsure, say N. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 8cb702c2cad1..c1c47465604f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -124,3 +124,4 @@ obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_VTL_CT36X) += vtl_ts/ obj-$(CONFIG_TOUCHSCREEN_GT1X) += gt1x/ obj-$(CONFIG_TOUCHSCREEN_HYN_CST2XX) += hyn_cst2xx/ +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/ diff --git a/drivers/input/touchscreen/focaltech_touch/Makefile b/drivers/input/touchscreen/focaltech_touch/Makefile new file mode 100644 index 000000000000..bf7df1543b01 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the focaltech touchscreen drivers. +# + +# Each configuration option enables a list of files. + +obj-y += focaltech_core.o +obj-y += focaltech_ex_fun.o +obj-y += focaltech_ex_mode.o +obj-y += focaltech_flash.o +obj-y += focaltech_gesture.o +obj-y += focaltech_esdcheck.o +obj-y += focaltech_i2c.o +obj-y += focaltech_point_report_check.o +obj-y += focaltech_flash/ +obj-y += focaltech_test/ + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_common.h b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h new file mode 100644 index 000000000000..7e911d1ac36f --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h @@ -0,0 +1,159 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_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.0a 20180929" + +#define BYTE_OFF_0(x) (u8)((x) & 0xFF) +#define BYTE_OFF_8(x) (u8)((x >> 8) & 0xFF) +#define BYTE_OFF_16(x) (u8)((x >> 16) & 0xFF) +#define BYTE_OFF_24(x) (u8)((x >> 24) & 0xFF) +#define FLAGBIT(x) (0x00000001 << (x)) +#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) << (x)) + +#define FLAG_ICSERIALS_LEN 8 +#define FLAG_HID_BIT 10 +#define FLAG_IDC_BIT 11 + +#define IC_SERIALS (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FTS_CHIP_TYPE_MAPPING {{0x10,0x82, 0x01, 0x80, 0x06, 0x80, 0xC6, 0x80, 0xB6}} + +#define I2C_BUFFER_LENGTH_MAXINUM 256 +#define FILE_NAME_LENGTH 128 +#define ENABLE 1 +#define DISABLE 0 +#define VALID 1 +#define INVALID 0 +#define FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_START_DELAY 10 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 +/*register address*/ +#define FTS_REG_INT_CNT 0x8F +#define FTS_REG_FLOW_WORK_CNT 0x91 +#define FTS_REG_WORKMODE 0x00 +#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40 +#define FTS_REG_WORKMODE_WORK_VALUE 0x00 +#define FTS_REG_ESDCHECK_DISABLE 0x8D +#define FTS_REG_CHIP_ID 0xA3 +#define FTS_REG_CHIP_ID2 0x9F +#define FTS_REG_POWER_MODE 0xA5 +#define FTS_REG_POWER_MODE_SLEEP_VALUE 0x03 +#define FTS_REG_FW_VER 0xA6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_LCD_BUSY_NUM 0xAB +#define FTS_REG_FACE_DEC_MODE_EN 0xB0 +#define FTS_REG_FACE_DEC_MODE_STATUS 0x01 +#define FTS_REG_IDE_PARA_VER_ID 0xB5 +#define FTS_REG_IDE_PARA_STATUS 0xB6 +#define FTS_REG_GLOVE_MODE_EN 0xC0 +#define FTS_REG_COVER_MODE_EN 0xC1 +#define FTS_REG_CHARGER_MODE_EN 0x8B +#define FTS_REG_GESTURE_EN 0xD0 +#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3 +#define FTS_REG_MODULE_ID 0xE3 +#define FTS_REG_LIC_VER 0xE4 +#define FTS_REG_ESD_SATURATE 0xED + +#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1') +#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0') + +#define kfree_safe(pbuf) do {\ + if (pbuf) {\ + kfree(pbuf);\ + pbuf = NULL;\ + }\ +} while(0) + +/***************************************************************************** +* Alternative mode (When something goes wrong, the modules may be able to solve the problem.) +*****************************************************************************/ +/* + * point report check + * default: disable + */ +#define FTS_POINT_REPORT_CHECK_EN 0 + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct ft_chip_t { + u64 type; + u8 chip_idh; + u8 chip_idl; + u8 rom_idh; + u8 rom_idl; + u8 pb_idh; + u8 pb_idl; + u8 bl_idh; + u8 bl_idl; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; +}; + +/***************************************************************************** +* DEBUG function define here +*****************************************************************************/ +#if FTS_DEBUG_EN +#define FTS_DEBUG_LEVEL 1 +#if (FTS_DEBUG_LEVEL == 2) +#define FTS_DEBUG(fmt, args...) printk("[FTS][%s]"fmt"\n", __func__, ##args) +#else +#define FTS_DEBUG(fmt, args...) printk("[FTS]"fmt"\n", ##args) +#endif +#define FTS_FUNC_ENTER() printk("[FTS]%s: Enter\n", __func__) +#define FTS_FUNC_EXIT() printk("[FTS]%s: Exit(%d)\n", __func__, __LINE__) +#else /* #if FTS_DEBUG_EN*/ +#define FTS_DEBUG(fmt, args...) +#define FTS_FUNC_ENTER() +#define FTS_FUNC_EXIT() +#endif + +#define FTS_INFO(fmt, args...) printk(KERN_INFO "[FTS][Info]"fmt"\n", ##args) +#define FTS_ERROR(fmt, args...) printk(KERN_ERR "[FTS][Error]"fmt"\n", ##args) + +#endif /* __LINUX_FOCALTECH_COMMON_H__ */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_config.h b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h new file mode 100644 index 000000000000..35f847005f66 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h @@ -0,0 +1,244 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/************************************************************************ +* +* File Name: focaltech_config.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: global configurations +* +* Version: v1.0 +* +************************************************************************/ +#ifndef _LINUX_FOCLATECH_CONFIG_H_ +#define _LINUX_FOCLATECH_CONFIG_H_ + +/**************************************************/ +/****** G: A, I: B, S: C, U: D ******************/ +/****** chip type defines, do not modify *********/ +#define _FT8716 0x87160805 +#define _FT8736 0x87360806 +#define _FT8006M 0x80060807 +#define _FT7250 0x72500807 +#define _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8006P 0x86220811 + +#define _FT5416 0x54160402 +#define _FT5426 0x54260402 +#define _FT5435 0x54350402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5526I 0x5526B402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT5446I 0x5446B402 +#define _FT5346I 0x5346B402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3C47U 0x3C47D402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 + +#define _FT5626 0x56260401 +#define _FT5726 0x57260401 +#define _FT5826B 0x5826B401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3D47 0x3D470401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 +#define _FT3817B 0x3817B401 +#define _FT3517U 0x3517D401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT3327DQQ_XXX 0x3327D482 +#define _FT5446DQS_XXX 0x5446D482 + +#define _FT3518 0x35180481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 + +#define _FT5446U 0x5446D083 +#define _FT5456U 0x5456D083 +#define _FT3417U 0x3417D083 +#define _FT5426U 0x5426D083 +#define _FT3428 0x34280083 +#define _FT3437U 0x3437D083 + +#define _FT7302 0x73020084 +#define _FT7202 0x72020084 +#define _FT3308 0x33080084 + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#define FTS_CHIP_TYPE _FT8201 + +/******************* 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 + +/* + * Gesture function enable + * default: disable + */ +#define FTS_GESTURE_EN 0 + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + +/* + * Production test enable + * 1: enable, 0:disable(default) + */ +#define FTS_TEST_EN 0 + +/* + * Glove mode enable + * 1: enable, 0:disable(default) + */ +#define FTS_GLOVE_EN 0 +/* + * cover enable + * 1: enable, 0:disable(default) + */ +#define FTS_COVER_EN 0 +/* + * Charger enable + * 1: enable, 0:disable(default) + */ +#define FTS_CHARGER_EN 0 + +/* + * Nodes for tools, please keep enable + */ +#define FTS_SYSFS_NODE_EN 0 +#define FTS_APK_NODE_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, please keep enable + */ +#define FTS_AUTO_UPGRADE_EN 0 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 1 + +/* + * Check vendor_id number + * 0:No check vendor_id (default) + * 1/2/3: Check vendor_id for vendor compatibility + */ +#define FTS_GET_VENDOR_ID_NUM 0 + +/* + * vendor_id(s) for vendor(s) to be compatible with. + * a confirmation of vendor_id(s) is recommended. + * FTS_VENDOR_ID = PANEL_ID << 8 + VENDOR_ID + * FTS_GET_VENDOR_ID_NUM == 0/1, no check vendor id, you may ignore them + * FTS_GET_VENDOR_ID_NUM > 1, compatible with FTS_VENDOR_ID + * FTS_GET_VENDOR_ID_NUM >= 2, compatible with FTS_VENDOR_ID2 + * FTS_GET_VENDOR_ID_NUM >= 3, compatible with FTS_VENDOR_ID3 + */ +#define FTS_VENDOR_ID 0x0000 +#define FTS_VENDOR_ID2 0x0000 +#define FTS_VENDOR_ID3 0x0000 + +/* + * 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_VENDOR_ID_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID + */ +#define FTS_UPGRADE_FW_FILE "include/firmware/fw_sample.h" + +/* + * if FTS_GET_VENDOR_ID_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_VENDOR_ID_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_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c new file mode 100644 index 000000000000..304ec0ed3250 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c @@ -0,0 +1,1784 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: entrance for focaltech ts driver +* +* Version: V1.0 +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define INTERVAL_READ_REG 100 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#if FTS_POWER_SOURCE_CUST_EN +#define FTS_VTG_MIN_UV 2600000 +#define FTS_VTG_MAX_UV 3300000 +#define FTS_I2C_VTG_MIN_UV 1800000 +#define FTS_I2C_VTG_MAX_UV 1800000 +#endif + +//#define CFG_SWAP_XY +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_ts_data *fts_data; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static void fts_release_all_finger(void); +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); + +/***************************************************************************** +* Name: fts_reset_proc +* Brief: Execute reset operation +* Input: hdelayms - delay time unit:ms +* Output: +* Return: +*****************************************************************************/ +int fts_reset_proc(int hdelayms) +{ + FTS_FUNC_ENTER(); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(5); + gpio_direction_output(fts_data->pdata->reset_gpio, 1); + if (hdelayms) { + msleep(hdelayms); + } + + FTS_FUNC_EXIT(); + return 0; +} + + +/***************************************************************************** +* Name: fts_wait_tp_to_valid +* Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG), +* need call when reset/power on/resume... +* Input: +* Output: +* Return: return 0 if tp valid, otherwise return error code +*****************************************************************************/ +int fts_wait_tp_to_valid(struct i2c_client *client) +{ + int ret = 0; + int cnt = 0; + u8 reg_value = 0; + u8 chip_id = fts_data->ic_info.ids.chip_idh; + fts_reset_proc(200); + do { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, ®_value); + if ((ret < 0) || (reg_value != chip_id)) { + FTS_DEBUG("TP Not Ready, ReadData = 0x%x", reg_value); + } else if (reg_value == chip_id) { + FTS_INFO("TP Ready, Device ID = 0x%x", reg_value); + return 0; + } + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} + +/************************************************************************ +* Name: fts_get_chip_types +* Brief: verity chip id and get chip type data +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + int i = 0; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t); + + if ((0x0 == id_h) || (0x0 == id_l)) { + FTS_ERROR("id_h/id_l is 0"); + return -EINVAL; + } + + FTS_DEBUG("verify id:0x%02x%02x", id_h, id_l); + for (i = 0; i < ctype_entries; i++) { + if (VALID == fw_valid) { + if ((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl)) + break; + } else { + if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl)) + || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl)) + || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl))) + break; + } + } + + /*if (i >= ctype_entries) { + return -ENODATA; + }*/ + if(ctype_entries >= 2) + ts_data->ic_info.ids = ctype[1]; + + return 0; +} + +/***************************************************************************** +* Name: fts_get_ic_information +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +*****************************************************************************/ +static int fts_get_ic_information(struct fts_ts_data *ts_data) +{ + int ret = 0; + int ret1 = 0; + int cnt = 0; + u8 chip_id[2] = { 0 }; + u8 id_cmd[4] = { 0 }; + u32 id_cmd_len = 0; + struct i2c_client *client = ts_data->client; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + fts_reset_proc(200); + + do { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id[0]); + ret1 = fts_i2c_read_reg(client, FTS_REG_CHIP_ID2, &chip_id[1]); + if ((ret < 0) || (ret1 < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_DEBUG("i2c read invalid, read:0x%02x%02x", chip_id[0], chip_id[1]); + } else { + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID); + if (!ret) + break; + else + FTS_DEBUG("TP not ready, read:0x%02x%02x", chip_id[0], chip_id[1]); + } + + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) { + FTS_INFO("fw is invalid, need read boot id"); + if (ts_data->ic_info.hid_supported) { + fts_i2c_hid2std(client); + } + + id_cmd[0] = FTS_CMD_START1; + id_cmd[1] = FTS_CMD_START2; + ret = fts_i2c_write(client, id_cmd, 2); + if (ret < 0) { + FTS_ERROR("start cmd write fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + id_cmd[0] = FTS_CMD_READ_ID; + id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; + if (ts_data->ic_info.is_incell) + id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + id_cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_i2c_read(client, id_cmd, id_cmd_len, chip_id, 2); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_ERROR("read boot id fail"); + return -EIO; + } + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_ERROR("can't get ic informaton"); + return ret; + } + } + + FTS_INFO("get ic information, chip id = 0x%02x%02x", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + + return 0; +} + +/***************************************************************************** +* Name: fts_tp_state_recovery +* Brief: Need execute this function when reset +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_tp_state_recovery(struct i2c_client *client) +{ + FTS_FUNC_ENTER(); + /* wait tp stable */ + fts_wait_tp_to_valid(client); + /* recover TP charger state 0x8B */ + /* recover TP glove state 0xC0 */ + /* recover TP cover state 0xC1 */ + fts_ex_mode_recovery(client); + /* recover TP gesture state 0xD0 */ +#if FTS_GESTURE_EN + fts_gesture_recovery(client); +#endif + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_irq_disable +* Brief: disable irq +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_irq_disable(void) +{ + unsigned long irqflags; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (!fts_data->irq_disabled) { + disable_irq_nosync(fts_data->irq); + fts_data->irq_disabled = true; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_irq_enable +* Brief: enable irq +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_irq_enable(void) +{ + unsigned long irqflags = 0; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (fts_data->irq_disabled) { + enable_irq(fts_data->irq); + fts_data->irq_disabled = false; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +#if FTS_POWER_SOURCE_CUST_EN +/***************************************************************************** +* Power Control +*****************************************************************************/ +static int fts_power_source_init(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + ret = PTR_ERR(data->vdd); + FTS_ERROR("get vdd regulator failed,ret=%d", ret); + return ret; + } + + if (regulator_count_voltages(data->vdd) > 0) { + ret = regulator_set_voltage(data->vdd, FTS_VTG_MIN_UV, FTS_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret); + goto err_set_vtg_vdd; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + ret = PTR_ERR(data->vcc_i2c); + FTS_ERROR("ret vcc_i2c regulator failed,ret=%d", ret); + goto err_get_vcc; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + ret = regulator_set_voltage(data->vcc_i2c, FTS_I2C_VTG_MIN_UV, FTS_I2C_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_vtg failed ret=%d", ret); + goto err_set_vtg_vcc; + } + } + + FTS_FUNC_EXIT(); + return 0; + +err_set_vtg_vcc: + regulator_put(data->vcc_i2c); +err_get_vcc: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV); +err_set_vtg_vdd: + regulator_put(data->vdd); + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_power_source_release(struct fts_ts_data *data) +{ + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV); + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV); + regulator_put(data->vcc_i2c); + + return 0; +} + +static int fts_power_source_ctrl(struct fts_ts_data *data, int enable) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + if (enable) { + if (data->power_disabled) { + FTS_DEBUG("regulator enable !"); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(1); + ret = regulator_enable(data->vdd); + if (ret) { + FTS_ERROR("enable vdd regulator failed,ret=%d", ret); + } + + ret = regulator_enable(data->vcc_i2c); + if (ret) { + FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret); + } + data->power_disabled = false; + } + } else { + if (!data->power_disabled) { + FTS_DEBUG("regulator disable !"); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(1); + ret = regulator_disable(data->vdd); + if (ret) { + FTS_ERROR("disable vdd regulator failed,ret=%d", ret); + } + ret = regulator_disable(data->vcc_i2c); + if (ret) { + FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret); + } + data->power_disabled = true; + } + } + + FTS_FUNC_EXIT(); + return ret; +} + +#if FTS_PINCTRL_EN +/***************************************************************************** +* Name: fts_pinctrl_init +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_pinctrl_init(struct fts_ts_data *ts) +{ + int ret = 0; + struct i2c_client *client = ts->client; + + ts->pinctrl = devm_pinctrl_get(&client->dev); + if (IS_ERR_OR_NULL(ts->pinctrl)) { + FTS_ERROR("Failed to get pinctrl, please check dts"); + ret = PTR_ERR(ts->pinctrl); + goto err_pinctrl_get; + } + + ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(ts->pins_active)) { + FTS_ERROR("Pin state[active] not found"); + ret = PTR_ERR(ts->pins_active); + goto err_pinctrl_lookup; + } + + ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(ts->pins_suspend)) { + FTS_ERROR("Pin state[suspend] not found"); + ret = PTR_ERR(ts->pins_suspend); + goto err_pinctrl_lookup; + } + + ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(ts->pins_release)) { + FTS_ERROR("Pin state[release] not found"); + ret = PTR_ERR(ts->pins_release); + } + + return 0; +err_pinctrl_lookup: + if (ts->pinctrl) { + devm_pinctrl_put(ts->pinctrl); + } +err_pinctrl_get: + ts->pinctrl = NULL; + ts->pins_release = NULL; + ts->pins_suspend = NULL; + ts->pins_active = NULL; + return ret; +} + +static int fts_pinctrl_select_normal(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_active) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_active); + if (ret < 0) { + FTS_ERROR("Set normal pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_suspend(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_suspend) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend); + if (ret < 0) { + FTS_ERROR("Set suspend pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_release(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl) { + if (IS_ERR_OR_NULL(ts->pins_release)) { + devm_pinctrl_put(ts->pinctrl); + ts->pinctrl = NULL; + } else { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_release); + if (ret < 0) + FTS_ERROR("Set gesture pin state error:%d", ret); + } + } + + return ret; +} +#endif /* FTS_PINCTRL_EN */ + +#endif /* FTS_POWER_SOURCE_CUST_EN */ + +/***************************************************************************** +* Reprot related +*****************************************************************************/ +#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2)) +char g_sz_debug[1024] = {0}; +static void fts_show_touch_buffer(u8 *buf, int point_num) +{ + int len = point_num * FTS_ONE_TCH_LEN; + int count = 0; + int i; + + memset(g_sz_debug, 0, 1024); + if (len > (fts_data->pnt_buf_size - 3)) { + len = fts_data->pnt_buf_size - 3; + } else if (len == 0) { + len += FTS_ONE_TCH_LEN; + } + count += snprintf(g_sz_debug, PAGE_SIZE, "%02X,%02X,%02X", buf[0], buf[1], buf[2]); + for (i = 0; i < len; i++) { + count += snprintf(g_sz_debug + count, PAGE_SIZE, ",%02X", buf[i + 3]); + } + FTS_DEBUG("buffer: %s", g_sz_debug); +} +#endif + +/***************************************************************************** + * Name: fts_release_all_finger + * Brief: report all points' up events, release touch + * Input: + * Output: + * Return: + *****************************************************************************/ +static void fts_release_all_finger(void) +{ + struct input_dev *input_dev = fts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&fts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < fts_data->pdata->max_touch_number; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + + mutex_unlock(&fts_data->report_mutex); + FTS_FUNC_EXIT(); +} + +/************************************************************************ + * Name: fts_input_report_key + * Brief: report key event + * Input: events info + * Output: + * Return: return 0 if success + ***********************************************************************/ +static int fts_input_report_key(struct fts_ts_data *data, int index) +{ + u32 ik; + int id = data->events[index].id; + int x = data->events[index].x; + int y = data->events[index].y; + int flag = data->events[index].flag; + u32 key_num = data->pdata->key_number; + + if (!KEY_EN(data)) { + return -EINVAL; + } + for (ik = 0; ik < key_num; ik++) { + if (TOUCH_IN_KEY(x, data->pdata->key_x_coords[ik])) { + if (EVENT_DOWN(flag)) { + data->key_down = true; + input_report_key(data->input_dev, data->pdata->keys[ik], 1); + FTS_DEBUG("Key%d(%d, %d) DOWN!", ik, x, y); + } else { + data->key_down = false; + input_report_key(data->input_dev, data->pdata->keys[ik], 0); + FTS_DEBUG("Key%d(%d, %d) Up!", ik, x, y); + } + return 0; + } + } + + FTS_ERROR("invalid touch for key, [%d](%d, %d)", id, x, y); + return -EINVAL; +} + + +#if FTS_MT_PROTOCOL_B_EN +static int fts_input_report_b(struct fts_ts_data *data) +{ + int i = 0; + int uppoint = 0; + int touchs = 0; + bool va_reported = false; + u32 max_touch_num = data->pdata->max_touch_number; + u32 key_y_coor = data->pdata->key_y_coord; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (KEY_EN(data) && TOUCH_IS_KEY(events[i].y, key_y_coor)) { + fts_input_report_key(data, i); + continue; + } + + if (events[i].id >= max_touch_num) + break; + + va_reported = true; + input_mt_slot(data->input_dev, events[i].id); + + if (EVENT_DOWN(events[i].flag)) { + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); + +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } +#ifdef CFG_SWAP_XY + swap(events[i].x,events[i].y); +#endif /* CFG_CTS_SWAP_XY */ + + + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); +#if 0 + input_report_abs(data->input_dev, ABS_MT_POSITION_X, 2176 - events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); +#else + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); +#endif + //printk("bb...x = %d, y - %d\n...", events[i].x, 2176 - events[i].y, events[i].p); + touchs |= BIT(events[i].id); + data->touchs |= BIT(events[i].id); + + FTS_DEBUG("[B]P%d(0x%x, 0x%x)[p:%d,tm:%d] DOWN!", events[i].id, events[i].x, + events[i].y, events[i].p, events[i].area); + } else { + uppoint++; + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + data->touchs &= ~BIT(events[i].id); + FTS_DEBUG("[B]P%d UP!", events[i].id); + } + } + + if (unlikely(data->touchs ^ touchs)) { + for (i = 0; i < max_touch_num; i++) { + if (BIT(i) & (data->touchs ^ touchs)) { + FTS_DEBUG("[B]P%d UP!", i); + va_reported = true; + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + } + } + } + data->touchs = touchs; + + if (va_reported) { + /* touchs==0, there's no point but key */ + if (EVENT_NO_DOWN(data) || (!touchs)) { + FTS_DEBUG("[B]Points All Up!"); + input_report_key(data->input_dev, BTN_TOUCH, 0); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} + +#else +static int fts_input_report_a(struct fts_ts_data *data) +{ + int i = 0; + int touchs = 0; + bool va_reported = false; + u32 key_y_coor = data->pdata->key_y_coord; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (KEY_EN(data) && TOUCH_IS_KEY(events[i].y, key_y_coor)) { + fts_input_report_key(data, i); + continue; + } + + va_reported = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, 2176 - events[i].y); + //printk("aa...x = %d, y - %d...p = %d\n...", events[i].x, 2176 - events[i].y, events[i].p); + input_mt_sync(data->input_dev); + + FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!", events[i].id, events[i].x, + events[i].y, events[i].p, events[i].area); + touchs++; + } + } + + /* last point down, current no point but key */ + if (data->touchs && !touchs) { + va_reported = true; + } + data->touchs = touchs; + + if (va_reported) { + if (EVENT_NO_DOWN(data)) { + FTS_DEBUG("[A]Points All Up!"); + input_report_key(data->input_dev, BTN_TOUCH, 1); + input_mt_sync(data->input_dev); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 0); + } + } + + input_sync(data->input_dev); + return 0; +} +#endif + +/***************************************************************************** +* Name: fts_read_touchdata +* Brief: +* Input: +* Output: +* Return: return 0 if succuss +*****************************************************************************/ +static int fts_read_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + int i = 0; + u8 pointid; + int base; + struct ts_event *events = data->events; + int max_touch_num = data->pdata->max_touch_number; + u8 *buf = data->point_buf; + struct i2c_client *client = data->client; + +#if FTS_GESTURE_EN + if (0 == fts_gesture_readdata(data)) { + FTS_INFO("succuss to get gesture data in irq handler"); + return 1; + } +#endif + +#if FTS_POINT_REPORT_CHECK_EN + fts_prc_queue_work(data); +#endif + + data->point_num = 0; + data->touch_point = 0; + + memset(buf, 0xFF, data->pnt_buf_size); + buf[0] = 0x00; + + ret = fts_i2c_read(data->client, buf, 1, buf, data->pnt_buf_size); + if (ret < 0) { + FTS_ERROR("read touchdata failed, ret:%d", ret); + return ret; + } + data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; + + if (data->ic_info.is_incell) { + if ((data->point_num == 0x0F) && (buf[1] == 0xFF) && (buf[2] == 0xFF) + && (buf[3] == 0xFF) && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) { + FTS_INFO("touch buff is 0xff, need recovery state"); + fts_tp_state_recovery(client); + return -EIO; + } + } + + if (data->point_num > max_touch_num) { + FTS_INFO("invalid point_num(%d)", data->point_num); + return -EIO; + } + +#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2)) + fts_show_touch_buffer(buf, data->point_num); +#endif + + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i; + + pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4; + if (pointid >= FTS_MAX_ID) + break; + else if (pointid >= max_touch_num) { + FTS_ERROR("ID(%d) beyond max_touch_number", pointid); + return -EINVAL; + } + + data->touch_point++; + + events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_X_L_POS + base] & 0xFF); + events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF); + events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6; + events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4; + events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4; + events[i].p = buf[FTS_TOUCH_PRE_POS + base]; + + if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } + if (data->touch_point == 0) { + FTS_INFO("no touch point information"); + return -EIO; + } + + return 0; +} + +/***************************************************************************** +* Name: fts_report_event +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_report_event(struct fts_ts_data *data) +{ +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(data); +#else + fts_input_report_a(data); +#endif +} + +/***************************************************************************** +* Name: fts_ts_interrupt +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static irqreturn_t fts_ts_interrupt(int irq, void *data) +{ + int ret = 0; + struct fts_ts_data *ts_data = (struct fts_ts_data *)data; + + + //FTS_ERROR("fts_ts_interrupt.......yyk...........\n"); + + if (!ts_data) { + FTS_ERROR("[INTR]: Invalid fts_ts_data"); + return IRQ_HANDLED; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(1); +#endif + + ret = fts_read_touchdata(ts_data); + if (ret == 0) { + mutex_lock(&ts_data->report_mutex); + fts_report_event(ts_data); + mutex_unlock(&ts_data->report_mutex); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(0); +#endif + + return IRQ_HANDLED; +} + +/***************************************************************************** +* Name: fts_irq_registration +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +static int fts_irq_registration(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + + ts_data->irq = gpio_to_irq(pdata->irq_gpio); + FTS_INFO("irq in ts_data:%d irq in client:%d", ts_data->irq, ts_data->client->irq); + if (ts_data->irq != ts_data->client->irq) + FTS_ERROR("IRQs are inconsistent, please check & in DTS"); + + if (0 == pdata->irq_gpio_flags) + pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING; + FTS_INFO("irq flag:%x", pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_ts_interrupt, + pdata->irq_gpio_flags | IRQF_ONESHOT, + ts_data->client->name, ts_data); + + /*ret = devm_request_irq(&ts_data->client->dev, ts_data->irq, fts_ts_interrupt, + IRQF_TRIGGER_FALLING|IRQ_TYPE_EDGE_RISING,ts_data->client->name, ts_data);*/ + return ret; +} + +/***************************************************************************** +* Name: fts_input_init +* Brief: input device init +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_input_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + int key_num = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev; + int point_num; + + FTS_FUNC_ENTER(); + + input_dev = input_allocate_device(); + if (!input_dev) { + FTS_ERROR("Failed to allocate memory for input device"); + return -ENOMEM; + } + + /* Init and register Input device */ + input_dev->name = FTS_DRIVER_NAME; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &ts_data->client->dev; + + input_set_drvdata(input_dev, ts_data); + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + if (pdata->have_key) { + FTS_INFO("set key capabilities"); + for (key_num = 0; key_num < pdata->key_number; key_num++) + input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]); + } + +#if FTS_MT_PROTOCOL_B_EN + input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT); +#else + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0f, 0, 0); +#endif + +#ifdef CFG_SWAP_XY + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->x_max, 0, 0); +#else /* CFG_CTS_SWAP_XY */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->y_max, 0, 0); +#endif /* CFG_CTS_SWAP_XY */ + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0); +#if FTS_REPORT_PRESSURE_EN + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); +#endif + point_num = pdata->max_touch_number; + ts_data->pnt_buf_size = point_num * FTS_ONE_TCH_LEN + 3; + ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size, GFP_KERNEL); + if (!ts_data->point_buf) { + FTS_ERROR("failed to alloc memory for point buf!"); + ret = -ENOMEM; + goto err_point_buf; + } + + ts_data->events = (struct ts_event *)kzalloc(point_num * sizeof(struct ts_event), GFP_KERNEL); + if (!ts_data->events) { + + FTS_ERROR("failed to alloc memory for point events!"); + ret = -ENOMEM; + goto err_event_buf; + } + + + ret = input_register_device(input_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + goto err_input_reg; + } + + ts_data->input_dev = input_dev; + + FTS_FUNC_EXIT(); + return 0; + +err_input_reg: + kfree_safe(ts_data->events); + +err_event_buf: + kfree_safe(ts_data->point_buf); + +err_point_buf: + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + + FTS_FUNC_EXIT(); + return ret; +} + + +/***************************************************************************** +* Name: fts_gpio_configure +* Brief: Configure IRQ&RESET GPIO +* Input: +* Output: +* Return: return 0 if succuss +*****************************************************************************/ +static int fts_gpio_configure(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + /* request irq gpio */ + if (gpio_is_valid(data->pdata->irq_gpio)) { + ret = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio"); + if (ret) { + FTS_ERROR("[GPIO]irq gpio request failed"); + goto err_irq_gpio_req; + } + + ret = gpio_direction_input(data->pdata->irq_gpio); + if (ret) { + FTS_ERROR("[GPIO]set_direction for irq gpio failed"); + goto err_irq_gpio_dir; + } + } + + /* request reset gpio */ + if (gpio_is_valid(data->pdata->reset_gpio)) { + ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio"); + if (ret) { + FTS_ERROR("[GPIO]reset gpio request failed"); + goto err_irq_gpio_dir; + } + + ret = gpio_direction_output(data->pdata->reset_gpio, 1); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_reset_gpio_dir; + } + } + + FTS_FUNC_EXIT(); + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_get_dt_coords +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +static int fts_get_dt_coords(struct device *dev, char *name, + struct fts_ts_platform_data *pdata) +{ + //int ret = 0; + //u32 coords[FTS_COORDS_ARR_SIZE] = { 0 }; + //struct property *prop; + //struct device_node *np = dev->of_node; + //int coords_size; + + 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; + + + + /*prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != FTS_COORDS_ARR_SIZE) { + FTS_ERROR("invalid:%s, size:%d", name, coords_size); + return -EINVAL; + } + + ret = of_property_read_u32_array(np, name, coords, coords_size); + if (ret && (ret != -EINVAL)) { + FTS_ERROR("Unable to read %s", name); + return -ENODATA; + } + + if (!strcmp(name, "focaltech,display-coords")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } else { + FTS_ERROR("unsupported property %s", 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 -EINVAL; + } +*/ + printk("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, pdata->y_min, pdata->y_max); + /*FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, + pdata->y_min, pdata->y_max);*/ + return 0; +} + +/***************************************************************************** +* Name: fts_parse_dt +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) +{ + int ret = 0; + struct device_node *np = dev->of_node; + u32 temp_val; + + FTS_FUNC_ENTER(); + + 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) + FTS_ERROR("Key number undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,keys", + pdata->keys, pdata->key_number); + + if (ret) + FTS_ERROR("Keys undefined!"); + else if (pdata->key_number > FTS_MAX_KEYS) + pdata->key_number = FTS_MAX_KEYS; + + ret = of_property_read_u32(np, "focaltech,key-y-coord", &pdata->key_y_coord); + + if (ret) + FTS_ERROR("Key Y Coord undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,key-x-coords", + pdata->key_x_coords, pdata->key_number); + + if (ret) + FTS_ERROR("Key X Coords undefined!"); + + FTS_INFO("VK(%d): (%d, %d, %d), [%d, %d, %d][%d]", + pdata->key_number, pdata->keys[0], pdata->keys[1], pdata->keys[2], + pdata->key_x_coords[0], pdata->key_x_coords[1], pdata->key_x_coords[2], + pdata->key_y_coord); + } + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0, &pdata->reset_gpio_flags); + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0, &pdata->irq_gpio_flags); + + ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val); + if (0 == ret) { + if (temp_val < 2) + pdata->max_touch_number = 2; + else if (temp_val > FTS_MAX_POINTS_SUPPORT) + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + else + pdata->max_touch_number = temp_val; + } else { + FTS_ERROR("Unable to get max-touch-number"); + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + } + + FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d", + pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); + + FTS_FUNC_EXIT(); + return 0; +} + +#if defined(CONFIG_FB) +/***************************************************************************** +* Name: fts_resume_work +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +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->client->dev); + +} + +/***************************************************************************** +* Name: fb_notifier_callback +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct fts_ts_data *fts_data = + container_of(self, struct fts_ts_data, fb_notif); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && + fts_data && fts_data->client) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + else if (*blank == FB_BLANK_POWERDOWN) + fts_ts_suspend(&fts_data->client->dev); + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +/***************************************************************************** +* Name: fts_ts_early_suspend +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_ts_early_suspend(struct early_suspend *handler) +{ + struct fts_ts_data *data = container_of(handler, + struct fts_ts_data, + early_suspend); + + fts_ts_suspend(&data->client->dev); +} + +/***************************************************************************** +* Name: fts_ts_late_resume +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_ts_late_resume(struct early_suspend *handler) +{ + struct fts_ts_data *data = container_of(handler, + struct fts_ts_data, + early_suspend); + + fts_ts_resume(&data->client->dev); +} +#endif + +extern int fts_test_exit(struct i2c_client *client); +extern int fts_test_init(struct i2c_client *client); +/***************************************************************************** +* Name: fts_ts_probe +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + + FTS_FUNC_ENTER(); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FTS_ERROR("I2C not supported"); + return -ENODEV; + } + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + FTS_ERROR("Failed to allocate memory for platform data"); + return -ENOMEM; + } + ret = fts_parse_dt(&client->dev, pdata); + if (ret) + FTS_ERROR("[DTS]DT parsing failed"); + } else { + pdata = client->dev.platform_data; + } + + if (!pdata) { + FTS_ERROR("no ts platform data found"); + return -EINVAL; + } + + ts_data = devm_kzalloc(&client->dev, sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("Failed to allocate memory for fts_data"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->client = client; + ts_data->pdata = pdata; + i2c_set_clientdata(client, ts_data); + + ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); + if (NULL == ts_data->ts_workqueue) { + FTS_ERROR("failed to create fts workqueue"); + } + + spin_lock_init(&ts_data->irq_lock); + mutex_init(&ts_data->report_mutex); + + ret = fts_input_init(ts_data); + if (ret) { + FTS_ERROR("fts input initialize fail"); + goto err_input_init; + } + + ret = fts_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("[GPIO]Failed to configure the gpios"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get vdd/vcc_i2c regulator"); + goto err_power_init; + } + + ts_data->power_disabled = true; + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret) { + FTS_ERROR("fail to enable vdd/vcc_i2c regulator"); + goto err_power_ctrl; + } + +#if FTS_PINCTRL_EN + ret = fts_pinctrl_init(ts_data); + if (0 == ret) { + fts_pinctrl_select_normal(ts_data); + } +#endif +#endif + +#if (!FTS_CHIP_IDC) + fts_reset_proc(200); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + +#if FTS_APK_NODE_EN + ret = fts_create_apk_debug_channel(ts_data); + if (ret) { + FTS_ERROR("create apk debug node fail"); + } +#endif + +#if FTS_SYSFS_NODE_EN + ret = fts_create_sysfs(client); + if (ret) { + FTS_ERROR("create sysfs node fail"); + } +#endif + +#if FTS_POINT_REPORT_CHECK_EN + ret = fts_point_report_check_init(ts_data); + if (ret) { + FTS_ERROR("init point report check fail"); + } +#endif + + ret = fts_ex_mode_init(client); + if (ret) { + FTS_ERROR("init glove/cover/charger fail"); + } + +#if FTS_GESTURE_EN + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } +#endif + +#if FTS_TEST_EN + ret = fts_test_init(client); + if (ret) { + FTS_ERROR("init production test fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + +#if FTS_AUTO_UPGRADE_EN + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } +#endif + +#if defined(CONFIG_FB) + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } + ts_data->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret); + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL; + ts_data->early_suspend.suspend = fts_ts_early_suspend; + ts_data->early_suspend.resume = fts_ts_late_resume; + register_early_suspend(&ts_data->early_suspend); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_irq_req: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); +err_gpio_config: +#if FTS_POWER_SOURCE_CUST_EN +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + fts_power_source_ctrl(ts_data, DISABLE); +err_power_ctrl: + fts_power_source_release(ts_data); +err_power_init: +#endif + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); + input_unregister_device(ts_data->input_dev); +err_input_init: + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + devm_kfree(&client->dev, ts_data); + + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_ts_remove +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_ts_remove(struct i2c_client *client) +{ + struct fts_ts_data *ts_data = i2c_get_clientdata(client); + + FTS_FUNC_ENTER(); + +#if FTS_POINT_REPORT_CHECK_EN + fts_point_report_check_exit(ts_data); +#endif + +#if FTS_APK_NODE_EN + fts_release_apk_debug_channel(ts_data); +#endif + +#if FTS_SYSFS_NODE_EN + fts_remove_sysfs(client); +#endif + + fts_ex_mode_exit(client); + +#if FTS_AUTO_UPGRADE_EN + fts_fwupg_exit(ts_data); +#endif + +#if FTS_TEST_EN + fts_test_exit(client); +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_exit(ts_data); +#endif + +#if FTS_GESTURE_EN + fts_gesture_exit(client); +#endif + +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif + + free_irq(ts_data->irq, ts_data); + input_unregister_device(ts_data->input_dev); + + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); + + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#if FTS_POWER_SOURCE_CUST_EN +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + fts_power_source_ctrl(ts_data, DISABLE); + fts_power_source_release(ts_data); +#endif + + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); + + devm_kfree(&client->dev, ts_data); + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_ts_suspend +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_ts_suspend(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_FUNC_ENTER(); + if (ts_data->suspended) { + FTS_INFO("Already in suspend state"); + return 0; + } + + if (ts_data->fw_loading) { + FTS_INFO("fw upgrade in process, can't suspend"); + return 0; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(); +#endif + +#if FTS_GESTURE_EN + if (fts_gesture_suspend(ts_data->client) == 0) { + ts_data->suspended = true; + return 0; + } +#endif + + fts_irq_disable(); + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_ctrl(ts_data, DISABLE); + if (ret < 0) { + FTS_ERROR("power off fail, ret=%d", ret); + } +#if FTS_PINCTRL_EN + fts_pinctrl_select_suspend(ts_data); +#endif +#else + /* TP enter sleep mode */ + ret = fts_i2c_write_reg(ts_data->client, FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP_VALUE); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); +#endif + + ts_data->suspended = true; + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_ts_resume +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + + fts_release_all_finger(); + +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_ctrl(ts_data, ENABLE); +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif +#endif + + if (!ts_data->ic_info.is_incell) { + fts_reset_proc(200); + } + + fts_tp_state_recovery(ts_data->client); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#if FTS_GESTURE_EN + if (fts_gesture_resume(ts_data->client) == 0) { + ts_data->suspended = false; + return 0; + } +#endif + + ts_data->suspended = false; + fts_irq_enable(); + + FTS_FUNC_EXIT(); + return 0; +} + +static const struct dev_pm_ops focaltech_ts_pm_ops = { + .suspend = fts_ts_suspend, + .resume = fts_ts_resume, +}; + +/***************************************************************************** +* I2C Driver +*****************************************************************************/ +static const struct i2c_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, fts_ts_id); + +static struct of_device_id fts_match_table[] = { + { .compatible = "focaltech,fts", }, + { }, +}; + +static struct i2c_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = fts_match_table, + #if !defined(CONFIG_FB) && defined(CONFIG_PM) + .pm = &focaltech_ts_pm_ops, + #endif + }, + .id_table = fts_ts_id, +}; + +/***************************************************************************** +* Name: fts_ts_init +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = i2c_add_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_ts_exit +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void __exit fts_ts_exit(void) +{ + i2c_del_driver(&fts_ts_driver); +} + +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h new file mode 100644 index 000000000000..606963c150b2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h @@ -0,0 +1,254 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.h + +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_CORE_H__ +#define __LINUX_FOCALTECH_CORE_H__ +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_MAX_POINTS_SUPPORT 10 /* constant value, can't be changed */ +#define FTS_MAX_KEYS 4 +#define FTS_KEY_WIDTH 50 +#define FTS_ONE_TCH_LEN 6 + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_X_H_POS 3 +#define FTS_TOUCH_X_L_POS 4 +#define FTS_TOUCH_Y_H_POS 5 +#define FTS_TOUCH_Y_L_POS 6 +#define FTS_TOUCH_PRE_POS 7 +#define FTS_TOUCH_AREA_POS 8 +#define FTS_TOUCH_POINT_NUM 2 +#define FTS_TOUCH_EVENT_POS 3 +#define FTS_TOUCH_ID_POS 5 +#define FTS_COORDS_ARR_SIZE 4 +#define FTS_X_MIN_DISPLAY_DEFAULT 0 +#define FTS_Y_MIN_DISPLAY_DEFAULT 0 +#define FTS_X_MAX_DISPLAY_DEFAULT 1600 +#define FTS_Y_MAX_DISPLAY_DEFAULT 2176 + +#define FTS_TOUCH_DOWN 0 +#define FTS_TOUCH_UP 1 +#define FTS_TOUCH_CONTACT 2 +#define EVENT_DOWN(flag) ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag)) +#define EVENT_UP(flag) (FTS_TOUCH_UP == flag) +#define EVENT_NO_DOWN(data) (!data->point_num) +#define KEY_EN(data) (data->pdata->have_key) +#define TOUCH_IS_KEY(y, key_y) (y == key_y) +#define TOUCH_IN_RANGE(val, key_val, half) ((val > (key_val - half)) && (val < (key_val + half))) +#define TOUCH_IN_KEY(x, key_x) TOUCH_IN_RANGE(x, key_x, FTS_KEY_WIDTH) + +#define FTX_MAX_COMPATIBLE_TYPE 4 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_ts_platform_data { + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool have_key; + u32 key_number; + u32 keys[FTS_MAX_KEYS]; + u32 key_y_coord; + u32 key_x_coords[FTS_MAX_KEYS]; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 max_touch_number; +}; + +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 fts_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct fts_ts_platform_data *pdata; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct work_struct fwupg_work; + struct delayed_work esdcheck_work; + struct delayed_work prc_work; + struct work_struct resume_work; + struct regulator *vdd; + struct regulator *vcc_i2c; + spinlock_t irq_lock; + struct mutex report_mutex; + int irq; + bool suspended; + bool fw_loading; + bool irq_disabled; + bool power_disabled; + /* multi-touch */ + struct ts_event *events; + u8 *point_buf; + int pnt_buf_size; + int touchs; + bool key_down; + int touch_point; + int point_num; + struct proc_dir_entry *proc; + u8 proc_opmode; +#if FTS_PINCTRL_EN + struct pinctrl *pinctrl; + struct pinctrl_state *pins_active; + struct pinctrl_state *pins_suspend; + struct pinctrl_state *pins_release; +#endif +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_ts_data *fts_data; + +/* i2c communication*/ +int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue); +int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue); +int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen); +int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen); +void fts_i2c_hid2std(struct i2c_client *client); +int fts_i2c_init(void); +int fts_i2c_exit(void); + +/* Gesture functions */ +#if FTS_GESTURE_EN +int fts_gesture_init(struct fts_ts_data *ts_data); +int fts_gesture_exit(struct i2c_client *client); +void fts_gesture_recovery(struct i2c_client *client); +int fts_gesture_readdata(struct fts_ts_data *ts_data); +int fts_gesture_suspend(struct i2c_client *i2c_client); +int fts_gesture_resume(struct i2c_client *client); +#endif + +/* Apk and functions */ +#if FTS_APK_NODE_EN +int fts_create_apk_debug_channel(struct fts_ts_data *); +void fts_release_apk_debug_channel(struct fts_ts_data *); +#endif + +/* ADB functions */ +#if FTS_SYSFS_NODE_EN +int fts_create_sysfs(struct i2c_client *client); +int fts_remove_sysfs(struct i2c_client *client); +#endif + +/* ESD */ +#if FTS_ESDCHECK_EN +int fts_esdcheck_init(struct fts_ts_data *ts_data); +int fts_esdcheck_exit(struct fts_ts_data *ts_data); +int fts_esdcheck_switch(bool enable); +int fts_esdcheck_proc_busy(bool proc_debug); +int fts_esdcheck_set_intr(bool intr); +int fts_esdcheck_suspend(void); +int fts_esdcheck_resume(void); +#endif + +/* Production test */ +#if FTS_TEST_EN +int fts_test_init(struct i2c_client *client); +int fts_test_exit(struct i2c_client *client); +#endif + +/* Point Report Check*/ +#if FTS_POINT_REPORT_CHECK_EN +int fts_point_report_check_init(struct fts_ts_data *ts_data); +int fts_point_report_check_exit(struct fts_ts_data *ts_data); +void fts_prc_queue_work(struct fts_ts_data *ts_data); +#endif + +/* FW upgrade */ +int fts_upgrade_bin(struct i2c_client *client, char *fw_name, bool force); +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_wait_tp_to_valid(struct i2c_client *client); +void fts_tp_state_recovery(struct i2c_client *client); +int fts_ex_mode_init(struct i2c_client *client); +int fts_ex_mode_exit(struct i2c_client *client); +int fts_ex_mode_recovery(struct i2c_client *client); + +void fts_irq_disable(void); +void fts_irq_enable(void); + +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c new file mode 100644 index 000000000000..cb8e6ec18bcf --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c @@ -0,0 +1,552 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_esdcheck.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-03 +* +* Abstract: ESD check function +* +* Version: v1.0 +* +* Revision History: +* v1.0: +* First release. By luougojin 2016-08-03 +* v1.1: By luougojin 2017-02-15 +* 1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_ESDCHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define ESDCHECK_WAIT_TIME 1000 /* ms */ +#define LCD_ESD_PATCH 0 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_esdcheck_st { + u8 mode : 1; /* 1- need check esd 0- no esd check */ + u8 suspend : 1; + u8 proc_debug : 1; /* apk or adb is accessing I2C */ + u8 intr : 1; /* 1- Interrupt trigger */ + u8 unused : 4; + u8 flow_work_hold_cnt; /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */ + u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */ + u32 hardware_reset_cnt; + u32 i2c_nack_cnt; + u32 i2c_dataerror_cnt; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_esdcheck_st fts_esdcheck_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +#if LCD_ESD_PATCH +/***************************************************************************** +* Name: lcd_esdcheck +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int lcd_need_reset; +static int tp_need_recovery; /* LCD reset cause Tp reset */ +int idc_esdcheck_lcderror(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 val = 0; + struct i2c_client *client = ts_data->client; + + FTS_DEBUG("[ESD]Check LCD ESD"); + if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) { + tp_need_recovery = 0; + /* LCD reset, need recover TP state */ + fts_tp_state_recovery(client); + } + + ret = fts_i2c_read_reg(client, FTS_REG_ESD_SATURATE, &val); + if ( ret < 0) { + FTS_ERROR("[ESD]: Read ESD_SATURATE(0xED) failed ret=%d!", ret); + return -EIO; + } + + if (val == 0xAA) { + /* + * 1. Set flag lcd_need_reset = 1; + * 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0 + * 3. recover TP state + */ + FTS_INFO("LCD ESD, Execute LCD reset!"); + lcd_need_reset = 1; + tp_need_recovery = 1; + } + + return 0; +} +#endif + +/***************************************************************************** +* Name: fts_esdcheck_tp_reset +* Brief: esd check algorithm +* Input: +* Output: +* Return: +*****************************************************************************/ +static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.hardware_reset_cnt++; + + fts_reset_proc(200); + fts_tp_state_recovery(ts_data->client); + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: get_chip_id +* Brief: Read Chip Id 3 times +* Input: +* Output: +* Return: 1 - Read Chip Id 3 times failed +* 0 - Read Chip Id pass +*****************************************************************************/ +static bool get_chip_id(struct fts_ts_data *ts_data) +{ + int ret = 0; + int i = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + struct i2c_client *client = ts_data->client; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + reg_addr = FTS_REG_CHIP_ID; + ret = fts_i2c_read(client, ®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!", ret); + fts_esdcheck_data.i2c_nack_cnt++; + } else { + if (reg_value == chip_id) { + break; + } else { + fts_esdcheck_data.i2c_dataerror_cnt++; + } + } + msleep(10); + } + + /* if can't get correct data in 3 times, then need hardware reset */ + if (i >= 3) { + FTS_ERROR("[ESD]: Read Chip id 3 times failed, need execute TP reset!!"); + return 1; + } + + return 0; +} + +/***************************************************************************** +* Name: get_flow_cnt +* Brief: Read flow cnt(0x91) +* Input: +* Output: +* Return: 1 - Reg 0x91(flow cnt) abnormal: hold a value for 5 times +* 0 - Reg 0x91(flow cnt) normal +*****************************************************************************/ +static bool get_flow_cnt(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + struct i2c_client *client = ts_data->client; + + reg_addr = FTS_REG_FLOW_WORK_CNT; + ret = fts_i2c_read(client, ®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", ret); + fts_esdcheck_data.i2c_nack_cnt++; + } else { + if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) { + fts_esdcheck_data.flow_work_hold_cnt++; + } else { + fts_esdcheck_data.flow_work_hold_cnt = 0; + } + + fts_esdcheck_data.flow_work_cnt_last = reg_value; + } + + /* if read flow work cnt 5 times and the value are all the same, then need hardware_reset */ + if (fts_esdcheck_data.flow_work_hold_cnt >= 5) { + FTS_DEBUG("[ESD]: Flow Work Cnt(reg0x91) keep a value for 5 times, need execute TP reset!!"); + return 1; + } + + return 0; +} + +/***************************************************************************** +* Name: esdcheck_algorithm +* Brief: esd check algorithm +* Input: +* Output: +* Return: +*****************************************************************************/ +static int esdcheck_algorithm(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + bool hardware_reset = 0; + struct i2c_client *client = ts_data->client; + + /* 1. esdcheck is interrupt, then return */ + if (fts_esdcheck_data.intr == 1) { + FTS_DEBUG("[ESD]: In interrupt state, not check esd, return immediately!!"); + return 0; + } + + /* 2. check power state, if suspend, no need check esd */ + if (fts_esdcheck_data.suspend == 1) { + FTS_DEBUG("[ESD]: In suspend, not check esd, return immediately!!"); + /* because in suspend state, adb can be used, when upgrade FW, will active ESD check(active = 1) + * But in suspend, then will don't queue_delayed_work, when resume, don't check ESD again + */ + return 0; + } + + /* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/ + if (fts_esdcheck_data.proc_debug == 1) { + FTS_INFO("[ESD]: In apk or adb command mode, not check esd, return immediately!!"); + return 0; + } + + /* 4. In factory mode, can't check esd */ + reg_addr = FTS_REG_WORKMODE; + ret = fts_i2c_read(client, ®_addr, 1, ®_value, 1); + if ( ret < 0 ) { + fts_esdcheck_data.i2c_nack_cnt++; + } else if ( (reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) { + FTS_DEBUG("[ESD]: not in work mode, no check esd, return immediately!!"); + return 0; + } + + /* 5. IDC esd check lcd default:close */ +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + + /* 6. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */ + if (!hardware_reset) { + hardware_reset = get_flow_cnt(ts_data); + } + + /* 8. If need hardware reset, then handle it here */ + if ( hardware_reset == 1) { + fts_esdcheck_tp_reset(ts_data); + } + + FTS_DEBUG("[ESD]: NoACK=%d, Error Data=%d, Hardware Reset=%d", fts_esdcheck_data.i2c_nack_cnt, fts_esdcheck_data.i2c_dataerror_cnt, fts_esdcheck_data.hardware_reset_cnt); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_func +* Brief: fts_esdcheck_func +* Input: +* Output: +* Return: +*****************************************************************************/ +static void esdcheck_func(struct work_struct *work) +{ + u8 val = 0; + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, esdcheck_work.work); + + FTS_FUNC_ENTER(); + if (ENABLE == fts_esdcheck_data.mode) { + if (ts_data->ic_info.is_incell) { + fts_i2c_read_reg(ts_data->client, FTS_REG_ESDCHECK_DISABLE, &val); + if (0xA5 == val) { + fts_esdcheck_data.mode = DISABLE; + return; + } + } + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_esdcheck_set_intr +* Brief: interrupt flag (main used in interrupt tp report) +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_set_intr(bool intr) +{ + /* interrupt don't add debug message */ + fts_esdcheck_data.intr = intr; + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_get_status(void) +* Brief: get current status +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_get_status(void) +{ + /* interrupt don't add debug message */ + return fts_esdcheck_data.mode; +} + +/***************************************************************************** +* Name: fts_esdcheck_proc_busy +* Brief: When APK or ADB command access TP via driver, then need set proc_debug, +* then will not check ESD. +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_proc_busy(bool proc_debug) +{ + fts_esdcheck_data.proc_debug = proc_debug; + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_switch(bool enable) +{ + struct fts_ts_data *ts_data = fts_data; + FTS_FUNC_ENTER(); + if (fts_esdcheck_data.mode == ENABLE) { + if (enable) { + FTS_DEBUG("[ESD]: ESD check start!!"); + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.flow_work_cnt_last = 0; + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } else { + FTS_DEBUG("[ESD]: ESD check stop!!"); + cancel_delayed_work(&ts_data->esdcheck_work); + } + } else { + FTS_DEBUG("[ESD]: ESD should disable!!"); + cancel_delayed_work(&ts_data->esdcheck_work); + } + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_suspend +* Brief: Run when tp enter into suspend +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_suspend(void) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(DISABLE); + fts_esdcheck_data.suspend = 1; + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_resume +* Brief: Run when tp resume +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_resume( void ) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ENABLE); + fts_esdcheck_data.suspend = 0; + FTS_FUNC_EXIT(); + return 0; +} + +/************************************************************************ +* Name: fts_esdcheck_store +* Brief: no +* Input: device, device attribute, char buf, char count +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_esdcheck_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_switch(ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_data.mode = DISABLE; + fts_esdcheck_switch(DISABLE); + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +/************************************************************************ +* Name: fts_esdcheck_show +* Brief: no +* Input: device, device attribute, char buf +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_esdcheck_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", fts_esdcheck_get_status() ? "On" : "Off"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* sysfs esd node + * read example: cat fts_esd_mode ---read esd mode + * write example:echo 01 > fts_esd_mode ---make esdcheck enable + * + */ +static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store); + +static struct attribute *fts_esd_mode_attrs[] = { + + &dev_attr_fts_esd_mode.attr, + NULL, +}; + +static struct attribute_group fts_esd_group = { + .attrs = fts_esd_mode_attrs, +}; +/***************************************************************************** +* Name: fts_create_gesture_sysfs +* Brief: +* Input: +* Output: +* Return: 0-success or others-error +*****************************************************************************/ +int fts_create_esd_sysfs(struct i2c_client *client) +{ + int ret = 0; + + ret = sysfs_create_group(&client->dev.kobj, &fts_esd_group); + if ( ret != 0) { + FTS_ERROR("fts_create_esd_sysfs(sysfs) create failed!"); + sysfs_remove_group(&client->dev.kobj, &fts_esd_group); + return ret; + } + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_init +* Brief: Init and create a queue work to check esd +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_esdcheck_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run esd check function"); + return -EINVAL; + } + + memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st)); + + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_switch(ENABLE); + fts_create_esd_sysfs(ts_data->client); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_exit +* Brief: When FTS TP driver is removed, then call this function to destory work queue +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_ESDCHECK_EN */ + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c new file mode 100644 index 000000000000..06511c515869 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_fun.c @@ -0,0 +1,1130 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: Focaltech_ex_fun.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +/*create apk debug channel*/ +#define PROC_UPGRADE 0 +#define PROC_READ_REGISTER 1 +#define PROC_WRITE_REGISTER 2 +#define PROC_AUTOCLB 4 +#define PROC_UPGRADE_INFO 5 +#define PROC_WRITE_DATA 6 +#define PROC_READ_DATA 7 +#define PROC_SET_TEST_FLAG 8 +#define PROC_SET_SLAVE_ADDR 10 +#define PROC_HW_RESET 11 +#define PROC_NAME "ftxxxx-debug" +#define PROC_BUF_SIZE 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ +#if FTS_SYSFS_NODE_EN +enum { + RWREG_OP_READ = 0, + RWREG_OP_WRITE = 1, +}; +static struct rwreg_operation_t { + int type; /* 0: read, 1: write */ + int reg; /* register */ + int len; /* read/write length */ + int val; /* length = 1; read: return value, write: op return */ + int res; /* 0: success, otherwise: fail */ + char *opbuf; /* length >= 1, read return value, write: op return */ +} rw_op; +#endif + +#if FTS_APK_NODE_EN + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +/************************************************************************ +* Name: fts_debug_write +* Brief:interface of write proc +* Input: file point, data buf, data len, no use +* Output: no +* Return: data len +***********************************************************************/ +static ssize_t fts_debug_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[25]; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + 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; + } + + ts_data->proc_opmode = writebuf[0]; + + switch (ts_data->proc_opmode) { + case PROC_SET_TEST_FLAG: + FTS_INFO("[APK]: PROC_SET_TEST_FLAG = %x!!", writebuf[1]); +#if FTS_ESDCHECK_EN + if (writebuf[1] == 0) { + fts_esdcheck_switch(ENABLE); + } else { + fts_esdcheck_switch(DISABLE); + } +#endif + break; + case PROC_READ_REGISTER: + writelen = 1; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + goto proc_write_err; + } + break; + case PROC_WRITE_REGISTER: + writelen = 2; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + goto proc_write_err; + } + break; + case PROC_SET_SLAVE_ADDR: +#if (FTS_CHIP_TYPE == _FT8201) + FTS_INFO("Original i2c addr 0x%x", client->addr << 1); + if (writebuf[1] != client->addr) { + client->addr = writebuf[1]; + FTS_INFO("Change i2c addr 0x%x to 0x%x", client->addr << 1, writebuf[1] << 1); + } +#endif + break; + + case PROC_HW_RESET: + snprintf(tmp, 25, "%s", writebuf + 1); + if (buflen > 25) { + FTS_INFO("PROC_HW_RESET bufflen %d is too long\n", buflen); + break; + } + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + break; + + case PROC_READ_DATA: + case PROC_WRITE_DATA: + writelen = buflen - 1; + if (writelen > 0) { + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + goto proc_write_err; + } + } + break; + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +/************************************************************************ +* Name: fts_debug_read +* Brief:interface of read proc +* Input: point to the data, no use, no use, read len, no use, no use +* Output: page point to data +* Return: read char number +***********************************************************************/ +static ssize_t fts_debug_read(struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *buf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + buf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == buf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + buf = tmpbuf; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + switch (ts_data->proc_opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_i2c_read(client, NULL, 0, buf, num_read_chars); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + goto proc_read_err; + } + break; + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_i2c_read(client, NULL, 0, buf, num_read_chars); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + goto proc_read_err; + } + break; + case PROC_WRITE_DATA: + break; + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + if (copy_to_user(buff, buf, num_read_chars)) { + FTS_ERROR("[APK]: copy to user error!!"); + ret = -EFAULT; + goto proc_read_err; + } + + ret = num_read_chars; +proc_read_err: + if ((buflen > PROC_BUF_SIZE) && buf) { + kfree(buf); + buf = NULL; + } + return ret; +} + +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .write = fts_debug_write, +}; + +#else +/* interface of write proc */ +/************************************************************************ +* Name: fts_debug_write +* Brief:interface of write proc +* Input: file point, data buf, data len, no use +* Output: no +* Return: data len +***********************************************************************/ +static int fts_debug_write(struct file *filp, + const char __user *buff, unsigned long len, void *data) +{ + int ret = 0; + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = len; + int writelen = 0; + char tmp[25]; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + 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; + } + + ts_data->proc_opmode = writebuf[0]; + + switch (ts_data->proc_opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x!!", writebuf[1]); +#if FTS_ESDCHECK_EN + if (writebuf[1] == 0) { + fts_esdcheck_switch(ENABLE); + } else { + fts_esdcheck_switch(DISABLE); + } +#endif + break; + case PROC_READ_REGISTER: + writelen = 1; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!n"); + goto proc_write_err; + } + break; + case PROC_WRITE_REGISTER: + writelen = 2; + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + goto proc_write_err; + } + break; + case PROC_SET_SLAVE_ADDR: +#if (FTS_CHIP_TYPE == _FT8201) + ret = client->addr; + FTS_DEBUG("Original i2c addr 0x%x ", ret << 1 ); + if (writebuf[1] != client->addr) { + client->addr = writebuf[1]; + FTS_DEBUG("Change i2c addr 0x%x to 0x%x", ret << 1, writebuf[1] << 1); + } +#endif + break; + + case PROC_HW_RESET: + snprintf(tmp, PAGE_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("Begin HW Reset"); + fts_reset_proc(0); + } + break; + + case PROC_READ_DATA: + case PROC_WRITE_DATA: + writelen = buflen - 1; + if (writelen > 0) { + ret = fts_i2c_write(client, writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("[APK]: write iic error!!"); + goto proc_write_err; + } + } + break; + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +/* interface of read proc */ +/************************************************************************ +* Name: fts_debug_read +* Brief:interface of read proc +* Input: point to the data, no use, no use, read len, no use, no use +* Output: page point to data +* Return: read char number +***********************************************************************/ +static int fts_debug_read( char *page, char **start, + off_t off, int count, int *eof, void *data ) +{ + int ret = 0; + u8 *buf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int num_read_chars = 0; + int buflen = count; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client = ts_data->client; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + buf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == buf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + buf = tmpbuf; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + switch (ts_data->proc_opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_i2c_read(client, NULL, 0, buf, num_read_chars); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + goto proc_read_err; + } + break; + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_i2c_read(client, NULL, 0, buf, num_read_chars); + if (ret < 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + FTS_ERROR("[APK]: read iic error!!"); + goto proc_read_err; + } + break; + case PROC_WRITE_DATA: + break; + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + memcpy(page, buf, num_read_chars); + + ret = num_read_chars; +proc_read_err: + if ((buflen > PROC_BUF_SIZE) && buf) { + kfree(buf); + buf = NULL; + } + return ret; +} +#endif /* LINUX_VERSION_CODE */ + +/************************************************************************ +* Name: fts_create_apk_debug_channel +* Brief: create apk debug channel +* Input: i2c info +* Output: +* Return: return 0 if success +***********************************************************************/ +int fts_create_apk_debug_channel(struct fts_ts_data *ts_data) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + ts_data->proc = proc_create(PROC_NAME, 0664, NULL, &fts_proc_fops); +#else + ts_data->proc = create_proc_entry(PROC_NAME, 0664, NULL); +#endif + if (NULL == ts_data->proc) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } else { + FTS_INFO("Create proc entry success!"); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) + ts_data->proc->write_proc = fts_debug_write; + ts_data->proc->read_proc = fts_debug_read; +#endif + } + + return 0; +} + +/************************************************************************ +* Name: fts_release_apk_debug_channel +* Brief: release apk debug channel +* Input: +* Output: +* Return: +***********************************************************************/ +void fts_release_apk_debug_channel(struct fts_ts_data *ts_data) +{ + + if (ts_data->proc) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc_remove(ts_data->proc); +#else + remove_proc_entry(PROC_NAME, NULL); +#endif + } +} +#endif /* FTS_APK_NODE_EN */ + +#if FTS_SYSFS_NODE_EN + +/************************************************************************ + * sysfs interface + ***********************************************************************/ + +/* + * fts_hw_reset interface + */ +static ssize_t fts_hw_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t fts_hw_reset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_dev = fts_data->input_dev; + ssize_t count = 0; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + count = snprintf(buf, PAGE_SIZE, "hw reset executed\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* + * fts_irq interface + */ +static ssize_t fts_irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[EX-FUN]enable irq"); + fts_irq_enable(); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]disable irq"); + fts_irq_disable(); + } + mutex_unlock(&input_dev->mutex); + return count; +} + +static ssize_t fts_irq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +/* + * fts_tpfwver interface + */ +static ssize_t fts_tpfwver_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + struct i2c_client *client = ts_data->client; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + mutex_lock(&input_dev->mutex); + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + if (fts_i2c_read_reg(client, FTS_REG_FW_VER, &fwver) < 0) { + num_read_chars = snprintf(buf, PAGE_SIZE, "I2c transfer error!\n"); + goto error; + } +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + if ((fwver == 0xFF) || (fwver == 0x00)) + num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n"); + else + num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver); + +error: + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +static ssize_t fts_tpfwver_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/************************************************************************ +* Name: fts_tprwreg_show +* Brief: no +* Input: device, device attribute, char buf +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_tprwreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (rw_op.len < 0) { + count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n"); + } else if (rw_op.len == 1) { + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } else { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } + } else { + if (RWREG_OP_READ == rw_op.type) { + count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len); + count += snprintf(buf + count, PAGE_SIZE, "Result: "); + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res); + } else { + if (rw_op.opbuf) { + for (i = 0; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%d ", 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, "%d ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res); + } else { + count += snprintf(buf + count, PAGE_SIZE, "Result: success\n"); + } + } + /*if (rw_op.opbuf) { + kfree(rw_op.opbuf); + rw_op.opbuf = NULL; + }*/ + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static int shex_to_int(const char *hex_buf, int size) +{ + int i; + int base = 1; + int value = 0; + char single; + + for (i = size - 1; i >= 0; i--) { + single = hex_buf[i]; + + if ((single >= '0') && (single <= '9')) { + value += (single - '0') * base; + } else if ((single >= 'a') && (single <= 'z')) { + value += (single - 'a' + 10) * base; + } else if ((single >= 'A') && (single <= 'Z')) { + value += (single - 'A' + 10) * base; + } else { + return -EINVAL; + } + + base *= 16; + } + + return value; +} + + +static u8 shex_to_u8(const char *hex_buf, int size) +{ + return (u8)shex_to_int(hex_buf, size); +} +/* + * Format buf: + * [0]: '0' write, '1' read(reserved) + * [1-2]: addr, hex + * [3-4]: length, hex + * [5-6]...[n-(n+1)]: data, hex + */ +static int fts_parse_buf(const char *buf, size_t cmd_len) +{ + int length; + int i; + char *tmpbuf; + + rw_op.reg = shex_to_u8(buf + 1, 2); + length = shex_to_int(buf + 3, 2); + + if (buf[0] == '1') { + rw_op.len = length; + rw_op.type = RWREG_OP_READ; + FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len); + } else { + if (cmd_len < (length * 2 + 5)) { + pr_err("data invalided!\n"); + return -EINVAL; + } + FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length); + + /* first byte is the register addr */ + rw_op.type = RWREG_OP_WRITE; + rw_op.len = length + 1; + } + + if (rw_op.len > 0) { + tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("allocate memory failed!\n"); + return -ENOMEM; + } + + if (RWREG_OP_WRITE == rw_op.type) { + tmpbuf[0] = rw_op.reg & 0xFF; + FTS_DEBUG("write buffer: "); + for (i = 1; i < rw_op.len; i++) { + tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2); + FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF); + } + } + rw_op.opbuf = tmpbuf; + } + + return rw_op.len; +} + +/************************************************************************ +* Name: fts_tprwreg_store +* Brief: read/write register +* Input: device, device attribute, char buf, char count +* Output: print register value +* Return: char count +***********************************************************************/ +static ssize_t fts_tprwreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + ssize_t cmd_length = 0; + + mutex_lock(&input_dev->mutex); + cmd_length = count - 1; + + if (rw_op.opbuf) { + kfree(rw_op.opbuf); + rw_op.opbuf = NULL; + } + + FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf); + /* compatible old ops */ + if (2 == cmd_length) { + rw_op.type = RWREG_OP_READ; + rw_op.len = 1; + + rw_op.reg = shex_to_int(buf, 2); + } else if (4 == cmd_length) { + rw_op.type = RWREG_OP_WRITE; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + rw_op.val = shex_to_int(buf + 2, 2); + + } else if (cmd_length < 5) { + FTS_ERROR("Invalid cmd buffer"); + mutex_unlock(&input_dev->mutex); + return -EINVAL; + } else { + rw_op.len = fts_parse_buf(buf, cmd_length); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + if (rw_op.len < 0) { + FTS_ERROR("cmd buffer error!"); + + } else { + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + rw_op.res = fts_i2c_read_reg(client, reg, &val); + rw_op.val = val; + } else { + char reg; + reg = rw_op.reg & 0xFF; + + rw_op.res = fts_i2c_read(client, ®, 1, rw_op.opbuf, rw_op.len); + } + + if (rw_op.res < 0) { + FTS_ERROR("Could not read 0x%02x", rw_op.reg); + } else { + FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len); + rw_op.res = 0; + } + + } else { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + val = rw_op.val & 0xFF; + rw_op.res = fts_i2c_write_reg(client, reg, val); + } else { + rw_op.res = fts_i2c_write(client, rw_op.opbuf, rw_op.len); + } + if (rw_op.res < 0) { + FTS_ERROR("Could not write 0x%02x", rw_op.reg); + + } else { + FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len); + rw_op.res = 0; + } + } + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* + * fts_upgrade_bin interface + */ +static ssize_t fts_fwupgradebin_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + struct i2c_client *client = ts_data->client; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, sizeof(fwname), "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + fts_upgrade_bin(client, fwname, 0); + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + ts_data->fw_loading = 0; + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* + * fts_force_upgrade interface + */ +static ssize_t fts_fwforceupg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + struct i2c_client *client = ts_data->client; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, sizeof(fwname), "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + fts_upgrade_bin(client, fwname, 1); + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + ts_data->fw_loading = 0; + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* + * fts_driver_info interface + */ +static ssize_t fts_driverinfo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = fts_data; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", FTS_DRIVER_VERSION); + + count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n", + pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max); + + count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", pdata->max_touch_number); + + count += snprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_driverinfo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* + * fts_dump_reg interface + */ +static ssize_t fts_dumpreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t fts_dumpreg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + fts_i2c_read_reg(client, FTS_REG_POWER_MODE, &val); + count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_LIC_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_IDE_PARA_VER_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_LCD_BUSY_NUM, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_CHARGER_MODE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_INT_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val); + + fts_i2c_read_reg(client, FTS_REG_FLOW_WORK_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + mutex_unlock(&input_dev->mutex); + return count; +} + +/* 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); + +/* 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, + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +/************************************************************************ +* Name: fts_create_sysfs +* Brief: create sysfs interface +* Input: +* Output: +* Return: return 0 if success +***********************************************************************/ +int fts_create_sysfs(struct i2c_client *client) +{ + int ret = 0; + + ret = sysfs_create_group(&client->dev.kobj, &fts_attribute_group); + if (ret) { + FTS_ERROR("[EX]: sysfs_create_group() failed!!"); + sysfs_remove_group(&client->dev.kobj, &fts_attribute_group); + return -ENOMEM; + } else { + FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); + } + + return ret; +} +/************************************************************************ +* Name: fts_remove_sysfs +* Brief: remove sysfs interface +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_remove_sysfs(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &fts_attribute_group); + return 0; +} +#endif diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c new file mode 100644 index 000000000000..bca972ebb040 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c @@ -0,0 +1,382 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_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 +*****************************************************************************/ +struct fts_mode_flag { + int fts_glove_mode_flag; + int fts_cover_mode_flag; + int fts_charger_mode_flag; +}; + +struct fts_mode_flag g_fts_mode_flag; + +/***************************************************************************** +* 4.Static variables +*****************************************************************************/ + +/***************************************************************************** +* 5.Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_enter_glove_mode(struct i2c_client *client, int mode ); +int fts_enter_cover_mode(struct i2c_client *client, int mode ); +int fts_enter_charger_mode(struct i2c_client *client, int mode ); + +/***************************************************************************** +* 6.Static function prototypes +*******************************************************************************/ + +#if FTS_GLOVE_EN +static ssize_t fts_touch_glove_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_GLOVE_MODE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Glove Mode: %s\n", g_fts_mode_flag.fts_glove_mode_flag ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Glove Reg(0xC0) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touch_glove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client; + + + client = ts_data->client; + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!g_fts_mode_flag.fts_glove_mode_flag) { + FTS_INFO("[Mode]enter glove mode"); + ret = fts_enter_glove_mode(client, true); + if (ret >= 0) { + g_fts_mode_flag.fts_glove_mode_flag = true; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (g_fts_mode_flag.fts_glove_mode_flag) { + FTS_INFO("[Mode]exit glove mode"); + ret = fts_enter_glove_mode(client, false); + if (ret >= 0) { + g_fts_mode_flag.fts_glove_mode_flag = false; + } + } + } + FTS_INFO("[Mode]glove mode status: %d", g_fts_mode_flag.fts_glove_mode_flag); + return count; +} + +/************************************************************************ +* Name: fts_enter_glove_mode +* Brief: change glove mode +* Input: glove mode +* Output: no +* Return: success >=0, otherwise failed +***********************************************************************/ +int fts_enter_glove_mode( struct i2c_client *client, int mode) +{ + int ret = 0; + static u8 buf_addr[2] = { 0 }; + static u8 buf_value[2] = { 0 }; + buf_addr[0] = FTS_REG_GLOVE_MODE_EN; /* glove control */ + + if (mode) + buf_value[0] = 0x01; + else + buf_value[0] = 0x00; + + ret = fts_i2c_write_reg( client, buf_addr[0], buf_value[0]); + if (ret < 0) { + FTS_ERROR("[Mode]fts_enter_glove_mode write value fail"); + } + + return ret ; + +} + +/* read and write glove mode +* read example: cat fts_touch_glove_mode---read glove mode +* write example:echo 01 > fts_touch_glove_mode ---write glove mode to 01 +* +*/ +static DEVICE_ATTR (fts_glove_mode, S_IRUGO | S_IWUSR, fts_touch_glove_show, fts_touch_glove_store); + +#endif + +#if FTS_COVER_EN +static ssize_t fts_touch_cover_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_COVER_MODE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Cover Mode: %s\n", g_fts_mode_flag.fts_cover_mode_flag ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Cover Reg(0xC1) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touch_cover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client; + + client = ts_data->client; + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!g_fts_mode_flag.fts_cover_mode_flag) { + FTS_INFO("[Mode]enter cover mode"); + ret = fts_enter_cover_mode(client, true); + if (ret >= 0) { + g_fts_mode_flag.fts_cover_mode_flag = true; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (g_fts_mode_flag.fts_cover_mode_flag) { + FTS_INFO("[Mode]exit cover mode"); + ret = fts_enter_cover_mode(client, false); + if (ret >= 0) { + g_fts_mode_flag.fts_cover_mode_flag = false; + } + } + } + FTS_INFO("[Mode]cover mode status: %d", g_fts_mode_flag.fts_cover_mode_flag); + return count; +} + +/************************************************************************ +* Name: fts_enter_cover_mode +* Brief: change cover mode +* Input: cover mode +* Output: no +* Return: success >=0, otherwise failed +***********************************************************************/ +int fts_enter_cover_mode( struct i2c_client *client, int mode) +{ + int ret = 0; + static u8 buf_addr[2] = { 0 }; + static u8 buf_value[2] = { 0 }; + buf_addr[0] = FTS_REG_COVER_MODE_EN; /* cover control */ + + if (mode) + buf_value[0] = 0x01; + else + buf_value[0] = 0x00; + + ret = fts_i2c_write_reg( client, buf_addr[0], buf_value[0]); + if (ret < 0) { + FTS_ERROR("[Mode] fts_enter_cover_mode write value fail \n"); + } + + return ret ; + +} + +/* read and write cover mode +* read example: cat fts_touch_cover_mode---read cover mode +* write example:echo 01 > fts_touch_cover_mode ---write cover mode to 01 +* +*/ +static DEVICE_ATTR (fts_cover_mode, S_IRUGO | S_IWUSR, fts_touch_cover_show, fts_touch_cover_store); + +#endif + +#if FTS_CHARGER_EN +static ssize_t fts_touch_charger_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_CHARGER_MODE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Charge Mode: %s\n", g_fts_mode_flag.fts_charger_mode_flag ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Charge Reg(0x8B) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touch_charger_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct fts_ts_data *ts_data = fts_data; + struct i2c_client *client; + + client = ts_data->client; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!g_fts_mode_flag.fts_charger_mode_flag) { + FTS_INFO("[Mode]enter charger mode"); + ret = fts_enter_charger_mode(client, true); + if (ret >= 0) { + g_fts_mode_flag.fts_charger_mode_flag = true; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (g_fts_mode_flag.fts_charger_mode_flag) { + FTS_INFO("[Mode]exit charger mode"); + ret = fts_enter_charger_mode(client, false); + if (ret >= 0) { + g_fts_mode_flag.fts_charger_mode_flag = false; + } + } + } + FTS_INFO("[Mode]charger mode status: %d", g_fts_mode_flag.fts_charger_mode_flag); + return count; +} + +/************************************************************************ +* Name: fts_enter_charger_mode +* Brief: change charger mode +* Input: charger mode +* Output: no +* Return: success >=0, otherwise failed +***********************************************************************/ +int fts_enter_charger_mode(struct i2c_client *client, int mode) +{ + int ret = 0; + static u8 buf_addr[2] = { 0 }; + static u8 buf_value[2] = { 0 }; + buf_addr[0] = FTS_REG_CHARGER_MODE_EN; /* charger control */ + + if (mode) + buf_value[0] = 0x01; + else + buf_value[0] = 0x00; + + ret = fts_i2c_write_reg( client, buf_addr[0], buf_value[0]); + if (ret < 0) { + FTS_DEBUG("[Mode]fts_enter_charger_mode write value fail"); + } + + return ret ; + +} + +/* read and write charger mode +* read example: cat fts_touch_charger_mode---read charger mode +* write example:echo 01 > fts_touch_charger_mode ---write charger mode to 01 +* +*/ +static DEVICE_ATTR (fts_charger_mode, S_IRUGO | S_IWUSR, fts_touch_charger_show, fts_touch_charger_store); + +#endif + +static struct attribute *fts_touch_mode_attrs[] = { +#if FTS_GLOVE_EN + &dev_attr_fts_glove_mode.attr, +#endif + +#if FTS_COVER_EN + &dev_attr_fts_cover_mode.attr, +#endif + +#if FTS_CHARGER_EN + &dev_attr_fts_charger_mode.attr, +#endif + + NULL, +}; + +static struct attribute_group fts_touch_mode_group = { + .attrs = fts_touch_mode_attrs, +}; + +int fts_ex_mode_init(struct i2c_client *client) +{ + int err = 0; + + g_fts_mode_flag.fts_glove_mode_flag = false; + g_fts_mode_flag.fts_cover_mode_flag = false; + g_fts_mode_flag.fts_charger_mode_flag = false; + + err = sysfs_create_group(&client->dev.kobj, &fts_touch_mode_group); + if (0 != err) { + FTS_ERROR("[Mode]create sysfs failed."); + sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group); + return -EIO; + } else { + FTS_DEBUG("[Mode]create sysfs succeeded"); + } + + return err; + +} + +int fts_ex_mode_exit(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group); + return 0; +} + +int fts_ex_mode_recovery(struct i2c_client *client) +{ + int ret = 0; +#if FTS_GLOVE_EN + if (g_fts_mode_flag.fts_glove_mode_flag) + ret = fts_enter_glove_mode(client, true); +#endif + +#if FTS_COVER_EN + if (g_fts_mode_flag.fts_cover_mode_flag) + ret = fts_enter_cover_mode(client, true); +#endif + +#if FTS_CHARGER_EN + if (g_fts_mode_flag.fts_charger_mode_flag) + ret = fts_enter_charger_mode(client, true); +#endif + + return ret; +} + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c new file mode 100644 index 000000000000..43672a78ddac --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.c @@ -0,0 +1,1971 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_flash.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +/* Upgrade FW/PRAMBOOT/LCD CFG */ +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_fw fw_list[] = { + {FTS_VENDOR_ID, fw_file, sizeof(fw_file)}, + {FTS_VENDOR_ID2, fw_file2, sizeof(fw_file2)}, + {FTS_VENDOR_ID3, fw_file3, sizeof(fw_file3)}, +}; + +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft8006m, +}; + +struct fts_upgrade *fwupgrade = NULL; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +u16 fts_crc16_calc_host(u8 *pbuf, u16 length) +{ + u16 ecc = 0; + u16 i = 0; + u16 j = 0; + + for ( i = 0; i < length; i += 2 ) { + ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF); + else + ecc >>= 1; + } + } + + return ecc; +} + +static u16 fts_pram_ecc_calc_host(u8 *pbuf, u16 length) +{ + return fts_crc16_calc_host(pbuf, length); +} + + +/************************************************************************ + * fts_pram_ecc_cal - Calculate and get pramboot ecc + * + * return pramboot ecc of tp if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_ecc_cal_algo( + struct i2c_client *client, + u32 start_addr, + u32 ecc_length) +{ + int ret = 0; + int i = 0; + int ecc = 0; + u8 val[2] = { 0 }; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_INFO("read out pramboot checksum"); + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(start_addr); + cmd[2] = BYTE_OFF_8(start_addr); + cmd[3] = BYTE_OFF_0(start_addr); + cmd[4] = BYTE_OFF_16(ecc_length); + cmd[5] = BYTE_OFF_8(ecc_length); + cmd[6] = BYTE_OFF_0(ecc_length); + ret = fts_i2c_write(client, cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("write pramboot ecc cal cmd fail"); + return ret; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < 100; i++) { + msleep(1); + ret = fts_i2c_read(client, cmd, 1, val, 1); + if (ret < 0) { + FTS_ERROR("ecc_finish read cmd fail"); + return ret; + } + if (0 == val[0]) + break; + } + if (i >= 100) { + FTS_ERROR("wait ecc finish fail"); + return -EIO; + } + + cmd[0] = FTS_CMD_READ_ECC; + ret = fts_i2c_read(client, cmd, 1, val, 2); + if (ret < 0) { + FTS_ERROR("read pramboot ecc fail"); + return ret; + } + + ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF; + return ecc; +} + +static int fts_pram_ecc_cal_xor(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + + FTS_INFO("read out pramboot checksum"); + + ret = fts_i2c_read_reg(client, FTS_ROMBOOT_CMD_ECC, ®_val); + if (ret < 0) { + FTS_ERROR("read pramboot ecc fail"); + return ret; + } + + return (int)reg_val; +} + +static int fts_pram_ecc_cal(struct i2c_client *client, u32 saddr, u32 len) +{ + if ((NULL == fwupgrade) || (NULL == fwupgrade->func)) { + FTS_ERROR("fwupgrade/func is null"); + return -EINVAL; + } + + if (fwupgrade->func->newmode) { + return fts_pram_ecc_cal_algo(client, saddr, len); + } else { + return fts_pram_ecc_cal_xor(client); + } +} + +/************************************************************************ + * fts_pram_write_buf - write pramboot data and calculate ecc + * + * return pramboot ecc of host if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_write_buf(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number; + u32 packet_len = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_tmp = 0; + int ecc_in_host = 0; + + FTS_INFO("write pramboot to pram"); + if ((NULL == fwupgrade) || (NULL == fwupgrade->func)) { + FTS_ERROR("fwupgrade/func is null"); + return -EINVAL; + } + + if (NULL == buf) { + FTS_ERROR("pramboot buf is null"); + return -EINVAL; + } + + FTS_INFO("pramboot len=%d", len); + if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) { + FTS_ERROR("pramboot length(%d) fail", len); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + if (!fwupgrade->func->newmode) { + ecc_tmp ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("pramboot write data(%d) fail", i); + return ret; + } + } + + if (fwupgrade->func->newmode) { + ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len); + } else { + ecc_in_host = (int)ecc_tmp; + } + + return ecc_in_host; +} + +/************************************************************************ + * fts_pram_start - remap to start pramboot + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_start(struct i2c_client *client) +{ + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + int ret = 0; + + FTS_INFO("remap to start pramboot"); + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + msleep(FTS_DELAY_PRAMBOOT_START); + + return 0; +} + +/************************************************************************ + * fts_pram_write_remap - write pramboot to pram and start pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_write_remap(struct i2c_client *client) +{ + int ret = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + u8 *pb_buf = NULL; + u32 pb_len = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("write pram and remap"); + + if (!upg || !upg->func || !upg->func->pramboot) { + FTS_ERROR("upgrade/pramboot is null"); + return -EINVAL; + } + + if (upg->func->pb_length < FTS_MIN_LEN) { + FTS_ERROR("pramboot length(%d) fail", upg->func->pb_length); + return -EINVAL; + } + + pb_buf = upg->func->pramboot; + pb_len = upg->func->pb_length; + + /* write pramboot to pram */ + ecc_in_host = fts_pram_write_buf(client, pb_buf, pb_len); + if (ecc_in_host < 0) { + FTS_ERROR( "write pramboot fail"); + return ecc_in_host; + } + + /* read out checksum */ + ecc_in_tp = fts_pram_ecc_cal(client, 0, pb_len); + if (ecc_in_tp < 0) { + FTS_ERROR( "read pramboot ecc fail"); + return ecc_in_tp; + } + + FTS_INFO("pram ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + /* pramboot checksum != fw checksum, upgrade fail */ + if (ecc_in_host != ecc_in_tp) { + FTS_ERROR("pramboot ecc check fail"); + return -EIO; + } + + /*start pram*/ + ret = fts_pram_start(client); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_pram_init - initialize pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_init(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + u8 wbuf[3] = { 0 }; + + FTS_INFO("pramboot initialization"); + + /* read flash ID */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + ret = fts_i2c_read(client, wbuf, 1, ®_val, 1); + if (ret < 0) { + FTS_ERROR("read flash type fail"); + return ret; + } + + /* set flash clk */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + wbuf[1] = reg_val; + wbuf[2] = 0x00; + ret = fts_i2c_write(client, wbuf, 3); + if (ret < 0) { + FTS_ERROR("write flash type fail"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_pram_write_init +* Brief: wirte pramboot to pram and initialize +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_write_init(struct i2c_client *client) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("**********pram write and init**********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + if (!upg->func->pramboot_supported) { + FTS_ERROR("ic not support pram"); + return -EINVAL; + } + + FTS_DEBUG("check whether tp is in romboot or not "); + /* need reset to romboot when non-romboot state */ + fts_fwupg_get_boot_state(client, &status); + if (status != FTS_RUN_IN_ROM) { + if (FTS_RUN_IN_PRAM == status) { + FTS_INFO("tp is in pramboot, need send reset cmd before upgrade"); + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("pramboot(before) init fail"); + return ret; + } + } + + FTS_INFO("tp isn't in romboot, need send reset to romboot"); + ret = fts_fwupg_reset_to_romboot(client); + if (ret < 0) { + FTS_ERROR("reset to romboot fail"); + return ret; + } + } + + /* check the length of the pramboot */ + ret = fts_pram_write_remap(client); + if (ret < 0) { + FTS_ERROR("pram write fail, ret=%d", ret); + return ret; + } + + FTS_DEBUG("after write pramboot, confirm run in pramboot"); + state = fts_fwupg_check_state(client, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("not in pramboot"); + return -EIO; + } + + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("pramboot init fail"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_check_fw_valid +* Brief: check fw in tp is valid or not +* Input: +* Output: +* Return: return true if fw is valid, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client) +{ + int ret = 0; + + ret = fts_wait_tp_to_valid(client); + if (ret < 0) { + FTS_INFO("tp fw invaild"); + return false; + } + + FTS_INFO("tp fw vaild"); + return true; +} + +/************************************************************************ +* Name: fts_fwupg_get_boot_state +* Brief: read boot id(rom/pram/bootloader), confirm boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_get_boot_state(struct i2c_client *client, enum FW_STATUS *fw_sts) +{ + int ret = 0; + u8 cmd[4] = { 0 }; + u32 cmd_len = 0; + u8 val[2] = { 0 }; + struct ft_chip_t ids = fts_data->ic_info.ids; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("**********read boot id**********"); + if ((NULL == fw_sts) || (NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func/fw_sts is null"); + return -EINVAL; + } + + if (upg->func->hid_supported) + fts_i2c_hid2std(client); + + cmd[0] = FTS_CMD_START1; + cmd[1] = FTS_CMD_START2; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("write 55 aa cmd fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + cmd[0] = FTS_CMD_READ_ID; + cmd[1] = cmd[2] = cmd[3] = 0x00; + if (fts_data->ic_info.is_incell) + cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_i2c_read(client, cmd, cmd_len, val, 2); + if (ret < 0) { + FTS_ERROR("write 90 cmd fail"); + return ret; + } + + FTS_INFO("read boot id:0x%02x%02x", val[0], val[1]); + if ((val[0] == ids.rom_idh) && (val[1] == ids.rom_idl)) { + FTS_INFO("tp run in romboot"); + *fw_sts = FTS_RUN_IN_ROM; + } else if ((val[0] == ids.pb_idh) && (val[1] == ids.pb_idl)) { + FTS_INFO("tp run in pramboot"); + *fw_sts = FTS_RUN_IN_PRAM; + } else if ((val[0] == ids.bl_idh) && (val[1] == ids.bl_idl)) { + FTS_INFO("tp run in bootloader"); + *fw_sts = FTS_RUN_IN_BOOTLOADER; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_check_state +* Brief: confirm tp run in romboot/pramboot/bootloader +* Input: +* Output: +* Return: return true if state is match, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_state(struct i2c_client *client, enum FW_STATUS rstate) +{ + int i = 0; + enum FW_STATUS cstate = FTS_RUN_IN_ERROR; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + fts_fwupg_get_boot_state(client, &cstate); + /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */ + if (cstate == rstate) + return true; + msleep(FTS_DELAY_READ_ID); + } + + return false; +} + +/************************************************************************ +* Name: fts_fwupg_reset_in_boot +* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_in_boot(struct i2c_client *client) +{ + int ret = 0; + u8 cmd = FTS_CMD_RESET; + + FTS_INFO("reset in boot environment"); + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_boot +* Brief: reset to boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_to_boot(struct i2c_client *client) +{ + int ret = 0; + + FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment"); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_AA); + if (ret < 0) { + FTS_ERROR("write FC=0xAA fail"); + return ret; + } + msleep(FTS_DELAY_FC_AA); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_55); + if (ret < 0) { + FTS_ERROR("write FC=0x55 fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_romboot +* Brief: reset to romboot, to load pramboot +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_to_romboot(struct i2c_client *client) +{ + int ret = 0; + int i = 0; + u8 cmd = FTS_CMD_RESET; + enum FW_STATUS state = FTS_RUN_IN_ERROR; + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + mdelay(10); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + fts_fwupg_get_boot_state(client, &state); + if (FTS_RUN_IN_ROM == state) + break; + mdelay(5); + } + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("reset to romboot fail"); + return -EIO; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_enter_into_boot +* Brief: enter into boot environment, ready for upgrade +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_enter_into_boot(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("***********enter into pramboot/bootloader***********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_reset_to_boot(client); + if (ret < 0) { + FTS_ERROR("enter into romboot/bootloader fail"); + return ret; + } + } else if (upg->func->read_boot_id_need_reset) { + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset before read boot id when fw invalid fail"); + return ret; + } + } + + if (upg->func->pramboot_supported) { + FTS_INFO("pram supported, write pramboot and init"); + /* pramboot */ + ret = fts_pram_write_init(client); + if (ret < 0) { + FTS_ERROR("pram write_init fail"); + return ret; + } + } else { + FTS_DEBUG("pram not supported, confirm in bootloader"); + /* bootloader */ + state = fts_fwupg_check_state(client, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("fw not in bootloader, fail"); + return -EIO; + } + } + + return 0; +} + +/************************************************************************ + * Name: fts_fwupg_check_flash_status + * Brief: read status from tp + * Input: flash_status: correct value from tp + * retries: read retry times + * retries_delay: retry delay + * Output: + * Return: return true if flash status check pass, otherwise return false +***********************************************************************/ +static bool fts_fwupg_check_flash_status( + struct i2c_client *client, + u16 flash_status, + int retries, + int retries_delay) +{ + int i = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + + for (i = 0; i < retries; i++) { + cmd = FTS_CMD_FLASH_STATUS; + fts_i2c_read(client, &cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (flash_status == read_status) { + /* FTS_DEBUG("[UPGRADE]flash status ok"); */ + return true; + } + /* FTS_DEBUG("flash status fail,ok:%04x read:%04x, retries:%d", flash_status, read_status, i); */ + msleep(retries_delay); + } + + return false; +} + +/************************************************************************ + * Name: fts_fwupg_erase + * Brief: erase flash area + * Input: delay - delay after erase + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_erase(struct i2c_client *client, u32 delay) +{ + int ret = 0; + u8 cmd = 0; + bool flag = false; + + FTS_INFO("**********erase now**********"); + + /*send to erase flash*/ + cmd = FTS_CMD_ERASE_APP; + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("erase cmd fail"); + return ret; + } + msleep(delay); + + /* read status 0xF0AA: success */ + flag = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ERASE_OK, + FTS_RETRIES_REASE, FTS_RETRIES_DELAY_REASE); + if (!flag) { + FTS_ERROR("ecc flash status check fail"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * Name: fts_fwupg_ecc_cal + * Brief: calculate and get ecc from tp + * Input: saddr - start address need calculate ecc + * len - length need calculate ecc + * Output: + * Return: return data ecc of tp if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_ecc_cal(struct i2c_client *client, u32 saddr, u32 len) +{ + int ret = 0; + u32 i = 0; + u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 }; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + int ecc = 0; + int ecc_len = 0; + u32 packet_num = 0; + u32 packet_len = 0; + u32 remainder = 0; + u32 addr = 0; + u32 offset = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO( "**********read out checksum**********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_i2c_write(client, wbuf, 1); + if (ret < 0) { + FTS_ERROR("ecc init cmd write fail"); + return ret; + } + + packet_num = len / FTS_MAX_LEN_ECC_CALC; + remainder = len % FTS_MAX_LEN_ECC_CALC; + if (remainder) + packet_num++; + packet_len = FTS_MAX_LEN_ECC_CALC; + FTS_INFO("ecc calc num:%d, remainder:%d", packet_num, remainder); + + /* send commond to start checksum */ + wbuf[0] = FTS_CMD_ECC_CAL; + for (i = 0; i < packet_num; i++) { + offset = FTS_MAX_LEN_ECC_CALC * i; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + if ((i == (packet_num - 1)) && remainder) + packet_len = remainder; + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + + FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len); + ret = fts_i2c_write(client, wbuf, FTS_CMD_ECC_CAL_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd write fail"); + return ret; + } + + msleep(packet_len / 256); + + /* read status if check sum is finished */ + ret = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ECC_OK, + FTS_RETRIES_ECC_CAL, FTS_RETRIES_DELAY_ECC_CAL); + if (ret < 0) { + FTS_ERROR("ecc flash status read fail"); + return ret; + } + } + + ecc_len = 1; + if (ECC_CHECK_MODE_CRC16 == upg->func->ecc_check_mode) { + ecc_len = 2; + } + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_i2c_read(client, wbuf, 1, val, ecc_len); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + + if (ECC_CHECK_MODE_CRC16 == upg->func->ecc_check_mode) { + ecc = (int)((u16)(val[0] << 8) + val[1]); + } else { + ecc = (int)val[0]; + } + + return ecc; +} + +/************************************************************************ + * Name: fts_flash_write_buf + * Brief: write buf data to flash address + * Input: saddr - start address data write to flash + * buf - data buffer + * len - data length + * delay - delay after write + * Output: + * Return: return data ecc of host if success, otherwise return error code + ***********************************************************************/ +int fts_flash_write_buf( + struct i2c_client *client, + u32 saddr, + u8 *buf, + u32 len, + u32 delay) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_in_host = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + u16 wr_ok = 0; + + FTS_INFO( "**********write data to flash**********"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("buf is NULL or len is 0"); + return -EINVAL; + } + + FTS_INFO("data buf start addr=0x%x, len=0x%x", saddr, len); + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + packet_buf[0] = FTS_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + ecc_in_host ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("app write fail"); + return ret; + } + mdelay(delay); + + /* read status */ + wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len; + for (j = 0; j < FTS_RETRIES_WRITE; j++) { + cmd = FTS_CMD_FLASH_STATUS; + fts_i2c_read(client, &cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + /* FTS_INFO("%x %x", wr_ok, read_status); */ + if (wr_ok == read_status) { + break; + } + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + return (int)ecc_in_host; +} + +#if FTS_AUTO_UPGRADE_EN +/************************************************************************ + * Name: fts_flash_read_buf + * Brief: read data from flash + * Input: saddr - start address data write to flash + * buf - buffer to store data read from flash + * len - read length + * Output: + * Return: return 0 if success, otherwise return error code + * + * Warning: can't call this function directly, need call in boot environment + ***********************************************************************/ +static int fts_flash_read_buf(struct i2c_client *client, u32 saddr, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u8 wbuf[FTS_CMD_READ_LEN]; + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("buf is NULL or len is 0"); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) { + packet_number++; + } + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_INFO("read packet_number:%d, remainder:%d", packet_number, remainder); + + wbuf[0] = FTS_CMD_READ; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + ret = fts_i2c_write(client, wbuf, FTS_CMD_READ_LEN); + if (ret < 0) { + FTS_ERROR("pram/bootloader write 03 command fail"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */ + ret = fts_i2c_read(client, NULL, 0, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03 command fail"); + return ret; + } + } + + return 0; +} + +/************************************************************************ + * Name: fts_flash_read + * Brief: + * Input: addr - address of flash + * len - length of read + * Output: buf - data read from flash + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_flash_read(struct i2c_client *client, u32 addr, u8 *buf, u32 len) +{ + int ret = 0; + + FTS_INFO("***********read flash***********"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("buf is NULL or len is 0"); + return -EINVAL; + } + + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail"); + goto read_flash_err; + } + + ret = fts_flash_read_buf(client, addr, buf, len); + if (ret < 0) { + FTS_ERROR("read flash fail"); + goto read_flash_err; + } + +read_flash_err: + /* reset to normal boot */ + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return ret; +} +#endif /* FTS_AUTO_UPGRADE_EN */ + +/************************************************************************ + * Name: fts_read_file + * Brief: read file + * Input: file name + * Output: + * Return: return file len if succuss, otherwise return error code + ***********************************************************************/ +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("filename/filebuf is NULL"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("open %s file fail", file_path); + return -ENOENT; + } + +#if 1 + inode = filp->f_inode; +#else + /* reserved for linux earlier verion */ + inode = filp->f_dentry->d_inode; +#endif + + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("file buf malloc fail"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + ret = vfs_read(filp, *file_buf, file_len , &pos); + if (ret < 0) + FTS_ERROR("read file fail"); + FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +/************************************************************************ +* Name: fts_upgrade_bin +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_upgrade_bin(struct i2c_client *client, char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN) || (ret > FTS_MAX_LEN_FILE)) { + FTS_ERROR("read fw bin file(sdcard) fail, len:%d", ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(client, fw_file_buf, fw_file_len); + } else { + FTS_INFO("force_upgrade function is null, no upgrade"); + goto err_bin; + } + } else { +#if FTS_AUTO_LIC_UPGRADE_EN + if (upg->func->lic_upgrade) { + ret = upg->func->lic_upgrade(client, fw_file_buf, fw_file_len); + } else { + FTS_INFO("lic_upgrade function is null, no upgrade"); + } +#endif + if (upg->func->upgrade) { + ret = upg->func->upgrade(client, fw_file_buf, fw_file_len); + } else { + FTS_INFO("upgrade function is null, no upgrade"); + } + } + + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + fts_fwupg_reset_in_boot(client); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + +err_bin: + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + return ret; +} + +#if (FTS_AUTO_LIC_UPGRADE_EN && FTS_AUTO_UPGRADE_EN) +static int fts_lic_get_vid_in_tp(struct i2c_client *client, u16 *vid) +{ + int ret = 0; + u8 val[2] = { 0 }; + + if (NULL == vid) { + FTS_ERROR("vid is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val[0]); + if (fts_data->ic_info.is_incell) + ret = fts_i2c_read_reg(client, FTS_REG_MODULE_ID, &val[1]); + if (ret < 0) { + FTS_ERROR("read vid from tp fail"); + return ret; + } + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_vid_in_host(u16 *vid) +{ + u8 val[2] = { 0 }; + u8 *licbuf = NULL; + u32 conf_saddr = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->lic || !vid) { + FTS_ERROR("upgrade/func/get_hlic_ver/lic/vid is null"); + return -EINVAL; + } + + if (upg->lic_length < FTS_MAX_LEN_SECTOR) { + FTS_ERROR("lic length(%x) fail", upg->lic_length); + return -EINVAL; + } + + licbuf = upg->lic; + conf_saddr = upg->func->fwcfgoff; + val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF]; + if (fts_data->ic_info.is_incell) + val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF]; + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_LIC_VER, ver); + if (ret < 0) { + FTS_ERROR("read lcd initcode ver from tp fail"); + return ret; + } + + return 0; +} + +static int fts_lic_get_ver_in_host(u8 *ver) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) { + FTS_ERROR("upgrade/func/get_hlic_ver/lic is null"); + return -EINVAL; + } + + ret = upg->func->get_hlic_ver(upg->lic); + if (ret < 0) { + FTS_ERROR("get host lcd initial code version fail"); + return ret; + } + + *ver = (u8)ret; + return ret; +} + +/* check if lcd init code need upgrade +* true-need false-no need +*/ +static bool fts_lic_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + u8 initcode_ver_in_tp = 0; + u8 initcode_ver_in_host = 0; + u16 vid_in_tp = 0; + u16 vid_in_host = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(client); + if ( !fwvalid) { + FTS_INFO("fw is invalid, no upgrade lcd init code"); + return false; + } + + ret = fts_lic_get_vid_in_host(&vid_in_host); + if (ret < 0) { + FTS_ERROR("vendor id in host invalid"); + return false; + } + + ret = fts_lic_get_vid_in_tp(client, &vid_in_tp); + if (ret < 0) { + FTS_ERROR("vendor id in tp invalid"); + return false; + } + + FTS_DEBUG("vid in tp:0x%04x, host:0x%04x", vid_in_tp, vid_in_host); + if (vid_in_tp != vid_in_host) { + FTS_INFO("vendor id in tp&host are different, no upgrade lic"); + return false; + } + + ret = fts_lic_get_ver_in_host(&initcode_ver_in_host); + if (ret < 0) { + FTS_ERROR("init code in host invalid"); + return false; + } + + ret = fts_lic_get_ver_in_tp(client, &initcode_ver_in_tp); + if (ret < 0) { + FTS_ERROR("read reg0xE4 fail"); + return false; + } + + FTS_DEBUG("lcd initial code version in tp:%x, host:%x", + initcode_ver_in_tp, initcode_ver_in_host); + if (0xA5 == initcode_ver_in_tp) { + FTS_INFO("lcd init code ver is 0xA5, don't upgade init code"); + return false; + } else if (0xFF == initcode_ver_in_tp) { + FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code"); + return true; + } else if (initcode_ver_in_tp < initcode_ver_in_host) + return true; + else + return false; +} + +static int fts_lic_upgrade(struct i2c_client *client, struct fts_upgrade *upg) +{ + int ret = 0; + bool hlic_upgrade = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_INFO("lcd initial code auto upgrade function"); + if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) { + FTS_ERROR("lcd upgrade function is null"); + return -EINVAL; + } + + hlic_upgrade = fts_lic_need_upgrade(client); + FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade); + if (hlic_upgrade) { + FTS_INFO("lcd initial code need upgrade, upgrade begin..."); + do { + FTS_INFO("lcd initial code upgrade times:%d", upgrade_count); + upgrade_count++; + + ret = upg->func->lic_upgrade(client, upg->lic, upg->lic_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_lic_get_ver_in_tp(client, &ver); + FTS_INFO("success upgrade to lcd initcode ver:%02x", ver); + break; + } + } while (upgrade_count < 2); + } else { + FTS_INFO("lcd initial code don't need upgrade"); + } + + return ret; +} +#endif /* FTS_AUTO_LIC_UPGRADE_EN && FTS_AUTO_UPGRADE_EN */ + + +static int fts_param_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_VER_ID, ver); + if (ret < 0) { + FTS_ERROR("read fw param ver from tp fail"); + return ret; + } + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("param version in tp invalid"); + return -EIO; + } + + return 0; +} + +static int fts_param_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgveroff) { + FTS_ERROR("fw len(%x) < paramcfg ver offset(%x)", + upg->fw_length, upg->func->paramcfgveroff); + return -EINVAL; + } + + FTS_INFO("fw paramcfg version offset:%x", upg->func->paramcfgveroff); + *ver = upg->fw[upg->func->paramcfgveroff]; + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("param version in host invalid"); + return -EIO; + } + + return 0; +} + +/* + * return: < 0 : error + * == 0: no ide + * == 1: ide + */ +static int fts_param_ide_in_host(void) +{ + u32 off = 0; + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw)) { + FTS_ERROR("fts_data/upgrade/func/fw is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN) { + FTS_INFO("fw len(%x) < paramcfg offset(%x), no IDE", + upg->fw_length, upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN); + return 0; + } + + off = upg->func->paramcfgoff; + if (0 == memcmp(&upg->fw[off], FTS_FW_IDE_SIG, FTS_FW_IDE_SIG_LEN)) { + FTS_INFO("fw in host is IDE version"); + return 1; + } + + FTS_INFO("fw in host isn't IDE version"); + return 0; +} + +/* + * return: < 0 : error + * 0 : no ide + * 1 : ide + */ +static int fts_param_ide_in_tp(struct i2c_client *client, u8 *val) +{ + int ret = 0; + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, val); + if (ret < 0) { + FTS_ERROR("read IDE PARAM STATUS in tp fail"); + return ret; + } + + if ((*val != 0xFF) && ((*val & 0x80) == 0x80)) { + FTS_INFO("fw in tp is IDE version"); + return 1; + } + + FTS_INFO("fw in tp isn't IDE version"); + return 0; +} + +/************************************************************************ + * fts_param_need_upgrade - check fw paramcfg need upgrade or not + * + * Return: < 0 : error if paramcfg need upgrade + * 0 : no need upgrade + * 1 : need upgrade app + param + * 2 : need upgrade param + ***********************************************************************/ +static int fts_param_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + u8 val = 0; + int ide_in_host = 0; + int ide_in_tp = 0; + u8 ver_in_host = 0; + u8 ver_in_tp = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(client); + if ( !fwvalid) { + FTS_INFO("fw is invalid, upgrade app+param"); + return 1; + } + + ide_in_host = fts_param_ide_in_host(); + if (ide_in_host < 0) { + FTS_INFO("fts_param_ide_in_host fail"); + return ide_in_host; + } + + ide_in_tp = fts_param_ide_in_tp(client, &val); + if (ide_in_tp < 0) { + FTS_INFO("fts_param_ide_in_tp fail"); + return ide_in_tp; + } + + if ((0 == ide_in_host) && (0 == ide_in_tp)) { + FTS_INFO("fw in host&tp are both no ide"); + return 0; + } else if (ide_in_host != ide_in_tp) { + FTS_INFO("fw in host&tp not equal, need upgrade app+param"); + return 1; + } else if ((1 == ide_in_host) && (1 == ide_in_tp)) { + FTS_INFO("fw in host&tp are both ide"); + if ((val & 0x7F) != 0x00) { + FTS_INFO("param invalid, need upgrade param"); + return 2; + } + + ret = fts_param_get_ver_in_host(&ver_in_host); + if (ret < 0) { + FTS_ERROR("param version in host invalid"); + return ret; + } + + ret = fts_param_get_ver_in_tp(client, &ver_in_tp); + if (ret < 0) { + FTS_ERROR("get IDE param ver in tp fail"); + return ret; + } + + FTS_INFO("fw paramcfg version in tp:%x, host:%x", ver_in_tp, ver_in_host); + if (ver_in_tp != ver_in_host) { + return 2; + } + } + + return 0; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_tp - read fw ver from tp register + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_FW_VER, ver); + if (ret < 0) { + FTS_ERROR("read fw ver from tp fail"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_host - read fw ver in host fw image + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->fwveroff) { + FTS_ERROR("fw len(0x%0x) < fw ver offset(0x%x)", + upg->fw_length, upg->func->fwveroff); + return -EINVAL; + } + + FTS_INFO("fw version offset:0x%x", upg->func->fwveroff); + *ver = upg->fw[upg->func->fwveroff]; + return 0; +} + +/************************************************************************ + * fts_fwupg_need_upgrade - check fw need upgrade or not + * + * Return: return true if fw need upgrade + ***********************************************************************/ +static bool fts_fwupg_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + u8 fw_ver_in_host = 0; + u8 fw_ver_in_tp = 0; + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_get_ver_in_host(&fw_ver_in_host); + if (ret < 0) { + FTS_ERROR("get fw ver in host fail"); + return false; + } + + ret = fts_fwupg_get_ver_in_tp(client, &fw_ver_in_tp); + if (ret < 0) { + FTS_ERROR("get fw ver in tp fail"); + return false; + } + + FTS_INFO("fw version in tp:%x, host:%x", fw_ver_in_tp, fw_ver_in_host); + if (fw_ver_in_tp != fw_ver_in_host) { + return true; + } + } else { + FTS_INFO("fw invalid, need upgrade fw"); + return true; + } + + return false; +} + +/************************************************************************ + * Name: fts_fw_upgrade + * Brief: fw upgrade main entry + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_upgrade(struct i2c_client *client, struct fts_upgrade *upg) +{ + int ret = 0; + bool upgrade_flag = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_INFO("fw auto upgrade function"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upg/upg->func is null"); + return -EINVAL; + } + + upgrade_flag = fts_fwupg_need_upgrade(client); + FTS_INFO("fw upgrade flag:%d", upgrade_flag); + do { + upgrade_count++; + if (upgrade_flag) { + FTS_INFO("upgrade fw app(times:%d)", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_fwupg_get_ver_in_tp(client, &ver); + FTS_INFO("success upgrade to fw version %02x", ver); + break; + } + } else { + FTS_ERROR("upgrade func/upgrade is null, return immediately"); + ret = -ENODATA; + break; + } + } else { + if (upg->func->param_upgrade) { + ret = fts_param_need_upgrade(client); + if (ret <= 0) { + FTS_INFO("param don't need upgrade"); + break; + } else if (1 == ret) { + FTS_INFO("force upgrade fw app(times:%d)", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + break; + } + } + } else if (2 == ret) { + FTS_INFO("upgrade param area(times:%d)", upgrade_count); + ret = upg->func->param_upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_param_get_ver_in_tp(client, &ver); + FTS_INFO("success upgrade to fw param version %02x", ver); + break; + } + } else + break; + } else { + break; + } + } + } while (upgrade_count < 2); + + return ret; +} + +#if FTS_AUTO_UPGRADE_EN +/************************************************************************ + * fts_fwupg_auto_upgrade - upgrade main entry + ***********************************************************************/ +void fts_fwupg_auto_upgrade(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct i2c_client *client = ts_data->client; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("********************FTS enter upgrade********************"); + + ret = fts_fwupg_upgrade(client, upg); + if (ret < 0) + FTS_ERROR("**********tp fw(app/param) upgrade failed**********"); + else + FTS_INFO("**********tp fw(app/param) no upgrade/upgrade success**********"); + +#if FTS_AUTO_LIC_UPGRADE_EN + ret = fts_lic_upgrade(client, upg); + if (ret < 0) + FTS_ERROR("**********lcd init code upgrade failed**********"); + else + FTS_INFO("**********lcd init code no upgrade/upgrade success**********"); +#endif + + FTS_INFO("********************FTS exit upgrade********************"); +} + +/* + * fts_fwupg_work - fw upgrade work function, handle upgrade + * @vid - u16 consit of module id and panel id + * + * return 0 if success, otherwise return error code + */ +int fts_fwupg_get_vendorid(struct fts_ts_data *ts_data, u16 *vid) +{ + int ret = 0; + bool fwvalid = false; + u8 vendor_id = 0; + u8 module_id = 0; + u32 fwcfg_addr = 0; + u8 cfgbuf[FTS_HEADER_LEN] = { 0 }; + struct i2c_client *client = ts_data->client; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("read vendor id from tp"); + if ((!upg) || (!upg->func) || (!vid)) { + FTS_ERROR("upgrade/func/vid is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &vendor_id); + if (ts_data->ic_info.is_incell) + ret = fts_i2c_read_reg(client, FTS_REG_MODULE_ID, &module_id); + } else { + fwcfg_addr = upg->func->fwcfgoff; + ret = fts_flash_read(client, fwcfg_addr, cfgbuf, FTS_HEADER_LEN); + vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF]; + if (ts_data->ic_info.is_incell) { + if ((cfgbuf[FTS_CONIFG_MODULEID_OFF] + + cfgbuf[FTS_CONIFG_MODULEID_OFF + 1]) == 0xFF) + module_id = cfgbuf[FTS_CONIFG_MODULEID_OFF]; + } + } + + if (ret < 0) { + FTS_ERROR("fail to get vendor id from tp"); + return ret; + } + + *vid = ((u16)module_id << 8) + vendor_id; + return 0; +} + +/* + * fts_fwupg_get_fw_file - get upgrade fw file in host driver + * + * return 0 if success, otherwise return error code + * + * call it to get upgrade file which include in host driver + * warning: + * 1. if more fw files, please modify FTS_GET_VENDOR_ID_NUM& + * FTS_VENDOR_ID + * 2. For incell ICs, vendor id = module id << 8 + panel id + * For others, vendor id = 0x0000 + panel id + * 3. get fw file from reques_firmware(), this function unactive + */ +static int fts_fwupg_get_fw_file(struct fts_ts_data *ts_data) +{ + struct upgrade_fw *fw = &fw_list[0]; + struct fts_upgrade *upg = fwupgrade; + +#if (FTS_GET_VENDOR_ID_NUM > 1) + int ret = 0; + int i = 0; + u16 vendor_id = 0; + + /* support multi vendor, must read correct vendor id */ + ret = fts_fwupg_get_vendorid(ts_data, &vendor_id); + if (ret < 0) { + FTS_ERROR("get vendor id failed"); + return ret; + } + FTS_INFO("success to read vendor id:%04x", vendor_id); + for (i = 0; i < FTS_GET_VENDOR_ID_NUM; i++) { + fw = &fw_list[i]; + if (vendor_id == fw->vendor_id) { + FTS_INFO("vendor id match, get fw file successfully"); + break; + } + } + if (i >= FTS_GET_VENDOR_ID_NUM) { + FTS_ERROR("no vendor id match, don't get file"); + return -ENODATA; + } +#endif + + if (upg) { + upg->fw = fw->fw_file; + upg->fw_length = fw->fw_len; + upg->lic = fw->fw_file; + upg->lic_length = fw->fw_len; + + FTS_INFO("upgrade fw file len:%d", upg->fw_length); + if ((upg->fw_length < FTS_MIN_LEN) + || (upg->fw_length > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file len(%d) fail", upg->fw_length); + return -ENODATA; + } + } + return 0; +} + +/* + * fts_fwupg_init_ic_detail - for ic detail initialaztion + */ +static void fts_fwupg_init_ic_detail(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (upg && upg->func && upg->func->init) { + upg->func->init(); + } +} + +/* + * fts_fwupg_work - fw upgrade work function + * 1. get fw image/file + * 2. call upgrade main function(fts_fwupg_auto_upgrade) + */ +static void fts_fwupg_work(struct work_struct *work) +{ + int ret = 0; + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, fwupg_work); + + FTS_INFO("fw upgrade work function"); + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + FTS_INFO("get upgrade fw file"); + ret = fts_fwupg_get_fw_file(ts_data); + fts_fwupg_init_ic_detail(); + if (ret < 0) { + FTS_ERROR("get file fail, can't upgrade"); + } else { + /* run auto upgrade */ + fts_fwupg_auto_upgrade(ts_data); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + ts_data->fw_loading = 0; +} + +/***************************************************************************** + * Name: fts_fwupg_init + * Brief: upgrade function initialization + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + *****************************************************************************/ +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + int ic_stype = ts_data->ic_info.ids.type; + struct upgrade_func *func = upgrade_func_list[0]; + int func_count = sizeof(upgrade_func_list) / sizeof(upgrade_func_list[0]); + + FTS_INFO("fw upgrade init function"); + + if (NULL == ts_data->ts_workqueue) { + FTS_ERROR("fts workqueue is NULL, can't run upgrade function"); + return -EINVAL; + } + + if (0 == func_count) { + FTS_ERROR("no upgrade function in tp driver"); + return -ENODATA; + } + + fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL); + if (NULL == fwupgrade) { + FTS_ERROR("malloc memory for upgrade fail"); + return -ENOMEM; + } + + if (1 == func_count) { + fwupgrade->func = func; + } else { + for (i = 0; i < func_count; i++) { + func = upgrade_func_list[i]; + for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) { + if (0 == func->ctype[j]) + break; + else if (func->ctype[j] == ic_stype) { + FTS_INFO("match upgrade function,type:%x", (int)func->ctype[j]); + fwupgrade->func = func; + } + } + } + } + + if (NULL == fwupgrade->func) { + FTS_ERROR("no upgrade function match, can't upgrade"); + return -ENODATA; + } + + INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work); + queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work); + + return 0; +} + +/***************************************************************************** + * Name: fts_fwupg_exit + * Brief: + * Input: + * Output: + * Return: + *****************************************************************************/ +int fts_fwupg_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (fwupgrade) { + kfree(fwupgrade); + } + FTS_FUNC_EXIT(); + + return 0; +} + +#endif /* #if FTS_AUTO_UPGRADE_EN */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h new file mode 100644 index 000000000000..d11f42ca8e1e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash.h @@ -0,0 +1,182 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_FOCALTECH_FLASH_H__ +#define __LINUX_FOCALTECH_FLASH_H__ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_CMD_RESET 0x07 +#define FTS_ROMBOOT_CMD_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 10 +#define FTS_ROMBOOT_CMD_ECC 0xCC + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#define FTS_CMD_FLASH_TYPE 0x05 +#define FTS_CMD_FLASH_MODE 0x09 +#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A +#define FLASH_MODE_UPGRADE_VALUE 0x0B +#define FLASH_MODE_LIC_VALUE 0x0C +#define FLASH_MODE_PARAM_VALUE 0x0D +#define FTS_CMD_ERASE_APP 0x61 +#define FTS_REASE_APP_DELAY 1350 +#define FTS_ERASE_SECTOR_DELAY 60 +#define FTS_RETRIES_REASE 50 +#define FTS_RETRIES_DELAY_REASE 200 +#define FTS_CMD_FLASH_STATUS 0x6A +#define FTS_CMD_FLASH_STATUS_LEN 2 +#define FTS_CMD_FLASH_STATUS_NOP 0x0000 +#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055 +#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA +#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000 +#define FTS_CMD_ECC_INIT 0x64 +#define FTS_CMD_ECC_CAL 0x65 +#define FTS_CMD_ECC_CAL_LEN 6 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#define FTS_CMD_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_WRITE 0xBF +#define FTS_RETRIES_WRITE 100 +#define FTS_RETRIES_DELAY_WRITE 1 +#define FTS_CMD_WRITE_LEN 6 +#define FTS_DELAY_READ_ID 20 +#define FTS_DELAY_UPGRADE_RESET 80 +#define PRAMBOOT_MIN_SIZE 0x120 +#define PRAMBOOT_MAX_SIZE (64*1024) +#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */ +#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */ +#define FTS_MIN_LEN 0x120 +#define FTS_MAX_LEN_FILE (128 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_SECTOR (4 * 1024) +#define FTS_CONIFG_VENDORID_OFF 0x04 +#define FTS_CONIFG_MODULEID_OFF 0x1E +#define FTS_CONIFG_PROJECTID_OFF 0x20 +#define FTS_APPINFO_OFF 0x100 +#define FTS_APPINFO_APPLEN_OFF 0x00 +#define FTS_APPINFO_APPLEN2_OFF 0x12 +#define FTS_REG_UPGRADE 0xFC +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_FC_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/sdcard/" +#define FTS_FW_IDE_SIG "IDE_" +#define FTS_FW_IDE_SIG_LEN 4 + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_CMD_READ_ECC 0xCD +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +enum FW_STATUS { + FTS_RUN_IN_ERROR, + FTS_RUN_IN_APP, + FTS_RUN_IN_ROM, + FTS_RUN_IN_PRAM, + FTS_RUN_IN_BOOTLOADER, +}; + +enum FW_FLASH_MODE { + FLASH_MODE_APP, + FLASH_MODE_LIC, + FLASH_MODE_PARAM, + FLASH_MODE_ALL, +}; + +enum ECC_CHECK_MODE { + ECC_CHECK_MODE_XOR, + ECC_CHECK_MODE_CRC16, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + int newmode; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + int ecc_check_mode; + bool read_boot_id_need_reset; + bool hid_supported; + bool pramboot_supported; + u8 *pramboot; + u32 pb_length; + int (*init)(void); + int (*upgrade)(struct i2c_client *, u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(struct i2c_client *, u8 *, u32); + int (*param_upgrade)(struct i2c_client *, u8 *, u32); + int (*force_upgrade)(struct i2c_client *, u8 *, u32); +}; + +struct fts_upgrade { + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; + struct upgrade_func *func; +}; + +struct upgrade_fw { + u16 vendor_id; + u8 *fw_file; + u32 fw_len; +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_upgrade *fwupgrade; +extern struct upgrade_func upgrade_func_ft8006m; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client); +int fts_fwupg_get_boot_state(struct i2c_client *client, enum FW_STATUS *fw_sts); +bool fts_fwupg_check_state(struct i2c_client *client, enum FW_STATUS rstate); +int fts_fwupg_reset_in_boot(struct i2c_client *client); +int fts_fwupg_reset_to_boot(struct i2c_client *client); +int fts_fwupg_reset_to_romboot(struct i2c_client *client); +int fts_fwupg_enter_into_boot(struct i2c_client *client); +int fts_fwupg_erase(struct i2c_client *client, u32 delay); +int fts_fwupg_ecc_cal(struct i2c_client *client, u32 saddr, u32 len); +int fts_flash_write_buf(struct i2c_client *client, u32 saddr, u8 *buf, u32 len, u32 delay); +int fts_fwupg_upgrade(struct i2c_client *client, struct fts_upgrade *upg); +u16 fts_crc16_calc_host(u8 *pbuf, u16 length); +#endif diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile new file mode 100644 index 000000000000..133dbcf8d701 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for the focaltech touchscreen drivers. +# + +# Each configuration option enables a list of files. + + +obj-y += focaltech_upgrade_ft8201.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft8201.c b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft8201.c new file mode 100644 index 000000000000..e32de88b2435 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_flash/focaltech_upgrade_ft8201.c @@ -0,0 +1,1033 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_upgrade_ft8006m.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-12-29 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "../focaltech_core.h" +#include "../focaltech_flash.h" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 pb_file_ft8006m[] = { +#include "../include/pramboot/FT8006M_Pramboot_V1.6_20180426_le.h" +}; + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_MAX_LEN_APP_FT8006M (94 * 1024) + +#define MAX_BANK_DATA 0x80 +#define MAX_GAMMA_LEN 0x180 +#define LIC_CHECKSUM_H_OFF 0x00 +#define LIC_CHECKSUM_L_OFF 0x01 +#define LIC_LCD_ECC_H_OFF 0x04 +#define LIC_LCD_ECC_L_OFF 0x05 +#define LIC_ECC_REG_H_OFF 0x43D +#define LIC_ECC_REG_L_OFF 0x43C +#define LIC_REG_2 0xB2 + +static int gamma_enable[] = { 0x040d, 0x91, 0x80, 0x00, 0x19, 0x01 }; +union short_bits { + u16 dshort; + struct bits { + u16 bit0: 1; + u16 bit1: 1; + u16 bit2: 1; + u16 bit3: 1; + u16 bit4: 1; + u16 bit5: 1; + u16 bit6: 1; + u16 bit7: 1; + u16 bit8: 1; + u16 bit9: 1; + u16 bit10: 1; + u16 bit11: 1; + u16 bit12: 1; + u16 bit13: 1; + u16 bit14: 1; + u16 bit15: 1; + } bits; +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/* calculate lcd init code ecc */ +static int cal_lcdinitcode_ecc(u8 *buf, u16 *ecc_val) +{ + u32 bank_crc_en = 0; + u8 bank_data[MAX_BANK_DATA] = { 0 }; + u16 bank_len = 0; + u16 bank_addr = 0; + u32 bank_num = 0; + u16 file_len = 0; + u16 pos = 0; + int i = 0; + union short_bits ecc; + union short_bits ecc_last; + union short_bits temp_byte; + u8 bank_mapping[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x18, + 0x19, 0x1A, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x23, 0x24 + }; /* Actaul mipi bank */ + u8 banknum = 0; + + ecc.dshort = 0; + ecc_last.dshort = 0; + temp_byte.dshort = 0; + + file_len = (u16)(((u16)buf[2] << 8) + buf[3]); + if ((file_len >= FTS_MAX_LEN_SECTOR) || (file_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", file_len); + return -EINVAL; + } + + bank_crc_en = (u32)(((u32)buf[9] << 24) + ((u32)buf[8] << 16) + \ + ((u32)buf[7] << 8) + (u32)buf[6]); + FTS_INFO("lcd init code len=%x bank en=%x", file_len, bank_crc_en); + + pos = 0x0A; /* addr of first bank */ + while (pos < file_len) { + bank_addr = (u16)(((u16)buf[pos + 0] << 8 ) + buf[pos + 1]); + bank_len = (u16)(((u16)buf[pos + 2] << 8 ) + buf[pos + 3]); + /* FTS_INFO("bank pos=%x bank_addr=%x bank_len=%x", pos, bank_addr, bank_len); */ + if (bank_len > MAX_BANK_DATA) + return -EINVAL; + memset(bank_data, 0, MAX_BANK_DATA); + memcpy(bank_data, buf + pos + 4, bank_len); + + bank_num = (bank_addr - 0x8000) / MAX_BANK_DATA; + /* FTS_INFO("actual mipi bank number = %x", bank_num); */ + for (i = 0; i < sizeof(bank_mapping) / sizeof(u8); i++) { + if (bank_num == bank_mapping[i]) { + banknum = i; + break; + } + } + if (i >= sizeof(bank_mapping) / sizeof(u8)) { + FTS_INFO("actual mipi bank(%d) not find in bank mapping, need jump", bank_num); + } else { + /* FTS_INFO("bank number = %d", banknum); */ + if ((bank_crc_en >> banknum) & 0x01) { + for (i = 0; i < MAX_BANK_DATA; i++) { + temp_byte.dshort = (u16)bank_data[i]; + /* if(i == 0) */ + /* FTS_INFO("data0=%x, %d %d %d %d %d %d %d %d", temp_byte.dshort, temp_byte.bits.bit0, */ + /* temp_byte.bits.bit1, temp_byte.bits.bit2, temp_byte.bits.bit3, temp_byte.bits.bit4, */ + /* temp_byte.bits.bit5, temp_byte.bits.bit6, temp_byte.bits.bit7); */ + + ecc.bits.bit0 = ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ ecc_last.bits.bit11 + ^ ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 + ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit1 = ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ ecc_last.bits.bit11 ^ ecc_last.bits.bit12 + ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 ^ temp_byte.bits.bit4 + ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit2 = ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1; + + ecc.bits.bit3 = ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2; + + ecc.bits.bit4 = ecc_last.bits.bit10 ^ ecc_last.bits.bit11 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3; + + ecc.bits.bit5 = ecc_last.bits.bit11 ^ ecc_last.bits.bit12 ^ temp_byte.bits.bit3 ^ temp_byte.bits.bit4; + + ecc.bits.bit6 = ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5; + + ecc.bits.bit7 = ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6; + + ecc.bits.bit8 = ecc_last.bits.bit0 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit9 = ecc_last.bits.bit1 ^ ecc_last.bits.bit15 ^ temp_byte.bits.bit7; + + ecc.bits.bit10 = ecc_last.bits.bit2; + + ecc.bits.bit11 = ecc_last.bits.bit3; + + ecc.bits.bit12 = ecc_last.bits.bit4; + + ecc.bits.bit13 = ecc_last.bits.bit5; + + ecc.bits.bit14 = ecc_last.bits.bit6; + + ecc.bits.bit15 = ecc_last.bits.bit7 ^ ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ ecc_last.bits.bit10 + ^ ecc_last.bits.bit11 ^ ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 + ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc_last.dshort = ecc.dshort; + + } + } + } + pos += bank_len + 4; + } + + *ecc_val = ecc.dshort; + return 0; +} + +/* calculate lcd init code checksum */ +static u16 cal_lcdinitcode_checksum(u8 *ptr , int length) +{ + /* CRC16 */ + u16 cfcs = 0; + int i, j; + + if (length % 2) { + return 0xFFFF; + } + + for ( i = 0; i < length; i += 2 ) { + cfcs ^= ((ptr[i] << 8) + ptr[i + 1]); + for (j = 0; j < 16; j ++) { + if (cfcs & 1) { + cfcs = (u16)((cfcs >> 1) ^ ((1 << 15) + (1 << 10) + (1 << 3))); + } else { + cfcs >>= 1; + } + } + } + return cfcs; +} + +static int print_data(u8 *buf, u32 len) +{ + int i = 0; + int n = 0; + u8 *p = NULL; + + p = kzalloc(len * 4, GFP_KERNEL); + for (i = 0; i < len; i++) { + n += snprintf(p + n, PAGE_SIZE, "%02x ", buf[i]); + } + + FTS_DEBUG("%s", p); + + kfree(p); + return 0; +} + +static int read_3gamma(struct i2c_client *client, u8 **gamma, u16 *len) +{ + int ret = 0; + int i = 0; + int packet_num = 0; + int packet_len = 0; + int remainder = 0; + u8 cmd[4] = { 0 }; + u32 addr = 0x01D000; + u8 gamma_header[0x20] = { 0 }; + u16 gamma_len = 0; + u16 gamma_len_n = 0; + u16 pos = 0; + bool gamma_has_enable = false; + u8 *pgamma = NULL; + int j = 0; + u8 gamma_ecc = 0; + + cmd[0] = 0x03; + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)addr; + fts_i2c_write(client, cmd, 4); + msleep(10); + ret = fts_i2c_read(client, NULL, 0, gamma_header, 0x20); + if (ret < 0) { + FTS_ERROR("read 3-gamma header fail"); + return ret; + } + + gamma_len = (u16)((u16)gamma_header[0] << 8) + gamma_header[1]; + gamma_len_n = (u16)((u16)gamma_header[2] << 8) + gamma_header[3]; + + if ((gamma_len + gamma_len_n) != 0xFFFF) { + FTS_INFO("gamma length check fail:%x %x", gamma_len, gamma_len); + return -EIO; + } + + if ((gamma_header[4] + gamma_header[5]) != 0xFF) { + FTS_INFO("gamma ecc check fail:%x %x", gamma_header[4], gamma_header[5]); + return -EIO; + } + + if (gamma_len > MAX_GAMMA_LEN) { + FTS_ERROR("gamma data len(%d) is too long", gamma_len); + return -EINVAL; + } + + *gamma = kzalloc(MAX_GAMMA_LEN, GFP_KERNEL); + if (NULL == *gamma) { + FTS_ERROR("malloc gamma memory fail"); + return -ENOMEM; + } + pgamma = *gamma; + + packet_num = gamma_len / 256; + packet_len = 256; + remainder = gamma_len % 256; + if (remainder) packet_num++; + FTS_INFO("3-gamma len:%d", gamma_len); + cmd[0] = 0x03; + addr += 0x20; + for (i = 0; i < packet_num; i++) { + addr += i * 256; + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)addr; + if ((i == packet_num - 1) && remainder) + packet_len = remainder; + fts_i2c_write(client, cmd, 4); + msleep(10); + ret = fts_i2c_read(client, NULL, 0, pgamma + i * 256, packet_len); + if (ret < 0) { + FTS_ERROR("read 3-gamma data fail"); + return ret; + } + } + + /* ecc */ + for (j = 0; j < gamma_len; j++) { + gamma_ecc ^= pgamma[j]; + } + FTS_INFO("back_3gamma_ecc: 0x%x, 0x%x", gamma_ecc, gamma_header[0x04]); + if (gamma_ecc != gamma_header[0x04]) { + FTS_ERROR("back gamma ecc check fail:%x %x", gamma_ecc, gamma_header[0x04]); + return -EIO; + } + + /* check last byte is 91 80 00 19 01 */ + pos = gamma_len - 5; + + if (pos > MAX_GAMMA_LEN) { + FTS_ERROR("pos len(%d) is too long", pos); + return -EINVAL; + } + + if ((gamma_enable[1] == pgamma[pos]) && (gamma_enable[2] == pgamma[pos + 1]) + && (gamma_enable[3] == pgamma[pos + 2]) && (gamma_enable[4] == pgamma[pos + 3])) { + gamma_has_enable = true; + } + + if (false == gamma_has_enable) { + FTS_INFO("3-gamma has no gamma enable info"); + pgamma[gamma_len++] = gamma_enable[1]; + pgamma[gamma_len++] = gamma_enable[2]; + pgamma[gamma_len++] = gamma_enable[3]; + pgamma[gamma_len++] = gamma_enable[4]; + pgamma[gamma_len++] = gamma_enable[5]; + } + + *len = gamma_len; + + FTS_DEBUG("read 3-gamma data:"); + print_data(*gamma, gamma_len); + + return 0; +} + +static int replace_3gamma(u8 *initcode, u8 *gamma, u16 gamma_len) +{ + u16 gamma_pos = 0; + int gamma_analog[] = { 0x003A, 0x85, 0x00, 0x00, 0x2C, 0x2B }; + int gamma_digital1[] = { 0x0355, 0x8D, 0x00, 0x00, 0x80, 0x80 }; + int gamma_digital2[] = { 0x03d9, 0x8D, 0x80, 0x00, 0x14, 0x13 }; + + /* Analog Gamma */ + if ((initcode[gamma_analog[0]] == gamma[gamma_pos]) + && (initcode[gamma_analog[0] + 1] == gamma[gamma_pos + 1])) { + memcpy(initcode + gamma_analog[0] + 4 , gamma + gamma_pos + 4, gamma_analog[5]); + gamma_pos += gamma_analog[5] + 4; + } else + goto find_gamma_bank_err; + + /* Digital1 Gamma */ + if ((initcode[gamma_digital1[0]] == gamma[gamma_pos]) + && (initcode[gamma_digital1[0] + 1] == gamma[gamma_pos + 1])) { + memcpy(initcode + gamma_digital1[0] + 4 , gamma + gamma_pos + 4, gamma_digital1[5]); + gamma_pos += gamma_digital1[5] + 4; + } else + goto find_gamma_bank_err; + + /* Digital2 Gamma */ + if ((initcode[gamma_digital2[0]] == gamma[gamma_pos]) + && (initcode[gamma_digital2[0] + 1] == gamma[gamma_pos + 1])) { + memcpy(initcode + gamma_digital2[0] + 4 , gamma + gamma_pos + 4, gamma_digital2[5]); + gamma_pos += gamma_digital2[5] + 4; + } else + goto find_gamma_bank_err; + + /* enable Gamma */ + if ((initcode[gamma_enable[0]] == gamma[gamma_pos]) + && (initcode[gamma_enable[0] + 1] == gamma[gamma_pos + 1])) { + if (gamma[gamma_pos + 4]) + initcode[gamma_enable[0] + 4 + 15] |= 0x01; + else + initcode[gamma_enable[0] + 4 + 15] &= 0xFE; + //gamma_pos += 1 + 4; + } else + goto find_gamma_bank_err; + + FTS_DEBUG("replace 3-gamma data:"); + print_data(initcode, 1100); + + return 0; + +find_gamma_bank_err: + FTS_INFO("3-gamma bank(%02x %02x) not find", + gamma[gamma_pos], gamma[gamma_pos + 1]); + return -ENODATA; +} + +/* + * read_replace_3gamma - read and replace 3-gamma data + */ +static int read_replace_3gamma(struct i2c_client *client, u8 *buf, bool flag) +{ + int ret = 0; + u16 initcode_ecc = 0; + u16 initcode_checksum = 0; + u8 *tmpbuf = NULL; + u8 *gamma = NULL; + u16 gamma_len = 0; + u16 hlic_len = 0; + int base_addr = 0; + int i = 0; + + FTS_FUNC_ENTER(); + + ret = read_3gamma(client, &gamma, &gamma_len); + if (ret < 0) { + FTS_INFO("no vaid 3-gamma data, not replace"); + if (gamma) { + kfree(gamma); + gamma = NULL; + } + return 0; + } + + base_addr = 0; + for (i = 0; i < 2; i++) { + if (1 == i) { + if (true == flag) + base_addr = 0x7C0; + else + break; + } + + tmpbuf = buf + base_addr; + ret = replace_3gamma(tmpbuf, gamma, gamma_len); + if (ret < 0) { + FTS_ERROR("replace 3-gamma fail"); + goto REPLACE_GAMMA_ERR; + } + + ret = cal_lcdinitcode_ecc(tmpbuf, &initcode_ecc); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate fail"); + goto REPLACE_GAMMA_ERR; + } + FTS_INFO("lcd init code cal ecc:%04x", initcode_ecc); + tmpbuf[LIC_LCD_ECC_H_OFF] = (u8)(initcode_ecc >> 8); + tmpbuf[LIC_LCD_ECC_L_OFF] = (u8)(initcode_ecc); + tmpbuf[LIC_ECC_REG_H_OFF] = (u8)(initcode_ecc >> 8); + tmpbuf[LIC_ECC_REG_L_OFF] = (u8)(initcode_ecc); + + hlic_len = (u16)(((u16)tmpbuf[2]) << 8) + tmpbuf[3]; + initcode_checksum = cal_lcdinitcode_checksum(tmpbuf + 2, hlic_len - 2); + FTS_INFO("lcd init code calc checksum:0x%04x", initcode_checksum); + tmpbuf[LIC_CHECKSUM_H_OFF] = (u8)(initcode_checksum >> 8); + tmpbuf[LIC_CHECKSUM_L_OFF] = (u8)(initcode_checksum); + } + + if (gamma) { + kfree(gamma); + gamma = NULL; + } + + FTS_FUNC_EXIT(); + return 0; + +REPLACE_GAMMA_ERR: + if (gamma) { + kfree(gamma); + gamma = NULL; + } + return ret; +} + +/* + * check_initial_code_valid - check initial code valid or not + */ +static int check_initial_code_valid(struct i2c_client *client, u8 *buf) +{ + int ret = 0; + u16 initcode_ecc = 0; + u16 buf_ecc = 0; + u16 initcode_checksum = 0; + u16 buf_checksum = 0; + u16 hlic_len = 0; + + hlic_len = (u16)(((u16)buf[2]) << 8) + buf[3]; + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + return -EINVAL; + } + + initcode_checksum = cal_lcdinitcode_checksum(buf + 2, hlic_len - 2); + buf_checksum = ((u16)((u16)buf[0] << 8) + buf[1]); + FTS_INFO("lcd init code calc checksum:0x%04x,0x%04x", initcode_checksum, buf_checksum); + if (initcode_checksum != buf_checksum) { + FTS_ERROR("Initial Code checksum fail"); + return -EINVAL; + } + + ret = cal_lcdinitcode_ecc(buf, &initcode_ecc); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate fail"); + return ret; + } + buf_ecc = ((u16)((u16)buf[4] << 8) + buf[5]); + FTS_INFO("lcd init code cal ecc:%04x, %04x", initcode_ecc, buf_ecc); + if (initcode_ecc != buf_ecc) { + FTS_ERROR("Initial Code ecc check fail"); + return -EINVAL; + } + + return 0; +} + +static bool fts_ft8006m_check_ide(u8 *buf, u32 len) +{ + u32 off = 0; + + FTS_INFO("Host FW file IDE version check"); + if (NULL == buf) { + FTS_ERROR("buf is null fail"); + return false; + } + + if (len < FTS_MAX_LEN_FILE) { + FTS_INFO("buf len(%x) abnormal, no IDE", len); + return false; + } + + off = upgrade_func_ft8006m.paramcfgoff; + if ((buf[off] == 'I') && (buf[off + 1] == 'D') && (buf[off + 2] == 'E')) + return true; + + return false; +} + +/* fts_ft8006m_write_ecc - write and check ecc + * return 0 if success + */ +static int fts_ft8006m_write_ecc( + struct i2c_client *client, + u32 saddr, + u8 *buf, + u32 len) +{ + int ecc_in_host = 0; + int ecc_in_tp = 0; + + ecc_in_host = fts_flash_write_buf(client, saddr, buf, len, 1); + if (ecc_in_host < 0 ) { + FTS_ERROR("write buffer to flash fail"); + return ecc_in_host; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(client, saddr, len); + if (ecc_in_tp < 0 ) { + FTS_ERROR("ecc read fail"); + return ecc_in_tp; + } + + FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * Name: fts_ft8006m_param_flash + * Brief: param upgrade(erase/write/ecc check) + * Input: buf - all.bin + * len - len of all.bin + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006m_param_flash(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 cmd[2] = { 0 }; + u32 delay = 0; + u32 start_addr = 0; + u32 paramcfg_len = 0; + u8 *tmpbuf = NULL; + + /* erase gesture & parameter sector */ + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_PARAM_VALUE; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto PARAM_FLASH_ERR; + } + + delay = FTS_ERASE_SECTOR_DELAY * 2; + ret = fts_fwupg_erase(client, delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto PARAM_FLASH_ERR; + } + + /* write flash */ + start_addr = upgrade_func_ft8006m.paramcfgoff; + paramcfg_len = FTS_MAX_LEN_SECTOR; + tmpbuf = buf + start_addr; + ret = fts_ft8006m_write_ecc(client, start_addr, tmpbuf, paramcfg_len); + if (ret < 0 ) { + FTS_ERROR("parameter configure area write fail"); + goto PARAM_FLASH_ERR; + } + + start_addr = upgrade_func_ft8006m.paramcfg2off; + paramcfg_len = FTS_MAX_LEN_SECTOR; + tmpbuf = buf + start_addr; + ret = fts_ft8006m_write_ecc(client, start_addr, tmpbuf, paramcfg_len); + if (ret < 0 ) { + FTS_ERROR("parameter2 configure area write fail"); + goto PARAM_FLASH_ERR; + } + + return 0; + +PARAM_FLASH_ERR: + return ret; +} + +/* + * fts_get_hlic_ver - read host lcd init code version + * + * return 0 if host lcd init code is valid, otherwise return error code + */ +static int fts_ft8006m_get_hlic_ver(u8 *initcode) +{ + u8 *hlic_buf = initcode; + u16 hlic_len = 0; + u8 hlic_ver[2] = { 0 }; + + hlic_len = (u16)(((u16)hlic_buf[2]) << 8) + hlic_buf[3]; + FTS_INFO("host lcd init code len:%x", hlic_len); + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + return -EINVAL; + } + + hlic_ver[0] = hlic_buf[hlic_len]; + hlic_ver[1] = hlic_buf[hlic_len + 1]; + + FTS_INFO("host lcd init code ver:%x %x", hlic_ver[0], hlic_ver[1]); + if (0xFF != (hlic_ver[0] + hlic_ver[1])) { + FTS_ERROR("host lcd init code version check fail"); + return -EINVAL; + } + + return hlic_ver[0]; +} + +/************************************************************************ + * Name: fts_ft8006m_upgrade + * Brief: + * Input: buf - all.bin + * len - len of all.bin + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006m_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u8 cmd[2] = { 0 }; + u32 delay = 0; + u32 start_addr = 0; + u32 app_1_len = 0; + u32 app_2_len = 0; + u32 app_len = 0; + u32 off = 0; + + FTS_INFO("app upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw file buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file buffer len(%x) fail", len); + return -EINVAL; + } + + off = upgrade_func_ft8006m.appoff + FTS_APPINFO_OFF + FTS_APPINFO_APPLEN_OFF; + app_1_len = (((u32)buf[off] << 8) + buf[off + 1]); + off = upgrade_func_ft8006m.appoff + FTS_APPINFO_OFF + FTS_APPINFO_APPLEN2_OFF; + app_2_len = (((u32)buf[off] << 8) + buf[off + 1]); + app_len = (app_2_len << 16) + app_1_len; + if ((app_len < FTS_MIN_LEN) || (app_len > FTS_MAX_LEN_APP_FT8006M)) { + FTS_ERROR("app len(%x) fail", app_len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto APP_UPG_ERR; + } + + /* erase gesture & parameter sector */ + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_UPGRADE_VALUE; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto APP_UPG_ERR; + } + + delay = FTS_ERASE_SECTOR_DELAY * (app_len / FTS_MAX_LEN_SECTOR); + ret = fts_fwupg_erase(client, delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto APP_UPG_ERR; + } + + /* write flash */ + start_addr = upgrade_func_ft8006m.appoff; + tmpbuf = buf + start_addr; + ret = fts_ft8006m_write_ecc(client, start_addr, tmpbuf, app_len); + if (ret < 0 ) { + FTS_ERROR("app buffer write fail"); + goto APP_UPG_ERR; + } + + if (fts_ft8006m_check_ide(buf, len)) { + FTS_INFO("erase and write param configure area"); + ret = fts_ft8006m_param_flash(client, buf, len); + if (ret < 0 ) { + FTS_ERROR("param upgrade(erase/write/ecc) fail"); + goto APP_UPG_ERR; + } + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + msleep(400); + return 0; + +APP_UPG_ERR: + return ret; +} + +/************************************************************************ + * Name: fts_ft8006m_lic_upgrade + * Brief: + * Input: buf - all.bin + * len - len of all.bin + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006m_lic_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u8 cmd[2] = { 0 }; + u32 delay = 0; + u32 start_addr = 0; + u32 lic_len = 0; + u8 val = 0; + bool flag = false; + + FTS_INFO("LCD initial code upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw file buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file buffer len(%x) fail", len); + return -EINVAL; + } + + ret = check_initial_code_valid(client, buf); + if (ret < 0) { + FTS_ERROR("initial code invalid, not upgrade lcd init code"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, LIC_REG_2, &val); + FTS_DEBUG("lic flag:%x", val); + if ((ret > 0) && (1 == val)) + flag = true; + + lic_len = FTS_MAX_LEN_SECTOR; + /* remalloc memory for initcode, need change content of initcode afterwise */ + tmpbuf = kzalloc(lic_len, GFP_KERNEL); + if (NULL == tmpbuf) { + FTS_INFO("initial code buf malloc fail"); + return -EINVAL; + } + start_addr = upgrade_func_ft8006m.licoff; + memcpy(tmpbuf, buf + start_addr, lic_len); + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto LIC_UPG_ERR; + } + + /* 3-gamma remap */ + ret = read_replace_3gamma(client, tmpbuf, flag); + if (ret < 0) { + FTS_ERROR("replace 3-gamma fail, not upgrade lcd init code"); + goto LIC_UPG_ERR; + } + + /* erase gesture & parameter sector */ + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_LIC_VALUE; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto LIC_UPG_ERR; + } + + delay = FTS_ERASE_SECTOR_DELAY * 1; + ret = fts_fwupg_erase(client, delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto LIC_UPG_ERR; + } + + ret = fts_ft8006m_write_ecc(client, start_addr, tmpbuf, lic_len); + if (ret < 0 ) { + FTS_ERROR("LCD initial code write fail"); + goto LIC_UPG_ERR; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } + + msleep(400); + return 0; + +LIC_UPG_ERR: + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } + return ret; +} + +/************************************************************************ + * Name: fts_ft8006m_param_upgrade + * Brief: + * Input: buf - all.bin + * len - len of all.bin + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006m_param_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + + FTS_INFO("parameter configure upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw file buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto PARAM_UPG_ERR; + } + + ret = fts_ft8006m_param_flash(client, buf, len); + if (ret < 0 ) { + FTS_ERROR("param upgrade(erase/write/ecc) fail"); + goto PARAM_UPG_ERR; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + msleep(400); + return 0; + +PARAM_UPG_ERR: + return ret; +} + +/************************************************************************ + * Name: fts_ft8006m_force_upgrade + * Brief: + * Input: buf - all.bin + * len - constant:128 * 1024 + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006m_force_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u8 cmd[2] = { 0 }; + u32 delay = 0; + u32 start_addr = 0; + u32 tmplen = 0; + + FTS_INFO("fw force upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw file buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto FORCE_UPG_ERR; + } + + /* erase 0k~116k flash */ + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_WRITE_FLASH_VALUE; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto FORCE_UPG_ERR; + } + + if (len > (116 * 1024)) { + tmplen = 116 * 1024; + } else { + tmplen = len; + } + delay = FTS_ERASE_SECTOR_DELAY * (tmplen / FTS_MAX_LEN_SECTOR); + ret = fts_fwupg_erase(client, delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto FORCE_UPG_ERR; + } + + /* write flash */ + start_addr = 0; + tmpbuf = buf + start_addr; + ret = fts_ft8006m_write_ecc(client, start_addr, tmpbuf, tmplen); + if (ret < 0 ) { + FTS_ERROR("app buffer write fail"); + goto FORCE_UPG_ERR; + } + + if (fts_ft8006m_check_ide(buf, len)) { + FTS_INFO("erase and write param configure area"); + ret = fts_ft8006m_param_flash(client, buf, len); + if (ret < 0 ) { + FTS_ERROR("param upgrade(erase/write/ecc) fail"); + goto FORCE_UPG_ERR; + } + } + + FTS_INFO("upgrade success, reset to normal boot"); +FORCE_UPG_ERR: + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + msleep(400); + return ret; +} + +struct upgrade_func upgrade_func_ft8006m = { + .ctype = {0x07, 0x10}, + .fwveroff = 0x510E, + .fwcfgoff = 0x0F80, + .appoff = 0x5000, + .licoff = 0x0000, + .paramcfgoff = 0x1F000, + .paramcfgveroff = 0x1F004, + .paramcfg2off = 0x4000, + .pramboot_supported = true, + .pramboot = pb_file_ft8006m, + .pb_length = sizeof(pb_file_ft8006m), + .hid_supported = false, + .upgrade = fts_ft8006m_upgrade, + .get_hlic_ver = fts_ft8006m_get_hlic_ver, + .lic_upgrade = fts_ft8006m_lic_upgrade, + .param_upgrade = fts_ft8006m_param_upgrade, + .force_upgrade = fts_ft8006m_force_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c new file mode 100644 index 000000000000..0874729e5d59 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c @@ -0,0 +1,597 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_gestrue.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#if FTS_GESTURE_EN +/****************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define KEY_GESTURE_U KEY_U +#define KEY_GESTURE_UP KEY_UP +#define KEY_GESTURE_DOWN KEY_DOWN +#define KEY_GESTURE_LEFT KEY_LEFT +#define KEY_GESTURE_RIGHT KEY_RIGHT +#define KEY_GESTURE_O KEY_O +#define KEY_GESTURE_E KEY_E +#define KEY_GESTURE_M KEY_M +#define KEY_GESTURE_L KEY_L +#define KEY_GESTURE_W KEY_W +#define KEY_GESTURE_S KEY_S +#define KEY_GESTURE_V KEY_V +#define KEY_GESTURE_C KEY_C +#define KEY_GESTURE_Z KEY_Z + +#define GESTURE_LEFT 0x20 +#define GESTURE_RIGHT 0x21 +#define GESTURE_UP 0x22 +#define GESTURE_DOWN 0x23 +#define GESTURE_DOUBLECLICK 0x24 +#define GESTURE_O 0x30 +#define GESTURE_W 0x31 +#define GESTURE_M 0x32 +#define GESTURE_E 0x33 +#define GESTURE_L 0x44 +#define GESTURE_S 0x46 +#define GESTURE_V 0x54 +#define GESTURE_Z 0x41 +#define GESTURE_C 0x34 +#define FTS_GESTRUE_POINTS 6 +#define FTS_GESTRUE_POINTS_HEADER 2 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* +* header - byte0:gesture id +* byte1:pointnum +* byte2~7:reserved +* coordinate_x - All gesture point x coordinate +* coordinate_y - All gesture point y coordinate +* mode - 1:enable gesture function(default) +* - 0:disable +* active - 1:enter into gesture(suspend) +* 0:gesture disable or resume +*/ +struct fts_gesture_st { + u8 header[FTS_GESTRUE_POINTS_HEADER]; + u16 coordinate_x[FTS_GESTRUE_POINTS]; + u16 coordinate_y[FTS_GESTRUE_POINTS]; + u8 mode; /*host driver enable gesture flag*/ + u8 active; /*gesture actutally work*/ +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_gesture_st fts_gesture_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static ssize_t fts_gesture_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t fts_gesture_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t fts_gesture_buf_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t fts_gesture_buf_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); + +/* sysfs gesture node + * read example: cat fts_gesture_mode ---read gesture mode + * write example:echo 01 > fts_gesture_mode ---write gesture mode to 01 + * + */ +static DEVICE_ATTR (fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show, fts_gesture_store); +/* + * read example: cat fts_gesture_buf ---read gesture buf + */ +static DEVICE_ATTR (fts_gesture_buf, S_IRUGO | S_IWUSR, fts_gesture_buf_show, fts_gesture_buf_store); +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + NULL, +}; + +static struct attribute_group fts_gesture_group = { + .attrs = fts_gesture_mode_attrs, +}; + +/************************************************************************ +* Name: fts_gesture_show +* Brief: +* Input: device, device attribute, char buf +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + u8 val; + struct input_dev *input_dev = fts_data->input_dev; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + mutex_lock(&input_dev->mutex); + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Gesture Mode: %s\n", fts_gesture_data.mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0) = %d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/************************************************************************ +* Name: fts_gesture_store +* Brief: +* Input: device, device attribute, char buf, char count +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[GESTURE]enable gesture"); + fts_gesture_data.mode = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[GESTURE]disable gesture"); + fts_gesture_data.mode = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} +/************************************************************************ +* Name: fts_gesture_buf_show +* Brief: +* Input: device, device attribute, char buf +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_buf_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Gesture ID: 0x%x\n", fts_gesture_data.header[0]); + count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum: %d\n", fts_gesture_data.header[1]); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Point Buf:\n"); + for (i = 0; i < fts_gesture_data.header[1]; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i, fts_gesture_data.coordinate_x[i], fts_gesture_data.coordinate_y[i]); + if ((i + 1) % 4 == 0) + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/************************************************************************ +* Name: fts_gesture_buf_store +* Brief: +* Input: device, device attribute, char buf, char count +* Output: +* Return: +***********************************************************************/ +static ssize_t fts_gesture_buf_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + /* place holder for future use */ + return -EPERM; +} + +/***************************************************************************** +* Name: fts_create_gesture_sysfs +* Brief: +* Input: +* Output: +* Return: 0-success or others-error +*****************************************************************************/ +int fts_create_gesture_sysfs(struct i2c_client *client) +{ + int ret = 0; + + ret = sysfs_create_group(&client->dev.kobj, &fts_gesture_group); + if ( ret != 0) { + FTS_ERROR( "[GESTURE]fts_gesture_mode_group(sysfs) create failed!"); + sysfs_remove_group(&client->dev.kobj, &fts_gesture_group); + return ret; + } + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_report +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_gesture_report(struct input_dev *input_dev, int gesture_id) +{ + int gesture; + + FTS_FUNC_ENTER(); + FTS_INFO("fts gesture_id==0x%x ", gesture_id); + switch (gesture_id) { + case GESTURE_LEFT: + gesture = KEY_GESTURE_LEFT; + break; + case GESTURE_RIGHT: + gesture = KEY_GESTURE_RIGHT; + break; + case GESTURE_UP: + gesture = KEY_GESTURE_UP; + break; + case GESTURE_DOWN: + gesture = KEY_GESTURE_DOWN; + break; + case GESTURE_DOUBLECLICK: + gesture = KEY_GESTURE_U; + break; + case GESTURE_O: + gesture = KEY_GESTURE_O; + break; + case GESTURE_W: + gesture = KEY_GESTURE_W; + break; + case GESTURE_M: + gesture = KEY_GESTURE_M; + break; + case GESTURE_E: + gesture = KEY_GESTURE_E; + break; + case GESTURE_L: + gesture = KEY_GESTURE_L; + break; + case GESTURE_S: + gesture = KEY_GESTURE_S; + break; + case GESTURE_V: + gesture = KEY_GESTURE_V; + break; + case GESTURE_Z: + gesture = KEY_GESTURE_Z; + break; + case GESTURE_C: + gesture = KEY_GESTURE_C; + break; + default: + gesture = -1; + break; + } + /* report event key */ + if (gesture != -1) { + FTS_DEBUG("Gesture Code=%d", gesture); + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); + } + + FTS_FUNC_EXIT(); +} + +/************************************************************************ +* Name: fts_gesture_read_buffer +* Brief: read data from TP register +* Input: +* Output: +* Return: fail <0 +***********************************************************************/ +static int fts_gesture_read_buffer(struct i2c_client *client, u8 *buf, int read_bytes) +{ + int remain_bytes; + int ret; + int i; + + if (read_bytes <= I2C_BUFFER_LENGTH_MAXINUM) { + ret = fts_i2c_read(client, buf, 1, buf, read_bytes); + } else { + ret = fts_i2c_read(client, buf, 1, buf, I2C_BUFFER_LENGTH_MAXINUM); + remain_bytes = read_bytes - I2C_BUFFER_LENGTH_MAXINUM; + for (i = 1; remain_bytes > 0; i++) { + if (remain_bytes <= I2C_BUFFER_LENGTH_MAXINUM) + ret = fts_i2c_read(client, buf, 0, buf + I2C_BUFFER_LENGTH_MAXINUM * i, remain_bytes); + else + ret = fts_i2c_read(client, buf, 0, buf + I2C_BUFFER_LENGTH_MAXINUM * i, I2C_BUFFER_LENGTH_MAXINUM); + remain_bytes -= I2C_BUFFER_LENGTH_MAXINUM; + } + } + + return ret; +} + +/************************************************************************ +* Name: fts_gesture_readdata +* Brief: read data from TP register +* Input: +* Output: +* Return: return 0 if succuss, otherwise reture error code +***********************************************************************/ +int fts_gesture_readdata(struct fts_ts_data *ts_data) +{ + u8 buf[FTS_GESTRUE_POINTS * 4] = { 0 }; + int ret = 0; + int i = 0; + int gestrue_id = 0; + int read_bytes = 0; + u8 pointnum; + u8 state; + struct i2c_client *client = ts_data->client; + struct input_dev *input_dev = ts_data->input_dev; + + if (!ts_data->suspended) { + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state); + if ((ret < 0) || (state != ENABLE)) { + FTS_DEBUG("gesture not enable, don't process gesture"); + return -EIO; + } + + /* init variable before read gesture point */ + memset(fts_gesture_data.header, 0, FTS_GESTRUE_POINTS_HEADER); + memset(fts_gesture_data.coordinate_x, 0, FTS_GESTRUE_POINTS * sizeof(u16)); + memset(fts_gesture_data.coordinate_y, 0, FTS_GESTRUE_POINTS * sizeof(u16)); + + buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + ret = fts_i2c_read(client, buf, 1, buf, FTS_GESTRUE_POINTS_HEADER); + if (ret < 0) { + FTS_ERROR("[GESTURE]Read gesture header data failed!!"); + FTS_FUNC_EXIT(); + return ret; + } + + memcpy(fts_gesture_data.header, buf, FTS_GESTRUE_POINTS_HEADER); + gestrue_id = buf[0]; + pointnum = buf[1]; + read_bytes = ((int)pointnum) * 4 + FTS_GESTRUE_POINTS_HEADER; + buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + FTS_DEBUG("[GESTURE]PointNum=%d", pointnum); + if (pointnum > FTS_GESTRUE_POINTS) { + FTS_ERROR("[GESTURE]gesture point number(%d) fail"); + return -EIO; + } + + ret = fts_gesture_read_buffer(client, buf, read_bytes); + if (ret < 0) { + FTS_ERROR("[GESTURE]Read gesture touch data failed!!"); + FTS_FUNC_EXIT(); + return ret; + } + + fts_gesture_report(input_dev, gestrue_id); + for (i = 0; i < pointnum; i++) { + fts_gesture_data.coordinate_x[i] = (((s16) buf[0 + (4 * i + 2)]) & 0x0F) << 8 + | (((s16) buf[1 + (4 * i + 2)]) & 0xFF); + fts_gesture_data.coordinate_y[i] = (((s16) buf[2 + (4 * i + 2)]) & 0x0F) << 8 + | (((s16) buf[3 + (4 * i + 2)]) & 0xFF); + } + + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_recovery +* Brief: recovery gesture state when reset or power on +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_gesture_recovery(struct i2c_client *client) +{ + if ((ENABLE == fts_gesture_data.mode) && (ENABLE == fts_gesture_data.active)) { + FTS_INFO("enter fts_gesture_recovery"); + fts_i2c_write_reg(client, 0xD1, 0xff); + fts_i2c_write_reg(client, 0xD2, 0xff); + fts_i2c_write_reg(client, 0xD5, 0xff); + fts_i2c_write_reg(client, 0xD6, 0xff); + fts_i2c_write_reg(client, 0xD7, 0xff); + fts_i2c_write_reg(client, 0xD8, 0xff); + fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, ENABLE); + } +} + +/***************************************************************************** +* Name: fts_gesture_suspend +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +int fts_gesture_suspend(struct i2c_client *client) +{ + int ret; + int i; + u8 state; + + FTS_INFO("gesture suspend..."); + /* gesture not enable, return immediately */ + if (fts_gesture_data.mode == DISABLE) { + FTS_INFO("gesture is disabled"); + return -EINVAL; + } + + for (i = 0; i < 5; i++) { + fts_i2c_write_reg(client, 0xd1, 0xff); + fts_i2c_write_reg(client, 0xd2, 0xff); + fts_i2c_write_reg(client, 0xd5, 0xff); + fts_i2c_write_reg(client, 0xd6, 0xff); + fts_i2c_write_reg(client, 0xd7, 0xff); + fts_i2c_write_reg(client, 0xd8, 0xff); + fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, ENABLE); + msleep(1); + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state); + if (state == ENABLE) + break; + } + + if (i >= 5) { + FTS_ERROR("[GESTURE]Enter into gesture(suspend) failed!\n"); + fts_gesture_data.active = DISABLE; + return -EIO; + } + + ret = enable_irq_wake(fts_data->irq); + if (ret) { + FTS_INFO("enable_irq_wake(irq:%d) failed", fts_data->irq); + } + + fts_gesture_data.active = ENABLE; + FTS_INFO("[GESTURE]Enter into gesture(suspend) successfully!"); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_resume +* Brief: +* Input: +* Output: +* Return: return 0 if succuss, otherwise return error code +*****************************************************************************/ +int fts_gesture_resume(struct i2c_client *client) +{ + int ret; + int i; + u8 state; + + FTS_INFO("gesture resume..."); + /* gesture not enable, return immediately */ + if (fts_gesture_data.mode == DISABLE) { + FTS_DEBUG("gesture is disabled"); + return -EINVAL; + } + + if (fts_gesture_data.active == DISABLE) { + FTS_DEBUG("gesture in suspend is failed, no running fts_gesture_resume"); + return -EINVAL; + } + + fts_gesture_data.active = DISABLE; + for (i = 0; i < 5; i++) { + fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, DISABLE); + msleep(1); + fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state); + if (state == DISABLE) + break; + } + + if (i >= 5) { + FTS_ERROR("[GESTURE]Clear gesture(resume) failed!\n"); + return -EIO; + } + + ret = disable_irq_wake(fts_data->irq); + if (ret) { + FTS_INFO("disable_irq_wake(irq:%d) failed", fts_data->irq); + } + + FTS_INFO("[GESTURE]resume from gesture successfully!"); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_gesture_init +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_gesture_init(struct fts_ts_data *ts_data) +{ + struct i2c_client *client = ts_data->client; + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C); + + __set_bit(KEY_GESTURE_RIGHT, input_dev->keybit); + __set_bit(KEY_GESTURE_LEFT, input_dev->keybit); + __set_bit(KEY_GESTURE_UP, input_dev->keybit); + __set_bit(KEY_GESTURE_DOWN, input_dev->keybit); + __set_bit(KEY_GESTURE_U, input_dev->keybit); + __set_bit(KEY_GESTURE_O, input_dev->keybit); + __set_bit(KEY_GESTURE_E, input_dev->keybit); + __set_bit(KEY_GESTURE_M, input_dev->keybit); + __set_bit(KEY_GESTURE_W, input_dev->keybit); + __set_bit(KEY_GESTURE_L, input_dev->keybit); + __set_bit(KEY_GESTURE_S, input_dev->keybit); + __set_bit(KEY_GESTURE_V, input_dev->keybit); + __set_bit(KEY_GESTURE_C, input_dev->keybit); + __set_bit(KEY_GESTURE_Z, input_dev->keybit); + + fts_create_gesture_sysfs(client); + fts_gesture_data.mode = ENABLE; + fts_gesture_data.active = DISABLE; + + FTS_FUNC_EXIT(); + return 0; +} + +/************************************************************************ +* Name: fts_gesture_exit +* Brief: call when driver removed +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_gesture_exit(struct i2c_client *client) +{ + FTS_FUNC_ENTER(); + sysfs_remove_group(&client->dev.kobj, &fts_gesture_group); + FTS_FUNC_EXIT(); + return 0; +} +#endif diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c new file mode 100644 index 000000000000..5e8294145b28 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c @@ -0,0 +1,240 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/************************************************************************ +* +* File Name: focaltech_i2c.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-04 +* +* Abstract: i2c communication with TP +* +* Version: v1.0 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define I2C_RETRY_NUMBER 3 +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static DEFINE_MUTEX(i2c_rw_access); + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ + +/************************************************************************ +* Name: fts_i2c_read +* Brief: i2c read +* Input: i2c info, write buf, write len, read buf, read len +* Output: get data in the 3rd buf +* Return: fail <0 +***********************************************************************/ +int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) +{ + int ret = 0; + int i = 0; + + mutex_lock(&i2c_rw_access); + + if (readlen > 0) { + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) { + FTS_ERROR("[IIC]: i2c_transfer(write) error, ret=%d!!", ret); + } else + break; + } + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) { + FTS_ERROR("[IIC]: i2c_transfer(read) error, ret=%d!!", ret); + } else + break; + } + } + } + + mutex_unlock(&i2c_rw_access); + return ret; +} + +/************************************************************************ +* Name: fts_i2c_write +* Brief: i2c write +* Input: i2c info, write buf, write len +* Output: no +* Return: fail <0 +***********************************************************************/ +int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen) +{ + int ret = 0; + int i = 0; + + mutex_lock(&i2c_rw_access); + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) { + FTS_ERROR("%s: i2c_transfer(write) error, ret=%d", __func__, ret); + } else + break; + } + } + mutex_unlock(&i2c_rw_access); + + return ret; +} + +/************************************************************************ +* Name: fts_i2c_write_reg +* Brief: write register +* Input: i2c info, reg address, reg value +* Output: no +* Return: fail <0 +***********************************************************************/ +int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue) +{ + u8 buf[2] = {0}; + + buf[0] = regaddr; + buf[1] = regvalue; + return fts_i2c_write(client, buf, sizeof(buf)); +} + +/************************************************************************ +* Name: fts_i2c_read_reg +* Brief: read register +* Input: i2c info, reg address, reg value +* Output: get reg value +* Return: fail <0 +***********************************************************************/ +int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue) +{ + return fts_i2c_read(client, ®addr, 1, regvalue, 1); +} + +/************************************************************************ +* HID to standard I2C +***********************************************************************/ +void fts_i2c_hid2std(struct i2c_client *client) +{ + int ret = 0; + u8 buf[3] = {0xeb, 0xaa, 0x09}; + + ret = fts_i2c_write(client, buf, 3); + if (ret < 0) + FTS_ERROR("hid2std cmd write fail"); + else { + msleep(10); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_i2c_read(client, NULL, 0, buf, 3); + if (ret < 0) + FTS_ERROR("hid2std cmd read fail"); + else if ((0xeb == buf[0]) && (0xaa == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("hidi2c change to stdi2c successful"); + } else { + FTS_ERROR("hidi2c change to stdi2c fail"); + } + } +} + +/************************************************************************ +* Name: fts_i2c_init +* Brief: fts i2c init +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_i2c_init(void) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +/************************************************************************ +* Name: fts_i2c_exit +* Brief: fts i2c exit +* Input: +* Output: +* Return: +***********************************************************************/ +int fts_i2c_exit(void) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c new file mode 100644 index 000000000000..7157f444080d --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c @@ -0,0 +1,135 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_point_report_check.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-11-16 +* +* Abstract: point report check function +* +* Version: v1.0 +* +* Revision History: +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_POINT_REPORT_CHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */ + +/***************************************************************************** +* functions body +*****************************************************************************/ +/***************************************************************************** +* Name: fts_prc_func +* Brief: fts point report check work func, report whole up of points +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_prc_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, prc_work.work); + struct input_dev *input_dev = ts_data->input_dev; + +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&ts_data->report_mutex); + +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < ts_data->pdata->max_touch_number; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + + mutex_unlock(&ts_data->report_mutex); + + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_prc_queue_work +* Brief: fts point report check queue work, call it when interrupt comes +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_prc_queue_work(struct fts_ts_data *ts_data) +{ + cancel_delayed_work(&ts_data->prc_work); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); +} + +/***************************************************************************** +* Name: fts_point_report_check_init +* Brief: +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_point_report_check_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run point report check function"); + return -EINVAL; + } + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_point_report_check_exit +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_point_report_check_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_POINT_REPORT_CHECK_EN */ + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/Makefile b/drivers/input/touchscreen/focaltech_touch/focaltech_test/Makefile new file mode 100644 index 000000000000..2a2945ec021a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# +#Makefile for test module +# + + + +obj-y += focaltech_test.o +obj-y += focaltech_test_ini.o +obj-y += supported_ic/ + diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.c b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.c new file mode 100644 index 000000000000..991bbba87c70 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.c @@ -0,0 +1,1948 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/************************************************************************ +* +* File Name: focaltech_test.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Modify: +* +* Abstract: create char device and proc node for the comm between APK and TP +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_test *fts_ftest; + +struct test_funcs *test_func_list[] = { + &test_func_ft8201, +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +void sys_delay(int ms) +{ + msleep(ms); +} + +int focal_abs(int value) +{ + if (value < 0) + value = 0 - value; + + return value; +} + +void *fts_malloc(size_t size) +{ + return kzalloc(size, GFP_KERNEL); +} + +void fts_free_proc(void *p) +{ + return kfree(p); +} + +void print_buffer(int *buffer, int length, int line_num) +{ + int i = 0; + + if (NULL == buffer) { + FTS_TEST_DBG("buffer is null"); + return; + } + + for (i = 0; i < length; i++) { + printk("%5d ", buffer[i]); + if ((0 == (i + 1) % line_num)) + printk("\n"); + } + printk("\n"); +} + +/******************************************************************** + * test i2c read/write interface + *******************************************************************/ +static int fts_test_i2c_read(u8 *writebuf, int writelen, u8 *readbuf, int readlen) +{ + int ret = 0; +#if 1 + if (NULL == fts_data) { + FTS_TEST_ERROR("fts_data is null, no test"); + return -EINVAL; + } + ret = fts_i2c_read(fts_data->client, writebuf, writelen, readbuf, readlen); +#else + ret = fts_i2c_read(writebuf, writelen, readbuf, readlen); +#endif + + if (ret < 0) + return ret; + else + return 0; +} + +static int fts_test_i2c_write(u8 *writebuf, int writelen) +{ + int ret = 0; +#if 1 + if (NULL == fts_data) { + FTS_TEST_ERROR("fts_data is null, no test"); + return -EINVAL; + } + ret = fts_i2c_write(fts_data->client, writebuf, writelen); +#else + ret = fts_i2c_write(writebuf, writelen); +#endif + + if (ret < 0) + return ret; + else + return 0; +} + +int fts_test_read_reg(u8 addr, u8 *val) +{ + return fts_test_i2c_read(&addr, 1, val, 1); +} + +int fts_test_write_reg(u8 addr, u8 val) +{ + int ret; + u8 cmd[2] = {0}; + + cmd[0] = addr; + cmd[1] = val; + ret = fts_test_i2c_write(cmd, 2); + + return ret; +} + +int fts_test_read(u8 addr, u8 *readbuf, int readlen) +{ + int ret = 0; + int i = 0; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = readlen; + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + // FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); + + ret = fts_test_i2c_read(&addr, 1, &readbuf[offset], packet_length); + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + for (i = 1; i < packet_num; i++) { + offset += packet_length; + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + + ret = fts_test_i2c_read(NULL, 0, &readbuf[offset], packet_length); + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + } + + return 0; +} + +int fts_test_write(u8 addr, u8 *writebuf, int writelen) +{ + int ret = 0; + int i = 0; + u8 data[BYTES_PER_TIME + 1] = { 0 }; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = writelen; + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); + + data[0] = addr; + for (i = 0; i < packet_num; i++) { + if (i != 0) { + data[0] = addr + 1; + } + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + memcpy(&data[1], &writebuf[offset], packet_length); + + ret = fts_test_i2c_write(data, packet_length + 1); + if (ret < 0) { + FTS_TEST_ERROR("write buffer fail"); + return ret; + } + + offset += packet_length; + } + + return 0; +} + +/******************************************************************** + * test global function enter work/factory mode + *******************************************************************/ +int enter_work_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + FTS_TEST_FUNC_ENTER(); + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x00); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) { + FTS_TEST_INFO("enter work mode success"); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter work mode fail"); + return -EIO; + } + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +int enter_factory_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x40); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) { + FTS_TEST_INFO("enter factory mode success"); + sys_delay(200); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter factory mode fail"); + return -EIO; + } + + return 0; +} + +/* + * read_mass_data - read rawdata/short test data + * addr - register addr which read data from + * byte_num - read data length, unit:byte + * buf - save data + * + * return 0 if read data succuss, otherwise return error code + */ +int read_mass_data(u8 addr, int byte_num, int *buf) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n"); + return -ENOMEM; + } + + /* read rawdata buffer */ + FTS_TEST_INFO("mass data len:%d", byte_num); + ret = fts_test_read(addr, data, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read mass data fail\n"); + goto read_massdata_err; + } + + for (i = 0; i < byte_num; i = i + 2) { + buf[i >> 1] = (int)(((int)(data[i]) << 8) + data[i + 1]); + } + + ret = 0; +read_massdata_err: + fts_free(data); + return ret; +} + +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf) +{ + int ret = 0; + int times = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + + /* Start ADC sample */ + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_EN, 0x01); + if (ret) { + FTS_TEST_SAVE_ERR("start short test fail\n"); + goto adc_err; + } + + sys_delay(ch_num * FACTORY_TEST_DELAY); + for (times = 0; times < FACTORY_TEST_RETRY; times++) { + ret = fts_test_read_reg(FACTORY_REG_SHORT_TEST_STATE, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTORY_REG_SHORT_TEST_STATE, short_state, times); + + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto adc_err; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR, byte_num, adc_buf); + if (ret) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + +adc_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +/* + * wait_state_update - wait fw status update + */ +int wait_state_update(u8 retval) +{ + int ret = 0; + int times = 0; + u8 state = 0xFF; + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + /* Wait register status update */ + state = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_PARAM_UPDATE_STATE, &state); + if ((ret >= 0) && (retval == state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", \ + FACTORY_REG_PARAM_UPDATE_STATE, state, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("Wait State Update fail\n"); + return -EIO; + } + + return 0; +} + +/* + * start_scan - start to scan a frame + */ +int start_scan(void) +{ + int ret = 0; + u8 addr = 0; + u8 val = 0; + u8 finish_val = 0; + int times = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func) ) { + FTS_TEST_ERROR("test/func is null\n"); + return -EINVAL; + } + + if (SCAN_SC == tdata->func->startscan_mode) { + /* sc ic */ + addr = FACTORY_REG_SCAN_ADDR2; + val = 0x01; + finish_val = 0x00; + } else { + addr = DEVIDE_MODE_ADDR; + val = 0xC0; + finish_val = 0x40; + } + + /* write register to start scan */ + ret = fts_test_write_reg(addr, val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write start scan mode fail\n"); + return ret; + } + + /* Wait for the scan to complete */ + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + + ret = fts_test_read_reg(addr, &val); + if ((ret >= 0) && (val == finish_val)) { + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", addr, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("scan timeout\n"); + return -EIO; + } + + return 0; +} + +static int read_rawdata( + u8 off_addr, + u8 off_val, + u8 rawdata_addr, + int byte_num, + int *data) +{ + int ret = 0; + + /* set line addr or rawdata start addr */ + ret = fts_test_write_reg(off_addr, off_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wirte line/start addr fail\n"); + return ret; + } + + ret = read_mass_data(rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata(int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func) ) { + FTS_TEST_ERROR("test/func is null\n"); + return -EINVAL; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + return ret; + } + + /* start scanning */ + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan fail\n"); + return ret; + } + + /* read rawdata */ + if (IC_HW_INCELL == tdata->func->hwtype) { + val = 0xAD; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR; + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + val = 0xAA; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + } else { + val = 0x0; + addr = FACTORY_REG_RAWDATA_SADDR_SC; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_SC; + } + + byte_num = tdata->node.node_num * 2; + ret = read_rawdata(addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +/* + * chip_clb - auto clb + */ +int chip_clb(void) +{ + int ret = 0; + u8 val = 0; + int times = 0; + + /* start clb */ + ret = fts_test_write_reg(FACTORY_REG_CLB, 0x04); + if (ret) { + FTS_TEST_SAVE_ERR("write start clb fail\n"); + return ret; + } + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + ret = fts_test_read_reg(FACTORY_REG_CLB, &val); + if ((0 == ret) && (0x02 == val)) { + /* clb ok */ + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", FACTORY_REG_CLB, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("chip clb timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * get_cb_incell - get cb data for incell IC + */ +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf) +{ + int ret = 0; + int i = 0; + u8 cb_addr = 0; + u8 addr_h = 0; + u8 addr_l = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int addr = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("cb buffer malloc fail\n"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + cb_addr = FACTORY_REG_CB_ADDR; + for (i = 0; i < packet_num; i++) { + offset = read_num * i; + addr = saddr + offset; + addr_h = (addr >> 8) & 0xFF; + addr_l = addr & 0xFF; + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_H, addr_h); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr high fail\n"); + goto TEST_CB_ERR; + } + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_L, addr_l); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr low fail\n"); + goto TEST_CB_ERR; + } + + ret = fts_test_read(cb_addr, data + offset, read_num); + if (ret) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto TEST_CB_ERR; + } + } + + for (i = 0; i < byte_num; i++) { + cb_buf[i] = data[i]; + } + +TEST_CB_ERR: + fts_free(data); + return ret; +} + +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + int i = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + u8 cb_addr = 0; + u8 off_addr = 0; + struct fts_test *tdata = fts_ftest; + u8 *cb = NULL; + + if ((NULL == tdata) || (NULL == tdata->func) ) { + FTS_TEST_ERROR("test/func is null\n"); + return -EINVAL; + } + + cb = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == cb) { + FTS_TEST_SAVE_ERR("malloc memory for cb buffer fail\n"); + return -ENOMEM; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_MC_SC_CB_ADDR; + off_addr = FACTORY_REG_MC_SC_CB_ADDR_OFF; + } else if (IC_HW_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_SC_CB_ADDR; + off_addr = FACTORY_REG_SC_CB_ADDR_OFF; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + offset = 0; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + for (i = 0; i < packet_num; i++) { + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(off_addr, offset); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb addr offset fail\n"); + goto cb_err; + } + + ret = fts_test_read(cb_addr, cb + offset, read_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto cb_err; + } + + offset += read_num; + } + + if (DATA_ONE_BYTE == mode) { + for (i = 0; i < byte_num; i++) { + cb_buf[i] = cb[i]; + } + } else if (DATA_TWO_BYTE == mode) { + for (i = 0; i < byte_num; i = i + 2) { + cb_buf[i >> 1] = (int)(((int)(cb[i]) << 8) + cb[i + 1]); + } + } + + ret = 0; +cb_err: + fts_free(cb); + return ret; +} + +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_va = tdata->node.node_num - tdata->node.key_num; + + if (!data || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/node_valid is null\n"); + return false; + } + + for (i = 0; i < node_va; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min) || (data[i] > max)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min, max); + result = false; + } + } + + if (key) { + for (i = node_va; i < tdata->node.node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min_vk) || (data[i] > max_vk)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, + data[i], min_vk, max_vk); + result = false; + } + } + } + + return result; +} + +bool compare_array(int *data, int *min, int *max, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_num = tdata->node.node_num; + + if (!data || !min || !max || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/min/max/node_valid is null\n"); + return false; + } + + if (!key) { + node_num -= tdata->node.key_num; + } + for (i = 0; i < node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min[i], max[i]); + result = false; + } + } + + return result; +} + +/* + * show_data - show and save test data to testresult.txt + */ +void show_data(int *data, bool key) +{ + int i = 0; + int j = 0; + struct fts_test *tdata = fts_ftest; + int node_num = tdata->node.node_num; + int tx_num = tdata->node.tx_num; + int rx_num = tdata->node.rx_num; + + FTS_TEST_FUNC_ENTER(); + for (i = 0; i < tx_num; i++) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", i + 1); + for (j = 0; j < rx_num; j++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i * rx_num + j]); + } + FTS_TEST_SAVE_INFO("\n"); + } + + if (key) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", tx_num + 1); + for (i = tx_num * rx_num; i < node_num; i++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + } + FTS_TEST_FUNC_EXIT(); +} + +/* + * save_testdata_incell - save data to testdata.csv + */ +void save_data_csv(int *data, char *name, u8 code, bool mc_sc, bool key) +{ +#if CSV_SUPPORT + int i = 0; + int tx = 0; + int rx = 0; + int csv_node_num = 0; + struct fts_test *tdata = fts_ftest; + struct fts_test_node *node = NULL; + struct csv_format *csv = &tdata->csv; + + FTS_TEST_FUNC_ENTER(); + if (!csv || !csv->line2_buffer || !csv->data_buffer) { + FTS_TEST_ERROR("csv buffer is null"); + return; + } + + if (mc_sc) { + node = &tdata->sc_node; + tx = 2; + } else { + node = &tdata->node; + tx = node->tx_num; + } + if (key) { + tx++; + } + rx = node->rx_num; + csv_node_num = tx * rx; + + /* line 2 */ + csv->line2_len += snprintf(csv->line2_buffer + csv->line2_len, \ + CSV_LINE2_BUFFER_LEN - csv->line2_len, + "%s, %d, %d, %d, %d, %d, ", \ + name, code, tx, rx, + csv->start_line, csv->item_count); + + if (csv->line2_len >= CSV_LINE2_BUFFER_LEN - 1) { + FTS_TEST_ERROR("csv line2 buffer length(%d) fail", csv->line2_len); + } + csv->start_line += tx; + csv->item_count++; + + /* test data */ + for (i = 0; i < csv_node_num; i++) { + if (((i + 1) % rx) == 0) { + csv->data_len += snprintf(csv->data_buffer + csv->data_len, \ + CSV_DATA_BUFFER_LEN - csv->data_len, \ + "%d, \n", data[i]); + } else { + csv->data_len += snprintf(csv->data_buffer + csv->data_len, \ + CSV_DATA_BUFFER_LEN - csv->data_len, \ + "%d, ", data[i]); + } + + if (csv->data_len >= CSV_DATA_BUFFER_LEN - 1) { + FTS_TEST_ERROR("csv data buffer length(%d) fail", csv->data_len); + } + } + + + FTS_TEST_FUNC_EXIT(); +#endif +} + + +/* mc_sc only */ +/* Only V3 Pattern has mapping & no-mapping */ +int mapping_switch(u8 mapping) +{ + int ret = 0; + u8 val = 0xFF; + struct fts_test *tdata = fts_ftest; + + if (tdata->v3_pattern) { + ret = fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + if (ret < 0) { + FTS_TEST_ERROR("read 0x54 register fail"); + return ret; + } + + if (val != mapping) { + ret = fts_test_write_reg(FACTORY_REG_NOMAPPING, mapping); + if (ret < 0) { + FTS_TEST_ERROR("write 0x54 register fail"); + return ret; + } + sys_delay(FACTORY_TEST_DELAY); + } + } + + return 0; +} + +bool get_fw_wp(u8 wp_ch_sel, enum wp_type water_proof_type) +{ + bool fw_wp_state = false; + + switch (water_proof_type) { + case WATER_PROOF_ON: + /* bit5: 0-check in wp on, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x20); + break; + case WATER_PROOF_ON_TX: + /* Bit6: 0-check Rx+Tx in wp mode 1-check one channel + Bit2: 0-check Tx in wp mode; 1-check Rx in wp mode + */ + fw_wp_state = (!(wp_ch_sel & 0x40) || !(wp_ch_sel & 0x04)); + break; + case WATER_PROOF_ON_RX: + fw_wp_state = (!(wp_ch_sel & 0x40) || (wp_ch_sel & 0x04)); + break; + case WATER_PROOF_OFF: + /* bit7: 0-check in wp off, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x80); + break; + case WATER_PROOF_OFF_TX: + /* Bit1-0: 00-check Tx in non-wp mode + 01-check Rx in non-wp mode + 10:check Rx+Tx in non-wp mode + */ + fw_wp_state = ((0x0 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + case WATER_PROOF_OFF_RX: + fw_wp_state = ((0x01 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + default: + break; + } + + return fw_wp_state; +} + +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, wp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc mode fail\n"); + return ret; + } + + /* read cb */ + ret = get_cb_sc(byte_num, cb_buf, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc cb fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc_sc(enum wp_type wp, int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func) ) { + FTS_TEST_ERROR("test/func is null\n"); + return -EINVAL; + } + + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + if (WATER_PROOF_ON == wp) { + val = 0xAC; + } else { + val = 0xAB; + } + + byte_num = tdata->sc_node.node_num * 2; + ret = read_rawdata(addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata) +{ + int ret = 0; + int i = 0; + + if (NULL == rawdata ) { + FTS_TEST_SAVE_ERR("rawdata buffer is null\n"); + return -EINVAL; + } + + /* set frequecy high/low */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + return ret; + } + + /* fir enable/disable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", fir); + return ret; + } + + /* get rawdata */ + for (i = 0; i < 3; i++) { + /* lost 3 frames, in order to obtain stable data */ + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + return ret; + } + + return 0; +} + +void short_print_mc(int *r, int num) +{ + int i = 0; + + for (i = 0; i < num; i++) { + printk("%4d ", r[i]); + } + + printk("\n"); +} + +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode) +{ + int ret = 0; + int i = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + /* select short test mode & start test */ + ret = fts_test_write_reg(FACTROY_REG_SHORT_TEST_EN, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write short test mode fail\n"); + goto test_err; + } + + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + + ret = fts_test_read_reg(FACTROY_REG_SHORT_TEST_EN, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTROY_REG_SHORT_TEST_EN, short_state, i); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto test_err; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR_MC, byte_num, adc_buf); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + + FTS_TEST_DBG("adc data:\n"); + short_print_mc(adc_buf, byte_num / 2); +test_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +bool compare_mc_sc(bool tx_check, bool rx_check, int *data, int *min, int *max) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + + if (rx_check) { + for (i = 0; i < tdata->sc_node.rx_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,rx%d=%5d,range=(%5d,%5d)\n", + i + 1, data[i], min[i], max[i]); + result = false; + } + } + } + + if (tx_check) { + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_INFO("test fail,tx%d=%5d,range=(%5d,%5d)\n", + i - tdata->sc_node.rx_num + 1, + data[i], min[i], max[i]); + result = false; + } + } + } + + return result; +} + +void show_data_mc_sc(int *data) +{ + int i = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_SAVE_INFO("SCap Rx: "); + for (i = 0; i < tdata->sc_node.rx_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + + FTS_TEST_SAVE_INFO("SCap Tx: "); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); +} +/* mc_sc end*/ + +/* + * fts_test_save_test_data - Save test data to SD card etc. + */ +static int fts_test_save_test_data(char *file_name, char *data_buf, int len) +{ + struct file *pfile = NULL; + char filepath[128]; + loff_t pos; + mm_segment_t old_fs; + + FTS_TEST_FUNC_ENTER(); + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, file_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(pfile, data_buf, len, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +static int fts_test_malloc_free_data_csv(struct fts_test *tdata, bool allocate) +{ +#if CSV_SUPPORT + struct csv_format *csv = &tdata->csv; + + if (true == allocate) { + csv->buffer = vmalloc(CSV_BUFFER_LEN); + if (NULL == csv->buffer) { + FTS_TEST_ERROR("csv->buffer malloc fail\n"); + return -ENOMEM; + } + csv->line2_buffer = vmalloc(CSV_LINE2_BUFFER_LEN); + if (NULL == csv->line2_buffer) { + FTS_TEST_ERROR("csv->line2_buffer malloc fail\n"); + return -ENOMEM; + } + csv->data_buffer = vmalloc(CSV_DATA_BUFFER_LEN); + if (NULL == csv->data_buffer) { + FTS_TEST_ERROR("csv->data_buffer malloc fail\n"); + return -ENOMEM; + } + + /* initialize variable */ + csv->length = 0; + csv->line2_len = 0; + csv->data_len = 0; + csv->start_line = 11; + csv->item_count = 1; + + } else { + if (csv->buffer) { + vfree(csv->buffer); + csv->buffer = NULL; + } + if (csv->line2_buffer) { + vfree(csv->line2_buffer); + csv->line2_buffer = NULL; + } + if (csv->data_buffer) { + vfree(csv->data_buffer); + csv->data_buffer = NULL; + } + } +#endif + + return 0; +} + +static int fts_test_malloc_free_data_txt(struct fts_test *tdata, bool allocate) +{ + if (true == allocate) { + tdata->testresult = vmalloc(TXT_BUFFER_LEN); + if (NULL == tdata->testresult) { + FTS_TEST_ERROR("tdata->testresult malloc fail\n"); + return -ENOMEM; + } + + tdata->testresult_len = 0; + FTS_TEST_SAVE_INFO("FW version:0x%02x\n", tdata->fw_ver); + FTS_TEST_SAVE_INFO("tx_num:%d, rx_num:%d\n", + tdata->node.tx_num, tdata->node.rx_num); + } else { + if (tdata->testresult) { + vfree(tdata->testresult); + tdata->testresult = NULL; + } + } + + return 0; +} + +static void fts_test_save_data_csv(struct fts_test *tdata) +{ +#if CSV_SUPPORT + struct csv_format *csv = &tdata->csv; + + if (!csv || !csv->buffer || !csv->line2_buffer || !csv->data_buffer) { + FTS_TEST_ERROR("csv buffer is null"); + return; + } + + /* line 1 */ + csv->length += snprintf(csv->buffer + csv->length, \ + CSV_BUFFER_LEN - csv->length, \ + "ECC, 85, 170, IC Name, %s, IC Code, %x\n", \ + tdata->ini.ic_name, \ + (tdata->ini.ic_code >> IC_CODE_OFFSET)); + + /* line 2 */ + csv->length += snprintf(csv->buffer + csv->length, \ + CSV_BUFFER_LEN - csv->length, \ + "TestItem Num, %d, ", \ + csv->item_count); + if (csv->line2_len > 0) { + memcpy(csv->buffer + csv->length, csv->line2_buffer, csv->line2_len); + csv->length += csv->line2_len; + } + + /* line 3 ~ 10 "\n" */ + csv->length += snprintf(csv->buffer + csv->length, \ + CSV_BUFFER_LEN - csv->length, \ + "\n\n\n\n\n\n\n\n\n"); + + /* line 11 ~ data area */ + if (csv->data_len > 0) { + memcpy(csv->buffer + csv->length, csv->data_buffer, csv->data_len); + csv->length += csv->data_len; + } + + FTS_TEST_INFO("csv length:%d", csv->length); + fts_test_save_test_data(FTS_CSV_FILE_NAME, csv->buffer, csv->length); +#endif +} + +static void fts_test_save_result_txt(struct fts_test *tdata) +{ + if (!tdata || !tdata->testresult) { + FTS_TEST_ERROR("test result is null"); + return; + } + + FTS_TEST_INFO("test result length in txt:%d", tdata->testresult_len); + fts_test_save_test_data(FTS_TXT_FILE_NAME, tdata->testresult, tdata->testresult_len); +} + +static int fts_test_malloc_free_incell(struct fts_test *tdata, bool allocate) +{ + struct incell_threshold *thr = &tdata->ic.incell.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + FTS_TEST_INFO("buflen:%d", buflen); + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata2_min, buflen); + fts_malloc_r(thr->rawdata2_max, buflen); + } + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata2_min); + fts_free(thr->rawdata2_max); + } + fts_free(thr->cb_min); + fts_free(thr->cb_max); + } + + return 0; +} + +static int fts_test_malloc_free_mc_sc(struct fts_test *tdata, bool allocate) +{ + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + int buflen_sc = tdata->sc_node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_h_min, buflen); + fts_malloc_r(thr->rawdata_h_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata_l_min, buflen); + fts_malloc_r(thr->rawdata_l_max, buflen); + } + fts_malloc_r(thr->tx_linearity_max, buflen); + fts_malloc_r(thr->tx_linearity_min, buflen); + fts_malloc_r(thr->rx_linearity_max, buflen); + fts_malloc_r(thr->rx_linearity_min, buflen); + + fts_malloc_r(thr->scap_cb_off_min, buflen_sc); + fts_malloc_r(thr->scap_cb_off_max, buflen_sc); + fts_malloc_r(thr->scap_cb_on_min, buflen_sc); + fts_malloc_r(thr->scap_cb_on_max, buflen_sc); + + fts_malloc_r(thr->scap_rawdata_off_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_off_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_max, buflen_sc); + + fts_malloc_r(thr->panel_differ_min, buflen); + fts_malloc_r(thr->panel_differ_max, buflen); + } else { + fts_free(thr->rawdata_h_min); + fts_free(thr->rawdata_h_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata_l_min); + fts_free(thr->rawdata_l_max); + } + fts_free(thr->tx_linearity_max); + fts_free(thr->tx_linearity_min); + fts_free(thr->rx_linearity_max); + fts_free(thr->rx_linearity_min); + + fts_free(thr->scap_cb_off_min); + fts_free(thr->scap_cb_off_max); + fts_free(thr->scap_cb_on_min); + fts_free(thr->scap_cb_on_max); + + fts_free(thr->scap_rawdata_off_min); + fts_free(thr->scap_rawdata_off_max); + fts_free(thr->scap_rawdata_on_min); + fts_free(thr->scap_rawdata_on_max); + + fts_free(thr->panel_differ_min); + fts_free(thr->panel_differ_max); + } + + return 0; +} + +static int fts_test_malloc_free_sc(struct fts_test *tdata, bool allocate) +{ + struct sc_threshold *thr = &tdata->ic.sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + fts_malloc_r(thr->dcb_sort, buflen); + fts_malloc_r(thr->dcb_base, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + fts_free(thr->cb_min); + fts_free(thr->cb_max); + fts_free(thr->dcb_sort); + fts_free(thr->dcb_base); + } + + return 0; +} + +static int fts_test_malloc_free_thr(struct fts_test *tdata, bool allocate) +{ + int ret = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + if (true == allocate) { + fts_malloc_r(tdata->node_valid, tdata->node.node_num * sizeof(int)); + fts_malloc_r(tdata->node_valid_sc, tdata->sc_node.node_num * sizeof(int)); + } else { + fts_free(tdata->node_valid); + fts_free(tdata->node_valid_sc); + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = fts_test_malloc_free_incell(tdata, allocate); + break; + case IC_HW_MC_SC: + ret = fts_test_malloc_free_mc_sc(tdata, allocate); + break; + case IC_HW_SC: + ret = fts_test_malloc_free_sc(tdata, allocate); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* default enable all test item */ +static void fts_test_init_item(struct fts_test *tdata) +{ + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + tdata->ic.incell.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_MC_SC: + tdata->ic.mc_sc.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_SC: + tdata->ic.sc.u.tmp = 0xFFFFFFFF; + break; + } +} + +static int get_tx_rx_num(u8 tx_rx_reg, u8 *ch_num, u8 ch_num_max) +{ + int ret = 0; + int i = 0; + + for (i = 0; i < 3; i++) { + ret = fts_test_read_reg(tx_rx_reg, ch_num); + if ((ret < 0) || (*ch_num > ch_num_max)) { + sys_delay(50); + } else + break; + } + + if (i >= 3) { + FTS_TEST_ERROR("get channel num fail"); + return -EIO; + } + + return 0; +} + +static int get_channel_num(struct fts_test *tdata) +{ + int ret = 0; + u8 tx_num = 0; + u8 rx_num = 0; + int key_num = 0; + + /* node structure */ + if (IC_HW_SC == tdata->func->hwtype) { + ret = get_tx_rx_num(FACTORY_REG_CH_NUM_SC, &tx_num, NUM_MAX_SC); + if (ret < 0) { + FTS_TEST_ERROR("get channel number fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_KEY_NUM_SC, &rx_num, KEY_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get key number fail"); + return ret; + } + + tdata->node.tx_num = 2; + tdata->node.rx_num = tx_num / 2; + tdata->node.channel_num = tx_num; + tdata->node.node_num = tx_num; + key_num = rx_num; + } else { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM, &rx_num, RX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get rx_num fail"); + return ret; + } + + tdata->node.tx_num = tx_num; + tdata->node.rx_num = rx_num; + if (IC_HW_INCELL == tdata->func->hwtype) + tdata->node.channel_num = tx_num * rx_num; + else if (IC_HW_MC_SC == tdata->func->hwtype) + tdata->node.channel_num = tx_num + rx_num; + tdata->node.node_num = tx_num * rx_num; + key_num = tdata->func->key_num_total; + } + + /* key */ + tdata->node.key_num = key_num; + tdata->node.node_num += tdata->node.key_num; + + /* sc node structure */ + tdata->sc_node = tdata->node; + if (IC_HW_MC_SC == tdata->func->hwtype) { + if (tdata->v3_pattern) { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM_NOMAP, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mappint tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM_NOMAP, &rx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mapping rx_num fail"); + return ret; + } + + tdata->sc_node.tx_num = tx_num; + tdata->sc_node.rx_num = rx_num; + } + tdata->sc_node.channel_num = tx_num + rx_num; + tdata->sc_node.node_num = tx_num + rx_num; + } + + if (tdata->node.tx_num > TX_NUM_MAX) { + FTS_TEST_ERROR("tx num(%d) fail", tdata->node.tx_num); + return -EIO; + } + + if (tdata->node.rx_num > RX_NUM_MAX) { + FTS_TEST_ERROR("rx num(%d) fail", tdata->node.rx_num); + return -EIO; + } + + FTS_TEST_INFO("node_num:%d, tx:%d, rx:%d", tdata->node.node_num, + tdata->node.tx_num, tdata->node.rx_num); + return 0; +} + +static void get_ic_version(struct fts_test *tdata) +{ + u8 val[4] = { 0 }; + + fts_test_read_reg(REG_FW_CHIP_IDH, &val[0]); + fts_test_read_reg(REG_FW_CHIP_IDL, &val[1]); + fts_test_read_reg(REG_FW_IC_TYPE, &val[2]); + fts_test_read_reg(REG_FW_IC_VERSION, &val[3]); + + tdata->ic_ver = TEST_IC_VERSION(val[0], val[1], val[2], val[3]); + FTS_TEST_INFO("test ic version:0x%8x", tdata->ic_ver); +} + +static int fts_test_init_basicinfo(struct fts_test *tdata) +{ + int ret = 0; + u8 val = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + get_ic_version(tdata); + + fts_test_read_reg(REG_FW_VERSION, &val); + tdata->fw_ver = val; + + if (IC_HW_INCELL == tdata->func->hwtype) { + fts_test_read_reg(REG_VA_TOUCH_THR, &val); + tdata->va_touch_thr = val; + fts_test_read_reg(REG_VKEY_TOUCH_THR, &val); + tdata->vk_touch_thr = val; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail\n"); + return ret; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + fts_test_read_reg(FACTORY_REG_PATTERN, &val); + tdata->v3_pattern = (1 == val) ? true : false; + fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + tdata->mapping = val; + } + + /* enter into factory mode and read tx/rx num */ + ret = get_channel_num(tdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get channel number fail\n"); + return ret; + } + + return ret; +} + +static int fts_test_main_init(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + /* get basic information: tx/rx num ... */ + ret = fts_test_init_basicinfo(tdata); + if (ret < 0) { + FTS_TEST_ERROR("test init basicinfo fail"); + return ret; + } + + /* allocate memory for test threshold */ + ret = fts_test_malloc_free_thr(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("test malloc for threshold fail"); + return ret; + } + + /* default enable all test item */ + fts_test_init_item(tdata); + + /* allocate memory for test data:csv&txt */ + ret = fts_test_malloc_free_data_csv(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("allocate memory for test data(csv) fail"); + return ret; + } + + ret = fts_test_malloc_free_data_txt(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("allocate memory for test data(txt) fail"); + return ret; + } + + /* allocate test data buffer */ + tdata->buffer_length = (tdata->node.tx_num + 1) * tdata->node.rx_num; + tdata->buffer_length *= sizeof(int); + FTS_TEST_INFO("test buffer length:%d", tdata->buffer_length); + tdata->buffer = (int *)fts_malloc(tdata->buffer_length); + if (NULL == tdata->buffer) { + FTS_TEST_ERROR("test buffer(%d) malloc fail", tdata->buffer_length); + return -ENOMEM; + } + memset(tdata->buffer, 0, tdata->buffer_length); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int fts_test_main_exit(void) +{ + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + fts_test_save_data_csv(tdata); + fts_test_save_result_txt(tdata); + + /* free memory */ + fts_test_malloc_free_data_txt(tdata, false); + fts_test_malloc_free_data_csv(tdata, false); + fts_test_malloc_free_thr(tdata, false); + /*free test data buffer*/ + fts_free(tdata->buffer); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + + +/* + * fts_test_get_testparams - get test parameter from ini + */ +static int fts_test_get_testparams(char *config_name) +{ + int ret = 0; + + ret = fts_test_get_testparam_from_ini(config_name); + + return ret; +} + +static int fts_test_start(void) +{ + int testresult = 0; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->func && tdata->func->start_test) { + testresult = tdata->func->start_test(); + } else { + FTS_TEST_ERROR("test func/start_test func is null"); + } + + return testresult; +} + +/* + * fts_test_entry - test main entry + * + * warning - need disable irq & esdcheck before call this function + * + */ +static int fts_test_entry(char *ini_file_name) +{ + int ret = 0; + + /* test initialize */ + ret = fts_test_main_init(); + if (ret < 0) { + FTS_TEST_ERROR("fts_test_main_init fail"); + goto test_err; + } + + /*Read parse configuration file*/ + FTS_TEST_SAVE_INFO("ini_file_name:%s\n", ini_file_name); + ret = fts_test_get_testparams(ini_file_name); + if (ret < 0) { + FTS_TEST_ERROR("get testparam fail"); + goto test_err; + } + + /* Start testing according to the test configuration */ + /* luoguojin ???? */ + if (true == fts_test_start()) { + FTS_TEST_SAVE_INFO("\n\n=======Tp test pass.\n"); + } else { + FTS_TEST_SAVE_INFO("\n\n=======Tp test failure.\n"); + } + + ret = 0; +test_err: + fts_test_main_exit(); + enter_work_mode(); + return ret; +} + +/************************************************************************ +* Name: fts_test_show +* Brief: no +* Input: device, device attribute, char buf +* Output: no +* Return: EPERM +***********************************************************************/ +static ssize_t fts_test_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +/************************************************************************ +* Name: fts_test_store +* Brief: upgrade from app.bin +* Input: device, device attribute, char buf, char count +* Output: no +* Return: char count +***********************************************************************/ +static ssize_t fts_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[128] = {0}; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev; + + if (ts_data->suspended) { + FTS_INFO("In suspend, no test, return now"); + return -EINVAL; + } + + input_dev = ts_data->input_dev; + memset(fwname, 0, sizeof(fwname)); + sprintf(fwname, "%s", buf); + fwname[count - 1] = '\0'; + FTS_TEST_DBG("fwname:%s.", fwname); + + mutex_lock(&input_dev->mutex); + disable_irq(ts_data->irq); + +#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN) + fts_esdcheck_switch(DISABLE); +#endif + + fts_test_entry(fwname); + +#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN) + fts_esdcheck_switch(ENABLE); +#endif + + enable_irq(ts_data->irq); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* test from test.ini +* example:echo "***.ini" > fts_test +*/ +static DEVICE_ATTR(fts_test, S_IRUGO | S_IWUSR, fts_test_show, fts_test_store); + +/* add your attr in here*/ +static struct attribute *fts_test_attributes[] = { + &dev_attr_fts_test.attr, + NULL +}; + +static struct attribute_group fts_test_attribute_group = { + .attrs = fts_test_attributes +}; + +static int fts_test_func_init(void) +{ + int i = 0; + int j = 0; + int ic_stype = fts_data->ic_info.ids.type; + struct test_funcs *func = NULL; + int func_count = sizeof(test_func_list) / sizeof(test_func_list[0]); + + FTS_TEST_INFO("init test function"); + if (0 == func_count) { + FTS_TEST_SAVE_ERR("test functions list is NULL, fail\n"); + return -ENODATA; + } + + fts_ftest = (struct fts_test *)kzalloc(sizeof(*fts_ftest), GFP_KERNEL); + if (NULL == fts_ftest) { + FTS_TEST_ERROR("malloc memory for test fail"); + return -ENOMEM; + } + + for (i = 0; i < func_count; i++) { + func = test_func_list[i]; + for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) { + if (0 == func->ctype[j]) + break; + else if (func->ctype[j] == ic_stype) { + FTS_TEST_INFO("match test function,type:%x", (int)func->ctype[j]); + fts_ftest->func = func; + } + } + } + if (NULL == fts_ftest->func) { + FTS_TEST_ERROR("no test function match, can't test"); + return -ENODATA; + } + + return 0; +} + +int fts_test_init(struct i2c_client *client) +{ + int ret = 0; + + FTS_TEST_FUNC_ENTER(); + + /* get test function, must be the first step */ + ret = fts_test_func_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("test functions init fail\n"); + return ret; + } + + ret = sysfs_create_group(&client->dev.kobj, &fts_test_attribute_group); + if (0 != ret) { + FTS_TEST_ERROR( "[focal] %s() - ERROR: sysfs_create_group() failed.", __func__); + sysfs_remove_group(&client->dev.kobj, &fts_test_attribute_group); + } else { + FTS_TEST_DBG("[focal] %s() - sysfs_create_group() succeeded.", __func__); + } + + FTS_TEST_FUNC_EXIT(); + + return ret; +} + +int fts_test_exit(struct i2c_client *client) +{ + FTS_TEST_FUNC_ENTER(); + + sysfs_remove_group(&client->dev.kobj, &fts_test_attribute_group); + fts_free(fts_ftest); + FTS_TEST_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.h b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.h new file mode 100644 index 000000000000..6f35fda7b942 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test.h @@ -0,0 +1,589 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _TEST_LIB_H +#define _TEST_LIB_H + +/***************************************************************************** +* Included header files +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include //iic +#include //msleep +#include +#include +#include + +#include "../focaltech_core.h" +#include "focaltech_test_ini.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_INI_FILE_PATH "/mnt/sdcard/" +#define FTS_CSV_FILE_NAME "testdata.csv" +#define FTS_TXT_FILE_NAME "testresult.txt" +#define false 0 +#define true 1 +#define TEST_ICSERIES_LEN (8) +#define TEST_ICSERIES(x) ((x) >> TEST_ICSERIES_LEN) + +#define TEST_OPEN_MAX_VALUE (255) +#define BYTES_PER_TIME (32) /* max:128 */ +/* CSV & TXT */ +#define CSV_LINE1_BUFFER_LEN (1024*1) +#define CSV_LINE2_BUFFER_LEN (1024*2) +#define CSV_DATA_BUFFER_LEN (1024*80) +#define CSV_BUFFER_LEN (CSV_LINE1_BUFFER_LEN + CSV_LINE2_BUFFER_LEN + CSV_DATA_BUFFER_LEN) +#define TXT_BUFFER_LEN (1024*80*5) + +/*----------------------------------------------------------- +Test Status +-----------------------------------------------------------*/ +#define RESULT_NULL 0 +#define RESULT_PASS 1 +#define RESULT_NG 2 + +#define TX_NUM_MAX 60 +#define RX_NUM_MAX 60 +#define NUM_MAX ((TX_NUM_MAX)*(RX_NUM_MAX)) +#define NUM_MAX_SC (144) +#define KEY_NUM_MAX 6 +#define TEST_ITEM_COUNT_MAX 32 +#define TEST_SHORT_RES_MAX 0xFFFF + +/* + * factory test registers + */ +#define ENTER_WORK_FACTORY_RETRIES 5 + +#define START_SCAN_RETRIES_INCELL 20 +#define START_SCAN_RETRIES_DELAY_INCELL 16 +#define FACTORY_TEST_RETRY 50 +#define FACTORY_TEST_DELAY 18 +#define FACTORY_TEST_RETRY_DELAY 100 + +#define DEVIDE_MODE_ADDR 0x00 +#define REG_FW_CHIP_IDL 0x9F +#define REG_FW_IC_TYPE 0xA0 +#define REG_FW_IC_VERSION 0xB1 +#define REG_FW_CHIP_IDH 0xA3 +#define REG_FW_VERSION 0xA6 +#define REG_VA_TOUCH_THR 0x80 +#define REG_VKEY_TOUCH_THR 0x82 + +#define FACTORY_REG_LINE_ADDR 0x01 +#define FACTORY_REG_CHX_NUM 0x02 +#define FACTORY_REG_CHY_NUM 0x03 +#define FACTORY_REG_CLB 0x04 +#define FACTORY_REG_DATA_SELECT 0x06 +#define FACTORY_REG_RAWBUF_SELECT 0x09 +#define FACTORY_REG_KEY_CBWIDTH 0x0B +#define FACTORY_REG_PARAM_UPDATE_STATE 0x0E +#define FACTORY_REG_SHORT_TEST_EN 0x0F +#define FACTORY_REG_SHORT_TEST_STATE 0x10 +#define FACTORY_REG_LCD_NOISE_START 0x11 +#define FACTORY_REG_LCD_NOISE_FRAME 0x12 +#define FACTORY_REG_LCD_NOISE_NUMBER 0x13 +#define FACTORY_REG_LCD_NOISE_TTHR 0x14 +#define FACTORY_REG_OPEN_START 0x15 +#define FACTORY_REG_OPEN_STATE 0x16 +#define FACTORY_REG_CB_ADDR_H 0x18 +#define FACTORY_REG_CB_ADDR_L 0x19 +#define FACTORY_REG_LCD_NOISE_STATE 0x1E +#define FACTORY_REG_KEYSHORT_EN 0x2E +#define FACTORY_REG_KEYSHORT_STATE 0x2F + +#define FACTORY_REG_LEFT_KEY 0x1E +#define FACTORY_REG_RIGHT_KEY 0x1F +#define FACTORY_REG_OPEN_REG20 0x20 +#define FACTORY_REG_OPEN_REG21 0x21 +#define FACTORY_REG_OPEN_REG22 0x22 +#define FACTORY_REG_OPEN_REG23 0x23 +#define FACTORY_REG_OPEN_REG86 0x86 +#define FACTORY_REG_K1 0x31 +#define FACTORY_REG_K2 0x32 +#define FACTORY_REG_RAWDATA_ADDR 0x6A +#define FACTORY_REG_CB_ADDR 0x6E +#define FACTORY_REG_SHORT_ADDR 0x89 +#define FACTORY_REG_RAWDATA_TEST_EN 0x9E +#define FACTORY_REG_CB_TEST_EN 0x9F +#define FACTORY_REG_OPEN_TEST_EN 0xA0 + +/* mc_sc */ +#define FACTORY_REG_FRE_LIST 0x0A +#define FACTORY_REG_NORMALIZE 0x16 +#define FACTORY_REG_RAWDATA_ADDR_MC_SC 0x36 +#define FACTORY_REG_PATTERN 0x53 +#define FACTORY_REG_NOMAPPING 0x54 +#define FACTORY_REG_CHX_NUM_NOMAP 0x55 +#define FACTORY_REG_CHY_NUM_NOMAP 0x56 +#define FACTORY_REG_WC_SEL 0x09 +#define FACTORY_REG_MC_SC_MODE 0x44 +#define FACTORY_REG_MC_SC_CB_ADDR_OFF 0x45 +#define FACTORY_REG_MC_SC_CB_ADDR 0x4E +#define FACTROY_REG_SHORT_TEST_EN 0x07 +#define FACTROY_REG_SHORT_CA 0x01 +#define FACTROY_REG_SHORT_CC 0x02 +#define FACTROY_REG_SHORT_CG 0x03 +#define FACTROY_REG_SHORT_OFFSET 0x04 +#define FACTROY_REG_SHORT_AB_CH 0x58 +#define FACTROY_REG_SHORT_DELAY 0x5A +#define FACTORY_REG_SHORT_ADDR_MC 0xF4 +#define FACTORY_REG_FIR 0xFB + +/* sc */ +#define FACTORY_REG_SCAN_ADDR2 0x08 +#define FACTORY_REG_CH_NUM_SC 0x0A +#define FACTORY_REG_KEY_NUM_SC 0x0B +#define FACTORY_REG_SC_CB_ADDR_OFF 0x33 +#define FACTORY_REG_SC_CB_ADDR 0x39 +#define FACTORY_REG_RAWDATA_SADDR_SC 0x34 +#define FACTORY_REG_RAWDATA_ADDR_SC 0x35 +#define FACTORY_REG_CB_SEL 0x41 +#define FACTORY_REG_FMODE 0xAE + +#define TEST_RETVAL_00 0x00 +#define TEST_RETVAL_AA 0xAA + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct csv_format { + u8 *buffer; + int length; + u8 *line2_buffer; + int line2_len; + int start_line; + int item_count; + u8 *data_buffer; + int data_len; +}; + +/* incell */ +struct incell_testitem { + u32 short_test : 1; + u32 open_test : 1; + u32 cb_test : 1; + u32 rawdata_test : 1; + u32 lcdnoise_test : 1; + u32 keyshort_test : 1; +}; + +struct incell_threshold_b { + int short_res_min; + int short_res_vk_min; + int open_cb_min; + int open_k1_check; + int open_k1_value; + int open_k2_check; + int open_k2_value; + int cb_min; + int cb_max; + int cb_vkey_check; + int cb_min_vk; + int cb_max_vk; + int rawdata_min; + int rawdata_max; + int rawdata_vkey_check; + int rawdata_min_vk; + int rawdata_max_vk; + int lcdnoise_frame; + int lcdnoise_coefficient; + int lcdnoise_coefficient_vkey; + int open_nmos; + int keyshort_k1; + int keyshort_cb_max; + int rawdata2_min; + int rawdata2_max; +}; + +struct incell_threshold { + struct incell_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *rawdata2_min; + int *rawdata2_max; + int *cb_min; + int *cb_max; +}; + +struct incell_test { + struct incell_threshold thr; + union { + int tmp; + struct incell_testitem item; + } u; +}; + +/* mc_sc */ +enum mapping_type { + MAPPING = 0, + NO_MAPPING = 1, +}; + +struct mc_sc_testitem { + u32 rawdata_test : 1; + u32 rawdata_uniformity_test : 1; + u32 scap_cb_test : 1; + u32 scap_rawdata_test : 1; + u32 short_test : 1; + u32 panel_differ_test : 1; +}; + +struct mc_sc_threshold_b { + int rawdata_h_min; + int rawdata_h_max; + int rawdata_set_hfreq; + int rawdata_l_min; + int rawdata_l_max; + int rawdata_set_lfreq; + int uniformity_check_tx; + int uniformity_check_rx; + int uniformity_check_min_max; + int uniformity_tx_hole; + int uniformity_rx_hole; + int uniformity_min_max_hole; + int scap_cb_off_min; + int scap_cb_off_max; + int scap_cb_wp_off_check; + int scap_cb_on_min; + int scap_cb_on_max; + int scap_cb_wp_on_check; + int scap_rawdata_off_min; + int scap_rawdata_off_max; + int scap_rawdata_wp_off_check; + int scap_rawdata_on_min; + int scap_rawdata_on_max; + int scap_rawdata_wp_on_check; + int short_cg; + int short_cc; + int panel_differ_min; + int panel_differ_max; + +}; + +struct mc_sc_threshold { + struct mc_sc_threshold_b basic; + int *rawdata_h_min; + int *rawdata_h_max; + int *rawdata_l_min; + int *rawdata_l_max; + int *tx_linearity_max; + int *tx_linearity_min; + int *rx_linearity_max; + int *rx_linearity_min; + int *scap_cb_off_min; + int *scap_cb_off_max; + int *scap_cb_on_min; + int *scap_cb_on_max; + int *scap_rawdata_off_min; + int *scap_rawdata_off_max; + int *scap_rawdata_on_min; + int *scap_rawdata_on_max; + int *panel_differ_min; + int *panel_differ_max; +}; + +struct mc_sc_test { + struct mc_sc_threshold thr; + union { + u32 tmp; + struct mc_sc_testitem item; + } u; +}; + +/* sc */ +struct sc_testitem { + u32 rawdata_test : 1; + u32 cb_test : 1; + u32 delta_cb_test : 1; +}; + +struct sc_threshold_b { + int rawdata_min; + int rawdata_max; + int cb_min; + int cb_max; + int dcb_base; + int dcb_differ_max; + int dcb_key_check; + int dcb_key_differ_max; + int dcb_ds1; + int dcb_ds2; + int dcb_ds3; + int dcb_ds4; + int dcb_ds5; + int dcb_ds6; + int dcb_critical_check; + int dcb_cs1; + int dcb_cs2; + int dcb_cs3; + int dcb_cs4; + int dcb_cs5; + int dcb_cs6; +}; + +struct sc_threshold { + struct sc_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *cb_min; + int *cb_max; + int *dcb_sort; + int *dcb_base; +}; + +struct sc_test { + struct sc_threshold thr; + union { + u32 tmp; + struct sc_testitem item; + } u; +}; + +enum test_hw_type { + IC_HW_INCELL = 1, + IC_HW_MC_SC, + IC_HW_SC, +}; + +enum test_scan_mode { + SCAN_NORMAL = 0, + SCAN_SC, +}; + +struct fts_test_node { + int channel_num; + int tx_num; + int rx_num; + int node_num; + int key_num; +}; + +struct fts_test { + struct fts_ts_data *ts_data; + struct fts_test_node node; + struct fts_test_node sc_node; + u32 ic_ver; + u8 fw_ver; + u8 va_touch_thr; + u8 vk_touch_thr; + bool key_support; + bool v3_pattern; + u8 mapping; + u8 normalize; + int test_num; + int *buffer; + int buffer_length; + int *node_valid; + int *node_valid_sc; + int basic_thr_count; + int code1; + int code2; + int offset; + union { + struct incell_test incell; + struct mc_sc_test mc_sc; + struct sc_test sc; + } ic; + + struct test_funcs *func; + struct csv_format csv; + char *testresult; + int testresult_len; + struct ini_data ini; +}; + +struct test_funcs { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + enum test_hw_type hwtype; + int startscan_mode; + int key_num_total; + bool rawdata2_support; + bool force_touch; + int (*param_init)(void); + int (*init)(void); + int (*start_test)(void); +}; + +enum byte_mode { + DATA_ONE_BYTE, + DATA_TWO_BYTE, +}; +/* mc_sc */ +enum normalize_type { + NORMALIZE_OVERALL, + NORMALIZE_AUTO, +}; + +enum wp_type { + WATER_PROOF_OFF = 0, + WATER_PROOF_ON = 1, + WATER_PROOF_ON_TX, + WATER_PROOF_ON_RX, + WATER_PROOF_OFF_TX, + WATER_PROOF_OFF_RX, +}; +/* mc end */ + +/* sc */ +enum factory_mode { + FACTORY_NORMAL, + FACTORY_TESTMODE_1, + FACTORY_TESTMODE_2, +}; + +enum dcb_sort_num { + DCB_SORT_MIN = 1, + DCB_SORT_MAX = 6, +}; + +struct dcb_sort_d { + int ch_num; + int deviation; + int critical; + int min; + int max; +}; +/* sc end */ + +enum csv_itemcode_incell { + CODE_ENTER_FACTORY_MODE = 0, + CODE_RAWDATA_TEST = 7, + CODE_CB_TEST = 12, + CODE_SHORT_TEST = 14, + CODE_OPEN_TEST = 15, + CODE_LCD_NOISE_TEST = 19, +}; + +enum csv_itemcode_mc_sc { + CODE_M_RAWDATA_TEST = 7, + CODE_M_SCAP_CB_TEST = 9, + CODE_M_SCAP_RAWDATA_TEST = 10, + CODE_M_WEAK_SHORT_CIRCUIT_TEST = 15, + CODE_M_PANELDIFFER_TEST = 20, +}; + +enum csv_itemcode_sc { + CODE_S_RAWDATA_TEST = 7, + CODE_S_CB_TEST = 13, + CODE_S_DCB_TEST = 14, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct test_funcs test_func_ft8201; + +extern struct fts_test *fts_ftest; + +void sys_delay(int ms); +int focal_abs(int value); +void print_buffer(int *buffer, int length, int line_num); +int fts_test_read_reg(u8 addr, u8 *val); +int fts_test_write_reg(u8 addr, u8 val); +int fts_test_read(u8 addr, u8 *readbuf, int readlen); +int fts_test_write(u8 addr, u8 *writebuf, int writelen); +int enter_work_mode(void); +int enter_factory_mode(void); +int read_mass_data(u8 addr, int byte_num, int *buf); +int chip_clb(void); +int wait_state_update(u8 retval); +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf); +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf); +int start_scan(void); +int get_rawdata(int *data); +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode); +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key); +bool compare_array(int *data, int *min, int *max, bool key); +void show_data(int *data, bool key); +void save_data_csv(int *data, char *name, u8 code, bool mc_sc, bool key); +/* mc_sc */ +int mapping_switch(u8 mapping); +bool get_fw_wp(u8 wp_channel_select, enum wp_type water_proof_type); +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode); +int get_rawdata_mc_sc(enum wp_type wp, int *data); +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata); +void short_print_mc(int *r, int num); +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode); +bool compare_mc_sc(bool, bool, int *, int *, int *); +void show_data_mc_sc(int *data); +void *fts_malloc(size_t size); +void fts_free_proc(void *p); +int fts_test_init(struct i2c_client *client); +int fts_test_exit(struct i2c_client *client); + +#define fts_malloc_r(p, size) do {\ + if (NULL == p) {\ + p = fts_malloc(size);\ + if (NULL == p) {\ + return -ENOMEM;\ + }\ + }\ +} while(0) + +#define fts_free(p) do {\ + if (p) {\ + fts_free_proc(p);\ + p = NULL;\ + }\ +} while(0) + +#define TEST_IC_VERSION(idh, idl, ver1, ver0) \ + (((idh) << 24) | ((idl) << 16) | ((ver1) << 8) | (ver0)) + +#define CSV_SUPPORT 1 + +#define FOCAL_TEST_DEBUG_EN 1 +#if (FOCAL_TEST_DEBUG_EN) +#define FTS_TEST_DBG(fmt, args...) do {printk("[FTS][TEST]%s. line: %d. "fmt"\n", __FUNCTION__, __LINE__, ##args);} while (0) +#define FTS_TEST_FUNC_ENTER() printk("[FTS][TEST]%s: Enter(%d)\n", __func__, __LINE__) +#define FTS_TEST_FUNC_EXIT() printk("[FTS][TEST]%s: Exit(%d)\n", __func__, __LINE__) +#else +#define FTS_TEST_DBG(fmt, args...) do{}while(0) +#define FTS_TEST_FUNC_ENTER() +#define FTS_TEST_FUNC_EXIT() +#endif + +#define FTS_TEST_INFO(fmt, args...) do { printk(KERN_ERR "[FTS][TEST][Info]%s. line: %d. "fmt"\n", __FUNCTION__, __LINE__, ##args);} while (0) +#define FTS_TEST_ERROR(fmt, args...) do { printk(KERN_ERR "[FTS][TEST][Error]%s. line: %d. "fmt"\n", __FUNCTION__, __LINE__, ##args);} while (0) + +#define FTS_TEST_SAVE_INFO(fmt, args...) do { \ + if (fts_ftest->testresult) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ +} while (0) + +#define FTS_TEST_SAVE_ERR(fmt, args...) do { \ + if (fts_ftest->testresult) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ + printk(KERN_ERR "[FTS][TEST][Error]%s. line: %d. "fmt"", __FUNCTION__, __LINE__, ##args);\ +} while (0) +#endif diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.c b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.c new file mode 100644 index 000000000000..a6ea11fd6025 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.c @@ -0,0 +1,1220 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +struct ini_ic_type ic_types[] = { + {"FT5X46", 0x54000002}, + {"FT5X46i", 0x54010002}, + {"FT5526", 0x54020002}, + {"FT3X17", 0x54030002}, + {"FT5436", 0x54040002}, + {"FT3X27", 0x54050002}, + {"FT5526i", 0x54060002}, + {"FT5416", 0x54070002}, + {"FT5426", 0x54080002}, + {"FT5435", 0x54090002}, + {"FT7681", 0x540A0002}, + {"FT7661", 0x540B0002}, + {"FT7511", 0x540C0002}, + {"FT7421", 0x540D0002}, + {"FT7311", 0x54100002}, + {"FT3327DQQ-001", 0x41000082}, + {"FT5446DQS-W01", 0x40000082}, + + {"FT5452", 0x55000081}, + {"FT3518", 0x55010081}, + {"FT3558", 0x55020081}, + {"FT3528", 0x55030081}, + {"FT5536", 0x55040081}, + + {"FT5472", 0x8F000083}, + {"FT5446U", 0x8F010083}, + {"FT5456U", 0x8F020083}, + {"FT3417U", 0x8F030083}, + {"FT5426U", 0x8F040083}, + {"FT3428", 0x8F050083}, + {"FT3437U", 0x8F060083}, + + {"FT5822", 0x58000001}, + {"FT5626", 0x58010001}, + {"FT5726", 0x58020001}, + {"FT5826B", 0x58030001}, + {"FT3617", 0x58040001}, + {"FT3717", 0x58050001}, + {"FT7811", 0x58060001}, + {"FT5826S", 0x58070001}, + {"FT3517U", 0x58090001}, + {"FT3557", 0x580A0001}, + + {"FT6X36", 0x63000003}, + {"FT3X07", 0x63010003}, + {"FT6416", 0x63020003}, + {"FT6336G/U", 0x63030003}, + {"FT7401", 0x63040003}, + {"FT3407U", 0x63050003}, + {"FT6236U", 0x63060003}, + {"FT6436U", 0x63070003}, + + {"FT3267", 0x63080004}, + {"FT3367", 0x63090004}, + + + {"FT8607", 0x81000009}, + {"FT8716", 0x82000005}, + {"FT8716U", 0x44000005}, + {"FT8716F", 0x8A000005}, + {"FT8613", 0x4500000C}, + + {"FT8736", 0x83000006}, + + {"FT8006M", 0x87000007}, + {"FT8201", 0x87010010}, + {"FT7250", 0x87020007}, + + {"FT8006U", 0x8900000B}, + {"FT8006S", 0x8901000B}, + + {"FT8719", 0x9000000D}, + {"FT8615", 0x9100000F}, + + {"FT8739", 0x8D00000E}, + + {"FT8006P", 0x93000011}, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) +static int fts_strncmp(const char *cs, const char *ct, int count) +{ + u8 c1 = 0, c2 = 0; + + while (count) { + if ((*cs == '\0') || (*ct == '\0')) + return -1; + c1 = TOLOWER(*cs++); + c2 = TOLOWER(*ct++); + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + count--; + } + + return 0; +} + +static int isspace(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static int isdigit(int x) +{ + if (x <= '9' && x >= '0') + return 1; + else + return 0; +} + +static long fts_atol(char *nptr) +{ + int c; /* current char */ + long total; /* current total */ + int sign; /* if ''-'', then negative, otherwise positive */ + /* skip whitespace */ + while ( isspace((int)(unsigned char)*nptr) ) + ++nptr; + c = (int)(unsigned char) * nptr++; + sign = c; /* save sign indication */ + if (c == '-' || c == '+') + c = (int)(unsigned char) * nptr++; /* skip sign */ + total = 0; + while (isdigit(c)) { + total = 10 * total + (c - '0'); /* accumulate digit */ + c = (int)(unsigned char) * nptr++; /* get next char */ + } + if (sign == '-') + return -total; + else + return total; /* return result, negated if necessary */ +} + +static int fts_atoi(char *nptr) +{ + return (int)fts_atol(nptr); +} + +static int fts_test_get_ini_size(char *config_name) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[128]; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, config_name); + + if (NULL == pfile) + pfile = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + filp_close(pfile, NULL); + + FTS_TEST_FUNC_ENTER(); + + return fsize; +} + +static int fts_test_read_ini_data(char *config_name, char *config_buf) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[128]; + loff_t pos = 0; + mm_segment_t old_fs; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, config_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_RDONLY, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(pfile, config_buf, fsize, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +static void str_space_remove(char *str) +{ + char *t = str; + char *s = str; + + while (*t != '\0') { + if (*t != ' ') { + *s = *t; + s++; + } + t++; + } + + *s = '\0'; +} + +#ifdef INI_PRINT +static void print_ini_data(struct ini_data *ini) +{ + int i = 0; + int j = 0; + struct ini_section *section; + struct ini_keyword *keyword; + + if (!ini || !ini->tmp) { + FTS_TEST_DBG("ini is null"); + return; + } + + FTS_TEST_DBG("section num:%d, keyword num total:%d\n", + ini->section_num, ini->keyword_num_total); + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + FTS_TEST_DBG("section name:[%s] keyword num:%d\n", + section->name, section->keyword_num); + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + FTS_TEST_DBG("%s=%s\n", keyword->name, keyword->value); + } + } +} +#endif + +static int ini_get_line(char *filedata, char *line_data, int *line_len) +{ + int i = 0; + int line_length = 0; + int type; + + /* get a line data */ + for (i = 0; i < MAX_INI_LINE_LEN; i++) { + if (('\n' == filedata[i]) || ('\r' == filedata[i])) { + line_data[line_length++] = '\0'; + if (('\n' == filedata[i + 1]) || ('\r' == filedata[i + 1])) { + line_length++; + } + break; + } else { + line_data[line_length++] = filedata[i]; + } + } + + if (i >= MAX_INI_LINE_LEN) { + FTS_TEST_ERROR("line length(%d)>max(%d)", line_length, MAX_INI_LINE_LEN); + return -ENODATA; + } + + /* remove space */ + str_space_remove(line_data); + + /* confirm line type */ + if (('\0' == line_data[0]) || ('#' == line_data[0])) { + type = LINE_OTHER; + } else if ('[' == line_data[0]) { + type = LINE_SECTION; + } else { + type = LINE_KEYWORD; /* key word */ + } + + *line_len = line_length; + return type; +} + +static int ini_parse_keyword(struct ini_data *ini, char *line_buffer) +{ + int i = 0; + int offset = 0; + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + for (i = 0; i < length; i++) { + if (line_buffer[i] == '=') + break; + } + + if ((i == 0) || (i >= length)) { + FTS_TEST_ERROR("mark(=)in keyword line fail"); + return -ENODATA; + } + + if ((ini->section_num > 0) && (ini->section_num < MAX_INI_SECTION_NUM)) { + section = &ini->section[ini->section_num - 1]; + } + + if (NULL == section) { + FTS_TEST_ERROR("section is null"); + return -ENODATA; + } + + offset = ini->keyword_num_total; + if (offset > MAX_KEYWORD_NUM) { + FTS_TEST_ERROR("keyword num(%d)>max(%d),please check MAX_KEYWORD_NUM", + ini->keyword_num_total, MAX_KEYWORD_NUM); + return -ENODATA; + } + memcpy(ini->tmp[offset].name, &line_buffer[0], i); + ini->tmp[offset].name[i] = '\0'; + memcpy(ini->tmp[offset].value, &line_buffer[i + 1], length - i - 1); + ini->tmp[offset].value[length - i - 1] = '\0'; + section->keyword_num++; + ini->keyword_num_total++; + + return 0; +} + +static int ini_parse_section(struct ini_data *ini, char *line_buffer) +{ + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + if ((length <= 2) || (length > MAX_KEYWORD_NAME_LEN)) { + FTS_TEST_ERROR("section line length fail"); + return -EINVAL; + } + + if ((ini->section_num < 0) || (ini->section_num > MAX_INI_SECTION_NUM)) { + FTS_TEST_ERROR("section_num(%d) fail", ini->section_num); + return -EINVAL; + } + section = &ini->section[ini->section_num]; + memcpy(section->name, line_buffer + 1, length - 2); + section->name[length - 2] = '\0'; + FTS_TEST_INFO("section:%s, keyword offset:%d", + section->name, ini->keyword_num_total); + section->keyword = (struct ini_keyword *)&ini->tmp[ini->keyword_num_total]; + section->keyword_num = 0; + ini->section_num++; + if (ini->section_num > MAX_INI_SECTION_NUM) { + FTS_TEST_ERROR("section num(%d)>max(%d), please check MAX_INI_SECTION_NUM", + ini->section_num, MAX_INI_SECTION_NUM); + return -ENOMEM; + } + + return 0; +} + +static int ini_init_inidata(struct ini_data *ini) +{ + int pos = 0; + int ret = 0; + char line_buffer[MAX_INI_LINE_LEN] = { 0 }; + int line_len = 0; + + if (!ini || !ini->data || !ini->tmp) { + FTS_TEST_DBG("ini/data/tmp is null"); + return -EINVAL; + } + + while (pos < ini->length) { + ret = ini_get_line(ini->data + pos, line_buffer, &line_len); + if (ret < 0) { + FTS_TEST_ERROR("ini_get_line fail"); + return ret; + } else if (ret == LINE_KEYWORD) { + ret = ini_parse_keyword(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_keyword fail"); + return ret; + } + } else if (ret == LINE_SECTION) { + ret = ini_parse_section(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_section fail"); + return ret; + } + } + + pos += line_len; + } + + return 0; +} + +static int ini_get_key(char *section_name, char *key_name, char *value) +{ + int i = 0; + int j = 0; + struct ini_data *ini = &fts_ftest->ini; + struct ini_section *section; + struct ini_keyword *keyword; + int key_len = 0; + +#ifdef INI_PRINT + FTS_TEST_DBG("section name:%s, key name:%s\n", section_name, key_name); + FTS_TEST_DBG("section num:%d\n", ini->section_num); +#endif + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + key_len = strlen(section_name); + if (key_len != strlen(section->name)) + continue; + if (fts_strncmp(section->name, section_name, key_len) != 0) + continue; +#ifdef INI_PRINT + FTS_TEST_DBG("section name:%s keyword num:%d\n", + section->name, section->keyword_num); +#endif + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + key_len = strlen(key_name); + if (key_len == strlen(keyword->name)) { + if (0 == fts_strncmp(keyword->name, key_name, key_len)) { + key_len = strlen(keyword->value); + memcpy(value, keyword->value, key_len); + FTS_TEST_DBG("section:%s,%s=%s\n", section_name, key_name, value); + return key_len; + } + } + } + } + + return -ENODATA; +} + +/* return keyword's value length if success */ +static int ini_get_string_value(char *section_name, char *key_name, char *rval) +{ + if (!section_name || !key_name || !rval) { + FTS_TEST_ERROR("section_name/key_name/rval is null"); + return -EINVAL; + } + + return ini_get_key(section_name, key_name, rval); +} + +int get_keyword_value(char *section, char *name, int *value) +{ + int ret = 0; + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + + ret = ini_get_string_value(section, name, str); + if (ret > 0) { + /* search successfully, so change value, otherwise keep default */ + *value = fts_atoi(str); + } + + return ret; +} + +static void fts_init_buffer(int *buffer, int value, int len) +{ + int i = 0; + + if (NULL == buffer) { + FTS_TEST_ERROR("buffer is null\n"); + return; + } + + for (i = 0; i < len; i++) { + buffer[i] = value; + } +} + +static int get_test_item(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + int tmpval = 0; + + if (length > TEST_ITEM_COUNT_MAX) { + FTS_TEST_SAVE_ERR("test item count(%d) > max(%d)\n", + length, TEST_ITEM_COUNT_MAX); + return -EINVAL; + } + + FTS_TEST_INFO("test items in total of driver:%d", length); + *val = 0; + for (i = 0; i < length; i++) { + tmpval = 0; + ret = get_value_testitem(name[i], &tmpval); + if (ret < 0) { + FTS_TEST_DBG("test item:%s not found", name[i]); + } else { + FTS_TEST_DBG("test item:%s=%d", name[i], tmpval); + *val |= (tmpval << i); + } + } + + return 0; +} + +static int get_basic_threshold(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_INFO("basic_thr string length(%d), count(%d)\n", length, tdata->basic_thr_count); + if (length > fts_ftest->basic_thr_count) { + FTS_TEST_SAVE_ERR("basic_thr string length > count\n"); + return -EINVAL; + } + + for (i = 0; i < length; i++) { + ret = get_value_basic(name[i], &val[i]); + if (ret < 0) { + FTS_TEST_DBG("basic thr:%s not found", name[i]); + } else { + FTS_TEST_DBG("basic thr:%s=%d", name[i], val[i]); + } + } + + return 0; +} + +static void get_detail_threshold(char *key_name, bool is_prex, int *thr) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + char str_temp[MAX_KEYWORD_NAME_LEN] = { 0 }; + char str_tmp[MAX_KEYWORD_VALUE_ONE_LEN] = { 0 }; + struct fts_test *tdata = fts_ftest; + int divider_pos = 0; + int index = 0; + int i = 0; + int j = 0; + int k = 0; + int tx_num = 0; + int rx_num = 0; + + if (!key_name || !thr) { + FTS_TEST_ERROR("key_name/thr is null"); + return; + } + + if (is_prex) { + tx_num = tdata->node.tx_num; + rx_num = tdata->node.rx_num; + } + for (i = 0; i < tx_num + 1; i++) { + if (is_prex) { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s%d", key_name, (i + 1)); + } else { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s", key_name); + } + divider_pos = ini_get_string_value("SpecialSet", str_temp, str); + if (divider_pos <= 0) + continue; + index = 0; + k = 0; + memset(str_tmp, 0, sizeof(str_tmp)); + for (j = 0; j < divider_pos; j++) { + if (',' == str[j]) { + thr[i * rx_num + k] = (short)(fts_atoi(str_tmp)); + index = 0; + memset(str_tmp, 0x00, sizeof(str_tmp)); + k++; + } else { + if (' ' == str[j]) + continue; + str_tmp[index] = str[j]; + index++; + } + } + } +} + +static int init_node_valid(void) +{ + char str[MAX_KEYWORD_NAME_LEN] = {0}; + int i = 0; + int j = 0; + int chy = 0; + int node_num = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (!tdata || !tdata->node_valid || !tdata->node_valid_sc) { + FTS_TEST_ERROR("tdata/node_valid/node_valid_sc is null"); + return -EINVAL; + } + + chy = tdata->node.rx_num; + node_num = tdata->node.node_num; + fts_init_buffer(tdata->node_valid, 1 , node_num); + if ((tdata->func->hwtype == IC_HW_INCELL) || (tdata->func->hwtype == IC_HW_MC_SC)) { + for (cnt = 0; cnt < node_num; cnt++) { + i = cnt / chy + 1; + j = cnt % chy + 1; + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNode[%d][%d]", i, j); + get_keyword_value("INVALID_NODE", str, &tdata->node_valid[cnt]); + } + } + + if (tdata->func->hwtype == IC_HW_MC_SC) { + chy = tdata->sc_node.rx_num; + node_num = tdata->sc_node.node_num; + fts_init_buffer(tdata->node_valid_sc, 1, node_num); + + for (cnt = 0; cnt < node_num; cnt++) { + i = (cnt >= chy) ? 2 : 1; + j = (cnt >= chy) ? (cnt - chy + 1) : (cnt + 1); + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNodeS[%d][%d]", i, j); + get_keyword_value("INVALID_NODES", str, &tdata->node_valid_sc[cnt]); + } + } + + print_buffer(tdata->node_valid, tdata->node.node_num, tdata->node.rx_num); + print_buffer(tdata->node_valid_sc, tdata->sc_node.node_num, tdata->sc_node.rx_num); + return 0; +} + +/* incell */ +static int get_test_item_incell(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_INCELL; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.incell.u.tmp = item_val; + return 0; +} + +static char bthr_name_incell[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_INCELL; +static int get_test_threshold_incell(void) +{ + int ret = 0; + int length = sizeof(bthr_name_incell) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + int node_num = tdata->node.node_num; + + tdata->basic_thr_count = sizeof(struct incell_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_incell, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num); + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata2_max, thr->basic.rawdata2_max, node_num); + fts_init_buffer(thr->rawdata2_min, thr->basic.rawdata2_min, node_num); + } + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num); + + /* detail threshold */ + get_detail_threshold("RawData_Max_Tx", true, thr->rawdata_max); + get_detail_threshold("RawData_Min_Tx", true, thr->rawdata_min); + get_detail_threshold("CB_Max_Tx", true, thr->cb_max); + get_detail_threshold("CB_Min_Tx", true, thr->cb_min); + + return 0; +} + +static void print_thr_incell(void) +{ + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_DBG("short_res_min:%d", thr->basic.short_res_min); + FTS_TEST_DBG("short_res_vk_min:%d", thr->basic.short_res_vk_min); + FTS_TEST_DBG("open_cb_min:%d", thr->basic.open_cb_min); + FTS_TEST_DBG("open_k1_check:%d", thr->basic.open_k1_check); + FTS_TEST_DBG("open_k1_value:%d", thr->basic.open_k1_value); + FTS_TEST_DBG("open_k2_check:%d", thr->basic.open_k2_check); + FTS_TEST_DBG("open_k2_value:%d", thr->basic.open_k2_value); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("cb_vkey_check:%d", thr->basic.cb_vkey_check); + FTS_TEST_DBG("cb_min_vk:%d", thr->basic.cb_min_vk); + FTS_TEST_DBG("cb_max_vk:%d", thr->basic.cb_max_vk); + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("rawdata_vkey_check:%d", thr->basic.rawdata_vkey_check); + FTS_TEST_DBG("rawdata_min_vk:%d", thr->basic.rawdata_min_vk); + FTS_TEST_DBG("rawdata_max_vk:%d", thr->basic.rawdata_max_vk); + FTS_TEST_DBG("lcdnoise_frame:%d", thr->basic.lcdnoise_frame); + FTS_TEST_DBG("lcdnoise_coefficient:%d", thr->basic.lcdnoise_coefficient); + FTS_TEST_DBG("lcdnoise_coefficient_vkey:%d", thr->basic.lcdnoise_coefficient_vkey); + + FTS_TEST_DBG("open_nmos:%d", thr->basic.open_nmos); + FTS_TEST_DBG("keyshort_k1:%d", thr->basic.keyshort_k1); + FTS_TEST_DBG("keyshort_cb_max:%d", thr->basic.keyshort_cb_max); + FTS_TEST_DBG("rawdata2_min:%d", thr->basic.rawdata2_min); + FTS_TEST_DBG("rawdata2_max:%d", thr->basic.rawdata2_max); + + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_incell(void) +{ + int ret = 0; + + ret = get_test_item_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell test item fail\n"); + return ret; + } + + + ret = get_test_threshold_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell threshold fail\n"); + return ret; + } + + print_thr_incell(); + return 0; +} + +/* mc_sc */ +static int get_test_item_mc_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_MC_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.mc_sc.u.tmp = item_val; + FTS_TEST_INFO("test item:0x%x in ini", fts_ftest->ic.mc_sc.u.tmp); + return 0; +} + +static char bthr_name_mc_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_MC_SC; +static int get_test_threshold_mc_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_mc_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int node_num = tdata->node.node_num; + int sc_num = tdata->sc_node.node_num; + + tdata->basic_thr_count = sizeof(struct mc_sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_mc_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_h_min, thr->basic.rawdata_h_min, node_num); + fts_init_buffer(thr->rawdata_h_max, thr->basic.rawdata_h_max, node_num); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata_l_min, thr->basic.rawdata_l_min, node_num); + fts_init_buffer(thr->rawdata_l_max, thr->basic.rawdata_l_max, node_num); + } + fts_init_buffer(thr->tx_linearity_max, thr->basic.uniformity_tx_hole, node_num); + fts_init_buffer(thr->tx_linearity_min, 0, node_num); + fts_init_buffer(thr->rx_linearity_max, thr->basic.uniformity_rx_hole, node_num); + fts_init_buffer(thr->rx_linearity_min, 0, node_num); + fts_init_buffer(thr->scap_cb_off_min, thr->basic.scap_cb_off_min, sc_num); + fts_init_buffer(thr->scap_cb_off_max, thr->basic.scap_cb_off_max, sc_num); + fts_init_buffer(thr->scap_cb_on_min, thr->basic.scap_cb_on_min, sc_num); + fts_init_buffer(thr->scap_cb_on_max, thr->basic.scap_cb_on_max, sc_num); + fts_init_buffer(thr->scap_rawdata_off_min, thr->basic.scap_rawdata_off_min, sc_num); + fts_init_buffer(thr->scap_rawdata_off_max, thr->basic.scap_rawdata_off_max, sc_num); + fts_init_buffer(thr->scap_rawdata_on_min, thr->basic.scap_rawdata_on_min, sc_num); + fts_init_buffer(thr->scap_rawdata_on_max, thr->basic.scap_rawdata_on_max, sc_num); + fts_init_buffer(thr->panel_differ_min, thr->basic.panel_differ_min, node_num); + fts_init_buffer(thr->panel_differ_max, thr->basic.panel_differ_max, node_num); + + /* detail threshold */ + get_detail_threshold("RawData_Min_High_Tx", true, thr->rawdata_h_min); + get_detail_threshold("RawData_Max_High_Tx", true, thr->rawdata_h_max); + if (tdata->func->rawdata2_support) { + get_detail_threshold("RawData_Min_Low_Tx", true, thr->rawdata_l_min); + get_detail_threshold("RawData_Max_Low_Tx", true, thr->rawdata_l_max); + } + get_detail_threshold("Tx_Linearity_Max_Tx", true, thr->tx_linearity_max); + get_detail_threshold("Rx_Linearity_Max_Tx", true, thr->rx_linearity_max); + get_detail_threshold("ScapCB_OFF_Min_", true, thr->scap_cb_off_min); + get_detail_threshold("ScapCB_OFF_Max_", true, thr->scap_cb_off_max); + get_detail_threshold("ScapCB_ON_Min_", true, thr->scap_cb_on_min); + get_detail_threshold("ScapCB_ON_Max_", true, thr->scap_cb_on_max); + get_detail_threshold("ScapRawData_OFF_Min_", true, thr->scap_rawdata_off_min); + get_detail_threshold("ScapRawData_OFF_Max_", true, thr->scap_rawdata_off_max); + get_detail_threshold("ScapRawData_ON_Min_", true, thr->scap_rawdata_on_min); + get_detail_threshold("ScapRawData_ON_Max_", true, thr->scap_rawdata_on_max); + get_detail_threshold("Panel_Differ_Min_Tx", true, thr->panel_differ_min); + get_detail_threshold("Panel_Differ_Max_Tx", true, thr->panel_differ_max); + + return 0; +} + +static void print_thr_mc_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_DBG("rawdata_h_min:%d", thr->basic.rawdata_h_min); + FTS_TEST_DBG("rawdata_h_max:%d", thr->basic.rawdata_h_max); + FTS_TEST_DBG("rawdata_set_hfreq:%d", thr->basic.rawdata_set_hfreq); + FTS_TEST_DBG("rawdata_l_min:%d", thr->basic.rawdata_l_min); + FTS_TEST_DBG("rawdata_l_max:%d", thr->basic.rawdata_l_max); + FTS_TEST_DBG("rawdata_set_lfreq:%d", thr->basic.rawdata_set_lfreq); + FTS_TEST_DBG("uniformity_check_tx:%d", thr->basic.uniformity_check_tx); + FTS_TEST_DBG("uniformity_check_rx:%d", thr->basic.uniformity_check_rx); + FTS_TEST_DBG("uniformity_check_min_max:%d", thr->basic.uniformity_check_min_max); + FTS_TEST_DBG("uniformity_tx_hole:%d", thr->basic.uniformity_tx_hole); + FTS_TEST_DBG("uniformity_rx_hole:%d", thr->basic.uniformity_rx_hole); + FTS_TEST_DBG("uniformity_min_max_hole:%d", thr->basic.uniformity_min_max_hole); + FTS_TEST_DBG("scap_cb_off_min:%d", thr->basic.scap_cb_off_min); + FTS_TEST_DBG("scap_cb_off_max:%d", thr->basic.scap_cb_off_max); + FTS_TEST_DBG("scap_cb_wp_off_check:%d", thr->basic.scap_cb_wp_off_check); + FTS_TEST_DBG("scap_cb_on_min:%d", thr->basic.scap_cb_on_min); + FTS_TEST_DBG("scap_cb_on_max:%d", thr->basic.scap_cb_on_max); + FTS_TEST_DBG("scap_cb_wp_on_check:%d", thr->basic.scap_cb_wp_on_check); + FTS_TEST_DBG("scap_rawdata_off_min:%d", thr->basic.scap_rawdata_off_min); + FTS_TEST_DBG("scap_rawdata_off_max:%d", thr->basic.scap_rawdata_off_max); + FTS_TEST_DBG("scap_rawdata_wp_off_check:%d", thr->basic.scap_rawdata_wp_off_check); + FTS_TEST_DBG("scap_rawdata_on_min:%d", thr->basic.scap_rawdata_on_min); + FTS_TEST_DBG("scap_rawdata_on_max:%d", thr->basic.scap_rawdata_on_max); + FTS_TEST_DBG("scap_rawdata_wp_on_check:%d", thr->basic.scap_rawdata_wp_on_check); + FTS_TEST_DBG("short_cg:%d", thr->basic.short_cg); + FTS_TEST_DBG("short_cc:%d", thr->basic.short_cc); + FTS_TEST_DBG("panel_differ_min:%d", thr->basic.panel_differ_min); + FTS_TEST_DBG("panel_differ_max:%d", thr->basic.panel_differ_max); + + print_buffer(thr->rawdata_h_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_h_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->scap_cb_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->panel_differ_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->panel_differ_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_mc_sc(void) +{ + int ret = 0; + + ret = get_test_item_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc threshold fail\n"); + return ret; + } + + print_thr_mc_sc(); + return 0; +} + +/* sc */ +static int get_test_item_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.sc.u.tmp = item_val; + return 0; +} + +static char bthr_name_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_SC; +static int get_test_threshold_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + int node_num = tdata->node.node_num; + + tdata->basic_thr_count = sizeof(struct sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num); + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num); + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num); + fts_init_buffer(thr->dcb_sort, 0, node_num); + fts_init_buffer(thr->dcb_base, thr->basic.dcb_base, node_num); + + /* detail threshold */ + get_detail_threshold("RawDataTest_Min", false, thr->rawdata_min); + get_detail_threshold("RawDataTest_Max", false, thr->rawdata_max); + get_detail_threshold("CbTest_Min", false, thr->cb_min); + get_detail_threshold("CbTest_Max", false, thr->cb_max); + get_detail_threshold("DeltaCxTest_Sort", false, thr->dcb_sort); + get_detail_threshold("DeltaCbTest_Base", false, thr->dcb_base); + + return 0; +} + +static void print_thr_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("dcb_differ_max:%d", thr->basic.dcb_differ_max); + FTS_TEST_DBG("dcb_key_check:%d", thr->basic.dcb_key_check); + FTS_TEST_DBG("dcb_key_differ_max:%d", thr->basic.dcb_key_differ_max); + FTS_TEST_DBG("dcb_ds1:%d", thr->basic.dcb_ds1); + FTS_TEST_DBG("dcb_ds2:%d", thr->basic.dcb_ds2); + FTS_TEST_DBG("dcb_ds3:%d", thr->basic.dcb_ds3); + FTS_TEST_DBG("dcb_ds4:%d", thr->basic.dcb_ds4); + FTS_TEST_DBG("dcb_ds5:%d", thr->basic.dcb_ds5); + FTS_TEST_DBG("dcb_ds6:%d", thr->basic.dcb_ds6); + FTS_TEST_DBG("dcb_critical_check:%d", thr->basic.dcb_critical_check); + FTS_TEST_DBG("dcb_cs1:%d", thr->basic.dcb_cs1); + FTS_TEST_DBG("dcb_cs2:%d", thr->basic.dcb_cs2); + FTS_TEST_DBG("dcb_cs3:%d", thr->basic.dcb_cs3); + FTS_TEST_DBG("dcb_cs4:%d", thr->basic.dcb_cs4); + FTS_TEST_DBG("dcb_cs5:%d", thr->basic.dcb_cs5); + FTS_TEST_DBG("dcb_cs6:%d", thr->basic.dcb_cs6); + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_sort, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_base, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_sc(void) +{ + int ret = 0; + + ret = get_test_item_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc threshold fail\n"); + return ret; + } + + print_thr_sc(); + return 0; +} + +static u32 ini_get_ic_code(char *ic_name) +{ + int i = 0; + int type_size = 0; + int ini_icname_len = 0; + int ic_types_len = 0; + + ini_icname_len = strlen(ic_name); + type_size = sizeof(ic_types) / sizeof(ic_types[0]); + for (i = 0; i < type_size; i++) { + ic_types_len = strlen(ic_name); + if (ini_icname_len == ic_types_len) { + if (0 == strncmp(ic_name, ic_types[i].ic_name, ic_types_len)) + return ic_types[i].ic_type; + } + } + + FTS_TEST_ERROR("no IC type match"); + return 0; +} + + +static void ini_init_interface(struct ini_data *ini) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + u32 value = 0; + struct fts_test *tdata = fts_ftest; + + /* IC type */ + ini_get_string_value("Interface", "IC_Type", str); + memcpy(ini->ic_name, str, sizeof(str)); + + value = ini_get_ic_code(str); + ini->ic_code = value; + FTS_TEST_INFO("ic name:%s, ic code:%x", ini->ic_name, ini->ic_code); + + if (IC_HW_MC_SC == tdata->func->hwtype) { + get_value_interface("Normalize_Type", &value); + tdata->normalize = (u8)value; + FTS_TEST_DBG("normalize:%d", tdata->normalize); + } +} + +static int ini_init_test(struct ini_data *ini) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + /* interface init */ + ini_init_interface(ini); + + /* node valid */ + ret = init_node_valid(); + if (ret < 0) { + FTS_TEST_ERROR("init node valid fail"); + return ret; + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = ini_init_test_incell(); + break; + case IC_HW_MC_SC: + ret = ini_init_test_mc_sc(); + break; + case IC_HW_SC: + ret = ini_init_test_sc(); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * fts_test_get_testparam_from_ini - get test parameters from ini + * + * read, parse the configuration file, initialize the test variable + * + * return 0 if succuss, else errro code + */ +int fts_test_get_testparam_from_ini(char *config_name) +{ + int ret = 0; + int inisize = 0; + struct ini_data *ini = &fts_ftest->ini; + + inisize = fts_test_get_ini_size(config_name); + FTS_TEST_DBG("ini file size:%d", inisize); + if (inisize <= 0) { + FTS_TEST_ERROR("get ini file size fail"); + return -ENODATA; + } + + ini->data = vmalloc(inisize + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("malloc memory for ini data fail"); + return -ENOMEM; + } + memset(ini->data, 0, inisize + 1); + ini->length = inisize + 1; + ini->keyword_num_total = 0; + ini->section_num = 0; + + ini->tmp = vmalloc(sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + if (NULL == ini->tmp) { + FTS_TEST_ERROR("malloc memory for ini tmp fail"); + ret = -ENOMEM; + goto ini_tmp_err; + } + memset(ini->tmp, 0, sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + + ret = fts_test_read_ini_data(config_name, ini->data); + if (ret) { + FTS_TEST_ERROR("read ini file fail"); + goto get_inidata_err; + } + ini->data[inisize] = '\n'; /* last line is null line */ + + /* parse ini data to get keyword name&value */ + ret = ini_init_inidata(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini_init_inidata fail"); + goto get_inidata_err; + } + + /* parse threshold & test item */ + ret = ini_init_test(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini init fail"); + goto get_inidata_err; + } + + ret = 0; +get_inidata_err: + if (ini->tmp) { + vfree(ini->tmp); + ini->tmp = NULL; + } +ini_tmp_err: + if (ini->data) { + vfree(ini->data); + ini->data = NULL; + } + + return ret; +} diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.h b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.h new file mode 100644 index 000000000000..a6c8d50c11d2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/focaltech_test_ini.h @@ -0,0 +1,144 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _INI_H +#define _INI_H +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define MAX_KEYWORD_NUM (500) +#define MAX_KEYWORD_NAME_LEN (50) +#define MAX_KEYWORD_VALUE_LEN (512) +#define MAX_KEYWORD_VALUE_ONE_LEN (16) +#define MAX_INI_LINE_LEN (MAX_KEYWORD_NAME_LEN + MAX_KEYWORD_VALUE_LEN) +#define MAX_INI_SECTION_NUM (20) +#define MAX_IC_NAME_LEN (512) //The original value is 32 +#define MAX_TEST_ITEM (20) +#define IC_CODE_OFFSET (16) + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct ini_ic_type { + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_type; +}; + +enum line_type { + LINE_SECTION = 1, + LINE_KEYWORD = 2 , + LINE_OTHER = 3, +}; + +struct ini_keyword { + char name[MAX_KEYWORD_NAME_LEN]; + char value[MAX_KEYWORD_VALUE_LEN]; +}; + +struct ini_section { + char name[MAX_KEYWORD_NAME_LEN]; + int keyword_num; + /* point to ini.tmp, don't need free */ + struct ini_keyword *keyword; +}; + +struct ini_data { + char *data; + int length; + int keyword_num_total; + int section_num; + struct ini_section section[MAX_INI_SECTION_NUM]; + struct ini_keyword *tmp; + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_code; +}; + +#define TEST_ITEM_INCELL { \ + "SHORT_CIRCUIT_TEST", \ + "OPEN_TEST", \ + "CB_TEST", \ + "RAWDATA_TEST", \ + "LCD_NOISE_TEST", \ + "KEY_SHORT_TEST", \ +} + +#define BASIC_THRESHOLD_INCELL { \ + "ShortCircuit_ResMin", "ShortCircuit_VkResMin", \ + "OpenTest_CBMin", "OpenTest_Check_K1", "OpenTest_K1Threshold", "OpenTest_Check_K2", "OpenTest_K2Threshold", \ + "CBTest_Min", "CBTest_Max", \ + "CBTest_VKey_Check", "CBTest_Min_Vkey", "CBTest_Max_Vkey", \ + "RawDataTest_Min", "RawDataTest_Max", \ + "RawDataTest_VKey_Check", "RawDataTest_Min_VKey", "RawDataTest_Max_VKey", \ + "LCD_NoiseTest_Frame", "LCD_NoiseTest_Coefficient", "LCD_NoiseTest_Coefficient_key", \ +} + + +#define TEST_ITEM_MC_SC { \ + "RAWDATA_TEST", \ + "UNIFORMITY_TEST", \ + "SCAP_CB_TEST", \ + "SCAP_RAWDATA_TEST", \ + "WEAK_SHORT_CIRCUIT_TEST", \ + "PANEL_DIFFER_TEST", \ +} + +#define BASIC_THRESHOLD_MC_SC { \ + "RawDataTest_High_Min", "RawDataTest_High_Max", "RawDataTest_HighFreq", \ + "RawDataTest_Low_Min", "RawDataTest_Low_Max", "RawDataTest_LowFreq", \ + "UniformityTest_Check_Tx", "UniformityTest_Check_Rx","UniformityTest_Check_MinMax", \ + "UniformityTest_Tx_Hole", "UniformityTest_Rx_Hole", "UniformityTest_MinMax_Hole", \ + "SCapCbTest_OFF_Min", "SCapCbTest_OFF_Max", "ScapCBTest_SetWaterproof_OFF", \ + "SCapCbTest_ON_Min", "SCapCbTest_ON_Max", "ScapCBTest_SetWaterproof_ON", \ + "SCapRawDataTest_OFF_Min", "SCapRawDataTest_OFF_Max", "SCapRawDataTest_SetWaterproof_OFF", \ + "SCapRawDataTest_ON_Min", "SCapRawDataTest_ON_Max", "SCapRawDataTest_SetWaterproof_ON", \ + "WeakShortTest_CG", "WeakShortTest_CC", \ + "PanelDifferTest_Min", "PanelDifferTest_Max", \ +} + +#define TEST_ITEM_SC { \ + "CB_TEST", \ + "DELTA_CB_TEST", \ + "RAWDATA_TEST", \ +} + +#define BASIC_THRESHOLD_SC { \ + "RawDataTest_Min", "RawDataTest_Max", \ + "CbTest_Min", "CbTest_Max", \ + "DeltaCbTest_Base", "DeltaCbTest_Differ_Max", \ + "DeltaCbTest_Include_Key_Test", "DeltaCbTest_Key_Differ_Max", \ + "DeltaCbTest_Deviation_S1", "DeltaCbTest_Deviation_S2", "DeltaCbTest_Deviation_S3", \ + "DeltaCbTest_Deviation_S4", "DeltaCbTest_Deviation_S5", "DeltaCbTest_Deviation_S6", \ + "DeltaCbTest_Set_Critical", "DeltaCbTest_Critical_S1", "DeltaCbTest_Critical_S2", \ + "DeltaCbTest_Critical_S3", "DeltaCbTest_Critical_S4", \ + "DeltaCbTest_Critical_S5", "DeltaCbTest_Critical_S6", \ +} + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_test_get_testparam_from_ini(char *config_name); +int get_keyword_value(char *section, char *name, int *value); + +#define get_value_interface(name, value) \ + get_keyword_value("Interface", name, value) +#define get_value_basic(name, value) \ + get_keyword_value("Basic_Threshold", name, value) +#define get_value_detail(name, value) \ + get_keyword_value("SpecialSet", name, value) +#define get_value_testitem(name, value) \ + get_keyword_value("TestItem", name, value) +#endif /* _INI_H */ diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/Makefile b/drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/Makefile new file mode 100644 index 000000000000..2e3a5e524418 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for test module +# +# Linux driver folder +obj-y += focaltech_test_ft8201.o diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/focaltech_test_ft8201.c b/drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/focaltech_test_ft8201.c new file mode 100644 index 000000000000..a5d666724e55 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_test/supported_ic/focaltech_test_ft8201.c @@ -0,0 +1,1034 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2018, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/******************************************************************************* +* Included header files +*******************************************************************************/ +#include "../focaltech_test.h" + +/******************************************************************************* +* Private constant and macro definitions using #define +*******************************************************************************/ +#define REG_MS_SELECT 0x26 +#define REG_CH_X_MASTER 0x50 +#define REG_CH_Y_MASTER 0x51 +#define REG_CH_X_SLAVE 0x52 +#define REG_CH_Y_SLAVE 0x53 +#define REG_FW_INFO_CNT 0x17 +#define I2C_ADDR_M 0 +#define I2C_ADDR_S 12 +#define REG_FW_INFO_ADDR 0x81 +#define REG_FW_INFO_LEN 32 +#define MAX_ADC_VALUE 4015 +#define FACTORY_NOISE_MODE_REG 0x5E + +/******************************************************************************* +* Private enumerations, structures and unions using typedef +*******************************************************************************/ +enum M_S_TYPE { + CHIP_AS_SLAVE = 0, + CHIP_AS_MASTER = 1, + SINGLE_CHIP = 3, +}; + +enum CASCADE_DIRECTION { + CASCADE_LEFT_RIGHT = 0, + CASCADE_UP_DOWN = 1, +}; + +/* + * m_s_sel - master/slave information + * m_i2c_addr - master ic I2C address + * s_i2c_addr - slave ic I2C address + * m_tx - master IC tx number + * m_rx - master IC rx number + * s_tx - slave IC tx number + * s_rx - slave IC rx number + */ +struct ft8201_info { + union m_s_sel { + struct bits { + u8 type : 6; + u8 direction : 1; + u8 s0_as_slave : 1; + } bits; + u8 byte_val; + } m_s_sel; + u8 m_i2c_addr; + u8 s_i2c_addr; + u8 m_tx; + u8 m_rx; + u8 s_tx; + u8 s_rx; + u8 current_slave_addr; +}; + +/******************************************************************************* +* Static function prototypes +*******************************************************************************/ +static void fts_array_copy(int *dest, const int *src, int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + dest[i] = src[i]; + } +} + +static void work_as_master(struct ft8201_info *info) +{ + if (fts_data->client->addr != info->m_i2c_addr) { + FTS_TEST_DBG("change i2c addr to master(0x%x)\n", info->m_i2c_addr); + fts_data->client->addr = info->m_i2c_addr; + } +} + +static void work_as_slave(struct ft8201_info *info) +{ + if (fts_data->client->addr != info->s_i2c_addr) { + FTS_TEST_DBG("change i2c addr to slave(0x%x)\n", info->s_i2c_addr); + fts_data->client->addr = info->s_i2c_addr; + } +} + +static int ft8201_write_reg(struct ft8201_info *info, u8 reg_addr, u8 reg_val) +{ + int ret = 0; + + /* write master reg */ + work_as_master(info); + ret = fts_test_write_reg(reg_addr, reg_val); + if (ret) { + FTS_TEST_SAVE_ERR("write master reg fail\n"); + return ret; + } + + /* write slave reg */ + work_as_slave(info); + ret = fts_test_write_reg(reg_addr, reg_val); + if (ret) { + FTS_TEST_SAVE_ERR("write slave reg fail\n"); + work_as_master(info); + return ret; + } + work_as_master(info); + + return 0; +} + +static void integrate_data(struct ft8201_info *info, int *m_buf, int *s_buf, int *data) +{ + int i = 0; + int *s0_buf; + int *s1_buf; + int s0_ch = 0; + int s0_tx = 0; + int s0_rx = 0; + int s1_ch = 0; + int s1_rx = 0; + int row = 0; + int s0_row = 0; + int s1_row = 0; + + FTS_TEST_FUNC_ENTER(); + + if (false == info->m_s_sel.bits.s0_as_slave) { + s0_buf = m_buf; + s0_tx = info->m_tx; + s0_rx = info->m_rx; + s0_ch = info->m_tx * info->m_rx; + s1_buf = s_buf; + s1_rx = info->s_rx; + s1_ch = info->s_tx * info->s_rx; + } else { + s0_buf = s_buf; + s0_tx = info->s_tx; + s0_rx = info->s_rx; + s0_ch = info->s_tx * info->s_rx; + s1_buf = m_buf; + s1_rx = info->m_rx; + s1_ch = info->m_tx * info->m_rx; + } + + FTS_TEST_DBG("%d %d %d %d %d", s0_tx, s0_rx, s0_ch, s1_rx, s1_ch); + if (CASCADE_LEFT_RIGHT == info->m_s_sel.bits.direction) { + /* cascade direction : left to right */ + for (i = 0; i < s0_tx; i++) { + row = i * (s0_rx + s1_rx); + s0_row = i * s0_rx; + s1_row = i * s1_rx; + + fts_array_copy(data + row, s0_buf + s0_row, s0_rx); + fts_array_copy(data + row + s0_rx, s1_buf + s1_row, s1_rx); + } + + } else { + /* cascade direction : up to down */ + fts_array_copy(data, s0_buf, s0_ch); + fts_array_copy(data + s0_ch, s1_buf, s1_ch); + } + + /* key */ + fts_array_copy(data + s0_ch + s1_ch, s0_buf + s0_ch, 6); + fts_array_copy(data + s0_ch + s1_ch + 6, s1_buf + s1_ch, 6); + + FTS_TEST_FUNC_EXIT(); +} + +static int check_ic_info_validity(struct ft8201_info *info) +{ + /* IC type */ + if ((info->m_s_sel.bits.type != CHIP_AS_SLAVE) + && (info->m_s_sel.bits.type != CHIP_AS_MASTER)) { + FTS_TEST_SAVE_ERR("IC cascade type(%d) fail\n", info->m_s_sel.bits.type); + return -EINVAL; + } + + /* I2C addr */ + if ((0 == info->m_i2c_addr) || (0 == info->s_i2c_addr)) { + FTS_TEST_SAVE_ERR("i2c addr of master(0x%x)/slave(0x%x) fail\n", + info->m_i2c_addr, info->s_i2c_addr); + return -EINVAL; + } + + /* tx/rx */ + if ((0 == info->m_tx) || (info->m_tx > TX_NUM_MAX)) { + FTS_TEST_SAVE_ERR("master tx(%d) fail\n", info->m_tx); + return -EINVAL; + } + + if ((0 == info->m_rx) || (info->m_rx > TX_NUM_MAX)) { + FTS_TEST_SAVE_ERR("master rx(%d) fail\n", info->m_rx); + return -EINVAL; + } + + if ((0 == info->s_tx) || (info->s_tx > TX_NUM_MAX)) { + FTS_TEST_SAVE_ERR("slave tx(%d) fail\n", info->s_tx); + return -EINVAL; + } + + if ((0 == info->s_rx) || (info->s_rx > TX_NUM_MAX)) { + FTS_TEST_SAVE_ERR("slave rx(%d) fail\n", info->s_rx); + return -EINVAL; + } + + return 0; +} + +static int get_chip_information(struct ft8201_info *info) +{ + int ret = 0; + u8 value[REG_FW_INFO_LEN] = { 0 }; + u8 cmd = 0; + + ret = fts_test_read_reg(REG_MS_SELECT, &value[0]); + if (ret) { + FTS_TEST_SAVE_ERR("read m/s select info fail\n"); + return ret; + } + info->m_s_sel.byte_val = value[0]; + + ret = fts_test_read_reg(REG_CH_X_MASTER, &value[0]); + if (ret) { + FTS_TEST_SAVE_ERR("read ch_x_m fail\n"); + return ret; + } + info->m_tx = value[0]; + + ret = fts_test_read_reg(REG_CH_Y_MASTER, &value[0]); + if (ret) { + FTS_TEST_SAVE_ERR("read ch_y_m fail\n"); + return ret; + } + info->m_rx = value[0]; + + ret = fts_test_read_reg(REG_CH_X_SLAVE, &value[0]); + if (ret) { + FTS_TEST_SAVE_ERR("read ch_x_s fail\n"); + return ret; + } + info->s_tx = value[0]; + + ret = fts_test_read_reg(REG_CH_Y_SLAVE, &value[0]); + if (ret) { + FTS_TEST_SAVE_ERR("read ch_y_s fail\n"); + return ret; + } + info->s_rx = value[0]; + + ret = fts_test_write_reg(REG_FW_INFO_CNT, 0); + if (ret) { + FTS_TEST_SAVE_ERR("write fw into cnt fail\n"); + return ret; + } + cmd = REG_FW_INFO_ADDR; + ret = fts_test_read(cmd, &value[0], REG_FW_INFO_LEN); + if (ret) { + FTS_TEST_SAVE_ERR("read fw info fail\n"); + return ret; + } + + if ((value[I2C_ADDR_M] + value[I2C_ADDR_M + 1]) == 0xFF) { + info->m_i2c_addr = value[I2C_ADDR_M] >> 1; + } + + if ((value[I2C_ADDR_S] + value[I2C_ADDR_S + 1]) == 0xFF) { + info->s_i2c_addr = value[I2C_ADDR_S] >> 1; + } + + FTS_TEST_DBG("%s=%d,%s=%d,%s=%d,%s=0x%x,%s=0x%x,%s=%d,%s=%d,%s=%d,%s=%d\n", + "type", info->m_s_sel.bits.type, + "direction", info->m_s_sel.bits.direction, + "s0_as_slave", info->m_s_sel.bits.s0_as_slave, + "m_i2c_addr", info->m_i2c_addr, + "s_i2c_addr", info->s_i2c_addr, + "m_tx", info->m_tx, + "m_rx", info->m_rx, + "s_tx", info->s_tx, + "s_rx", info->s_rx + ); + + ret = check_ic_info_validity(info); + if (ret) { + FTS_TEST_SAVE_ERR("ic information invalid\n"); + return ret; + } + + return 0; +} + +static int ft8201_test_init(struct ft8201_info *info) +{ + int ret = 0; + + /* initialize info */ + memset(info, 0, sizeof(struct ft8201_info)); + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail, can't get tx/rx num\n"); + return ret; + } + + /* get chip info */ + ret = get_chip_information(info); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get chip information fail\n"); + return ret; + } + + return 0; +} + +static u8 ft8201_chip_clb(struct ft8201_info *info) +{ + int ret = 0; + + FTS_TEST_FUNC_ENTER(); + /* master clb */ + work_as_master(info); + ret = chip_clb(); + if (ret) { + FTS_TEST_SAVE_ERR("master clb fail\n"); + return ret; + } + + /* slave clb */ + work_as_slave(info); + ret = chip_clb(); + if (ret) { + FTS_TEST_SAVE_ERR("master clb fail\n"); + work_as_master(info); + return ret; + } + work_as_master(info); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +static int ft8201_get_tx_rx_cb(struct ft8201_info *info, u8 start_node, int read_num, int *read_buffer) +{ + int ret = 0; + int *buffer_master = NULL; + int *buffer_slave = NULL; + int master_tx = info->m_tx; + int master_rx = info->m_rx; + int slave_tx = info->s_tx; + int slave_rx = info->s_rx; + + FTS_TEST_FUNC_ENTER(); + + buffer_master = fts_malloc((master_tx + 1) * master_rx * sizeof(int)); + if (NULL == buffer_master) { + FTS_TEST_SAVE_ERR("%s:master buf malloc fail\n", __func__); + ret = -ENOMEM; + goto GET_CB_ERR; + } + + buffer_slave = fts_malloc((slave_tx + 1) * slave_rx * sizeof(int)); + if (NULL == buffer_slave) { + FTS_TEST_SAVE_ERR("%s:slave buf malloc fail\n", __func__); + ret = -ENOMEM; + goto GET_CB_ERR; + } + + /* master cb */ + work_as_master(info); + ret = get_cb_incell(0, master_tx * master_rx + 6, buffer_master); + if (ret ) { + FTS_TEST_SAVE_ERR("master clb fail\n"); + goto GET_CB_ERR; + } + + /* slave cb */ + work_as_slave(info); + ret = get_cb_incell(0, slave_tx * slave_rx + 6, buffer_slave); + if (ret ) { + FTS_TEST_SAVE_ERR("slave clb fail\n"); + work_as_master(info); + goto GET_CB_ERR; + } + work_as_master(info); + + integrate_data(info, buffer_master, buffer_slave, read_buffer); + +GET_CB_ERR: + fts_free(buffer_master); + fts_free(buffer_slave); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int read_adc_data(u8 retval, int byte_num, int *adc_buf) +{ + int ret = 0; + int times = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + + for (times = 0; times < FACTORY_TEST_RETRY; times++) { + ret = fts_test_read_reg(FACTORY_REG_SHORT_TEST_STATE, &short_state); + if ((0 == ret) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTORY_REG_SHORT_TEST_STATE, short_state, times); + + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto ADC_ERROR; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR, byte_num, adc_buf); + if (ret) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + +ADC_ERROR: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static u8 ft8201_weakshort_get_adcdata(struct ft8201_info *info, int *rbuf) +{ + int ret = 0; + int master_adc_num = 0; + int slave_adc_num = 0; + int *buffer_master = NULL; + int *buffer_slave = NULL; + int master_tx = info->m_tx; + int master_rx = info->m_rx; + int slave_tx = info->s_tx; + int slave_rx = info->s_rx; + int ch_num = 0; + + FTS_TEST_FUNC_ENTER(); + + buffer_master = fts_malloc((master_tx + 1) * master_rx * sizeof(int)); + if (NULL == buffer_master) { + FTS_TEST_SAVE_ERR("%s:master buf malloc fail\n", __func__); + ret = -ENOMEM; + goto ADC_ERROR; + } + + buffer_slave = fts_malloc((slave_tx + 1) * slave_rx * sizeof(int)); + if (NULL == buffer_slave) { + FTS_TEST_SAVE_ERR("%s:slave buf malloc fail\n", __func__); + ret = -ENOMEM; + goto ADC_ERROR; + } + + /* Start ADC sample */ + ch_num = master_tx + master_rx; + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_EN, 0x01); + if (ret) { + FTS_TEST_SAVE_ERR("start short test fail\n"); + goto ADC_ERROR; + } + sys_delay(ch_num * FACTORY_TEST_DELAY); + + /* read master adc data */ + master_adc_num = (master_tx * master_rx + 6) * 2; + work_as_master(info); + ret = read_adc_data(TEST_RETVAL_00, master_adc_num, buffer_master); + if (ret) { + FTS_TEST_SAVE_ERR("read master adc data fail\n"); + goto ADC_ERROR; + } + + /* read slave adc data */ + slave_adc_num = (slave_tx * slave_rx + 6) * 2; + work_as_slave(info); + ret = read_adc_data(TEST_RETVAL_00, slave_adc_num, buffer_slave); + if (ret) { + FTS_TEST_SAVE_ERR("read master adc data fail\n"); + work_as_master(info); + goto ADC_ERROR; + } + work_as_master(info); + + /* data integration */ + integrate_data(info, buffer_master, buffer_slave, rbuf); + +ADC_ERROR: + fts_free(buffer_master); + fts_free(buffer_slave); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8201_short_test(struct ft8201_info *info, struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = true; + int *adcdata = NULL; + int tmp_adc = 0; + int i = 0; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: short test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + adcdata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret) { + FTS_TEST_SAVE_ERR("//Failed to Enter factory mode.ret=%d\n", ret); + goto test_err; + } + + ret = ft8201_weakshort_get_adcdata(info, adcdata); + if (ret) { + FTS_TEST_SAVE_ERR("//Failed to get AdcData. ret=%d\n", ret); + goto test_err; + } + + /* change adc to resistance */ + for (i = 0; i < tdata->node.node_num; ++i) { + tmp_adc = adcdata[i]; + /* avoid calculating the value of the resistance is too large, limiting the size of the ADC value */ + if (tmp_adc > MAX_ADC_VALUE) + tmp_adc = MAX_ADC_VALUE; + adcdata[i] = (tmp_adc * 100) / (4095 - tmp_adc); + } + + /* save */ + show_data(adcdata, true); + save_data_csv(adcdata, "Short Circuit Test", \ + CODE_SHORT_TEST, false, true); + + /* compare */ + tmp_result = compare_data(adcdata, thr->basic.short_res_min, TEST_SHORT_RES_MAX, thr->basic.short_res_vk_min, TEST_SHORT_RES_MAX, true); + + ret = 0; +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ Short Circuit Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ Short Circuit Test NG\n"); + } + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8201_open_test(struct ft8201_info *info, struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + u8 reg20_val = 0; + u8 reg86_val = 0; + u8 tmp_val = 0; + int min = 0; + int max = 0; + int *opendata = NULL; + int byte_num = 0; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Open Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + opendata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret) { + FTS_TEST_SAVE_ERR("Enter Factory Failed\n"); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_OPEN_REG86, ®86_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read 0x86 fail\n"); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_OPEN_REG20, ®20_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read 0x20 fail\n"); + goto test_err; + } + + + /* set open mode */ + ret = ft8201_write_reg(info, FACTORY_REG_OPEN_REG86, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0x86 fail\n"); + goto restore_reg; + } + + /* set Bit4~Bit5 of reg0x20 is set to 2b'10 (Source to GND) */ + tmp_val = reg20_val | (1 << 5); + tmp_val &= ~(1 << 4); + ret = ft8201_write_reg(info, FACTORY_REG_OPEN_REG20, tmp_val); + if (ret) { + FTS_TEST_SAVE_ERR("Failed to Read or Write Reg\n"); + goto restore_reg; + } + + /* wait fw state update before clb */ + ret = wait_state_update(TEST_RETVAL_00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + + ret = ft8201_chip_clb(info); + if (ret) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + goto restore_reg; + } + + /* get cb data */ + byte_num = tdata->node.tx_num * tdata->node.rx_num; + ret = ft8201_get_tx_rx_cb(info, 0, byte_num, opendata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get cb fail\n"); + goto restore_reg; + } + + /* show open data */ + show_data(opendata, false); + /* save data */ + save_data_csv(opendata, "Open Test", \ + CODE_OPEN_TEST, false, false); + + /* compare */ + min = thr->basic.open_cb_min; + max = 256; + FTS_TEST_DBG("open %d %d\n", min, opendata[0]); + tmp_result = compare_data(opendata, min, max, 0, 0, false); + + //ret = 0; + +restore_reg: + /* restore */ + ret = ft8201_write_reg(info, FACTORY_REG_OPEN_REG86, reg86_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg86 fail\n"); + } + + ret = ft8201_write_reg(info, FACTORY_REG_OPEN_REG20, reg20_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg20 fail\n"); + } + + ret = wait_state_update(TEST_RETVAL_00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + + ret = ft8201_chip_clb(info); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + } + +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ Open Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ Open Test NG\n"); + } + FTS_TEST_FUNC_EXIT(); + return ret; +} + + +static int ft8201_cb_test(struct ft8201_info *info, struct fts_test *tdata, bool *test_result) +{ + bool tmp_result = false; + int ret = 0; + bool key_check = false; + int byte_num = 0; + int *cbdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: CB Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + cbdata = tdata->buffer; + + if (!thr->cb_min || !thr->cb_max) { + FTS_TEST_SAVE_ERR("cb_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret) { + FTS_TEST_SAVE_ERR("// Failed to Enter factory mode.ret:%d\n", ret); + goto test_err; + } + + ret = ft8201_chip_clb(info); + if (ret) { + FTS_TEST_SAVE_ERR("//========= auto clb Failed\n"); + goto test_err; + } + + byte_num = tdata->node.node_num; + ret = ft8201_get_tx_rx_cb(info, 0, byte_num, cbdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get cb fail\n"); + goto test_err; + } + + key_check = thr->basic.cb_vkey_check; + + show_data(cbdata, key_check); + save_data_csv(cbdata, "CB Test", \ + CODE_CB_TEST, false, key_check); + /* compare */ + tmp_result = compare_array(cbdata, thr->cb_min, thr->cb_max, key_check); + + ret = 0; + +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ CB Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ CB Test NG\n"); + } + FTS_TEST_FUNC_EXIT(); + return ret; + +} + +static int ft8201_rawdata_test(struct ft8201_info *info, struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int i = 0; + bool key_check = true; + int *rawdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: RawData Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + + if (!thr->rawdata_min || !thr->rawdata_max) { + FTS_TEST_SAVE_ERR("rawdata_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* read rawdata */ + for (i = 0 ; i < 3; i++) { + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get RawData fail,ret=%d\n", ret); + goto test_err; + } + + /* save */ + show_data(rawdata, key_check); + save_data_csv(rawdata, "RawData Test", \ + CODE_RAWDATA_TEST, false, key_check); + /* compare */ + tmp_result = compare_array(rawdata, + thr->rawdata_min, + thr->rawdata_max, + key_check); + + +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ RawData Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ RawData Test NG\n"); + } + FTS_TEST_FUNC_EXIT(); + return ret; + +} + +static int ft8201_lcdnoise_test(struct ft8201_info *info, struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int frame_num = 0; + int i = 0; + int max = 0; + int max_vk = 0; + int byte_num = 0; + u8 old_mode = 0; + u8 reg_value = 0; + u8 status = 0; + int *lcdnoise = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: LCD Noise Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + lcdnoise = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + + ret = fts_test_read_reg(FACTORY_REG_DATA_SELECT, &old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg06 fail\n"); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_NOISE_MODE_REG, ®_value); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg5e fail\n"); + goto test_err; + } + + ret = ft8201_write_reg(info, FACTORY_REG_DATA_SELECT, 0x64); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0x64 to reg5e fail\n"); + goto restore_reg; + } + + ret = ft8201_write_reg(info, FACTORY_REG_DATA_SELECT, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 1 to reg06 fail\n"); + goto restore_reg; + } + + frame_num = thr->basic.lcdnoise_frame; + ret = ft8201_write_reg(info, FACTORY_REG_LCD_NOISE_FRAME, frame_num & 0xff); + if (ret < 0) { + FTS_TEST_SAVE_INFO("write frame num fail\n"); + goto restore_reg; + } + ret = ft8201_write_reg(info, FACTORY_REG_LCD_NOISE_FRAME + 1, (frame_num >> 8) & 0xff); + if (ret < 0) { + FTS_TEST_SAVE_INFO("write frame num fail\n"); + goto restore_reg; + } + + /* read noise data */ + ret = fts_test_write_reg(FACTORY_REG_LINE_ADDR, 0xAD); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0xAD to reg01 fail\n"); + goto restore_reg; + } + + /* start test */ + ret = ft8201_write_reg(info, FACTORY_REG_LCD_NOISE_START, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_INFO("start lcdnoise test fail\n"); + goto restore_reg; + } + sys_delay(frame_num * FACTORY_TEST_DELAY / 2); + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + status = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_LCD_NOISE_START, &status ); + if ((ret >= 0) && (0x00 == status)) { + break; + } else { + FTS_TEST_DBG("reg%x=%x,retry:%d\n", FACTORY_REG_LCD_NOISE_START, status, i); + } + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("lcdnoise test timeout\n"); + //ret = -ENODATA; + goto restore_reg; + } + + byte_num = tdata->node.node_num * 2; + ret = read_mass_data(FACTORY_REG_RAWDATA_ADDR, byte_num, lcdnoise); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + goto restore_reg; + } + + /* save */ + show_data(lcdnoise, true); + save_data_csv(lcdnoise, "LCD Noise Test", \ + CODE_LCD_NOISE_TEST, false, true); + + /* compare */ + max = thr->basic.lcdnoise_coefficient * tdata->va_touch_thr * 32 / 100; + max_vk = thr->basic.lcdnoise_coefficient_vkey * tdata->vk_touch_thr * 32 / 100; + tmp_result = compare_data(lcdnoise, 0, max, 0, max_vk, true); + + //ret = 0; + +restore_reg: + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg06 fail\n"); + } + ret = fts_test_write_reg(FACTORY_NOISE_MODE_REG, reg_value); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg5e fail\n"); + } + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg11 fail\n"); + } + +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ LCD Noise Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ LCD Noise Test NG\n"); + } + FTS_TEST_FUNC_EXIT(); + return ret; + +} + +static int start_test_ft8201(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + struct incell_testitem *test_item = &tdata->ic.incell.u.item; + bool temp_result = false; + bool test_result = true; + struct ft8201_info info; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_INFO("test item:0x%x", fts_ftest->ic.incell.u.tmp); + + if (!tdata || !tdata->testresult || !tdata->buffer) { + FTS_TEST_ERROR("tdata is null"); + return -EINVAL; + } + + ret = ft8201_test_init(&info); + if (ret) { + FTS_TEST_SAVE_ERR("test init fail\n"); + return ret; + } + + /* short test */ + if (true == test_item->short_test) { + ret = ft8201_short_test(&info, tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* open test */ + if (true == test_item->open_test) { + ret = ft8201_open_test(&info, tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* cb test */ + if (true == test_item->cb_test) { + ret = ft8201_cb_test(&info, tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* rawdata test */ + if (true == test_item->rawdata_test) { + ret = ft8201_rawdata_test(&info, tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* lcd noise test */ + if (true == test_item->lcdnoise_test) { + ret = ft8201_lcdnoise_test(&info, tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + return test_result; +} + +struct test_funcs test_func_ft8201 = { + .ctype = {0x10}, + .hwtype = IC_HW_INCELL, + .key_num_total = 12, + .start_test = start_test_ft8201, +}; + diff --git a/drivers/input/touchscreen/focaltech_touch/include/firmware/fw_sample.h b/drivers/input/touchscreen/focaltech_touch/include/firmware/fw_sample.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/input/touchscreen/focaltech_touch/include/pramboot/FT8006M_Pramboot_V1.6_20180426_le.h b/drivers/input/touchscreen/focaltech_touch/include/pramboot/FT8006M_Pramboot_V1.6_20180426_le.h new file mode 100644 index 000000000000..0db3f051175a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch/include/pramboot/FT8006M_Pramboot_V1.6_20180426_le.h @@ -0,0 +1,299 @@ +0x60,0x00,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00, +0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00, +0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00, +0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00, +0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00, +0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00,0x98,0x06,0x00,0x00, +0x00,0x3c,0x60,0xe0,0x60,0xe0,0x02,0xcc,0x00,0xea,0x10,0x3c,0x3f,0xe0,0x03,0xc8, +0x00,0xfa,0x7c,0xc8,0x00,0xf0,0x82,0xcf,0x00,0xe0,0x80,0xcf,0x60,0xe0,0x00,0x14, +0x00,0xea,0x17,0x3c,0x00,0xe0,0x02,0xcc,0x24,0xe0,0x00,0xcc,0x79,0x3c,0x9c,0x14, +0x00,0xea,0x08,0x01,0x01,0xe0,0x02,0xcd,0x28,0x3d,0x0c,0xcd,0x42,0xd8,0x00,0xe0, +0x6c,0x14,0x0c,0xe0,0x00,0x00,0x02,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x57,0xed,0xa8,0x12,0x2c,0x49,0xd3,0xb6,0xa6,0x29,0x59,0xd6,0xfe,0x01,0x72,0x8d, +0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xfe,0x01,0x00,0x00,0xc6,0x80,0x00,0x00, +0xff,0x00,0x8f,0x70,0xa3,0x08,0x72,0x8d,0xa9,0x02,0x63,0x48,0xfe,0x01,0x8d,0x72, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x01,0xff,0xff, +0x38,0x53,0x54,0x46,0x50,0x36,0x30,0x30,0x00,0x31,0x30,0x30,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x00,0x31,0x30,0x30,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x30,0x38,0x54,0x46,0x50,0x5f,0x36,0x30,0x62,0x6d,0x61,0x72,0x5f,0x74,0x6f,0x6f, +0x36,0x2e,0x31,0x56,0x72,0x70,0x41,0x5f,0x20,0x36,0x32,0x20,0x38,0x31,0x30,0x32, +0xef,0x17,0x7c,0xc0,0x02,0xe8,0x87,0x6f,0x00,0xe2,0x9c,0x4f,0x30,0x01,0xea,0xcf, +0x00,0xe2,0x94,0x02,0x25,0x01,0x85,0xcf,0xe6,0xcf,0x00,0xe2,0x00,0xe2,0x26,0x01, +0x87,0x01,0x83,0xcf,0xaa,0x67,0x02,0xe8,0x01,0xe0,0x1f,0x3c,0x07,0x6c,0x08,0x8c, +0x6f,0x3c,0x87,0x4f,0x82,0x38,0x04,0xc0,0xb3,0xcf,0x01,0xe2,0x01,0xe2,0x74,0x01, +0x13,0x01,0xcc,0xcf,0x90,0xcf,0x01,0xe2,0x05,0x2c,0xf4,0x01,0xd4,0x4f,0x00,0xe8, +0xef,0x00,0x87,0x6f,0x01,0xcc,0x00,0xe2,0x00,0xe8,0xec,0x01,0xf9,0x00,0xd5,0x4f, +0x00,0xe8,0x68,0x2c,0xf5,0x00,0xc6,0x4f,0xa0,0x4f,0x02,0xe8,0x05,0x2c,0xf2,0x00, +0x88,0x5f,0x01,0xe8,0xed,0x00,0xc8,0x37,0x01,0xcc,0x00,0xe2,0x01,0xe8,0xda,0x01, +0xe7,0x00,0x88,0x5f,0x02,0xe8,0x00,0x17,0x02,0xe8,0x1e,0x6f,0x00,0xe2,0x9c,0x4f, +0x02,0xe0,0xe5,0xcf,0xb2,0x02,0x4f,0x01,0x89,0xcf,0x00,0xe2,0x3b,0x01,0x02,0xe0, +0x00,0xe2,0x96,0x02,0x01,0xe0,0x83,0xcf,0x88,0x02,0x5f,0x01,0x81,0xcf,0x00,0xe2, +0x01,0x17,0x8d,0x01,0x1e,0x6f,0x02,0xe8,0x00,0xe2,0x89,0x00,0x02,0xe0,0x85,0xcf, +0x00,0xe2,0x0c,0x01,0x02,0xe0,0x87,0xcf,0x82,0x38,0x12,0x01,0xd5,0xcf,0x00,0xe2, +0x87,0x02,0x27,0x01,0x90,0xcf,0x00,0xe2,0x02,0xe8,0xf9,0x01,0xf6,0x00,0x92,0x6c, +0xe1,0xcf,0x00,0xe2,0x4b,0x01,0x02,0xe0,0xe4,0xcf,0x00,0xe2,0x02,0xe8,0xef,0x01, +0x80,0x17,0x20,0x6f,0xc0,0x7f,0x00,0xe8,0x01,0xe2,0xe9,0x00,0x00,0xe0,0xb0,0xcf, +0x91,0x02,0x43,0x01,0xea,0xcf,0x00,0xe2,0x84,0x02,0x55,0x01,0xe6,0xcf,0x00,0xe2, +0x01,0xe2,0xd0,0x00,0x29,0x01,0x90,0xcf,0xaa,0xcf,0x01,0xe2,0x02,0xe8,0xd7,0x01, +0xd4,0x00,0x20,0x6f,0xb3,0xcf,0x01,0xe2,0x1e,0x01,0x01,0xe0,0x01,0xe2,0x97,0x02, +0xcc,0x01,0xb2,0xcf,0x05,0xcc,0x00,0xe2,0xe6,0x02,0x00,0xe0,0x8e,0x7f,0x06,0xea, +0xfe,0xc7,0xff,0xe9,0x1f,0x3c,0x21,0x34,0x1f,0x3c,0x08,0x94,0x7c,0xff,0x02,0x3c, +0x4c,0x01,0x2a,0x01,0x98,0x00,0x8a,0x00,0x01,0xe2,0xa4,0x00,0x6b,0x01,0xbf,0xcf, +0xcc,0xcf,0x01,0xe2,0xa6,0x00,0xff,0xe7,0x03,0xcc,0x00,0xe2,0xaf,0x02,0xff,0xe7, +0x90,0x1f,0x00,0xe0,0x20,0x6f,0x02,0xe8,0x8f,0xac,0x98,0x3f,0x1b,0x24,0xff,0xe7, +0xa5,0x00,0xff,0xe7,0x03,0xcc,0x00,0xe2,0xa1,0x02,0xff,0xe7,0xdc,0x7f,0x06,0xea, +0xfe,0xc7,0xff,0xe9,0x08,0x8c,0x1f,0x3c,0x02,0x3c,0x1f,0x3c,0x26,0x16,0x04,0x04, +0x01,0xe8,0x80,0x17,0x01,0xe8,0x87,0x77,0x02,0xe8,0x86,0x77,0xff,0xe7,0xa0,0x6f, +0x01,0xe8,0x8e,0x00,0xa8,0x34,0x87,0x5f,0x01,0xe8,0x9f,0x3c,0xff,0xe7,0x87,0x74, +0x01,0xe8,0x86,0x00,0x00,0x17,0x87,0x5f,0x86,0x17,0x9f,0x3c,0x87,0x74,0x01,0xe8, +0xc2,0x7f,0x00,0xe8,0xa5,0x00,0xff,0xe7,0x2a,0x67,0x02,0xe8,0x9f,0x3c,0x8e,0xa7, +0xfe,0xe7,0x8e,0xbc,0x02,0xe8,0xf4,0x00,0xa8,0x34,0xaa,0x67,0xfe,0xe7,0xaf,0xb4, +0x02,0xe8,0xee,0x00,0x2f,0x9f,0xaa,0x67,0xaf,0xb4,0x9e,0x3c,0x3f,0xb7,0x00,0x17, +0xe5,0x00,0xfe,0xe7,0x9c,0x4f,0x02,0xe8,0xb2,0xcf,0x01,0xe2,0x02,0xe8,0x87,0x01, +0x0f,0xe2,0xa0,0x4f,0x02,0xe8,0xcf,0x3c,0x02,0xe8,0xa0,0x6f,0x3f,0x9f,0xaa,0x67, +0x81,0xc6,0xee,0x3e,0x1f,0x3f,0xbf,0xb6,0x8e,0xac,0x01,0xe0,0xaf,0x9e,0x3f,0x9f, +0xfe,0xe7,0xfe,0x3e,0x09,0xe8,0xcc,0x02,0x00,0x17,0xa6,0x4e,0x0d,0x3f,0x2e,0xe0, +0x26,0x6f,0x09,0xe8,0xa6,0x7f,0x02,0xe8,0x00,0xe8,0x82,0x17,0x02,0xe8,0xc2,0x7f, +0x01,0xe2,0x9c,0x4f,0xfe,0xe7,0xb2,0xcf,0x07,0x17,0xba,0x01,0x42,0x7f,0x00,0xe8, +0xb5,0x00,0xfe,0xe7,0x02,0xcc,0x00,0xe2,0x00,0xe2,0x19,0x01,0x1c,0x01,0x03,0xcc, +0x01,0xcc,0x00,0xe2,0xab,0x01,0xfe,0xe7,0xa6,0x4f,0x09,0xe8,0x00,0xe0,0x8c,0x27, +0x02,0xe8,0x8c,0x1f,0x09,0xe4,0xaa,0x7f,0x02,0xe8,0xa0,0x34,0x8f,0xbc,0xaa,0x67, +0x9d,0x00,0xfe,0xe7,0x8b,0x1f,0x00,0xe0,0x02,0xe8,0xf5,0x00,0x0f,0xa7,0xaa,0x67, +0x9e,0x3c,0xa8,0x34,0x02,0xe8,0xf5,0x00,0x0f,0xa7,0xaa,0x67,0x8f,0xbc,0x9e,0x3c, +0x02,0xe8,0x01,0x17,0x81,0x17,0xa6,0x7f,0x9e,0x6f,0x02,0xe8,0x42,0x7f,0x00,0xe8, +0x9c,0x4f,0x02,0xe8,0xb3,0xcf,0x01,0xe2,0x81,0x01,0xfe,0xe7,0x00,0xe8,0x88,0x17, +0xfd,0xe7,0xc2,0x7f,0x00,0xe2,0xfc,0x00,0x81,0x17,0x02,0xcc,0x02,0xe8,0x83,0x01, +0x02,0xe8,0x94,0x6f,0xfd,0xe7,0x9e,0x6f,0x02,0xe0,0xf2,0x00,0x0f,0xaf,0x84,0x17, +0x0f,0xaf,0x81,0xc7,0x86,0x17,0x02,0xe0,0x81,0xc7,0x0f,0xaf,0x00,0x17,0x0f,0xaf, +0x9d,0x67,0x00,0xe8,0x1f,0xb7,0x03,0xe0,0x02,0xe8,0x81,0x17,0xfd,0xe7,0x95,0x6f, +0x00,0xe2,0xde,0x00,0x85,0x01,0x01,0xcc,0xc7,0x6c,0x00,0xe8,0xd7,0x00,0xfd,0xe7, +0x02,0xcc,0x00,0xe2,0xd3,0x01,0xfd,0xe7,0x93,0x6c,0x02,0xe8,0xcf,0x00,0xfd,0xe7, +0x00,0xe2,0x7f,0xc4,0xfd,0xe7,0x04,0xcc,0x0a,0xea,0xca,0x02,0xff,0xe9,0x90,0x7f, +0x21,0x34,0xfe,0xc7,0x08,0x94,0x1f,0x3c,0x02,0xe8,0x1f,0x3c,0x02,0x3c,0xaa,0x67, +0x4e,0xff,0x0a,0x00,0x18,0x00,0x12,0x00,0x09,0xe4,0x1e,0x00,0xff,0xe7,0xa0,0x34, +0x0f,0xa7,0x98,0x00,0x9f,0x00,0xff,0xe7,0xfe,0xe7,0xa8,0x34,0x2f,0x9f,0xc1,0x00, +0xaf,0xb4,0x9e,0x3c,0xfe,0xe7,0x03,0x17,0x84,0x17,0xf3,0x00,0xc2,0x7f,0x00,0xe8, +0x20,0x6f,0x02,0xe8,0x00,0xe8,0x00,0x17,0xfd,0xe7,0x40,0x7f,0xb2,0x08,0xa0,0x00, +0x37,0x0c,0x00,0xe8,0x80,0x17,0x7c,0xc0,0x81,0x17,0x87,0x6f,0x1b,0x61,0x00,0xe8, +0xc8,0x6f,0x00,0xe8,0x00,0xe2,0x82,0xa7,0x9e,0x27,0x81,0xc7,0x7e,0x17,0x82,0xa7, +0x82,0xbf,0xde,0x3f,0x87,0x6f,0xf2,0x8f,0x9d,0x4f,0x02,0xe8,0x87,0x4f,0x84,0x2f, +0x9c,0x6f,0x02,0xe8,0x9d,0x4f,0x02,0xe8,0x6f,0x3c,0x07,0x4f,0x5b,0xde,0xee,0x3c, +0x9d,0x4f,0x02,0xe8,0x02,0xe8,0x81,0xc7,0x02,0xe8,0x9d,0x6f,0x84,0x27,0x9e,0x4f, +0x0f,0xde,0x00,0x14,0x82,0xa7,0x72,0xac,0x00,0xe2,0xc1,0x37,0x8f,0x27,0x81,0xc7, +0x7d,0x17,0x82,0xa7,0x82,0xbf,0xde,0x3f,0x9d,0x4f,0x02,0xe8,0x02,0xe8,0x81,0xc7, +0x02,0xe8,0x9d,0x6f,0x6f,0x3c,0x9d,0x4f,0x72,0xac,0xfc,0xdd,0x61,0xe0,0x88,0x17, +0x04,0xc0,0x91,0x3f,0xa8,0x0b,0x00,0xf0,0x00,0x38,0x21,0x09,0x00,0xe8,0xbf,0x0e, +0xfe,0x16,0x1b,0x67,0xdd,0x3f,0xbe,0xa7,0x80,0x17,0xbe,0xbf,0x9d,0x6f,0x02,0xe8, +0x61,0xe0,0x90,0x17,0xad,0x0f,0x91,0x3f,0xb6,0x08,0x00,0x38,0x00,0xc0,0xff,0xe1, +0x84,0x4f,0x02,0xe8,0xe9,0x39,0x68,0x3a,0x8a,0x27,0xea,0x3a,0x8e,0x1c,0x00,0xe0, +0xa2,0xe0,0x20,0x15,0x06,0xd3,0x00,0x14,0x02,0xe8,0x80,0x17,0x01,0xe0,0x84,0x6f, +0xa1,0x00,0x00,0x13,0x01,0xe2,0x63,0x39,0x82,0x05,0x00,0xc9,0xff,0xe3,0x66,0x39, +0x64,0x3c,0x7f,0xc1,0x00,0xe2,0x62,0x3d,0xe6,0xd2,0x84,0x7c,0x85,0x8f,0xa2,0x39, +0xff,0xc1,0xff,0xe3,0x00,0xe2,0x12,0x3a,0x00,0x17,0x84,0x7e,0x80,0xe1,0x01,0xc7, +0xff,0xe3,0x1d,0x8e,0x7e,0x39,0x7f,0xc7,0xcc,0x3f,0x0f,0xe2,0xff,0xc7,0x01,0xe2, +0x85,0xaf,0xf6,0x01,0x01,0xe0,0xe0,0x29,0x61,0x0b,0x00,0xc0,0x02,0xcc,0x00,0xea, +0x00,0xcc,0x02,0xe0,0x00,0xea,0x80,0x14,0x0b,0xe0,0x02,0xcd,0x28,0x3d,0x20,0xcd, +0x00,0xea,0x88,0xd5,0x00,0xe0,0x00,0xcc,0x02,0x24,0x00,0xcc,0x00,0x14,0x01,0x3c, +0xf1,0xd3,0x80,0x14,0x00,0x00,0x80,0x3a,0xb1,0x08,0x80,0x3a,0x60,0xe0,0xb3,0xd5, +0x00,0xe0,0x87,0x3f,0x01,0xe0,0x08,0x1f,0x00,0xe0,0x3f,0xbf,0x01,0xe0,0x0f,0x1f, +0x00,0xe0,0x4f,0xbf,0x02,0xe0,0x06,0x1f,0xe1,0x08,0x4f,0xbf,0xa0,0x1f,0x00,0xe0, +0x5f,0xb7,0x7f,0x17,0x03,0x17,0x1e,0xe0,0x00,0xe8,0x3f,0xb7,0x13,0x27,0x56,0x4f, +0x14,0x17,0xa0,0xe0,0x20,0xe0,0x7f,0xb7,0x01,0xe0,0x04,0x17,0x00,0xeb,0x1f,0xb7, +0x2f,0xa7,0x80,0xcf,0x5d,0x3f,0xf8,0x16,0x1f,0xa7,0x2f,0xbf,0x49,0xcf,0xc0,0xe0, +0x82,0x38,0x1f,0xbf,0x7f,0xb7,0x04,0x17,0x00,0xe0,0xf1,0x00,0x02,0xe0,0xa0,0x1e, +0x02,0xe2,0x9d,0x9f,0x95,0x27,0x80,0xc7,0x9d,0x9f,0x02,0xe0,0x7f,0x16,0xfb,0xe7, +0x6f,0x3f,0xc9,0x37,0x01,0xc7,0x00,0xe2,0x56,0x6f,0x00,0xe8,0x8f,0x3f,0x41,0xe0, +0x00,0xe2,0x0d,0xa7,0xa9,0x37,0x81,0xc7,0xce,0x3f,0x5c,0x3f,0x82,0x38,0x8d,0xbf, +0x00,0xe8,0x83,0x17,0xfc,0x00,0xd6,0x6f,0x18,0x8c,0x88,0x8f,0x01,0xe0,0x98,0x3f, +0x28,0xe0,0x7f,0x14,0x82,0x38,0x88,0x3f,0x00,0xe0,0xb4,0x08,0x68,0x39,0x83,0x19, +0x63,0x3c,0x98,0x39,0xf1,0xdf,0x69,0x3a,0x83,0x8f,0x04,0x24,0x61,0x0a,0x84,0xaf, +0x87,0x1f,0x00,0xe0,0x62,0x3c,0x1f,0x39,0x7a,0x24,0xe8,0xdf,0xf7,0x00,0x82,0x8f, +0xd6,0x4f,0x00,0xe8,0xb1,0x08,0x93,0x27,0x83,0x1c,0x00,0xe0,0x1f,0xe0,0x0e,0x15, +0x42,0xd2,0x00,0x14,0x87,0x1c,0x00,0xe0,0x22,0xe0,0x0e,0x15,0x3c,0xd2,0x20,0x14, +0xd9,0x7c,0x00,0xea,0xa1,0x08,0x00,0x14,0x82,0x38,0xd8,0x00,0xd6,0x4f,0x00,0xe8, +0x00,0xe0,0xfe,0x16,0x91,0x2f,0x21,0x1f,0xd8,0x4f,0x00,0xe8,0x01,0xe0,0xdd,0x3f, +0x61,0xe0,0xbe,0xaf,0x88,0xcf,0x82,0x3f,0x92,0x3f,0x61,0xe0,0x82,0x3f,0x61,0xe0, +0x61,0xe0,0x90,0xcf,0x82,0x38,0x92,0x3f,0xd9,0x4f,0x00,0xe8,0xbf,0x0e,0xf0,0x00, +0xc9,0x67,0x00,0xe8,0x00,0xe8,0x81,0xc7,0x00,0xe8,0xc9,0x7f,0x81,0xc7,0xca,0x67, +0xca,0x7f,0x00,0xe8,0xca,0x67,0x00,0xe8,0x84,0xcf,0x00,0xe2,0x84,0x17,0x84,0x05, +0xca,0x7f,0x00,0xe8,0x00,0xe0,0xdf,0x16,0xbe,0xa7,0x22,0x1f,0xbe,0xbf,0xdd,0x3f, +0x80,0x17,0x20,0xe0,0x91,0x3f,0x61,0xe0,0x00,0x38,0xad,0x0f,0x22,0x1f,0x00,0xe0, +0xbe,0xbf,0x91,0x17,0xae,0xa7,0xfc,0x16,0x82,0xcf,0xdd,0x3f,0x0a,0xe0,0xae,0xbf, +0x9e,0xb7,0x80,0x17,0x80,0x17,0x20,0xe0,0x91,0x3f,0x61,0xe0,0x82,0x3f,0x61,0xe0, +0x80,0xcf,0x20,0xe0,0x92,0x3f,0x61,0xe0,0x7c,0xc0,0x82,0x38,0x80,0x17,0xa0,0xe0, +0x00,0xe0,0x81,0x7f,0x01,0xe0,0xa0,0x1f,0xff,0xe3,0x1f,0x9f,0x01,0xe0,0x7b,0xc7, +0xff,0x16,0x1f,0xb7,0x9e,0x3e,0x01,0x67,0x7c,0x2f,0x81,0x7e,0x1f,0x9f,0x01,0xe0, +0x01,0xe0,0x04,0xcf,0x04,0xc0,0x1f,0xb7,0xff,0xe1,0x82,0x38,0x7f,0xc4,0xff,0x17, +0x7f,0xc4,0xff,0xe3,0x0a,0x01,0x7f,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x38,0xf3,0x00,0x00,0xe0,0xb3,0x08, +0x00,0x17,0x8a,0x1f,0x6f,0x3d,0x01,0x16,0x9e,0x3f,0x0d,0xe0,0x00,0xe2,0x01,0xc7, +0x0d,0xae,0x10,0xcf,0x03,0x17,0xfa,0x01,0x81,0x10,0x3f,0xaf,0x02,0x17,0x4f,0xaf, +0x00,0x17,0x6f,0xaf,0x8d,0x1d,0x00,0xe0,0x7f,0xaf,0x01,0xe0,0x6b,0x3c,0x80,0x17, +0x0d,0xe0,0xef,0x3c,0x0e,0xe0,0xa2,0x37,0x8e,0xbc,0x9d,0x3d,0x1f,0x3d,0x02,0xe0, +0x00,0x17,0x98,0x3e,0x1e,0x3d,0x0c,0xe0,0x82,0x89,0x0c,0x8e,0x86,0x03,0xfc,0x39, +0x0c,0xe0,0x8d,0xa1,0x43,0x3e,0xbe,0x30,0x01,0xc7,0x0d,0xbe,0x10,0xcf,0x00,0xe2, +0x81,0xc7,0xf2,0x01,0x90,0xcf,0x00,0xe2,0x00,0xe0,0xe5,0x01,0x00,0x17,0x89,0x1f, +0x0f,0xaf,0x06,0xe0,0x00,0xe0,0xe1,0x09,0x0f,0xe0,0x8d,0x1e,0x9f,0x3e,0x22,0x34, +0xb3,0x27,0x8d,0xa7,0x00,0xe0,0x83,0x3f,0x06,0xe0,0x89,0x1f,0x60,0xe0,0x0f,0x8f, +0x24,0x37,0x09,0x3e,0x0e,0xbe,0x1f,0x3f,0x0f,0x8f,0x06,0xe0,0x0a,0x3e,0x60,0xe0, +0x1f,0x3f,0x24,0x37,0x06,0xe0,0x1e,0xbe,0x60,0xe0,0x0f,0x8f,0x24,0x37,0x08,0x3e, +0x2e,0xbe,0x1f,0x3f,0x0f,0x8f,0x06,0xe0,0x02,0x3e,0x61,0xe0,0x1f,0x3f,0x24,0x37, +0x06,0xe0,0x3e,0xbe,0x01,0xc7,0x0f,0x8f,0x0f,0xaf,0x06,0xe0,0x61,0xe0,0x8d,0xa7, +0x81,0x17,0x92,0x3f,0xb8,0x37,0x08,0xe0,0x11,0x3c,0x61,0xe0,0x80,0x3f,0x60,0xe0, +0x60,0xe0,0x82,0xcf,0x00,0x3b,0x90,0x3f,0x00,0xe0,0x82,0x38,0x0e,0xe0,0x8d,0x1f, +0x9e,0x3f,0x22,0x34,0xa7,0x27,0x8f,0xa7,0x00,0xe0,0x83,0x3f,0x06,0xe0,0x89,0x1f, +0x7f,0xc7,0x0f,0x8f,0x0f,0xaf,0x06,0xe0,0x0f,0x8f,0x06,0xe0,0x1f,0x3f,0x24,0x37, +0x60,0xe0,0x0e,0xa7,0x06,0xe0,0x19,0x3f,0x24,0x37,0x0f,0x8f,0x1e,0xa7,0x1f,0x3f, +0x1a,0x3f,0x60,0xe0,0x0f,0x8f,0x06,0xe0,0x1f,0x3f,0x24,0x37,0x60,0xe0,0x2e,0xa7, +0x06,0xe0,0x18,0x3f,0x24,0x37,0x0f,0x8f,0xbf,0xa7,0x9e,0x3f,0x92,0x3f,0x61,0xe0, +0x82,0x38,0x00,0x3b,0x08,0xe0,0x81,0x17,0x61,0xe0,0xb8,0x37,0xfa,0x00,0x11,0x3c, +0x19,0x67,0x00,0xe8,0x8e,0xa7,0x7e,0xac,0x00,0xe2,0xce,0x37,0xfc,0x27,0x81,0xc7, +0x7f,0xe7,0x8e,0xa7,0xdd,0x3f,0xff,0x16,0x8e,0xbf,0x01,0x14,0x00,0xe8,0x82,0x38, +0x00,0x17,0x99,0x67,0x0f,0xa7,0x7f,0xaf,0x00,0xe2,0x4e,0x37,0x7c,0x27,0x01,0xc7, +0x7f,0xe7,0x0f,0xa7,0x5d,0x3f,0xff,0x16,0xff,0x8f,0x0f,0xbf,0x82,0x38,0x6f,0x3c, +0x99,0x67,0x00,0xe8,0x80,0x16,0x98,0x3c,0x7f,0x16,0x7f,0xe7,0x82,0x01,0x79,0x3c, +0xff,0xae,0x82,0x38,0x4e,0x37,0x0f,0xa7,0x01,0xc7,0x00,0xe2,0x0f,0xa7,0x7c,0x27, +0x0f,0xbf,0x5c,0x3f,0x80,0xe1,0x7f,0x8f,0xf1,0x00,0x18,0xaf,0x00,0xe8,0xb2,0x08, +0x7b,0x17,0x19,0x61,0x00,0xe2,0x82,0xa7,0xde,0x3f,0x01,0xcc,0x87,0x01,0x82,0xbf, +0xbf,0xdf,0x06,0x14,0x84,0xcf,0x82,0xa7,0x61,0x09,0x82,0xbf,0xfa,0x00,0x04,0x14, +0x00,0xe8,0xb2,0x08,0x7b,0x17,0x19,0x61,0x05,0x14,0x82,0xa7,0x82,0xbf,0xde,0x3f, +0xbe,0xdf,0xb0,0xdf,0x00,0xe2,0x82,0xa7,0x84,0xcf,0x01,0xc4,0x61,0x09,0x82,0xbf, +0x01,0x14,0xb2,0x08,0x00,0xe8,0xdc,0xdf,0x7b,0x17,0x19,0x61,0x01,0xe0,0x82,0xa7, +0xde,0x3f,0x10,0x14,0x9d,0xdf,0x82,0xbf,0x82,0xa7,0xab,0xdf,0x82,0xbf,0x84,0xcf, +0xb4,0x08,0x61,0x09,0xcb,0xdf,0x01,0x14,0x19,0x61,0x00,0xe8,0x02,0xa7,0xfb,0x11, +0x53,0x3f,0x05,0x14,0x8d,0xdf,0x02,0xbf,0x02,0xa7,0x9b,0xdf,0x04,0xcf,0x68,0x3a, +0x01,0x14,0x02,0xbf,0x82,0xa7,0xbc,0xdf,0xd3,0x3f,0x01,0x14,0x81,0xdf,0x82,0xbf, +0x01,0xe2,0x64,0x3c,0x7d,0xdf,0x03,0xc4,0x84,0xcf,0x82,0xa7,0xc1,0xdf,0x82,0xbf, +0x61,0x0a,0x7f,0x2c,0x1e,0x67,0x00,0xe8,0x81,0xcf,0x9e,0xa7,0x00,0xe8,0x9e,0xbf, +0x3f,0x9f,0x9d,0x67,0x00,0xcf,0x1e,0xe0,0x01,0xe0,0x3f,0xb7,0x80,0xe0,0x1f,0x9f, +0x01,0xe0,0x00,0xcf,0x61,0xe0,0x1f,0xb7,0xef,0xe7,0x82,0x3f,0xde,0x3f,0x7f,0x17, +0x92,0x3f,0x61,0xe0,0x99,0x67,0x00,0xe8,0x06,0x17,0x26,0xe0,0xc2,0x00,0x0f,0xbf, +0x00,0xe8,0xb2,0x08,0xfb,0x16,0x19,0x67,0x69,0x39,0x8e,0xa7,0x8e,0xbf,0xdd,0x3f, +0x08,0xe4,0x50,0xdf,0x01,0xe2,0x40,0x31,0x4b,0xdf,0x7f,0xc4,0x48,0x31,0x08,0xe0, +0x7f,0xc4,0x01,0xe2,0x62,0x3c,0x46,0xdf,0x7f,0xc4,0x01,0xe2,0xfe,0xe7,0x21,0x09, +0xb4,0x08,0xc1,0x00,0x01,0x14,0x68,0x3a,0xea,0x39,0x69,0x39,0xe4,0x3c,0x70,0xdf, +0xdf,0xdf,0x02,0x14,0x00,0xe8,0xe2,0x3c,0xff,0xc4,0x99,0x67,0x7f,0xe7,0x00,0x17, +0x73,0x3f,0x7f,0x16,0x0f,0xa7,0x95,0x01,0x0f,0xbf,0x04,0xcf,0x06,0x24,0x72,0xdf, +0xc9,0x67,0x00,0xe8,0xb2,0xcf,0x00,0xe2,0x00,0xe8,0xfa,0x05,0x00,0xe2,0xc9,0x67, +0x95,0x05,0xb2,0xcf,0x08,0x14,0x27,0xe0,0xfc,0xe7,0x21,0x0a,0x80,0xe0,0xe0,0x00, +0xff,0xae,0x99,0x8e,0xce,0x36,0x8f,0xa6,0x81,0xc6,0x00,0xe2,0x8f,0xa6,0xfc,0x26, +0xdc,0x3e,0x01,0xc7,0x7f,0xc7,0xff,0xe3,0xdc,0x00,0x8f,0xbe,0xb2,0x08,0x61,0x0a, +0x19,0x67,0x00,0xe8,0x8e,0xa7,0xfb,0x16,0xdd,0x3f,0x68,0x39,0x0b,0x14,0x8e,0xbf, +0x08,0xe4,0x00,0xdf,0x01,0xe2,0x40,0x31,0xfb,0xde,0x7f,0xc4,0x48,0x31,0x08,0xe0, +0x7f,0xc4,0x01,0xe2,0x62,0x3c,0xf6,0xde,0x7f,0xc4,0x01,0xe2,0x00,0x14,0xf2,0xde, +0xfd,0xe7,0x21,0x09,0xb3,0x08,0xef,0x00,0xea,0x39,0x69,0x39,0x62,0x3c,0xe1,0xdf, +0x07,0xdf,0xe3,0x3c,0x19,0x67,0x00,0xe8,0x8e,0xa7,0x01,0x14,0x8e,0xbf,0x84,0xcf, +0xb7,0x08,0xe1,0x09,0x38,0x0c,0x00,0xf8,0x00,0xe2,0xe8,0x39,0x01,0xe0,0xff,0xc1, +0x03,0xe0,0x80,0x13,0xff,0xe3,0xa3,0x3b,0xfa,0x39,0xff,0xc1,0x92,0xdf,0x83,0x04, +0x6a,0x39,0xa1,0x00,0x68,0x3a,0xe9,0x3a,0x13,0x3a,0x23,0x39,0xff,0xe3,0x63,0x3d, +0x95,0x39,0x7f,0xc1,0xe2,0x3a,0x87,0xdf,0x18,0xe0,0x64,0x3b,0x00,0xe2,0xa4,0x39, +0x09,0xf0,0xff,0xca,0x92,0x02,0x16,0x3c,0x47,0x31,0x0a,0xe0,0x27,0x35,0x09,0xe0, +0x00,0xc5,0xff,0xe5,0x19,0x3a,0x08,0xe0,0xff,0xe3,0x12,0x3d,0x93,0x3c,0x7f,0xc5, +0x01,0x14,0x5f,0x2d,0x28,0x0c,0x00,0xf8,0x66,0x3c,0xe1,0x0b,0xff,0xe1,0x67,0x3d, +0x68,0xdf,0x80,0xc2,0xff,0xc2,0xff,0xe3,0x00,0xc3,0x01,0xe0,0xb2,0x08,0xe1,0x00, +0x01,0x14,0x68,0x39,0x09,0xe0,0xd4,0xde,0x20,0x14,0x2c,0x31,0x00,0xe8,0x42,0xdf, +0x8e,0xa7,0x19,0x67,0x8e,0xbf,0x84,0xcf,0x06,0x24,0xdc,0xde,0xc9,0x67,0x00,0xe8, +0xf4,0xcf,0x03,0xe2,0x00,0xe8,0xfa,0x05,0x03,0xe2,0xc9,0x67,0x86,0x05,0xf4,0xcf, +0x08,0x14,0x27,0xe0,0xfb,0xe7,0x21,0x09,0x61,0x09,0xca,0x00,0x68,0x39,0xb2,0x08, +0xb5,0xde,0x01,0x14,0x20,0x31,0x09,0xe4,0x58,0x14,0x01,0xe0,0x00,0xe8,0x22,0xdf, +0x8e,0xa7,0x19,0x67,0x8e,0xbf,0x84,0xcf,0x06,0x24,0xbc,0xde,0xc9,0x67,0x00,0xe8, +0xdc,0xcf,0x0b,0xe2,0x00,0xe8,0xfa,0x05,0x0b,0xe2,0xc9,0x67,0x86,0x05,0xdc,0xcf, +0x08,0x14,0x27,0xe0,0xfb,0xe7,0x21,0x09,0x61,0x09,0xaa,0x00,0x01,0x14,0xb2,0x08, +0x00,0xe8,0x96,0xde,0x7b,0x17,0x19,0x61,0x00,0xe0,0x82,0xa7,0xde,0x3f,0x60,0x14, +0x57,0xde,0x82,0xbf,0x84,0xcf,0x82,0xa7,0x9b,0xde,0x82,0xbf,0x61,0x09,0x7f,0x2c, +0xc7,0x4f,0x00,0xe8,0x8a,0xcf,0x00,0xe2,0x00,0xe8,0x28,0x01,0x02,0xe0,0xc7,0x4f, +0x9f,0xe3,0x80,0x17,0x7e,0x3c,0x7f,0x17,0xb4,0x08,0xa3,0x02,0x40,0x67,0x00,0xe8, +0xfe,0x3f,0x6a,0x39,0x68,0x3a,0xe9,0x39,0x00,0xe0,0x8d,0x01,0x20,0x15,0x0e,0x1c, +0x20,0x15,0xd8,0xd1,0xff,0x14,0x01,0xe0,0x0b,0xd2,0x63,0x3c,0x02,0xe8,0x81,0x17, +0x05,0x21,0x84,0x6f,0xe3,0x3c,0x62,0x3d,0x4c,0xdf,0x64,0x3c,0x40,0x65,0x00,0xe8, +0x00,0xe8,0x1a,0x39,0x61,0x0a,0x40,0x79,0x80,0x17,0xa2,0xe0,0x82,0x38,0xdb,0x00, +0x85,0x1f,0x00,0xe0,0x84,0x1e,0x00,0xe0,0x7f,0x16,0x6f,0x3f,0x1f,0xae,0x80,0xe1, +0xfd,0x01,0xfd,0x3f,0x87,0x5f,0x01,0xe8,0x85,0x1c,0x00,0xe0,0xc8,0x37,0x0d,0xe0, +0xef,0x3e,0x8e,0xae,0x80,0xcf,0xfe,0xe5,0xae,0xaf,0xc8,0x37,0xff,0xc6,0x01,0xe2, +0xa0,0x4f,0x02,0xe8,0x20,0x15,0x9e,0xae,0x41,0xe0,0xce,0xaf,0x02,0xe8,0x8d,0x3e, +0xa0,0xe3,0xa0,0x4f,0x41,0xe0,0x00,0x14,0xbe,0xae,0x8f,0x3f,0xfe,0xe7,0xde,0xaf, +0xa0,0xe3,0x9a,0x00,0x1f,0x3c,0xa0,0x17,0xff,0x17,0x9f,0xe3,0xa8,0x05,0x7f,0x3c, +0x18,0x3d,0x0d,0xe0,0x80,0x17,0xc0,0xe3,0xa2,0x02,0xff,0x3e,0x87,0x5e,0x01,0xe8, +0xe0,0xce,0x1f,0xe2,0x01,0xe8,0x9d,0x02,0x9a,0x3f,0x86,0x5f,0xff,0xc7,0xff,0xe3, +0x86,0x77,0x01,0xe8,0xff,0x3e,0x0f,0x25,0xb1,0x08,0x8d,0x04,0x01,0xe8,0xfb,0xde, +0x01,0xe8,0x06,0x5f,0x7f,0x3f,0x87,0x5f,0xa1,0x08,0x84,0x01,0xb2,0x00,0xff,0xe7, +0x01,0xe8,0xe1,0x08,0x01,0xe8,0x06,0x5f,0x7f,0x3f,0x87,0x5f,0x82,0x38,0x78,0x01, +0xf0,0x17,0xff,0xe2,0x0f,0xbf,0x00,0x17,0xb1,0x08,0x82,0x38,0x2f,0xdc,0x4f,0xdc, +0xf0,0x17,0xff,0xe2,0x0f,0xbf,0x00,0x17,0x83,0xdc,0x46,0xde,0x16,0xdc,0x98,0xdc, +0xe1,0x08,0x00,0x3b,0x00,0xf8,0xb7,0x08,0xe1,0xe7,0x3c,0x0c,0xee,0xdf,0xaa,0x11, +0x80,0x13,0xff,0xe7,0x55,0x14,0xe0,0xf7,0x80,0x14,0xa0,0xf3,0x00,0x12,0x02,0xe0, +0x9b,0x62,0x00,0xe8,0x40,0xf0,0x00,0x11,0x01,0x13,0x65,0x3d,0x7c,0x4c,0x00,0xe8, +0x00,0xf8,0x04,0x2c,0xe1,0x0b,0x28,0x0e,0xc2,0x67,0x00,0xe8,0x00,0xe2,0xff,0xc7, +0xb9,0x02,0x87,0xcf,0x46,0x7f,0x1d,0xea,0x7e,0xc7,0xff,0xe9,0x9e,0x3f,0xa1,0x37, +0x9e,0x3f,0x8f,0x9f,0xa4,0x00,0x82,0x3f,0xf6,0x00,0x10,0x00,0x5e,0x00,0x16,0x01, +0x82,0x00,0x10,0x02,0x00,0xe8,0xc8,0x00,0x00,0xe8,0x42,0x79,0x01,0xe8,0xfd,0x4f, +0x84,0x27,0x08,0x71,0x00,0xe8,0xab,0xdc,0x00,0xe8,0x7d,0x69,0x6f,0x3f,0xc7,0x4f, +0xa6,0x67,0x02,0xe8,0x0b,0xcf,0x00,0xe2,0x88,0xc4,0xef,0x3c,0x00,0xe8,0x06,0x01, +0x00,0xe2,0x47,0x4f,0x9d,0x01,0x0a,0xcf,0x2f,0x9d,0x0f,0xa4,0x02,0xe8,0x1a,0xdf, +0x00,0xe8,0xa6,0x67,0x0f,0xa4,0x7d,0x6b,0xe4,0xd0,0xaf,0x9c,0x00,0xc4,0x20,0xe0, +0x08,0x74,0x01,0xe8,0x95,0x4f,0x02,0xe8,0xba,0x27,0xff,0xe7,0x08,0x14,0x27,0xe0, +0x00,0xe8,0x9d,0xdc,0x7f,0x17,0x9d,0x67,0x3f,0xaf,0x07,0xe0,0xaa,0x00,0xff,0xe7, +0x2f,0x9d,0x0f,0xa4,0xe4,0x00,0x6f,0xde,0x42,0x79,0x00,0xe8,0xfd,0x4f,0x00,0xe8, +0x08,0x71,0x01,0xe8,0x72,0xdc,0x84,0x27,0x7d,0x69,0x00,0xe8,0xa6,0x64,0x02,0xe8, +0x29,0x9d,0x09,0xa4,0x46,0xdf,0x88,0xc4,0x02,0xe8,0xd3,0x00,0x00,0xe8,0xa6,0x64, +0x64,0x3d,0x42,0x79,0x29,0xa4,0x80,0xe1,0x00,0xe8,0x47,0xde,0xd3,0x27,0xc8,0x4f, +0xa6,0x67,0x02,0xe8,0x8f,0x8f,0x01,0xe0,0xfa,0xaf,0x00,0xe8,0x02,0xe8,0xcc,0x00, +0x00,0xe8,0xa6,0x64,0x64,0x3d,0x42,0x79,0x0f,0xe8,0x89,0xa7,0x89,0xbf,0x99,0x3f, +0x29,0xa4,0x80,0xe1,0x00,0xe8,0x31,0xde,0xff,0xe7,0xc8,0x4f,0x02,0xe8,0xbd,0x27, +0x01,0xe0,0xa6,0x67,0xf5,0xaf,0x8f,0x8f,0xb6,0x00,0xff,0xe7,0x42,0x79,0x00,0xe8, +0x20,0x7d,0x02,0xea,0xa6,0x67,0x02,0xe8,0x08,0x71,0x01,0xe8,0xaf,0x9c,0x0f,0xa4, +0x34,0xdc,0x15,0xdb,0x08,0x74,0x01,0xf8,0xa6,0x00,0xff,0xe7,0x42,0x79,0x00,0xe8, +0x08,0x71,0x01,0xe8,0xc7,0x4f,0x00,0xe8,0x8a,0xcf,0x00,0xe2,0x00,0xe8,0x9e,0x01, +0x01,0xe2,0xc6,0x4f,0x97,0x01,0x80,0xcf,0x71,0xde,0x62,0x3c,0x90,0x15,0x00,0xf0, +0x6b,0x3c,0x40,0xe8,0x40,0xe8,0x4d,0xde,0x81,0xc7,0xeb,0x3f,0xef,0x3d,0x40,0xf0, +0xff,0xc5,0x01,0xf2,0x9d,0xcd,0x00,0xf2,0x10,0xdc,0xf4,0x01,0x88,0x71,0x01,0xe8, +0x82,0x00,0xff,0xe7,0xfa,0x00,0x7c,0xde,0xc7,0x4f,0x00,0xe8,0x8b,0xcf,0x00,0xe2, +0x00,0xe8,0xa6,0x01,0x01,0xe2,0xc6,0x4f,0x91,0x01,0x80,0xcf,0x85,0x15,0x00,0xf0, +0x6b,0x3c,0x40,0xe8,0x40,0xe8,0x2d,0xde,0x81,0xc7,0xeb,0x3f,0xef,0x3d,0x40,0xf0, +0xff,0xc5,0x01,0xf2,0x9d,0xcd,0x00,0xf2,0xe0,0x00,0xf4,0x01,0x84,0x15,0x00,0xf0, +0x6b,0x3c,0x40,0xe8,0x40,0xe8,0x1d,0xde,0x81,0xc7,0xeb,0x3f,0xef,0x3d,0x40,0xf0, +0xff,0xc5,0x01,0xf2,0xc0,0xcd,0x00,0xf2,0xd0,0x00,0xf4,0x01,0xc7,0x4f,0x00,0xe8, +0x8c,0xcf,0x00,0xe2,0x62,0x3c,0x84,0x01,0xc8,0x00,0x0b,0xde,0xc7,0x4f,0x00,0xe8, +0x8d,0xcf,0x00,0xe2,0x04,0x14,0x85,0x01,0x1f,0x14,0x03,0xde,0x00,0xe8,0xf6,0x00, +0x40,0xf0,0xc7,0x4f,0x01,0xf4,0x6f,0x3e,0x1b,0xe0,0x00,0xce,0x02,0xe8,0xaf,0x3b, +0x0f,0xf8,0x13,0x4f,0x01,0xe2,0x1b,0x3e,0xfe,0x3f,0xff,0xc7,0xaf,0x03,0xff,0xe7, +0x6c,0x3c,0x40,0xe8,0x40,0xe8,0xed,0xdd,0x81,0xc7,0xec,0x3f,0x6f,0x3e,0x40,0xf0, +0x7f,0xc6,0x01,0xf2,0x00,0xe8,0xed,0x00,0x1d,0x14,0x42,0x79,0x08,0x71,0x01,0xe8, +0x00,0x15,0xd4,0x00,0x81,0x17,0x81,0x00,0x2d,0xe1,0x00,0x17,0x84,0x26,0x19,0x3c, +0x8e,0x3f,0x2d,0xe1,0x00,0x17,0x88,0x2e,0x00,0xe2,0x8c,0x2f,0x68,0xe0,0x00,0xcd, +0x82,0x38,0x2e,0x39,0x80,0xcc,0x00,0xe2,0xa1,0x34,0x77,0x04,0xee,0x00,0xa1,0x37, +0x83,0x04,0x79,0x3c,0x4f,0x3f,0x29,0x3c,0xc1,0x34,0xc1,0x37,0x00,0x00,0xee,0x00, +0x00,0xe2,0xe8,0x3d,0xab,0x05,0x03,0xcd,0x49,0x3c,0x0c,0xe0,0x03,0xc6,0x00,0xe2, +0x7c,0xc5,0x26,0x2e,0x21,0x35,0x0c,0xe0,0x18,0xc6,0x00,0xe2,0x99,0xa7,0x80,0xe1, +0x22,0xe0,0x06,0x26,0xac,0x3e,0xf0,0x16,0x82,0x3e,0x6f,0x3f,0x93,0x06,0x70,0xc5, +0x19,0xa7,0x80,0xe1,0x9b,0xbf,0x80,0xe1,0x99,0xa7,0x80,0xe1,0x1b,0xbf,0x80,0xe1, +0x19,0xa7,0x80,0xe1,0x9b,0xbf,0x80,0xe1,0x99,0xa7,0x80,0xe1,0x80,0xe1,0x70,0xc5, +0x6f,0x06,0x1b,0xbf,0x9b,0xbf,0x80,0xe1,0x03,0xc5,0x00,0xe2,0x87,0x06,0x7f,0xc5, +0x19,0x86,0x80,0xe1,0x80,0xe1,0x7f,0xc5,0x7b,0x06,0x1b,0xae,0x00,0x00,0x82,0x38, +0x03,0xcd,0x00,0xe2,0x01,0xe2,0xaf,0x05,0x0c,0xe0,0xff,0xc4,0xcc,0x3c,0xa8,0x34, +0xa0,0x34,0x0c,0xe4,0x41,0xe0,0xcc,0x3c,0x00,0xe2,0x48,0x3e,0x0b,0xe0,0x03,0xc6, +0x0d,0xe0,0x18,0x3e,0x6d,0x3e,0x2c,0x3d,0x0c,0xc6,0x00,0xe2,0x23,0xe0,0x05,0x26, +0xac,0x3f,0xdc,0x17,0xff,0xe1,0x82,0x3f,0x8b,0x06,0xf0,0xc6,0x9b,0xbc,0x80,0xe1, +0x9b,0xbc,0x80,0xe1,0x9b,0xbc,0x80,0xe1,0x80,0xe1,0xf0,0xc6,0x77,0x06,0x9b,0xbc, +0xc0,0xe0,0x18,0x3d,0x9a,0xac,0xba,0xac,0x88,0xac,0xaa,0xac,0xa8,0xac,0x98,0xac, +0xe8,0x3d,0x82,0x38,0x85,0x06,0x7f,0xc5,0x9b,0xac,0x80,0xe1,0x7d,0x06,0x7f,0xc5, +0xb1,0x08,0x82,0x38,0x87,0x3c,0x60,0xe0,0x80,0x7f,0x04,0xea,0x60,0x15,0x00,0xe0, +0x87,0xdf,0x6f,0x3c,0x17,0x3c,0x60,0xe0,0x00,0x00,0xe1,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xac,0x04,0x02,0x00,0x90,0x05,0x02,0x00, +0x70,0x05,0x02,0x00,0xce,0x07,0x00,0x00,0xcc,0x04,0x02,0x00,0x4e,0x05,0x00,0x00, +0xec,0x04,0x02,0x00,0x20,0x05,0x02,0x00,0x60,0x02,0x02,0x00,0x6c,0x03,0x02,0x00, +0x30,0x05,0x02,0x00,0x78,0x04,0x02,0x00,0xdc,0x05,0x00,0x00,0x9c,0x04,0x02,0x00, +0x0b,0x80,0x06,0x80,0x00,0x00,0x00,0x01,0x18,0x01,0x02,0x00,0x00,0x00,0x00,0x01, +0xff,0x03,0xc6,0x80,0x00,0x00,0x70,0x72,0x00,0x0d,0x00,0x80,0x00,0x09,0x00,0x80, +0x00,0x07,0x00,0x80,0x00,0x06,0x00,0x80,0x00,0x04,0x00,0x80,0x00,0x02,0x00,0x80, +0x00,0x01,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x80, +0x00,0x04,0x00,0x80,0x00,0x02,0x00,0x80, \ No newline at end of file