From 1533b6cd8cce6420f408cf8dc9ee5f50c8469c22 Mon Sep 17 00:00:00 2001 From: chenx Date: Mon, 6 Jan 2020 18:10:03 +0800 Subject: [PATCH 1/2] input: touchscreen: Add STMicroelectronics Touchscreen driver This is the reference source driver code from ST Microelectronics. Change-Id: I7319385daedf19761428fbd5542069d63f7192d5 Signed-off-by: chenx Git-commit: 0f965cac4d6bf76bcd7b6221228b814532e59463 Git-repo: https://source.codeaurora.org/external/thundersoft/ihvjointlab/sensor-driver/log/?h=STMicroelectronics_ctp_V4.1.0_2019 Signed-off-by: Fei Mao --- drivers/input/touchscreen/st/Kconfig | 29 + drivers/input/touchscreen/st/Makefile | 6 + drivers/input/touchscreen/st/fts.c | 5059 +++++++++++++++++ drivers/input/touchscreen/st/fts.h | 350 ++ drivers/input/touchscreen/st/fts_aoi_event.c | 102 + .../input/touchscreen/st/fts_driver_test.c | 1107 ++++ drivers/input/touchscreen/st/fts_gui.c | 412 ++ drivers/input/touchscreen/st/fts_lib/Makefile | 9 + .../touchscreen/st/fts_lib/ftsCompensation.c | 744 +++ .../touchscreen/st/fts_lib/ftsCompensation.h | 207 + .../touchscreen/st/fts_lib/ftsCrossCompile.c | 77 + .../touchscreen/st/fts_lib/ftsCrossCompile.h | 75 + .../input/touchscreen/st/fts_lib/ftsError.c | 262 + .../input/touchscreen/st/fts_lib/ftsError.h | 181 + .../input/touchscreen/st/fts_lib/ftsFlash.c | 1178 ++++ .../input/touchscreen/st/fts_lib/ftsFlash.h | 109 + .../input/touchscreen/st/fts_lib/ftsFrame.c | 519 ++ .../input/touchscreen/st/fts_lib/ftsFrame.h | 76 + .../input/touchscreen/st/fts_lib/ftsGesture.c | 651 +++ .../input/touchscreen/st/fts_lib/ftsGesture.h | 123 + .../touchscreen/st/fts_lib/ftsHardware.h | 213 + drivers/input/touchscreen/st/fts_lib/ftsIO.c | 526 ++ drivers/input/touchscreen/st/fts_lib/ftsIO.h | 64 + .../touchscreen/st/fts_lib/ftsSoftware.h | 191 + .../input/touchscreen/st/fts_lib/ftsTest.c | 3844 +++++++++++++ .../input/touchscreen/st/fts_lib/ftsTest.h | 193 + .../input/touchscreen/st/fts_lib/ftsTime.c | 109 + .../input/touchscreen/st/fts_lib/ftsTime.h | 54 + .../input/touchscreen/st/fts_lib/ftsTool.c | 1348 +++++ .../input/touchscreen/st/fts_lib/ftsTool.h | 107 + 30 files changed, 17925 insertions(+) create mode 100644 drivers/input/touchscreen/st/Kconfig create mode 100644 drivers/input/touchscreen/st/Makefile create mode 100644 drivers/input/touchscreen/st/fts.c create mode 100644 drivers/input/touchscreen/st/fts.h create mode 100644 drivers/input/touchscreen/st/fts_aoi_event.c create mode 100644 drivers/input/touchscreen/st/fts_driver_test.c create mode 100644 drivers/input/touchscreen/st/fts_gui.c create mode 100644 drivers/input/touchscreen/st/fts_lib/Makefile create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsCompensation.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsCompensation.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsError.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsError.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsFlash.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsFlash.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsFrame.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsFrame.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsGesture.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsGesture.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsHardware.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsIO.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsIO.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsSoftware.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsTest.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsTest.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsTime.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsTime.h create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsTool.c create mode 100644 drivers/input/touchscreen/st/fts_lib/ftsTool.h diff --git a/drivers/input/touchscreen/st/Kconfig b/drivers/input/touchscreen/st/Kconfig new file mode 100644 index 000000000000..8cd119717c18 --- /dev/null +++ b/drivers/input/touchscreen/st/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# STMicroelectronics touchscreen driver configuration +# + +config TOUCHSCREEN_ST + bool "STMicroelectronics Touchscreen Driver" + default n + depends on I2C + help + Say Y here if you have a STMicroelectronics Touchscreen. + + If unsure, say N. + To compile this driver as a module, choose M here: the + module will be called st. + + +#if TOUCHSCREEN_ST + +config TOUCHSCREEN_ST_I2C + #tristate "STMicroelectronics i2c touchscreen" + string "STMicroelectronics ts directory name" + default "st" + depends on TOUCHSCREEN_ST + help + This enables support for ST touch panel over I2C based touchscreens. + +#endif + diff --git a/drivers/input/touchscreen/st/Makefile b/drivers/input/touchscreen/st/Makefile new file mode 100644 index 000000000000..2764bdc2c509 --- /dev/null +++ b/drivers/input/touchscreen/st/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +## Makefile for the STMicroelectronics touchscreen driver. +# + +obj-$(CONFIG_TOUCHSCREEN_ST) += fts.o fts_gui.o fts_driver_test.o fts_aoi_event.o fts_lib/ diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c new file mode 100644 index 000000000000..28313a9b0c1f --- /dev/null +++ b/drivers/input/touchscreen/st/fts.c @@ -0,0 +1,5059 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include + +#include +#include +#include + +#if defined(CONFIG_FB_MSM) +#include +#include +#else +#include +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#include +#endif + +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsGesture.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" +#include "linux/moduleparam.h" + +#define LINK_KOBJ_NAME "tp" + +#define FTS_DVDD_VOL_MIN 1800000 +#define FTS_DVDD_VOL_MAX 1800000 +#define FTS_AVDD_VOL_MIN 3000000 +#define FTS_AVDD_VOL_MAX 3300000 + +/* + * Uncomment to use polling mode instead of interrupt mode. + * + */ +// #define FTS_USE_POLLING_MODE + +/* + * Event installer helpers + */ +#define event_id(_e) EVENTID_##_e +#define handler_name(_h) fts_##_h##_event_handler + +#define install_handler(_i, _evt, _hnd) \ + (_i->event_dispatch_table[event_id(_evt)].handler = handler_name(_hnd)) + +/* + * Asyncronouns command helper + */ +#define WAIT_WITH_TIMEOUT(_info, _timeout, _command) \ +do { \ + if (wait_for_completion_timeout(&_info->cmd_done, _timeout) == 0) { \ + dev_warn(_info->dev, "Waiting for %s command: timeout\n", \ + #_command); \ + } \ +} while (0) + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) +static struct class *fts_cmd_class; +#endif + +//struct chipInfo ftsInfo; + +/** + * #ifdef PHONE_GESTURE + * extern struct mutex gestureMask_mutex; + * #endif + */ + +static char tag[8] = "[ FTS ]\0"; + +static char fts_ts_phys[64]; +static u32 typeOfComand[CMD_STR_LEN] = {0}; +static int numberParameters; +#ifdef USE_ONE_FILE_NODE +static int feature_feasibility = ERROR_OP_NOT_ALLOW; +#endif +#ifdef PHONE_GESTURE +static u8 mask[GESTURE_MASK_SIZE + 2]; +//extern u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX]; +//extern u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX]; +//extern int gesture_coords_reported; +//extern struct mutex gestureMask_mutex; +#ifdef USE_CUSTOM_GESTURES +static int custom_gesture_res; +#endif +#endif +#ifdef USE_NOISE_PARAM +static u8 noise_params[NOISE_PARAMETERS_SIZE] = {0}; +#endif +static void fts_interrupt_enable(struct fts_ts_info *info); +static int fts_init_afterProbe(struct fts_ts_info *info); +static int fts_mode_handler(struct fts_ts_info *info, int force); +static int fts_command(struct fts_ts_info *info, unsigned char cmd); +static int fts_chip_initialization(struct fts_ts_info *info); +static int fts_enable_reg(struct fts_ts_info *info, bool enable); + +static struct drm_panel *active_panel; + +void touch_callback(unsigned int status) +{ + /* Empty */ +} + +unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[0] + (unsigned int) ptr[1] * 0x100; +} + +unsigned int be_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[1] + (unsigned int) ptr[0] * 0x100; +} + +void release_all_touches(struct fts_ts_info *info) +{ + unsigned int type = MT_TOOL_FINGER; + int i; + + for (i = 0; i < TOUCH_ID_MAX; i++) { +#ifdef STYLUS_MODE + if (test_bit(i, &info->stylus_id)) + type = MT_TOOL_PEN; +#endif + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, type, 0); + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + } + input_sync(info->input_dev); + info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif +} + +/************************* FW UPGGRADE *********************************/ +/* update firmware*/ +/** + * echo 01/00 > fwupdate perform a fw update taking the FW to burn always + * from a bin file stored in /system/etc/firmware, 01= force the FW update + * whicheve fw_version and config_id; 00=perform a fw update only if the fw + * in the file has a greater fw_version or config_id + */ + +/** + * cat fwupdate to show the result of the burning procedure + * (example output in the terminal = "AA00000001BB" if the switch is enabled) + */ + +/** + * echo 01/00 > fwupdate; cat fwupdate to perform both operation stated before + * in just one call + */ +static ssize_t fts_fwupdate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret, mode; + /*const struct firmware *fw = NULL;*/ + /*char *firmware_name = "st_fts.bin";*/ + struct Firmware fwD; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int orig_size; + u8 *orig_data; + + /* reading out firmware upgrade mode */ + ret = kstrtoint(buf, 10, &mode); + if (ret != 0) { + pr_err("%s: ret = %d\n", __func__, ret); + return -EINVAL; + } + + fwD.data = NULL; + ret = getFWdata_nocheck(PATH_FILE_FW, &orig_data, &orig_size, 0); + if (ret < OK) { + logError(1, "%s %s: impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto END; + } + + ret = parseBinFile(orig_data, orig_size, &fwD, !mode); + if (ret < OK) { + logError(1, "%s %s: impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto END; + } + + logError(0, "%s Starting flashing procedure...\n", tag); + ret = flash_burn(&fwD, mode, !mode); + + if (ret < OK && ret != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) + logError(0, "%s flashProcedure: ERROR %02X\n", + tag, ERROR_FLASH_PROCEDURE); + logError(0, "%s flashing procedure Finished!\n", tag); + +END: + kfree(fwD.data); + info->fwupdate_stat = ret; + + if (ret < OK) + logError(1, "%s %s Unable to upgrade firmware! ERROR %08X\n", + tag, __func__, ret); + + return count; +} + +static ssize_t fts_fwupdate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + //fwupdate_stat: ERROR code Returned by flashProcedure. + return snprintf(buf, PAGE_SIZE, "AA%08XBB\n", info->fwupdate_stat); +} + +/****UTILITIES (current fw_ver/conf_id, active mode, file fw_ver/conf_id)****/ +/** + * cat appid show on the terminal fw_version.config_id of + * the FW running in the IC + */ +static ssize_t fts_appid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int error; + + error = snprintf(buf, PAGE_SIZE, "%x.%x\n", ftsInfo.u16_fwVer, + ftsInfo.u16_cfgId); + return error; +} + +/** + * cat mode_active to show the bitmask of which indicate the modes/features + * which are running on the IC in a specific istant oftime (example output in + * the terminal = "AA10000000BB" only senseOn performed) + */ +static ssize_t fts_mode_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(1, "%s Current mode active = %08X\n", tag, info->mode); + //return sprintf(buf, "AA%08XBB\n", info->mode); + return snprintf(buf, PAGE_SIZE, "AA%08XBB\n", info->mode); +} + +/** + * cat fw_file_test show on the terminal fw_version and config_id of the FW + * stored in the fw file/header file + */ +static ssize_t fts_fw_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Firmware fw; + int ret; + + fw.data = NULL; + ret = readFwFile(PATH_FILE_FW, &fw, 0); + + if (ret < OK) + logError(1, "%s Error during reading FW file! ERROR %08X\n", + tag, ret); + else { + logError(1, "%s fw_version = %04X, config_version = %04X, ", + tag, fw.fw_ver, fw.config_id); + logError(1, "size = %dbytes\n", fw.data_size); + } + + kfree(fw.data); + return 0; +} + +/** + * cat lockdown_info to show the lockdown info on the terminal + * (example output in the terminal = "AA00000000X1X2..X10BB" ) + * where first 4 bytes correspond t + */ +static ssize_t fts_lockdown_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[LOCKDOWN_CODE_SIZE] = {0}; + int ret, size = 100; + char buff[CMD_STR_LEN] = {0}; + char all_strbuff[100] = {0}; + + ret = fts_disableInterrupt(); + if (ret < OK) + goto END; + + ret = lockDownInfo((u8 *)data, LOCKDOWN_CODE_SIZE); + if (ret < OK) + goto END; + +END: + ret |= fts_enableInterrupt(); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%08X", ret); + strlcat(all_strbuff, buff, size); + if (ret >= OK) { + for (ret = 0; ret < LOCKDOWN_CODE_SIZE; ret++) { + snprintf(buff, sizeof(buff), "%02X", data[ret]); + strlcat(all_strbuff, buff, size); + } + } else { + logError(1, "%s Error while reading lockdown info = %08X\n", + tag, ret); + } + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); +} + +/** + * cat strength_frame to obtain strength data + * the string returned in the shell is made up as follow: + * AA = start byte + * X1X2X3X4 = 4 bytes in HEX format which represent an + * error code (00000000 no error) + * + * if error code is all 0s + * FF = 1 byte in HEX format number of rows + * SS = 1 byte in HEX format number of columns + * N1, ... = the decimal value of each node separated by a coma + * + * BB = end byte + */ +static ssize_t fts_strength_frame_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + /*unsigned int temp;*/ + /*int res;*/ + /*struct i2c_client *client = to_i2c_client(dev); */ + /*struct fts_ts_info *info = i2c_get_clientdata(client); */ + + if (sscanf(p, "%x ", &typeOfComand[0]) != 1) + return -EINVAL; + + logError(1, "%s %s: Type of Strength Frame selected: %d\n", tag, + __func__, typeOfComand[0]); + return count; +} + +static ssize_t fts_strength_frame_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct MutualSenseFrame frame; + int res = ERROR_OP_NOT_ALLOW, j, size = 6*2; + int count = 0; + u16 type = 0; + char *all_strbuff = NULL; + char buff[CMD_STR_LEN] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + frame.node_data = NULL; + + res = fts_disableInterrupt(); + if (res < OK) + goto END; + + res = senseOn(); +#ifdef PHONE_KEY + res = keyOn(); +#endif + if (res < OK) { + logError(1, "%s %s: could not start scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + msleep(WAIT_FOR_FRESH_FRAMES); + + res = senseOff(); +#ifdef PHONE_KEY + res = keyOff(); +#endif + if (res < OK) { + logError(1, "%s %s: could not finish scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + /* mdelay(WAIT_AFTER_SENSEOFF); */ + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); + + switch (typeOfComand[0]) { + case 1: + type = ADDR_NORM_TOUCH; + break; +#ifdef PHONE_KEY + case 2: + type = ADDR_NORM_MS_KEY; + break; +#endif + default: + logError(1, "%s %s: Strength type %d not valid! ERROR %08X\n", + tag, __func__, typeOfComand[0], ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + res = getMSFrame(type, &frame, 0); + if (res < OK) { + logError(1, "%s %s: could not get the frame! ERROR %08X\n", + tag, __func__, res); + goto END; + } else { + size += (res * 6); + logError(0, "%s The frame size is %d words\n", tag, res); + res = OK; + print_frame_short("MS Strength frame =", + array1dTo2d_short(frame.node_data, + frame.node_data_size, + frame.header.sense_node), + frame.header.force_node, + frame.header.sense_node); + } + +END: + flushFIFO(); + release_all_touches(info); + fts_mode_handler(info, 1); + + all_strbuff = kmalloc_array(size, sizeof(char), GFP_KERNEL); + + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + snprintf(buff, sizeof(buff), "%02X", + (u8) frame.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frame.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frame.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%d,", + frame.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frame.node_data); + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s: Unable toallocate all_strbuff!ERROR %08X\n", + tag, ERROR_ALLOC); + } + + fts_enableInterrupt(); + return count; +} + +/********** FEATURES *********************/ + +/** + * TODO: edit this function according to the features policy to + * allow during the screen on/off, following is shown an example + * but check always with ST for more details + */ +int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) +{ + int res = OK; + + /** + * Example based on the status of the screen and + * on the feature that is trying to enable + */ + + /*Example based only on the feature that is going to be activated*/ + switch (feature) { + case FEAT_GESTURE: + if (info->cover_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + + logError(1, "%s %s:Feature not allowed when in Cover ", + tag, __func__); + logError(1, "mode %08X\n", res); + /** + * for example here can be place a code for + * disabling the cover mode when gesture is + * activated + */ + } + break; + + case FEAT_COVER: + if (info->gesture_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + /*logError(1,"Feature not allowed*/ + /*when Gestures enabled!");*/ + logError(1, "s %s: Feature not allowed when Gestures ", + tag, __func__); + logError(1, "enabled%08X\n", res); + /** + * for example here can be place a code for + * disabling the gesture mode when cover is + * activated (that means that cover mode has + * an higher priority on gesture mode) + */ + } + break; + + default: + logError(1, "%s %s: Feature Allowed!\n", tag, __func__); + } + return res; +} + +#ifdef USE_ONE_FILE_NODE +/** + * echo XXXX 00/01 > feature_enable + * set the feature to disable/enable. + * XXXX = 4 bytes which identify the feature + * + * cat feature_enable + * set the enabled mode/features in the IC + * and return an error code + * + * echo XXXX 00/01 > feature_enable; + * cat feature_enable to perform both action stated + * before in just one call + */ +static ssize_t fts_feature_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + char *p = (char *)buf; + unsigned int temp; + int res = OK; + + if ((count - 8 + 1) / 3 != 1) { + logError(1, "%s fts_feature_enable: ", tag); + logError(1, "Number of parameter wrong! %d > %d\n", + (count - 8 + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%08X ", &temp) != 1) + return -EINVAL; + p += 9; + res = check_feature_feasibility(info, temp); + if (res < OK) + return -EINVAL; + + switch (temp) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: + if (sscanf(p, "%02X ", &info->gesture_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Gesture Enabled = %d\n", + tag, info->gesture_enabled); + + break; +#endif + +#ifdef GLOVE_MODE + case FEAT_GLOVE: + if (sscanf(p, "%02X ", &info->glove_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Glove Enabled = %d\n", + tag, info->glove_enabled); + + break; +#endif + +#ifdef STYLUS_MODE + case FEAT_STYLUS: + if (sscanf(p, "%02X ", &info->stylus_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Stylus Enabled = %d\n", + tag, info->stylus_enabled); + + break; +#endif + +#ifdef COVER_MODE + case FEAT_COVER: + if (sscanf(p, "%02X ", &info->cover_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Cover Enabled = %d\n", + tag, info->cover_enabled); + + break; +#endif + +#ifdef CHARGER_MODE + case FEAT_CHARGER: + if (sscanf(p, "%02X ", &info->charger_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Charger Enabled= %d\n", + tag, info->charger_enabled); + + break; +#endif + +#ifdef VR_MODE + case FEAT_VR: + if (sscanf(p, "%02X ", &info->vr_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: VR Enabled = %d\n", + tag, info->vr_enabled); + + break; +#endif + +#ifdef EDGE_REJ + case FEAT_EDGE_REJECTION: + if (sscanf(p, "%02X ", &info->edge_rej_enabled) != 1) + return -EINVAL; + logError(1, "%s %s: Edge Rejection Enabled= %d\n", + tag, __func__, info->edge_rej_enabled); + + break; +#endif + +#ifdef CORNER_REJ + case FEAT_CORNER_REJECTION: + if (sscanf(p, "%02X ", &info->corner_rej_enabled) != 1) + return -EINVAL; + + logError(1, "%s %s: Corner Rejection Enabled= %d\n", + tag, __func__, info->corner_rej_enabled); + + break; +#endif + +#ifdef EDGE_PALM_REJ + case FEAT_EDGE_PALM_REJECTION: + if (sscanf(p, "%02X", &info->edge_palm_rej_enabled) != 1) + return -EINVAL; + + logError(1, "%s %s:Edge Palm RejectionEnabled= %d\n", + tag, __func__, info->edge_palm_rej_enabled); + + break; +#endif + default: + logError(1, "%s %s: Feature %08X not valid! ERROR %08X\n", + tag, __func__, temp, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + feature_feasibility = res; + + if (feature_feasibility >= OK) + feature_feasibility = fts_mode_handler(info, 1); + else { + logError(1, "%s %s: Call echo XXXX 00/01 > feature_enable ", + tag, __func__); + logError(1, "with a correct feature! ERROR %08X\n", res); + } + return count; +} + +static ssize_t fts_feature_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + if (feature_feasibility < OK) { + logError(1, + "%s %s:Call before echo 00/01 > feature_enable %08X\n", + tag, __func__, feature_feasibility); + } + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", feature_feasibility); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + feature_feasibility = ERROR_OP_NOT_ALLOW; + return count; +} + +#else + +#ifdef EDGE_REJ +/** + * echo 01/00 > edge_rej to enable/disable edge rejection + * cat edge_rej to show the status of the edge_rej_enabled + * switch (example output in the terminal = "AA00000001BB" + * if the switch is enabled) + * + * echo 01/00 > edge_rej; cat edge_rej to enable/disable + * edge rejection and see the switch status in just one call + */ +static ssize_t fts_edge_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: edge_rej_enabled = %d\n", + tag, __func__, info->edge_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->edge_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + + return count; +} + +static ssize_t fts_edge_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_EDGE_REJECTION); + if (res < OK && temp != FEAT_DISABLE) + return -EINVAL; + + info->edge_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef CORNER_REJ +/** + * echo 01/00 > corner_rej to enable/disable corner rejection + * cat corner_rej to show the status of the + * corner_rej_enabled switch (example output in the terminal + * = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > corner_rej; cat corner_rej to enable/disable + * corner rejection and see the switch status in just one call + */ +static ssize_t fts_corner_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: corner_rej_enabled = %d\n", + tag, __func__, info->corner_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->corner_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s%s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_corner_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature + * can be enabled + * + * second step: call fts_mode_handler to + * actually enable it + * + * NOTE: Disabling a feature is always + * allowed by default + */ + res = check_feature_feasibility(info, FEAT_CORNER_REJECTION); + if (res >= OK || temp == FEAT_DISABLE) { + info->corner_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s: During fts_mode_handler!ERROR %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef EDGE_PALM_REJ +/** + * echo 01/00 > edge_palm_rej + * to enable/disable edge palm rejection + * + * cat edge_palm_rej to show the status of the + * edge_palm_rej_enabled switch (example output + * in the terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > edge_palm_rej; cat edge_palm_rej + * to enable/disable edge palm rejection and see + * the switch status in just one call + */ +static ssize_t fts_edge_palm_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: edge_palm_rej_enabled = %d\n", + tag, __func__, info->edge_palm_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", + info->edge_palm_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", + all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s%s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_edge_palm_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s%s:Number bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_EDGE_PALM_REJECTION); + if (res >= OK || temp == FEAT_DISABLE) { + info->edge_palm_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s%s:Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef CHARGER_MODE +/** + * echo 01/00 > charger_mode to enable/disable charger mode + * + * cat charger_mode to show the status of + * the charger_enabled switch (example output in the terminal + * = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > charger_mode; cat charger_mode + * to enable/disable charger mode and see the + * switch status in just one call + */ +static ssize_t fts_charger_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s:charger_enabled = %d\n", + tag, __func__, info->charger_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->charger_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_charger_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature + * can be enabled + * second step: call fts_mode_handler to + * actually enable it + * + * NOTE: Disabling a feature is always + * allowed by default + */ + res = check_feature_feasibility(info, FEAT_CHARGER); + if (res >= OK || temp == FEAT_DISABLE) { + info->charger_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error during fts_mode_handler! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } + } + + return count; +} +#endif + +#ifdef GLOVE_MODE +/** + * echo 01/00 > glove_mode + * to enable/disable glove mode + * + * cat glove_mode to show the status of + * the glove_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > glove_mode; cat glove_mode + * to enable/disable glove mode and see the + * switch status in just one call + */ +static ssize_t fts_glove_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s:glove_enabled = %d\n", + tag, __func__, info->glove_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->glove_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_glove_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_GLOVE); + if (res >= OK || temp == FEAT_DISABLE) { + info->glove_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error during fts_mode_handler! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } + } + + return count; +} +#endif + +#ifdef VR_MODE +/** + * echo 01/00 > vr_mode to enable/disable vr mode + * + * cat vr_mode to show the status of + * the vr_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > vr_mode; cat vr_mode to enable/disable + * vr mode and see the switch status in just one call + */ +static ssize_t fts_vr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: vr_enabled = %d\n", + tag, __func__, info->vr_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->vr_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_vr_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_VR); + if (res >= OK || temp == FEAT_DISABLE) { + info->vr_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef COVER_MODE +/** + * echo 01/00 > cover_mode to enable/disable cover mode + * cat cover_mode to show the status of the + * cover_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > cover_mode; cat cover_mode to + * enable/disable cover mode and see the switch + * status in just one call + * + * NOTE: the cover can be handled also using a notifier, + * in this case the body of these functions + * should be copied in the notifier callback + */ +static ssize_t fts_cover_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: cover_enabled = %d\n", + tag, __func__, info->cover_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->cover_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_cover_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_COVER); + if (res >= OK || temp == FEAT_DISABLE) { + info->cover_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s%s:Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + + return count; +} +#endif + +#ifdef STYLUS_MODE +/** + * echo 01/00 > stylus_mode to enable/disable stylus mode + * cat stylus_mode to show the status of + * the stylus_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > stylus_mode; cat stylus_mode to + * enable/disable stylus mode and see the + * switch status in just one call + */ +static ssize_t fts_stylus_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: stylus_enabled = %d\n", + tag, __func__, info->stylus_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->stylus_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_stylus_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_STYLUS); + if (res >= OK || temp == FEAT_DISABLE) { + info->stylus_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s:Error during fts_mode_handler! %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#endif + +/************** GESTURES *************/ +#ifdef PHONE_GESTURE +#ifdef USE_GESTURE_MASK + +/** + * if this define is used, a gesture bit mask + * is used as method to select the gestures + * to enable/disable + */ + +/** + * echo EE X1 X2 ... X8 > gesture_mask set + * the gesture mask to disable/enable; + * EE = 00(disable) or 01(enable); + * X1 ... X8 = gesture mask (example 06 00 ... 00 + * this gesture mask represent the gestures with ID = 1 and 2) + * can be specified from 1 to 8 bytes, if less than 8 bytes + * are specified the remaining bytes are kept as previous settings + * + * cat gesture_mask enable/disable the given mask, + * if one or more gestures is enabled the driver will + * automatically enable the gesture mode. + * If all the gestures are disabled the driver + * automatically will disable the gesture mode. + * At the end an error code will be printed + * (example output in the terminal = "AA00000000BB" + * if there are no errors) + * + * echo EE X1 X2 ... X8 > gesture_mask; + * cat gesture_mask perform in one + * command both actions stated before + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res, temp; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s:Call before echo enable/disable xx xx >", + tag, __func__); + logError(1, "%s %s: gesture_mask with a correct number of ", + tag, __func__); + logError(1, "parameters! ERROR %08X\n", res); + return -EINVAL; + } + + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) { + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", + tag, res); + } + + res |= check_feature_feasibility(info, FEAT_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + + logError(1, "%s fts_gesture_mask_store:Gesture Enabled = %d\n", + tag, info->gesture_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + mask[0] = 0; + return count; +} + + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + + if ((count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, "%s %s: Number of bytes of parameter wrong! ", tag, + __func__); + logError(1, "%d > (enable/disable + %d )\n", (count + 1) / 3, + GESTURE_MASK_SIZE); + mask[0] = 0; + return -EINVAL; + } + mask[0] = ((count + 1) / 3) - 1; + for (n = 1; n <= (count + 1) / 3; n++) { + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + mask[n] = (u8)temp; + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + } + + return count; +} + +#else + +/** + * if this define is not used, + * to select the gestures to enable/disable + * are used the IDs of the gestures + * + * echo EE X1 X2 ... > gesture_mask set + * the gesture to disable/enable; EE = 00(disable) + * or 01(enable); X1 ... = gesture IDs + * (example 01 02 05... represent the gestures with + * ID = 1, 2 and 5) there is no limit of the parameters + * that can be passed, but of course the gesture IDs + * should be valid (all the valid IDs are listed + * in ftsGesture.h) + * + * cat gesture_mask enable/disable the + * given gestures, if one or more gestures is enabled + * the driver will automatically enable the gesture mode. + * If all the gestures are disabled the driver automatically + * will disable the gesture mode. At the end an error code + * will be printed (example output in the terminal = + * "AA00000000BB" if there are no errors) + * + * echo EE X1 X2 ... > gesture_mask; cat gesture_mask + * perform in one command both actions stated before + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: gesture_enabled = %d\n", tag, __func__, + info->gesture_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->gesture_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if ((count + 1) / 3 < 2 || (count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, + "%s %s:Number bytes of parameter wrong! %d %d bytes)\n", + tag, __func__, (count + 1) / 3, GESTURE_MASK_SIZE); + mask[0] = 0; + return -EINVAL; + } + + memset(mask, 0, GESTURE_MASK_SIZE + 2); + mask[0] = ((count + 1) / 3) - 1; + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + mask[1] = (u8)temp; + for (n = 1; n < (count + 1) / 3; n++) { + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + gestureIDtoGestureMask((u8)temp, &mask[2]); + } + + for (n = 0; n < GESTURE_MASK_SIZE + 2; n++) + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s: Call before echo enable/disable xx xx ....", + tag, __func__); + logError(1, " > gesture_mask with parameters! ERROR %08X\n", + res); + + } else { + + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) + logError(1, "%s %s: ERROR %08X\n", tag, __func__, res); + + } + + res = check_feature_feasibility(info, FEAT_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + res = fts_mode_handler(info, 0); + + return count; +} +#endif + +#ifdef USE_CUSTOM_GESTURES +/** + * allow to use user defined gestures + * + * echo ID X1 Y1 X2 Y2 ... X30 Y30 > + * add_custom_gesture add a custom gesture; + * ID = 1 byte that represent the gesture ID of + * the custom gesture (can be chosen only between + * the custom IDs defined in ftsGesture.h); + * X1 Y1 ... = a series of 30 points (x,y) which + * represent the gesture template. + * The loaded gesture is enabled automatically + * + * cat add_custom_gesture/remove_custom_gesture + * Print the error code of the last operation + * performed with the custom gestures + * (example output in the terminal = "AA00000000BB" + * if there are no errors) + * + * echo ID X1 Y1 X2 Y2 ... X30 Y30 > + * add_custom_gesture; cat add_custom_gesture + * perform in one command both actions stated before + */ +static ssize_t fts_add_custom_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + logError(0, "%s %s:Last Operation Result = %08X\n", + tag, __func__, custom_gesture_res); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", custom_gesture_res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_add_custom_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + u8 gestureID; + u8 gestMask[GESTURE_MASK_SIZE] = {0}; + u8 template[GESTURE_CUSTOM_POINTS]; + int res; + /*struct i2c_client *client = to_i2c_client(dev);*/ + /*struct fts_ts_info *info = i2c_get_clientdata(client);*/ + + if ((count + 1) / 3 != GESTURE_CUSTOM_POINTS + 1) { + logError(1, + "%s %s: Number bytes of parameter wrong! %d != %d\n", + tag, __func__, (count + 1) / 3, + GESTURE_CUSTOM_POINTS + 1); + res = ERROR_OP_NOT_ALLOW; + return -EINVAL; + } + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + gestureID = (u8)temp; + + for (n = 1; n < (count + 1) / 3; n++) { + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + template[n-1] = (u8)temp; + logError(1, "%s template[%d] = %02X\n", + tag, n-1, template[n-1]); + } + + res = fts_disableInterrupt(); + if (res >= OK) { + logError(1, "%s %s: Adding custom gesture ID = %02X\n", + tag, __func__, gestureID); + res = addCustomGesture(template, + GESTURE_CUSTOM_POINTS, gestureID); + if (res < OK) { + logError(1, + "%s %s:error during add custom gesture ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } else { + logError(1, + "%s %s:Enabling in the gesture mask...\n", + tag, __func__); + gestureIDtoGestureMask(gestureID, gestMask); + res = enableGesture(gestMask, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s %s:error during enable gesture", + tag, __func__); + logError(1, " mask: ERROR %08X\n", res); + } else { + /*if(check_feature_feasibility(info,*/ + /*FEAT_GESTURE)==OK)*/ + /*info->gesture_enabled =*/ + /*isAnyGestureActive();*/ + /*uncomment if you want to activate*/ + /* automatically*/ + /*the gesture mode when a custom gesture*/ + /*is loaded*/ + logError(1, "%s %s:Custom Gesture enabled!\n", + tag, __func__, res); + } + } + } + res |= fts_enableInterrupt(); + + custom_gesture_res = res; + + return count; +} + + +/** + * echo ID > remove_custom_gesture + * remove a custom gesture; + * ID = 1 byte that represent the gesture ID + * of the custom gesture (can be chosen only + * between the custom IDs defined in ftsGesture.h); + * the same gesture is disabled automatically + */ +static ssize_t fts_remove_custom_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + logError(0, "%s %s:Last Operation Result = %08X\n", + tag, __func__, custom_gesture_res); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", custom_gesture_res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_remove_custom_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + u8 gestureID; + u8 gestMask[GESTURE_MASK_SIZE] = {0}; + /*struct i2c_client *client = to_i2c_client(dev);*/ + /*struct fts_ts_info *info = i2c_get_clientdata(client);*/ + + if ((count + 1) / 3 < 1) { + logError(1, + "%s %s:Number bytes of parameter wrong! %d != %d\n", + tag, __func__, (count + 1) / 3, 1); + res = ERROR_OP_NOT_ALLOW; + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + gestureID = (u8)temp; + res = fts_disableInterrupt(); + if (res >= OK) { + logError(1, + "%s %s: Removing custom gesture ID = %02X\n", + tag, __func__, gestureID); + res = removeCustomGesture(gestureID); + if (res < OK) { + logError(1, + "%s %s:error in custom gesture:%08X\n", + tag, __func__, res); + } else { + logError(1, "%s %s: Enabling in the gesture mask...\n", + tag, __func__); + gestureIDtoGestureMask(gestureID, gestMask); + res = disableGesture(gestMask, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, + "%s %s:error in enable gesture mask:%08X\n", + tag, __func__, res); + } else { + /*if(check_feature_feasibility*/ + /*(info,FEAT_GESTURE)==OK)*/ + /*info->gesture_enabled = */ + /*isAnyGestureActive();*/ + /** + * uncomment if you want to disable + * automatically + * the gesture mode when a custom gesture is + * removed and no other gestures were enabled + */ + logError(1, "%s %s: Custom Gesture disabled!\n", + tag, __func__, res); + } + + } + } + + res |= fts_enableInterrupt(); + + custom_gesture_res = res; + return count; +} +#endif + + +/** + * cat gesture_coordinates to obtain the gesture coordinates + * the string returned in the shell follow this up as follow: + * AA = start byte + * X1X2X3X4 = 4 bytes in HEX format + * which represent an error code (00000000 no error) + */ + /**** if error code is all 0s ****/ +/** + * CC = 1 byte in HEX format number of coords + * (pair of x,y) returned + * + * X1X2 Y1Y2 ... = X1X2 2 bytes in HEX format for + * x[i] and Y1Y2 2 bytes in HEX format for y[i] (MSB first) + */ +/********************************/ +/* BB = end byte*/ +static ssize_t fts_gesture_coordinates_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + //u8 coords_num; + u8 *all_strbuff = NULL; + int count = 0, res, i = 0; + + logError(0, "%s %s: Getting gestures coordinates...\n", tag, __func__); + + if (gesture_coords_reported < OK) { + logError(1, "%s %s:invalid coordinates! ERROR %08X\n", + tag, __func__, gesture_coords_reported); + res = gesture_coords_reported; + } else { + /*coords are pairs of x,y (*2) where each coord*/ + /*is a short(2bytes=4char)(*4) + 1 byte(2char) num*/ + /*of coords (+2)*/ + size += gesture_coords_reported * 2 * 4 + 2; + /*coords_num = res;*/ + res = OK; + /*set error code to OK*/ + } + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + snprintf(buff, sizeof(buff), "%02X", + gesture_coords_reported); + strlcat(all_strbuff, buff, size); + + for (i = 0; i < gesture_coords_reported; i++) { + snprintf(buff, sizeof(buff), "%04X", + gesture_coordinates_x[i]); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%04X", + gesture_coordinates_y[i]); + strlcat(all_strbuff, buff, size); + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_gesture_coordinates_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return 0; +} +#endif + +/***************** PRODUCTION TEST ****************/ +static ssize_t fts_stm_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int n; + char *p = (char *) buf; + + memset(typeOfComand, 0, CMD_STR_LEN * sizeof(u32)); + + logError(1, "%s\n", tag); + for (n = 0; n < (count + 1) / 3; n++) { + + if (sscanf(p, "%02X ", &typeOfComand[n]) != 1) + return -EINVAL; + p += 3; + logError(1, "%s typeOfComand[%d] = %02X\n", + tag, n, typeOfComand[n]); + + } + + numberParameters = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); + return count; +} + +static ssize_t fts_stm_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int res, j, doClean = 0, count; + + int size = 6 * 2; + u8 *all_strbuff = NULL; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + struct MutualSenseData compData = {0}; + struct SelfSenseData comData = {0}; + struct MutualSenseFrame frameMS = {0}; + struct SelfSenseFrame frameSS = {0}; + + /** + * struct used for defining which test + * perform during the production test + */ + struct TestToDo todoDefault; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + + if (numberParameters >= 1) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s fts_disableInterrupt: ERROR %08X\n", + tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + +#if defined(CONFIG_FB_MSM) + res = fb_unregister_client(&info->notifier); +#else + if (active_panel) + res = drm_panel_notifier_unregister(active_panel, + &info->notifier); +#endif + if (res < 0) { + logError(1, "%s ERROR: unregister notifier failed!\n", + tag); + goto END; + } + + switch (typeOfComand[0]) { + /*ITO TEST*/ + case 0x01: + res = production_test_ito(); + break; + /*PRODUCTION TEST*/ + case 0x00: + if (ftsInfo.u32_mpPassFlag != INIT_MP) { + logError(0, "%s MP Flag not set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 1, + &todoDefault, INIT_MP); + } else { + logError(0, "%s MP Flag set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 0, + &todoDefault, INIT_MP); + } + break; + /*read mutual raw*/ + case 0x13: + logError(0, "%s Get 1 MS Frame\n", tag); + //res = getMSFrame(ADDR_RAW_TOUCH, &frame, 0); + res = getMSFrame2(MS_TOUCH_ACTIVE, &frameMS); + if (res < 0) { + logError(0, + "%s Error in taking MS frame.%02X\n", + tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2; + /* set res to OK because if getMSFrame is*/ + /* successful res = number of words read*/ + res = OK; + print_frame_short("MS frame =", + array1dTo2d_short(frameMS.node_data, + frameMS.node_data_size, + frameMS.header.sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); + } + break; + /*read self raw*/ + case 0x15: + logError(0, "%s Get 1 SS Frame\n", tag); + res = getSSFrame2(SS_TOUCH, &frameSS); + + if (res < OK) { + logError(0, + "%s Error while taking the SS frame%02X\n", + tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2 + 1; + /** + * set res to OK because if getMSFrame is + * successful res = number of words read + */ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short(frameSS.force_data, + frameSS.header.force_node, 1), + frameSS.header.force_node, 1); + print_frame_short("SS sense frame =", + array1dTo2d_short(frameSS.sense_data, + frameSS.header.sense_node, + frameSS.header.sense_node), + 1, + frameSS.header.sense_node); + } + break; + + /*read mutual comp data*/ + case 0x14: + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, + &compData); + + if (res < 0) { + logError(0, + "%s Error MS compensation data%02X\n", + tag, res); + } else { + logError(0, + "%s MS Data Reading Finished!\n", + tag); + size = ((compData.node_data_size + 9) * + sizeof(u8)) * 2; + print_frame_u8("MS Data (Cx2) =", + array1dTo2d_u8(compData.node_data, + compData.node_data_size, + compData.header.sense_node), + compData.header.force_node, + compData.header.sense_node); + } + break; + + /*read self comp data*/ + case 0x16: + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData(SS_TOUCH, &comData); + if (res < 0) { + logError(0, "%s Error reading SS data%02X\n", + tag, res); + } else { + logError(0, "%s SS Data Reading Finished!\n", + tag); + size = ((comData.header.force_node + + comData.header.sense_node) * 2 + 12); + size *= sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header.force_node, 1), + comData.header.force_node, + 1); + print_frame_u8("SS Data Cx2_fm = ", + array1dTo2d_u8(comData.cx2_fm, + comData.header.force_node, 1), + comData.header.force_node, + 1); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", + array1dTo2d_u8(comData.cx2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + } + break; + + /* MS Raw DATA TEST */ + case 0x03: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_raw(LIMITS_FILE, + 1, &todoDefault); + break; + /* MS CX DATA TEST */ + case 0x04: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_cx(LIMITS_FILE, + 1, &todoDefault); + break; + /* SS RAW DATA TEST */ + case 0x05: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_raw(LIMITS_FILE, + 1, &todoDefault); + break; + /* SS IX CX DATA TEST */ + case 0x06: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_ix_cx(LIMITS_FILE, + 1, &todoDefault); + break; + + case 0xF0: + /* TOUCH ENABLE/DISABLE */ + case 0xF1: + doClean = (int) (typeOfComand[0] & 0x01); + res = cleanUp(doClean); + break; + + default: + logError(1, + "%s COMMAND NOT VALID!! Insert a proper value\n", + tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + doClean = fts_enableInterrupt(); + if (doClean < 0) { + logError(0, "%s fts_enableInterrupt: ERROR %08X\n", + tag, (doClean|ERROR_ENABLE_INTER)); + } + } else { + logError(1, "%s NO COMMAND SPECIFIED!!!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + +#if defined(CONFIG_FB_MSM) + if (fb_register_client(&info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#else + if (active_panel && + drm_panel_notifier_register(active_panel, &info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#endif + +END: + /*here start the reporting phase,*/ + /* assembling the data to send in the file node */ + all_strbuff = kmalloc(size, GFP_KERNEL); + if (!all_strbuff) + return 0; + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, 8); + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (typeOfComand[0]) { + case 0x13: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, 2); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameMS.node_data[j]); + strlcat(all_strbuff, buff, 4); + } + + kfree(frameMS.node_data); + break; + + case 0x15: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, 2); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.force_data[j]); + strlcat(all_strbuff, buff, 4); + } + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.sense_data[j]); + strlcat(all_strbuff, buff, 4); + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case 0x14: + snprintf(buff, sizeof(buff), "%02X", + (u8) compData.header.force_node); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", + (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, 2); + + /* Cpying CX1 value */ + snprintf(buff, sizeof(buff), "%02X", compData.cx1); + strlcat(all_strbuff, buff, 2); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%02X", + *(compData.node_data + j)); + strlcat(all_strbuff, buff, 2); + } + + kfree(compData.node_data); + break; + + case 0x16: + snprintf(buff, sizeof(buff), "%02X", + comData.header.force_node); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", + comData.header.sense_node); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, 2); + + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, 2); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_sn[j]); + strlcat(all_strbuff, buff, 2); + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_fm[j]); + strlcat(all_strbuff, buff, 2); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_sn[j]); + strlcat(all_strbuff, buff, 2); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + default: + break; + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, 2); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + /** + * need to reset the number of parameters + * in order to wait the next command, + * comment if you want to repeat + * the last command sent just doing a cat + */ + numberParameters = 0; + /* logError(0,"%s numberParameters = %d\n",tag, numberParameters);*/ + kfree(all_strbuff); + + return count; +} + +static DEVICE_ATTR_RW(fts_fwupdate); +static DEVICE_ATTR_RO(fts_appid); +static DEVICE_ATTR_RO(fts_mode_active); +static DEVICE_ATTR_RO(fts_lockdown_info); +static DEVICE_ATTR_RW(fts_strength_frame); +static DEVICE_ATTR_RO(fts_fw_test); +static DEVICE_ATTR_RW(fts_stm_cmd); +#ifdef USE_ONE_FILE_NODE +static DEVICE_ATTR_RW(fts_feature_enable); +#else + +#ifdef EDGE_REJ +static DEVICE_ATTR_RW(fts_edge_rej); +#endif + +#ifdef CORNER_REJ +static DEVICE_ATTR_RW(fts_corner_rej); +#endif + +#ifdef EDGE_PALM_REJ +static DEVICE_ATTR_RW(fts_edge_palm_rej); +#endif + +#ifdef CHARGER_MODE +static DEVICE_ATTR_RW(fts_charger_mode); +#endif + +#ifdef GLOVE_MODE +static DEVICE_ATTR_RW(fts_glove_mode); +#endif + +#ifdef VR_MODE +static DEVICE_ATTR_RW(fts_vr_mode); +#endif + +#ifdef COVER_MODE +static DEVICE_ATTR_RW(fts_cover_mode); +#endif + +#ifdef STYLUS_MODE +static DEVICE_ATTR_RW(fts_stylus_mode); +#endif + +#endif + +#ifdef PHONE_GESTURE +static DEVICE_ATTR_RW(fts_gesture_mask); +static DEVICE_ATTR_RW(fts_gesture_coordinates); +#ifdef USE_CUSTOM_GESTURES +static DEVICE_ATTR_RW(fts_add_custom_gesture); +static DEVICE_ATTR_RW(fts_remove_custom_gesture); +#endif +#endif + +/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ +static struct attribute *fts_attr_group[] = { + &dev_attr_fts_fwupdate.attr, + &dev_attr_fts_appid.attr, + &dev_attr_fts_mode_active.attr, + &dev_attr_fts_lockdown_info.attr, + &dev_attr_fts_strength_frame.attr, + &dev_attr_fts_fw_test.attr, + &dev_attr_fts_stm_cmd.attr, +#ifdef USE_ONE_FILE_NODE + &dev_attr_fts_feature_enable.attr, +#else + +#ifdef EDGE_REJ + &dev_attr_fts_edge_rej.attr, +#endif +#ifdef CORNER_REJ + &dev_attr_fts_corner_rej.attr, +#endif +#ifdef EDGE_PALM_REJ + &dev_attr_fts_edge_palm_rej.attr, +#endif +#ifdef CHARGER_MODE + &dev_attr_fts_charger_mode.attr, +#endif +#ifdef GLOVE_MODE + &dev_attr_fts_glove_mode.attr, +#endif +#ifdef VR_MODE + &dev_attr_fts_vr_mode.attr, +#endif +#ifdef COVER_MODE + &dev_attr_fts_cover_mode.attr, +#endif +#ifdef STYLUS_MODE + &dev_attr_fts_stylus_mode.attr, +#endif + +#endif + +#ifdef PHONE_GESTURE + &dev_attr_fts_gesture_mask.attr, + &dev_attr_fts_gesture_coordinates.attr, +#ifdef USE_CUSTOM_GESTURES + &dev_attr_fts_add_custom_gesture.attr, + &dev_attr_fts_remove_custom_gesture.attr, +#endif + +#endif + NULL, +}; + +static int fts_command(struct fts_ts_info *info, unsigned char cmd) +{ + unsigned char regAdd; + int ret; + + regAdd = cmd; + + ret = fts_writeCmd(®Add, sizeof(regAdd)); /* 0 = ok */ + + logError(0, "%s Issued command 0x%02x, return value %08X\n", cmd, ret); + + return ret; +} + +void fts_input_report_key(struct fts_ts_info *info, int key_code) +{ + mutex_lock(&info->input_report_mutex); + input_report_key(info->input_dev, key_code, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, key_code, 0); + input_sync(info->input_dev); + mutex_unlock(&info->input_report_mutex); +} + +/* + * New Interrupt handle implementation + */ +static inline unsigned char *fts_next_event(unsigned char *evt) +{ + /* Nothing to do with this event, moving to the next one */ + evt += FIFO_EVENT_SIZE; + + /* the previous one was the last event ? */ + return (evt[-1] & 0x1F) ? evt : NULL; +} + +/* EventId : 0x00 */ +static void fts_nop_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + /** + * logError(1, + * "%s %s Doing nothing for event = + * %02X %02X %02X %02X %02X %02X %02X %02X\n", + * tag, __func__, event[0], event[1], event[2], + * event[3], event[4], event[5], event[6], event[7]); + */ + /* return fts_next_event(event); */ +} + +/* EventId : 0x03 */ +static void fts_enter_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + unsigned char touchId, touchcount; + int x, y; + int minor; + int major, distance; + u8 touchsize; + + distance = 0; + if (!info->resume_bit) + goto no_report; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + touchsize = (event[5] & 0xC0) >> 6; + major = (event[5] & 0x1F); // bit0-bit4: major + minor = event[6]; // event6:minor + + __set_bit(touchId, &info->touch_id); + + x = (event[2] << 4) | (event[4] & 0xF0) >> 4; + y = (event[3] << 4) | (event[4] & 0x0F); + + if (info->bdata->x_flip) + x = X_AXIS_MAX - x; + if (info->bdata->y_flip) + y = Y_AXIS_MAX - y; + + if (x == X_AXIS_MAX) + x--; + + if (y == Y_AXIS_MAX) + y--; + + input_mt_slot(info->input_dev, touchId); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1); + + if (touchcount == 1) { + input_report_key(info->input_dev, BTN_TOUCH, 1); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 1); + } + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, minor); + input_report_abs(info->input_dev, ABS_MT_DISTANCE, distance); + +no_report: + return; + +} + +/* EventId : 0x04 */ +static void fts_leave_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + unsigned char touchId, touchcount; + u8 touchsize; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + touchsize = (event[5] & 0xC0) >> 6; + + input_mt_slot(info->input_dev, touchId); + + __clear_bit(touchId, &info->touch_id); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); + + if (touchcount == 0) { + input_report_key(info->input_dev, BTN_TOUCH, 0); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 0); + } + + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + +} + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +#ifdef PHONE_KEY +/* EventId : 0x0E */ +static void fts_key_status_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int value; + + logError(0, + "%s %sReceived event %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + /* + * TODO: the customer should handle the events coming + * from the keys according his needs (this is an example + * that report only the single pressure of one key at time) + */ + /* event[2] contain the bitmask of the keys that are actually pressed */ + if (event[2] != 0) { + switch (event[2]) { + case KEY1: + value = KEY_HOMEPAGE; + logError(0, "%s %s: Button HOME!\n", tag, __func__); + break; + + case KEY2: + value = KEY_BACK; + logError(0, "%s %s: Button Back !\n", tag, __func__); + break; + + case KEY3: + value = KEY_MENU; + logError(0, "%s %s: Button Menu !\n", tag, __func__); + break; + + default: + logError(0, + "%s %s:No valid Button ID or more than one key pressed!\n", + tag, __func__); + return; + } + + fts_input_report_key(info, value); + } else { + logError(0, "%s %s: All buttons released!\n", tag, __func__); + } +} +#endif + +/* EventId : 0x0F */ +static void fts_error_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int error = 0; + + logError(0, + "%s %sReceived event:%02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: /* esd */ + /* before reset clear all slot */ + release_all_touches(info); + + fts_chip_powercycle(info); + + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, + "%s %s Cannot restore the device ERROR %08X\n", + tag, __func__, error); + } + break; + case EVENT_TYPE_WATCHDOG_ERROR: /* watch dog timer */ + /* if (event[2] == 0) { */ + dumpErrorInfo(); + /* before reset clear all slot */ + release_all_touches(info); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, + "%s %s Cannot reset the device ERROR %08X\n", + tag, __func__, error); + } + /* } */ + break; +} + /* return fts_next_event(event); */ +} + +/* EventId : 0x10 */ +static void fts_controller_ready_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int error; + + logError(0, "%s %s Received event 0x%02x\n", tag, __func__, event[0]); + release_all_touches(info); + setSystemResettedUp(1); + setSystemResettedDown(1); + error = fts_mode_handler(info, 0); + if (error < OK) { + logError(1, + "%s %s Cannot restore the device status ERROR %08X\n", + tag, __func__, error); + } + /* return fts_next_event(event); */ +} + +/* EventId : 0x16 */ +static void fts_status_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + /* logError(1, "%s Received event 0x%02x\n", tag, event[0]); */ + + switch (event[1]) { + case EVENT_TYPE_MS_TUNING_CMPL: + case EVENT_TYPE_SS_TUNING_CMPL: + case FTS_FORCE_CAL_SELF_MUTUAL: + case FTS_FLASH_WRITE_CONFIG: + case FTS_FLASH_WRITE_COMP_MEMORY: + case FTS_FORCE_CAL_SELF: + case FTS_WATER_MODE_ON: + case FTS_WATER_MODE_OFF: + default: + logError(0, "%s %s Received unhandled status event = ", + tag, __func__); + logError(0, "%02X %02X %02X %02X %02X %02X %02X %02X\n", + event[0], event[1], event[2], event[3], event[4], + event[5], event[6], event[7]); + break; + } + + /* return fts_next_event(event); */ +} + +#ifdef PHONE_GESTURE +/** + * TODO: Customer should implement their own action + * in respons of a gesture event. + * This is an example that simply print the gesture received + */ +static void fts_gesture_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + unsigned char touchId; + int value; + int needCoords = 0; + + logError(0, + "%s gesture event: %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + + if (event[1] == 0x03) { + logError(1, "%s %s: Gesture ID %02X enable_status = %02X\n", + tag, __func__, event[2], event[3]); + } + + if (event[1] == EVENT_TYPE_ENB && event[2] == 0x00) { + switch (event[3]) { + case GESTURE_ENABLE: + logError(1, "%s %s: Gesture Enabled! res = %02X\n", + tag, __func__, event[4]); + break; + + case GESTURE_DISABLE: + logError(1, "%s %s: Gesture Disabled! res = %02X\n", + tag, __func__, event[4]); + break; + + default: + logError(1, "%s %s: Event not Valid!\n", tag, __func__); + } + } + + if (event[0] == EVENTID_GESTURE && (event[1] == EVENT_TYPE_GESTURE_DTC1 + || event[1] == EVENT_TYPE_GESTURE_DTC2)) { + /* always use touchId zero */ + touchId = 0; + __set_bit(touchId, &info->touch_id); + + /* by default read the coordinates*/ + /* for all gestures excluding double tap */ + needCoords = 1; + + switch (event[2]) { + case GES_ID_DBLTAP: + value = KEY_WAKEUP; + logError(0, "%s %s: double tap!\n", tag, __func__); + needCoords = 0; + break; + + case GES_ID_AT: + value = KEY_WWW; + logError(0, "%s %s: @!\n", tag, __func__); + break; + + case GES_ID_C: + value = KEY_C; + logError(0, "%s %s: C !\n", tag, __func__); + break; + + case GES_ID_E: + value = KEY_E; + logError(0, "%s %s: e !\n", tag, __func__); + break; + + case GES_ID_F: + value = KEY_F; + logError(0, "%s %s: F !\n", tag, __func__); + break; + + case GES_ID_L: + value = KEY_L; + logError(0, "%s %s: L !\n", tag, __func__); + break; + + case GES_ID_M: + value = KEY_M; + logError(0, "%s %s: M !\n", tag, __func__); + break; + + case GES_ID_O: + value = KEY_O; + logError(0, "%s %s: O !\n", tag, __func__); + break; + + case GES_ID_S: + value = KEY_S; + logError(0, "%s %s: S !\n", tag, __func__); + break; + + case GES_ID_V: + value = KEY_V; + logError(0, "%s %s: V !\n", tag, __func__); + break; + + case GES_ID_W: + value = KEY_W; + logError(0, "%s %s: W !\n", tag, __func__); + break; + + case GES_ID_Z: + value = KEY_Z; + logError(0, "%s %s: Z !\n", tag, __func__); + break; + + case GES_ID_HFLIP_L2R: + value = KEY_RIGHT; + logError(0, "%s %s: -> !\n", tag, __func__); + break; + + case GES_ID_HFLIP_R2L: + value = KEY_LEFT; + logError(0, "%s %s: <- !\n", tag, __func__); + break; + + case GES_ID_VFLIP_D2T: + value = KEY_UP; + logError(0, "%s %s: UP !\n", tag, __func__); + break; + + case GES_ID_VFLIP_T2D: + value = KEY_DOWN; + logError(0, "%s %s: DOWN !\n", tag, __func__); + break; + + case GES_ID_CUST1: + value = KEY_F1; + logError(0, "%s %s: F1 !\n", tag, __func__); + break; + + case GES_ID_CUST2: + value = KEY_F1; + logError(0, "%s %s: F2 !\n", tag, __func__); + break; + + case GES_ID_CUST3: + value = KEY_F3; + logError(0, "%s %s: F3 !\n", tag, __func__); + break; + + case GES_ID_CUST4: + value = KEY_F1; + logError(0, "%s %s: F4 !\n", tag, __func__); + break; + + case GES_ID_CUST5: + value = KEY_F1; + logError(0, "%s %s: F5 !\n", tag, __func__); + break; + + case GES_ID_LEFTBRACE: + value = KEY_LEFTBRACE; + logError(0, "%s %s: < !\n", tag, __func__); + break; + + case GES_ID_RIGHTBRACE: + value = KEY_RIGHTBRACE; + logError(0, "%s %s: > !\n", tag, __func__); + break; + default: + logError(0, "%s %s: No valid GestureID!\n", + tag, __func__); + goto gesture_done; + } + + /* no coordinates for gestures reported by FW */ + if (event[1] == EVENT_TYPE_GESTURE_DTC1) + needCoords = 0; + + if (needCoords == 1) + readGestureCoords(event); + + fts_input_report_key(info, value); + +gesture_done: + /* Done with gesture event, clear bit. */ + __clear_bit(touchId, &info->touch_id); + } + /* return fts_next_event(event); */ +} +#endif + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +/* + * This handler is called each time there is at least + * one new event in the FIFO + */ +static void fts_event_handler(struct work_struct *work) +{ + struct fts_ts_info *info; + int error = 0, count = 0; + unsigned char regAdd; + unsigned char data[FIFO_EVENT_SIZE] = {0}; + unsigned char eventId; + + struct event_dispatch_handler_t event_handler; + + info = container_of(work, struct fts_ts_info, work); + /* + * read all the FIFO and parsing events + */ + + __pm_wakeup_event(info->wakeup_source, HZ); + regAdd = FIFO_CMD_READONE; + + for (count = 0; count < FIFO_DEPTH; count++) { + error = fts_readCmd(®Add, sizeof(regAdd), data, + FIFO_EVENT_SIZE); + if (error == OK && data[0] != EVENTID_NO_EVENT) + eventId = data[0]; + else + break; + + if (eventId < EVENTID_LAST) { + event_handler = info->event_dispatch_table[eventId]; + event_handler.handler(info, (data)); + } + } + input_sync(info->input_dev); + + fts_interrupt_enable(info); +} + +static void fts_fw_update_auto(struct work_struct *work) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + int event_to_search[2] = {(int)EVENTID_ERROR_EVENT, + (int)EVENT_TYPE_CHECKSUM_ERROR}; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int flag_init = 0; + int retval = 0; + int retval1 = 0; + int ret; + struct fts_ts_info *info; + struct delayed_work *fwu_work = container_of(work, + struct delayed_work, work); + int crc_status = 0; + int error = 0; + struct Firmware fwD; + int orig_size; + u8 *orig_data; + + info = container_of(fwu_work, struct fts_ts_info, fwu_work); + logError(0, "%s Fw Auto Update is starting...\n", tag); + + ret = getFWdata(PATH_FILE_FW, &orig_data, &orig_size, 0); + if (ret < OK) { + logError(0, "%s %s: impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto NO_FIRMWARE_UPDATE; + } + + ret = parseBinFile(orig_data, orig_size, &fwD, 1); + if (ret < OK) { + logError(1, "%s %s: impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + kfree(fwD.data); + goto NO_FIRMWARE_UPDATE; + } + + fts_chip_powercycle(info); + retval = flash_burn(&fwD, crc_status, 1); + + if ((retval & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s:firmware update retry! ERROR %08X\n", + tag, __func__, retval); + fts_chip_powercycle(info); + + retval1 = flash_burn(&fwD, crc_status, 1); + + if ((retval1 & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s: update failed again! ERROR %08X\n", + tag, __func__, retval1); + logError(1, "%s Fw Auto Update Failed!\n", tag); + } + } + + kfree(fwD.data); + u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); + ret = fts_writeCmd(cmd, 4); + if (ret < OK) { + logError(1, "%s %s Can't send reset command! ERROR %08X\n", + tag, __func__, ret); + } else { + setSystemResettedDown(1); + setSystemResettedUp(1); + ret = pollForEvent(event_to_search, 2, readData, + GENERAL_TIMEOUT); + if (ret < OK) { + logError(0, "%s %s: No CX CRC Found!\n", tag, __func__); + } else { + if (readData[2] == CRC_CX_MEMORY) { + logError(1, "%s %s: CRC Error! ERROR:%02X\n\n", + tag, __func__, readData[2]); + + flag_init = 1; + } + } + } + + if (ftsInfo.u8_msScrConfigTuneVer != ftsInfo.u8_msScrCxmemTuneVer || + ftsInfo.u8_ssTchConfigTuneVer != ftsInfo.u8_ssTchCxmemTuneVer) + ret = ERROR_GET_INIT_STATUS; + else if (((ftsInfo.u32_mpPassFlag != INIT_MP) + && (ftsInfo.u32_mpPassFlag != INIT_FIELD)) || flag_init == 1) + ret = ERROR_GET_INIT_STATUS; + else + ret = OK; + + if (ret == ERROR_GET_INIT_STATUS) { + error = fts_chip_initialization(info); + if (error < OK) + logError(1, "%s %s Can't initialize chip! ERROR %08X", + tag, __func__, error); + } + +NO_FIRMWARE_UPDATE: + error = fts_init_afterProbe(info); + if (error < OK) + logError(1, "%s Can't initialize hardware device ERROR %08X\n", + tag, error); + + logError(0, "%s Fw Auto Update Finished!\n", tag); +} + +static int fts_chip_initialization(struct fts_ts_info *info) +{ + int ret2 = 0; + int retry; + int initretrycnt = 0; + struct TestToDo todoDefault; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 0; + todoDefault.MutualCx2Adj = 0; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 0; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 0; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 0; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + for (retry = 0; retry <= INIT_FLAG_CNT; retry++) { + ret2 = production_test_main(LIMITS_FILE, 1, 1, &todoDefault, + INIT_FIELD); + if (ret2 == OK) + break; + initretrycnt++; + logError(1, "%s %s: cycle count = %04d - ERROR %08X\n", + tag, __func__, initretrycnt, ret2); + fts_chip_powercycle(info); + } + + if (ret2 < OK) + logError(1, "%s failed to initializate 3 times\n", tag); + + return ret2; +} + +#ifdef FTS_USE_POLLING_MODE + +static enum hrtimer_restart fts_timer_func(struct hrtimer *timer) +{ + struct fts_ts_info *info = + container_of(timer, struct fts_ts_info, timer); + + queue_work(info->event_wq, &info->work); + return HRTIMER_NORESTART; +} +#else + +static irqreturn_t fts_interrupt_handler(int irq, void *handle) +{ + struct fts_ts_info *info = handle; + + disable_irq_nosync(info->client->irq); + + queue_work(info->event_wq, &info->work); + + return IRQ_HANDLED; +} +#endif + +static int fts_interrupt_install(struct fts_ts_info *info) +{ + int i, error = 0; + size_t len; + + len = sizeof(struct event_dispatch_handler_t) * EVENTID_LAST; + info->event_dispatch_table = kzalloc(len, GFP_KERNEL); + + if (!info->event_dispatch_table) { + logError(1, "%s OOM allocating event dispatch table\n", tag); + return -ENOMEM; + } + + for (i = 0; i < EVENTID_LAST; i++) + info->event_dispatch_table[i].handler = fts_nop_event_handler; + install_handler(info, ENTER_POINTER, enter_pointer); + install_handler(info, LEAVE_POINTER, leave_pointer); + install_handler(info, MOTION_POINTER, motion_pointer); + install_handler(info, ERROR_EVENT, error); + install_handler(info, CONTROL_READY, controller_ready); + install_handler(info, STATUS_UPDATE, status); +#ifdef PHONE_GESTURE + install_handler(info, GESTURE, gesture); +#endif +#ifdef PHONE_KEY + install_handler(info, KEY_STATUS, key_status); +#endif + /* disable interrupts in any case */ + error = fts_disableInterrupt(); + +#ifdef FTS_USE_POLLING_MODE + logError(0, "%s Polling Mode\n"); + hrtimer_init(&info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + info->timer.function = fts_timer_func; + hrtimer_start(&info->timer, ktime_set(1, 0), HRTIMER_MODE_REL); +#else + logError(0, "%s Interrupt Mode\n", tag); + if (request_threaded_irq(info->client->irq, NULL, fts_interrupt_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, info->client->name, info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } +#endif + return error; +} + +static void fts_interrupt_uninstall(struct fts_ts_info *info) +{ + fts_disableInterrupt(); + + kfree(info->event_dispatch_table); +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + free_irq(info->client->irq, info); +#endif +} + +static void fts_interrupt_enable(struct fts_ts_info *info) +{ +#ifdef FTS_USE_POLLING_MODE + hrtimer_start(&info->timer, ktime_set(0, 10000000), HRTIMER_MODE_REL); +#else + enable_irq(info->client->irq); +#endif + /* enable the touch IC irq */ + fts_enableInterrupt(); +} + +static void fts_interrupt_disable(struct fts_ts_info *info) +{ + /* disable the touch IC irq */ + fts_disableInterrupt(); + +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + disable_irq(info->client->irq); +#endif + +} + +static int fts_init(struct fts_ts_info *info) +{ + int error; + + error = fts_system_reset(); + if (error < OK && error != (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Cannot reset the device! ERROR %08X\n", + tag, error); + return error; + } + if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Setting default Chip INFO!\n", tag); + defaultChipInfo(0); + } else { + error = readChipInfo(0); + if (error < OK) { + logError(1, "%s Cannot read Chip Info!ERROR:%08X\n", + tag, error); + } + } + + error = fts_interrupt_install(info); + + if (error != OK) + logError(1, "%s Init (1) error (ERROR = %08X)\n", tag, error); + + return error; +} + +int fts_chip_powercycle(struct fts_ts_info *info) +{ + int error = 0; + + logError(0, "%s %s: Power Cycle Starting...\n", tag, __func__); + + /* + * if IRQ pin is short with DVDD a call to + * the ISR will triggered when the regulator is turned off + */ + + logError(0, "%s %s: Disabling IRQ...\n", tag, __func__); + disable_irq_nosync(info->client->irq); + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + else + msleep(300); + + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); + } + } + /* time needed by the regulators for reaching the regime values */ + msleep(20); + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + /* time to wait before bring up the reset */ + /* gpio after the power up of the regulators */ + msleep(20); + gpio_set_value(info->bdata->reset_gpio, 1); + /* mdelay(300); */ + } + + release_all_touches(info); + + logError(0, "%s %s: Enabling IRQ...\n", tag, __func__); + enable_irq(info->client->irq); + logError(0, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", + tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep) +{ + int error = 0; + + logError(0, "%s %s: Power Cycle Starting...\n", tag, __func__); + + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + + msleep(sleep); + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); + } + } + /* time needed by the regulators for reaching the regime values */ + msleep(500); + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + /* + * time to wait before bring up the reset + * gpio after the power up of the regulators + */ + msleep(20); + gpio_set_value(info->bdata->reset_gpio, 1); + /* msleep(300); */ + } + + /* before reset clear all slot */ + release_all_touches(info); + + logError(0, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", + tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +static int fts_init_afterProbe(struct fts_ts_info *info) +{ + int error = 0; + + /* system reset */ + error = cleanUp(0); + + /* enable the features and the sensing */ + error |= fts_mode_handler(info, 0); + + /* enable the interrupt */ + error |= fts_enableInterrupt(); + +#if defined(CONFIG_FB_MSM) + error |= fb_register_client(&info->notifier); +#else + if (active_panel) + error |= drm_panel_notifier_register(active_panel, + &info->notifier); +#endif + + if (error < OK) + logError(1, "%s %s Init after Probe error (ERROR = %08X)\n", + tag, __func__, error); + + return error; +} + +/* + * TODO: change this function according with the needs + * of customer in terms of feature to enable/disable + */ +static int fts_mode_handler(struct fts_ts_info *info, int force) +{ + int res = OK; + int ret = OK; + + /* initialize the mode to Nothing in order */ + /* to be updated depending on the features enabled */ + info->mode = MODE_NOTHING; + + logError(0, "%s %s: Mode Handler starting...\n", tag, __func__); + switch (info->resume_bit) { + case 0: + /* screen down */ + logError(0, "%s %s: Screen OFF...\n", tag, __func__); + /* + * do sense off in order to avoid the flooding + * of the fifo with touch events if someone is + * touching the panel during suspend + */ + logError(0, "%s %s: Sense OFF!\n", tag, __func__); + /* + *we need to use fts_command for speed reason + * (no need to check echo in this case and interrupt + * can be enabled) + */ + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_OFF); +#ifdef PHONE_KEY + logError(0, "%s %s: Key OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_OFF); +#endif + +#ifdef PHONE_GESTURE + if (info->gesture_enabled == 1) { + logError(0, "%s %s: enter in gesture mode!\n", + tag, __func__); + ret = enterGestureMode(isSystemResettedDown()); + if (ret >= OK) { + info->mode |= FEAT_GESTURE; + } else { + logError(1, + "%s %s:enterGestureMode failed!%08X recovery in senseOff\n", + tag, __func__, ret); + } + res |= ret; + } +#endif + if (info->mode != (FEAT_GESTURE|MODE_NOTHING) + || info->gesture_enabled == 0) + info->mode |= MODE_SENSEOFF; + setSystemResettedDown(0); + break; + + case 1: + /* screen up */ + logError(0, "%s %s: Screen ON...\n", tag, __func__); + +#ifdef FEAT_GLOVE + if ((info->glove_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Glove Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->glove_enabled, + FEAT_GLOVE); + if (ret < OK) { + logError(1, + "%s %s:error in setting GLOVE_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { + info->mode |= FEAT_GLOVE; + logError(1, "%s %s: GLOVE_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: GLOVE_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_STYLUS + if ((info->stylus_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Stylus Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->stylus_enabled, + FEAT_STYLUS); + if (ret < OK) { + logError(1, + "%s %s:error in set STYLUS_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->stylus_enabled == FEAT_ENABLE) { + info->mode |= FEAT_STYLUS; + logError(1, "%s %s: STYLUS_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: STYLUS_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_COVER + if ((info->cover_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Cover Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->cover_enabled, + FEAT_COVER); + if (ret < OK) { + logError(1, + "%s %s:error setting COVER_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->cover_enabled == FEAT_ENABLE) { + info->mode |= FEAT_COVER; + logError(1, "%s %s: COVER_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: COVER_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_CHARGER + if ((info->charger_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Charger Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->charger_enabled, + FEAT_CHARGER); + if (ret < OK) { + logError(1, + "%s %s:error set CHARGER_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->charger_enabled == FEAT_ENABLE) { + info->mode |= FEAT_CHARGER; + logError(1, "%s %s: CHARGER_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: CHARGER_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_VR + if ((info->vr_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Vr Mode setting\n", tag, __func__); + ret = featureEnableDisable(info->vr_enabled, FEAT_VR); + if (ret < OK) { + logError(1, + "%s %s:error setting VR_MODE!:%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->vr_enabled == FEAT_ENABLE) { + info->mode |= FEAT_VR; + logError(1, "%s %s: VR_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: VR_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_EDGE_REJECTION + if ((info->edge_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Edge Rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->edge_rej_enabled, + FEAT_EDGE_REJECTION); + if (ret < OK) { + logError(1, + "%s %s:err set EDGE_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->edge_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_EDGE_REJECTION; + logError(1, + "%s %s:EDGE_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s %s:EDGE_REJECTION_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_CORNER_REJECTION + if ((info->corner_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Corner rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->corner_rej_enabled, + FEAT_CORNER_REJECTION); + if (ret < OK) { + logError(1, + "%s%s:err CORNER_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->corner_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_CORNER_REJECTION; + logError(1, + "%s%s:CORNER_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s%s:CORNER_REJECTION_MODE Disabled\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_EDGE_PALM_REJECTION + if ((info->edge_palm_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s:Edge Palm rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->edge_palm_rej_enabled, + FEAT_EDGE_PALM_REJECTION); + if (ret < OK) { + logError(1, + "%s %s:err EDGE_PALM_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->edge_palm_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_EDGE_PALM_REJECTION; + logError(1, + "%s %s:EDGE_PALM_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s %s:EDGE_PALM_REJECTION_MODE Disabled!\n", + tag, __func__); + } + } +#endif + logError(0, "%s %s: Sense ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_ON); + info->mode |= MODE_SENSEON; +#ifdef PHONE_KEY + logError(0, "%s %s: Key ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_ON); +#endif + setSystemResettedUp(0); + break; + + default: + logError(1, + "%s %s: invalid resume_bit value = %d! ERROR %08X\n", + tag, __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + logError(0, "%s %s: Mode Handler finished! res = %08X\n", tag, __func__, + res); + return res; +} + +static int fts_chip_power_switch(struct fts_ts_info *info, bool on) +{ + int error = -1; + + if (info->bdata->pwr_on_suspend) { + if (!info->ts_pinctrl) + return 0; + + if (on) { + error = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (error < 0) + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_ACTIVE); + } else { + error = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_suspend); + if (error < 0) + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_SUSPEND); + } + + return 0; + } + + if (on) { + if (info->bus_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable AVDD\n", + tag, __func__); + } + + if (info->pwr_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable DVDD\n", + tag, __func__); + } + + if (info->ts_pinctrl) { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active) < 0) { + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_ACTIVE); + } + } + } else { + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + else + msleep(300); + + if (info->ts_pinctrl) { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_suspend) < 0) { + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_SUSPEND); + } + } + + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable DVDD\n", + tag, __func__); + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable AVDD\n", + tag, __func__); + } + } + return error; +} + + +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_info *info; + + info = container_of(work, struct fts_ts_info, resume_work); + + __pm_wakeup_event(info->wakeup_source, HZ); + + fts_chip_power_switch(info, true); + + info->resume_bit = 1; + + fts_system_reset(); +#ifdef USE_NOISE_PARAM + readNoiseParameters(noise_params); +#endif + +#ifdef USE_NOISE_PARAM + writeNoiseParameters(noise_params); +#endif + + release_all_touches(info); + + fts_mode_handler(info, 0); + + info->sensor_sleep = false; + + fts_interrupt_enable(info); +} + +static void fts_suspend_work(struct work_struct *work) +{ + struct fts_ts_info *info; + + info = container_of(work, struct fts_ts_info, suspend_work); + + __pm_wakeup_event(info->wakeup_source, HZ); + + info->resume_bit = 0; + + fts_mode_handler(info, 0); + + fts_interrupt_disable(info); + release_all_touches(info); + info->sensor_sleep = true; + + fts_chip_power_switch(info, false); +} + +#if defined(CONFIG_FB_MSM) +static int fts_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, + struct fts_ts_info, notifier); + struct fb_event *evdata = data; + unsigned int blank; + + if (!evdata || (evdata->id != 0)) + return 0; + + if (val != FB_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + + if (evdata->data && val == FB_EVENT_BLANK && info) { + + blank = *(int *) (evdata->data); + + switch (blank) { + case FB_BLANK_POWERDOWN: + if (info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_POWERDOWN\n", + tag, __func__); + + queue_work(info->event_wq, &info->suspend_work); + + break; + + case FB_BLANK_UNBLANK: + if (!info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_UNBLANK\n", + tag, __func__); + + queue_work(info->event_wq, &info->resume_work); + break; + default: + break; + } + } + return NOTIFY_OK; +} + +#else +static int fts_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, struct fts_ts_info, + notifier); + struct drm_panel_notifier *evdata = data; + unsigned int blank; + + if (!evdata) + return 0; + + if (val != DRM_PANEL_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + if (evdata->data && val == DRM_PANEL_EVENT_BLANK && info) { + blank = *(int *) (evdata->data); + + switch (blank) { + case DRM_PANEL_BLANK_POWERDOWN: + if (info->sensor_sleep && info->aoi_notify_enabled) + break; + + if (info->aoi_notify_enabled) + info->aoi_wake_on_suspend = true; + else + info->aoi_wake_on_suspend = false; + + if (info->aoi_wake_on_suspend) { + info->sensor_sleep = true; + __pm_stay_awake(info->wakeup_source); + } else { + queue_work(info->event_wq, &info->suspend_work); + } + break; + + case DRM_PANEL_BLANK_UNBLANK: + if (info->aoi_wake_on_suspend) + __pm_relax(info->wakeup_source); + + if (!info->sensor_sleep) + break; + + if (!info->resume_bit) + queue_work(info->event_wq, &info->resume_work); + + if (info->aoi_wake_on_suspend) + info->sensor_sleep = false; + + break; + default: + break; + } + } + return NOTIFY_OK; +} +#endif + +static struct notifier_block fts_noti_block = { + .notifier_call = fts_fb_state_chg_callback, +}; + +static int fts_pinctrl_init(struct fts_ts_info *info) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + info->ts_pinctrl = devm_pinctrl_get(info->dev); + if (IS_ERR_OR_NULL(info->ts_pinctrl)) { + retval = PTR_ERR(info->ts_pinctrl); + logError(1, "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + info->pinctrl_state_active + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(info->pinctrl_state_active)) { + retval = PTR_ERR(info->pinctrl_state_active); + logError(1, "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_suspend + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(info->pinctrl_state_suspend)) { + retval = PTR_ERR(info->pinctrl_state_suspend); + logError(1, "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_release + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + retval = PTR_ERR(info->pinctrl_state_release); + logError(1, "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(info->ts_pinctrl); +err_pinctrl_get: + info->ts_pinctrl = NULL; + return retval; +} + +static int fts_get_reg(struct fts_ts_info *info, bool get) +{ + int retval; + const struct fts_i2c_platform_data *bdata = info->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { + info->pwr_reg = regulator_get(info->dev, + bdata->pwr_reg_name); + if (IS_ERR(info->pwr_reg)) { + logError(1, "%s %s: Failed to get power regulator\n", + tag, __func__); + retval = PTR_ERR(info->pwr_reg); + goto regulator_put; + } + + retval = regulator_set_load(info->pwr_reg, 62000); + if (retval < 0) { + logError(1, "%s %s: Failed to set power load\n", + tag, __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(info->pwr_reg, + FTS_DVDD_VOL_MIN, FTS_DVDD_VOL_MAX); + if (retval < 0) { + logError(1, "%s %s: Failed to set power voltage\n", + tag, __func__); + goto regulator_put; + } + } + + if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { + info->bus_reg = regulator_get(info->dev, + bdata->bus_reg_name); + if (IS_ERR(info->bus_reg)) { + logError(1, + "%s %s:Failed to get bus pullup regulator\n", + tag, __func__); + retval = PTR_ERR(info->bus_reg); + goto regulator_put; + } + + retval = regulator_set_load(info->bus_reg, 20000); + if (retval < 0) { + logError(1, "%s %s: Failed to set power load\n", + tag, __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(info->bus_reg, + FTS_AVDD_VOL_MIN, FTS_AVDD_VOL_MAX); + + if (retval < 0) { + logError(1, "%s %s: Failed to set power voltage\n", + tag, __func__); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (info->pwr_reg) { + regulator_put(info->pwr_reg); + info->pwr_reg = NULL; + } + + if (info->bus_reg) { + regulator_put(info->bus_reg); + info->bus_reg = NULL; + } + + return retval; +} + +static int fts_enable_reg(struct fts_ts_info *info, + bool enable) +{ + int retval; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (info->bus_reg) { + retval = regulator_enable(info->bus_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable bus regulator\n", + tag, __func__); + goto exit; + } + } + + if (info->pwr_reg) { + retval = regulator_enable(info->pwr_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable power regulator\n", + tag, __func__); + goto disable_bus_reg; + } + } + + return OK; + +disable_pwr_reg: + if (info->pwr_reg) + regulator_disable(info->pwr_reg); + +disable_bus_reg: + if (info->bus_reg) + regulator_disable(info->bus_reg); + +exit: + return retval; +} + +static int fts_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, 16, "fts_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + logError(1, "%s %s: Failed to get gpio %d (code: %d)", + tag, __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + logError(1, "%s %s: Failed to set gpio %d direction", + tag, __func__, gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return retval; +} + +static int fts_set_gpio(struct fts_ts_info *info) +{ + int retval; + const struct fts_i2c_platform_data *bdata = + info->bdata; + + retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure irq GPIO\n", + tag, __func__); + goto err_gpio_irq; + } + + if (bdata->reset_gpio >= 0) { + retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure reset GPIO\n", + tag, __func__); + goto err_gpio_reset; + } + } + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, 0); + msleep(20); + gpio_set_value(bdata->reset_gpio, 1); + } + + setResetGpio(bdata->reset_gpio); + return OK; + +err_gpio_reset: + fts_gpio_setup(bdata->irq_gpio, false, 0, 0); + setResetGpio(GPIO_NOT_DEFINED); +err_gpio_irq: + return retval; +} + +static int parse_dt(struct device *dev, + struct fts_i2c_platform_data *bdata) +{ + int retval; + const char *name; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "st,irq-gpio", 0, NULL); + + logError(0, "%s irq_gpio = %d\n", tag, bdata->irq_gpio); + + bdata->pwr_on_suspend = + of_property_read_bool(np, "st,power_on_suspend"); + + retval = of_property_read_string(np, "st,regulator_dvdd", &name); + if (retval == -EINVAL) + bdata->pwr_reg_name = NULL; + else if (retval < 0) + return retval; + + bdata->pwr_reg_name = name; + logError(0, "%s pwr_reg_name = %s\n", tag, name); + + retval = of_property_read_string(np, "st,regulator_avdd", &name); + if (retval == -EINVAL) + bdata->bus_reg_name = NULL; + else if (retval < 0) + return retval; + + bdata->bus_reg_name = name; + logError(0, "%s bus_reg_name = %s\n", tag, name); + + if (of_property_read_bool(np, "st,reset-gpio")) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "st,reset-gpio", 0, NULL); + logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio); + } else { + bdata->reset_gpio = GPIO_NOT_DEFINED; + } + + bdata->x_flip = of_property_read_bool(np, "st,x-flip"); + bdata->y_flip = of_property_read_bool(np, "st,y-flip"); + + return OK; +} + +static int check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return OK; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return OK; + } + } + + return -ENODEV; +} + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + pr_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + pr_err(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + pr_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} + +static int fts_probe_internal(struct i2c_client *client, + const struct i2c_device_id *idp) +{ + struct fts_ts_info *info = NULL; + int error = 0; + struct device_node *dp = client->dev.of_node; + int retval; + int skip_5_1 = 0; + + logError(0, "%s %s: driver probe begin!\n", tag, __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + logError(1, "%s Unsupported I2C functionality\n", tag); + error = -EIO; + goto ProbeErrorExit_0; + } + + openChannel(client); + + info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL); + if (!info) { + logError(1, + "%s Out of memory, can't to allocate struct info!\n", + tag); + error = -ENOMEM; + goto ProbeErrorExit_0; + } + + info->client = client; + + i2c_set_clientdata(client, info); + + info->i2c_data = kmalloc(I2C_DATA_MAX_LEN, GFP_KERNEL); + if (info->i2c_data == NULL) { + error = -ENOMEM; + goto ProbeErrorExit_0P1; + } + info->i2c_data_len = I2C_DATA_MAX_LEN; + + logError(0, "%s i2c address: %x\n", tag, client->addr); + info->dev = &info->client->dev; + if (dp) { + info->bdata = devm_kzalloc(&client->dev, + sizeof(struct fts_i2c_platform_data), + GFP_KERNEL); + if (!info->bdata) { + logError(1, "%s ERROR:info.bdata kzalloc failed\n", + tag); + goto ProbeErrorExit_1; + } + parse_dt(&client->dev, info->bdata); + } + + logError(0, "%s SET Regulators:\n", tag); + retval = fts_get_reg(info, true); + if (retval < 0) { + logError(1, "%s ERROR: %s: Failed to get regulators\n", + tag, __func__); + goto ProbeErrorExit_1; + } + + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to enable regulators\n", + tag, __func__); + goto ProbeErrorExit_2; + } + + logError(0, "%s SET GPIOS:\n", tag); + retval = fts_set_gpio(info); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", + tag, __func__); + goto ProbeErrorExit_2; + } + info->client->irq = gpio_to_irq(info->bdata->irq_gpio); + + retval = fts_pinctrl_init(info); + if (!retval && info->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + retval = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (retval < 0) { + logError(1, "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + + logError(0, "%s SET Auto Fw Update:\n", tag); + info->fwu_workqueue = alloc_workqueue("fts-fwu-queue", + WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); + if (!info->fwu_workqueue) { + logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + goto ProbeErrorExit_3; + } + + INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); + + logError(0, "%s SET Event Handler:\n", tag); + info->wakeup_source = wakeup_source_register(&client->dev, + dev_name(&client->dev)); + + info->event_wq = alloc_workqueue("fts-event-queue", + WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); + if (!info->event_wq) { + logError(1, "%s ERROR: Cannot create work thread\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_4; + } + + INIT_WORK(&info->work, fts_event_handler); + + INIT_WORK(&info->resume_work, fts_resume_work); + INIT_WORK(&info->suspend_work, fts_suspend_work); + + logError(0, "%s SET Input Device Property:\n", tag); + /* info->dev = &info->client->dev; */ + info->input_dev = input_allocate_device(); + if (!info->input_dev) { + logError(1, "%s ERROR: No such input device defined!\n", + tag); + error = -ENODEV; + goto ProbeErrorExit_5; + } + info->input_dev->dev.parent = &client->dev; + info->input_dev->name = FTS_TS_DRV_NAME; + snprintf(fts_ts_phys, sizeof(fts_ts_phys), "%s/input0", + info->input_dev->name); + info->input_dev->phys = fts_ts_phys; + info->input_dev->id.bustype = BUS_I2C; + info->input_dev->id.vendor = 0x0001; + info->input_dev->id.product = 0x0002; + info->input_dev->id.version = 0x0100; + + __set_bit(EV_SYN, info->input_dev->evbit); + __set_bit(EV_KEY, info->input_dev->evbit); + __set_bit(EV_ABS, info->input_dev->evbit); + __set_bit(BTN_TOUCH, info->input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, info->input_dev->keybit); + + input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT); + + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, + X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, + Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, + AREA_MIN, AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, + AREA_MIN, AREA_MAX, 0, 0); + +#ifdef PHONE_GESTURE + input_set_capability(info->input_dev, EV_KEY, KEY_WAKEUP); + + input_set_capability(info->input_dev, EV_KEY, KEY_M); + input_set_capability(info->input_dev, EV_KEY, KEY_O); + input_set_capability(info->input_dev, EV_KEY, KEY_E); + input_set_capability(info->input_dev, EV_KEY, KEY_W); + input_set_capability(info->input_dev, EV_KEY, KEY_C); + input_set_capability(info->input_dev, EV_KEY, KEY_L); + input_set_capability(info->input_dev, EV_KEY, KEY_F); + input_set_capability(info->input_dev, EV_KEY, KEY_V); + input_set_capability(info->input_dev, EV_KEY, KEY_S); + input_set_capability(info->input_dev, EV_KEY, KEY_Z); + input_set_capability(info->input_dev, EV_KEY, KEY_WWW); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFT); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHT); + input_set_capability(info->input_dev, EV_KEY, KEY_UP); + input_set_capability(info->input_dev, EV_KEY, KEY_DOWN); + + input_set_capability(info->input_dev, EV_KEY, KEY_F1); + input_set_capability(info->input_dev, EV_KEY, KEY_F2); + input_set_capability(info->input_dev, EV_KEY, KEY_F3); + input_set_capability(info->input_dev, EV_KEY, KEY_F4); + input_set_capability(info->input_dev, EV_KEY, KEY_F5); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFTBRACE); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHTBRACE); +#endif + +#ifdef PHONE_KEY + /* KEY associated to the touch screen buttons */ + input_set_capability(info->input_dev, EV_KEY, KEY_HOMEPAGE); + input_set_capability(info->input_dev, EV_KEY, KEY_BACK); + input_set_capability(info->input_dev, EV_KEY, KEY_MENU); +#endif + + mutex_init(&(info->input_report_mutex)); + +#ifdef PHONE_GESTURE + mutex_init(&gestureMask_mutex); +#endif + + /* register the multi-touch input device */ + error = input_register_device(info->input_dev); + if (error) { + logError(1, "%s ERROR: No such input device\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5_1; + } + + skip_5_1 = 1; + /* track slots */ + info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif + + /* init hardware device */ + logError(0, "%s Device Initialization:\n", tag); + error = fts_init(info); + if (error < OK) { + logError(1, "%s Cannot initialize the device ERROR %08X\n", + tag, error); + error = -ENODEV; + goto ProbeErrorExit_6; + } + + /* + * init feature switches (by default all the features + * are disable, if one feature want to be enabled from + * the start, set the corresponding value to 1) + */ + info->gesture_enabled = 0; + info->glove_enabled = 0; + info->charger_enabled = 0; + info->stylus_enabled = 0; + info->vr_enabled = 0; + info->cover_enabled = 0; + info->edge_rej_enabled = 0; + info->corner_rej_enabled = 0; + info->edge_palm_rej_enabled = 0; + + info->resume_bit = 1; + info->notifier = fts_noti_block; + + logError(0, "%s SET Device File Nodes:\n", tag); + /* sysfs stuff */ + info->attrs.attrs = fts_attr_group; + error = sysfs_create_group(&client->dev.kobj, &info->attrs); + if (error) { + logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_7; + } + +#ifdef SCRIPTLESS + /* I2C cmd */ + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->i2c_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_i2c"); + if (IS_ERR(info->i2c_cmd_dev)) { + logError(1, + "%s ERROR: Failed to create device for the sysfs!\n", + tag); + goto ProbeErrorExit_8; + } + + dev_set_drvdata(info->i2c_cmd_dev, info); + + error = sysfs_create_group(&info->i2c_cmd_dev->kobj, + &i2c_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_9; + } +#endif + +#ifdef DRIVER_TEST + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->test_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_driver_test"); + if (IS_ERR(info->test_cmd_dev)) { + logError(1, + "%s ERROR: Failed to create device for the sysfs!\n", + tag); + goto ProbeErrorExit_10; + } + + dev_set_drvdata(info->test_cmd_dev, info); + + error = sysfs_create_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_11; + } +#endif + + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->aoi_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "touch_aoi"); + if (IS_ERR(info->aoi_cmd_dev)) { + logError(1, + "%s ERROR: Failed to create device for the sysfs\n", + tag); + goto ProbeErrorExit_10; + } + + dev_set_drvdata(info->aoi_cmd_dev, info); + + error = sysfs_create_group(&info->aoi_cmd_dev->kobj, + &aoi_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group\n", tag); + goto ProbeErrorExit_11; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + logError(1, "%s Probe Finished!\n", tag); + + return OK; + + /* error exit path */ +#ifdef DRIVER_TEST +ProbeErrorExit_11: +#ifndef SCRIPTLESS + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + +ProbeErrorExit_10: +#ifndef SCRIPTLESS + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif +#endif + +#ifdef SCRIPTLESS +ProbeErrorExit_9: + device_destroy(fts_cmd_class, DCHIP_ID_0); + +ProbeErrorExit_8: + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif + +ProbeErrorExit_7: + /* fb_unregister_client(&info->notifier); */ + +ProbeErrorExit_6: + input_unregister_device(info->input_dev); + +ProbeErrorExit_5_1: + if (skip_5_1 != 1) + input_free_device(info->input_dev); + +ProbeErrorExit_5: + destroy_workqueue(info->event_wq); + +ProbeErrorExit_4: + destroy_workqueue(info->fwu_workqueue); + wakeup_source_unregister(info->wakeup_source); + +ProbeErrorExit_3: + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release)) + logError(1, "%s:Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + } + } + fts_enable_reg(info, false); + fts_gpio_setup(info->bdata->irq_gpio, false, 0, 0); + fts_gpio_setup(info->bdata->reset_gpio, false, 0, 0); + +ProbeErrorExit_2: + fts_get_reg(info, false); + +ProbeErrorExit_1: + kfree(info->i2c_data); +ProbeErrorExit_0P1: + kfree(info); + +ProbeErrorExit_0: + logError(1, "%s Probe Failed!\n", tag); + + return error; +} + +static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp) +{ + int error = 0; + struct device_node *dp = client->dev.of_node; + + if (check_dt(dp)) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + error = -EPROBE_DEFER; + else + error = -ENODEV; + + return error; + } + + device_init_wakeup(&client->dev, true); + return fts_probe_internal(client, idp); +} + +static int fts_remove(struct i2c_client *client) +{ + struct fts_ts_info *info = i2c_get_clientdata(client); + +#ifdef DRIVER_TEST + sysfs_remove_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); +#endif + +#ifdef SCRIPTLESS + /* I2C cmd */ + sysfs_remove_group(&info->i2c_cmd_dev->kobj, &i2c_cmd_attr_group); +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + + /* sysfs stuff */ + sysfs_remove_group(&client->dev.kobj, &info->attrs); + + /* remove interrupt and event handlers */ + fts_interrupt_uninstall(info); + +#if defined(CONFIG_FB_MSM) + fb_unregister_client(&info->notifier); +#else + if (active_panel) + drm_panel_notifier_register(active_panel, &info->notifier); +#endif + + /* unregister the device */ + input_unregister_device(info->input_dev); + + /* input_free_device(info->input_dev ); */ + + /* Empty the FIFO buffer */ + fts_command(info, FIFO_CMD_FLUSH); + /* flushFIFO(); */ + + /* Remove the work thread */ + destroy_workqueue(info->event_wq); + /* wake_lock_destroy(&info->wakelock); */ + wakeup_source_unregister(info->wakeup_source); + destroy_workqueue(info->fwu_workqueue); + + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release); + } + } + fts_enable_reg(info, false); + fts_gpio_setup(info->bdata->irq_gpio, false, 0, 0); + fts_gpio_setup(info->bdata->reset_gpio, false, 0, 0); + + fts_get_reg(info, false); + + /* free all */ + kfree(info->i2c_data); + kfree(info); + + device_init_wakeup(&client->dev, false); + return OK; +} + +static const struct of_device_id fts_of_match_table[] = { + { + .compatible = "st,fts", + }, + {}, +}; +static const struct i2c_device_id fts_device_id[] = { + {FTS_TS_DRV_NAME, 0}, + {} +}; + +static struct i2c_driver fts_i2c_driver = { + .driver = { + .name = FTS_TS_DRV_NAME, + .of_match_table = fts_of_match_table, + }, + .probe = fts_probe, + .remove = fts_remove, + .id_table = fts_device_id, +}; + +static int __init fts_driver_init(void) +{ + return i2c_add_driver(&fts_i2c_driver); +} + +static void __exit fts_driver_exit(void) +{ + i2c_del_driver(&fts_i2c_driver); +} + +late_initcall(fts_driver_init); +module_exit(fts_driver_exit); + +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h new file mode 100644 index 000000000000..9e1edb5283dd --- /dev/null +++ b/drivers/input/touchscreen/st/fts.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _LINUX_FTS_I2C_H_ +#define _LINUX_FTS_I2C_H_ + +/*#include */ +#include +#include + +#include "fts_lib/ftsSoftware.h" +#include "fts_lib/ftsHardware.h" +#include "fts_lib/ftsGesture.h" + +#define FTS_POWER_ON 1 +#define FTS_POWER_OFF 0 + +/****************** CONFIGURATION SECTION ******************/ + +/**** CODE CONFIGURATION ****/ + +#define FTS_TS_DRV_NAME "fts" +#define FTS_TS_DRV_VERSION "4.2.14" /* version */ + +/*#define SCRIPTLESS*/ /*allow to work in scriptless mode with the GUI*/ +#ifdef SCRIPTLESS +#define SCRIPTLESS_DEBUG +/** + * uncomment this macro definition to print debug + * message for script less support + */ +#endif + +#define DRIVER_TEST + +/* #define FW_H_FILE */ /*include the FW as header file*/ +#ifdef FW_H_FILE + #define FW_SIZE_NAME myArray_size + #define FW_ARRAY_NAME myArray +#endif + +/*#define LIMITS_H_FILE*/ /*include the limits file as header file*/ +#ifdef LIMITS_H_FILE + #define LIMITS_SIZE_NAME myArray2_size + #define LIMITS_ARRAY_NAME myArray2 +#endif + +/**** END ****/ + + +/**** FEATURES USED IN THE IC ***/ +/* #define PHONE_KEY enable the keys */ + +#define PHONE_GESTURE /*allow to use the gestures*/ +#ifdef PHONE_GESTURE + #define USE_GESTURE_MASK + #define USE_CUSTOM_GESTURES +#endif + +#define USE_ONE_FILE_NODE +/*allow to enable/disable all the features just using one file node*/ + +#define EDGE_REJ +/*allow edge rej feature (comment to disable)*/ + +#define CORNER_REJ +/*allow corn rej feature (comment to disable)*/ + +#define EDGE_PALM_REJ +/*allow edge palm rej feature (comment to disable)*/ + +#define CHARGER_MODE +/*allow charger mode feature (comment to disable)*/ + +#define GLOVE_MODE +/*allow glove mode feature (comment to disable)*/ + +#define VR_MODE +/*allow vr mode feature (comment to disable)*/ + +#define COVER_MODE +/*allow cover mode feature (comment to disable)*/ + +#define STYLUS_MODE +/*allow stylus mode feature (comment to disable)*/ + +#define USE_NOISE_PARAM +/*set noise params during resume (comment to disable)*/ + +/**** END ****/ + + +/**** PANEL SPECIFICATION ****/ +#define X_AXIS_MAX 1440 +#define X_AXIS_MIN 0 +#define Y_AXIS_MAX 2880 +#define Y_AXIS_MIN 0 + +#define PRESSURE_MIN 0 +#define PRESSURE_MAX 127 + +#define TOUCH_ID_MAX 10 + +#define AREA_MIN PRESSURE_MIN +#define AREA_MAX PRESSURE_MAX +/**** END ****/ + +/*********************************************************/ + +/* Flash programming */ + +#define INIT_FLAG_CNT 3 + +/* KEYS */ +#define KEY1 0x02 +#define KEY2 0x01 +#define KEY3 0x04 + +/* + * Configuration mode + */ +/** + * bitmask which can assume the value defined as + * features in ftsSoftware.h or the following values + */ + +#define MODE_NOTHING 0x00000000 +#define MODE_SENSEON 0x10000000 +#define MODE_SENSEOFF 0x20000000 +#define FEAT_GESTURE 0x40000000 + + +/* + * Status Event Field: + * id of command that triggered the event + */ + +#define FTS_FLASH_WRITE_CONFIG 0x03 +#define FTS_FLASH_WRITE_COMP_MEMORY 0x04 +#define FTS_FORCE_CAL_SELF_MUTUAL 0x05 +#define FTS_FORCE_CAL_SELF 0x06 +#define FTS_WATER_MODE_ON 0x07 +#define FTS_WATER_MODE_OFF 0x08 + + +#define EXP_FN_WORK_DELAY_MS 1000 + +#define CMD_STR_LEN 32 +#define I2C_DATA_MAX_LEN 32 + +#ifdef SCRIPTLESS +/* + * I2C Command Read/Write Function + */ + +#define CMD_RESULT_STR_LEN 2048 +#endif + +#define TSP_BUF_SIZE 4096 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +/*add by guchong*/ +#ifdef PHONE_GESTURE +extern u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX]; +extern u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX]; +extern int gesture_coords_reported; +extern struct mutex gestureMask_mutex; +#endif + +struct fts_i2c_platform_data { + bool x_flip; + bool y_flip; + int (*power)(bool on); + int irq_gpio; + int reset_gpio; + const char *pwr_reg_name; + const char *bus_reg_name; + bool pwr_on_suspend; +}; + +/* + * Forward declaration + */ +struct fts_ts_info; + +/* + * Dispatch event handler + */ +struct event_dispatch_handler_t { + void (*handler)(struct fts_ts_info *info, unsigned char *data); +}; + +/* + * struct fts_ts_info - FTS capacitive touch screen device information + * @dev: Pointer to the structure device + * @client: I2C client structure + * @input_dev Input device structure + * @work Work thread + * @event_wq Event queue for work thread + * @event_dispatch_table Event dispatch table handlers + * @attrs SysFS attributes + * @mode Device operating mode (bitmask) + * @touch_id Bitmask for touch id (mapped to input slots) + * @stylus_id Bitmask for tracking the stylus touches + * (mapped using the touchId) + * @timer Timer when operating in polling mode + * @power Power on/off routine + * @bdata HW info retrived from device tree + * @pwr_reg DVDD power regulator + * @bus_reg AVDD power regulator + * @resume_bit Indicate if screen off/on + * @fwupdate_stat Store the result of a fw update triggered by the host + * @notifier Used for be notified from a suspend/resume event + * @sensor_sleep true susped was called, false resume was called + * @wakelock Wake Lock struct + * @input_report_mutex mutex for handling the pressure of keys + * @series of switches to store the enabling status of a particular + * feature from the host + */ +struct fts_ts_info { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + + struct work_struct work; + struct work_struct suspend_work; + struct work_struct resume_work; + struct workqueue_struct *event_wq; + + struct delayed_work fwu_work; + struct workqueue_struct *fwu_workqueue; + struct completion cmd_done; + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + + struct event_dispatch_handler_t *event_dispatch_table; + + struct attribute_group attrs; + + unsigned int mode; + unsigned long touch_id; +#ifdef STYLUS_MODE + unsigned long stylus_id; +#endif + + +#ifdef FTS_USE_POLLING_MODE + struct hrtimer timer; +#endif + + +#ifdef SCRIPTLESS + /*I2C cmd*/ + struct device *i2c_cmd_dev; + char cmd_read_result[CMD_RESULT_STR_LEN]; + char cmd_wr_result[CMD_RESULT_STR_LEN]; + char cmd_write_result[20]; +#endif + +#ifdef DRIVER_TEST + struct device *test_cmd_dev; +#endif + int (*power)(bool on); + + struct fts_i2c_platform_data *bdata; + struct regulator *pwr_reg; + struct regulator *bus_reg; + + int resume_bit; + int fwupdate_stat; + + struct notifier_block notifier; + bool sensor_sleep; + struct wakeup_source *wakeup_source; + + /* input lock */ + struct mutex input_report_mutex; + + /*switches for features*/ + unsigned int gesture_enabled; + unsigned int glove_enabled; + unsigned int charger_enabled; + unsigned int stylus_enabled; + unsigned int vr_enabled; + unsigned int cover_enabled; + unsigned int edge_rej_enabled; + unsigned int corner_rej_enabled; + unsigned int edge_palm_rej_enabled; + + uint8_t *i2c_data; + uint8_t i2c_data_len; + + struct device *aoi_cmd_dev; + bool aoi_notify_enabled; + bool aoi_wake_on_suspend; + + /* aoi region */ + int aoi_left; + int aoi_top; + int aoi_bottom; + int aoi_right; +}; + +extern struct chipInfo ftsInfo; + +int fts_chip_powercycle(struct fts_ts_info *info); +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep); +/*int fts_get_fw_version(struct fts_ts_info *info);*/ +/*extern unsigned int le_to_uint(const unsigned char *ptr);*/ +/*extern unsigned int be_to_uint(const unsigned char *ptr);*/ +extern int input_register_notifier_client(struct notifier_block *nb); +extern int input_unregister_notifier_client(struct notifier_block *nb); + +extern struct attribute_group aoi_cmd_attr_group; + +#ifdef SCRIPTLESS +extern struct attribute_group i2c_cmd_attr_group; +#endif + +#ifdef DRIVER_TEST +extern struct attribute_group test_cmd_attr_group; +#endif + + +#endif + diff --git a/drivers/input/touchscreen/st/fts_aoi_event.c b/drivers/input/touchscreen/st/fts_aoi_event.c new file mode 100644 index 000000000000..070b998fcc96 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_aoi_event.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + */ + +#include +#include +#include "fts.h" + +static ssize_t touch_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return 0; +} + +ssize_t aoi_set_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int left, top, right, bottom; + + ret = sscanf(buf, "%d %d %d %d", &left, &top, &right, &bottom); + if (ret != 4) + return -EINVAL; + + if (right > X_AXIS_MAX) + right = X_AXIS_MAX; + if (bottom > Y_AXIS_MAX) + bottom = Y_AXIS_MAX; + + if (left < 0 || left > X_AXIS_MAX || right < 0 || + top > Y_AXIS_MAX || bottom < 0) + return -EINVAL; + + if (left >= right || top >= bottom) { + info->aoi_left = 0; + info->aoi_top = 0; + info->aoi_right = 0; + info->aoi_bottom = 0; + info->aoi_notify_enabled = false; + return count; + } + + info->aoi_left = left; + info->aoi_top = top; + info->aoi_right = right; + info->aoi_bottom = bottom; + + info->aoi_notify_enabled = true; + return count; +} + +static ssize_t aoi_set_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + size_t len = 0; + + len = scnprintf(buf + len, PAGE_SIZE, + "%d %d %d %d", + info->aoi_left, + info->aoi_top, + info->aoi_right, + info->aoi_bottom); + + return len; +} + +static ssize_t power_set_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int enable; + + if (kstrtoint(buf, 10, &enable)) + return -EINVAL; + + return count; +} + +static DEVICE_ATTR_RO(touch_event); +static DEVICE_ATTR_RW(aoi_set); +static DEVICE_ATTR_WO(power_set); + +static struct attribute *aoi_cmd_attributes[] = { + &dev_attr_touch_event.attr, + &dev_attr_aoi_set.attr, + &dev_attr_power_set.attr, + NULL, +}; + +struct attribute_group aoi_cmd_attr_group = { + .attrs = aoi_cmd_attributes, +}; diff --git a/drivers/input/touchscreen/st/fts_driver_test.c b/drivers/input/touchscreen/st/fts_driver_test.c new file mode 100644 index 000000000000..fa9ed503cf63 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_driver_test.c @@ -0,0 +1,1107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include + +#include +#include +#include +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#ifdef DRIVER_TEST + +#define MAX_PARAMS 50 + +/*DEFINE COMMANDS TO TEST*/ +#define CMD_READ 0x00 +#define CMD_WRITE 0x01 +#define CMD_READU16 0x02 +#define CMD_READB2 0x03 +#define CMD_READB2U16 0x04 +#define CMD_POLLFOREVENT 0x05 +#define CMD_SYSTEMRESET 0x06 +#define CMD_CLEANUP 0x07 +#define CMD_GETFORCELEN 0x08 +#define CMD_GETSENSELEN 0x09 +#define CMD_GETMSFRAME 0x0A +/*#define CMD_GETMSKEYFRAME 0x0B*/ +#define CMD_GETSSFRAME 0x0C +#define CMD_REQCOMPDATA 0x0D +#define CMD_READCOMPDATAHEAD 0x0E +#define CMD_READMSCOMPDATA 0x0F +#define CMD_READSSCOMPDATA 0x10 +#define CMD_READGNCOMPDATA 0x11 +#define CMD_GETFWVER 0x12 +#define CMD_FLASHSTATUS 0x13 +#define CMD_FLASHUNLOCK 0x14 +#define CMD_READFWFILE 0x15 +#define CMD_FLASHPROCEDURE 0x16 +#define CMD_ITOTEST 0x17 +#define CMD_INITTEST 0x18 +#define CMD_MSRAWTEST 0x19 +#define CMD_MSINITDATATEST 0x1A +#define CMD_SSRAWTEST 0x1B +#define CMD_SSINITDATATEST 0x1C +#define CMD_MAINTEST 0x1D +#define CMD_POWERCYCLE 0x1E +#define CMD_FWWRITE 0x1F +#define CMD_READCHIPINFO 0x20 +#define CMD_REQFRAME 0x21 + +static char tag[8] = "[ FTS ]\0"; +static u32 functionToTest[MAX_PARAMS]; +static int numberParam; + +static ssize_t stm_driver_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int n; + char *p = (char *)buf; + int ret; + + memset(functionToTest, 0, MAX_PARAMS * sizeof(u32)); + + for (n = 0; n < (count + 1) / 3 && n < MAX_PARAMS; n++) { + ret = sscanf(p, "%02X ", &functionToTest[n]); + if (ret != 1) + return -EINVAL; + p += 3; + logError(1, "%s functionToTest[%d] = %02X\n", tag, n, + functionToTest[n]); + } + + numberParam = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParam); + return count; +} + +static ssize_t stm_driver_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int res = -1, j, count; + int size = 6 * 2; + int temp = 0; + int i; + int byteToRead = 0; + u8 *readData = NULL; + u8 *all_strbuff = NULL; + u8 *cmd = NULL; + + struct MutualSenseFrame frameMS = {0}; + struct SelfSenseFrame frameSS = {0}; + + struct DataHeader dataHead = {0}; + struct MutualSenseData compData = {0}; + struct SelfSenseData comData = {0}; + struct GeneralData gnData = {0}; + + u16 address = 0; + u16 fw_version = 0; + u16 config_id = 0; + + struct Firmware fw; + + /*struct used for defining which test*/ + /*perform during the MP test*/ + + struct TestToDo todoDefault; + + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + fw.data = NULL; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + if (numberParam < 1) { + logError(1, "%s NO COMMAND SPECIFIED!!! ", tag); + logError(1, "do: 'echo [cmd_code] [args] > stm_fts_cmd' "); + logError(1, "before looking for result!\n"); + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s %s: ERROR %08X\n", tag, __func__, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + switch (functionToTest[0]) { + case CMD_READ: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass:cmdLength + * cmd[0]cmd[1]…cmd[cmdLength-1] + * byteToRead + */ + temp = (int)functionToTest[1]; + if (numberParam == 4 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + if (!cmd) { + res = ERROR_OP_NOT_ALLOW; + break; + } + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + byteToRead = functionToTest[i + 2]; + readData = (u8 *)kmalloc_array(byteToRead, sizeof(u8), + GFP_KERNEL); + if (!readData) { + kfree(cmd); + res = ERROR_OP_NOT_ALLOW; + break; + } + res = fts_readCmd(cmd, temp, readData, byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITE: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass:cmdLength + * cmd[0] cmd[1]…cmd[cmdLength-1] + */ + temp = (int)functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + if (!cmd) { + res = ERROR_OP_NOT_ALLOW; + break; + } + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + res = fts_writeCmd(cmd, temp); + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FWWRITE: + if (numberParam != 3) { + logError(1, "%s Wrong number parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass:cmdLength + * cmd[0] cmd[1]…cmd[cmdLength-1] + */ + temp = (int)functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + if (!cmd) { + res = ERROR_OP_NOT_ALLOW; + break; + } + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + res = fts_writeFwCmd(cmd, temp); + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READU16: + if (numberParam != 6) { + logError(1, "%s Wrong number parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass: cmd addr[0] addr[1] + * byteToRead hasDummyByte + */ + byteToRead = functionToTest[4]; + readData = kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = readCmdU16((u8)functionToTest[1], + (u16)((((u8) functionToTest[2] + & 0x00FF) << 8) + ((u8) functionToTest[3] + & 0x00FF)), + readData, + byteToRead, + functionToTest[5]); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_READB2: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to pass: addr[0] addr[1] byteToRead*/ + byteToRead = functionToTest[3]; + readData = kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = readB2((u16)( + (((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8) functionToTest[2] & 0x00FF)), + readData, + byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_READB2U16: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to pass: addr[0] addr[1] byteToRead*/ + byteToRead = functionToTest[3]; + readData = (u8 *)kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = readB2U16((u16)((((u8)functionToTest[1] + & 0x00FF) << 8) + ((u8)functionToTest[2] + & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_POLLFOREVENT: + if (numberParam < 5) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /** + * need to pass: eventLength event[0] event[1] + * … event[eventLength-1] timeTowait + */ + temp = (int)functionToTest[1]; + if (numberParam == 5 + (temp - 1) && temp != 0) { + readData = (u8 *)kmalloc_array(FIFO_EVENT_SIZE, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = pollForEvent((int *)&functionToTest[2], + temp, + readData, + ((functionToTest[temp + 2] & 0x00FF) << 8) + + (functionToTest[temp + 3] & 0x00FF)); + //pollForEvent return the number of error found + if (res >= OK) + res = OK; + size += (FIFO_EVENT_SIZE * sizeof(u8)) * 2; + byteToRead = FIFO_EVENT_SIZE; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SYSTEMRESET: + res = fts_system_reset(); + break; + + case CMD_READCHIPINFO: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to pass: doRequest */ + res = readChipInfo(functionToTest[1]); + break; + + /* TOUCH ENABLE/DISABLE */ + case CMD_CLEANUP: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /* need to pass: enableTouch*/ + res = cleanUp(functionToTest[1]); + break; + + case CMD_GETFORCELEN: + /*read number Tx channels */ + temp = getForceLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)) * 2; + res = OK; + } + break; + + case CMD_GETSENSELEN: + /* read number Rx channels */ + temp = getSenseLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)) * 2; + res = OK; + } + break; + + case CMD_REQFRAME: + /* request a frame */ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + logError(0, "%s Requesting Frame\n", tag); + res = requestFrame((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Err requesting frame ERROR:%02X\n", + tag, res); + } else { + logError(0, "%s Requesting Frame Finished!\n", tag); + } + break; + + case CMD_GETMSFRAME: + if (numberParam != 3) { + logError(1, "%s Wrong number of param!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + logError(0, "%s Get 1 MS Frame\n", tag); + flushFIFO(); + /** + * delete the events related to some + * touch (allow to call this function + * while touching the sreen without + * having a flooding of the FIFO) + */ + res = getMSFrame2((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), &frameMS); + if (res < 0) { + logError(0, "%s Err while taking MS frame:%02X\n", + tag, res); + } else { + logError(0, "%s:frame size is %d words\n", tag, res); + size = (res * sizeof(short) + 8) * 2; + /*set res to OK because if getMSFrame is*/ + /*successful res = number of words read*/ + res = OK; + print_frame_short("MS frame =", + array1dTo2d_short(frameMS.node_data, + frameMS.node_data_size, + frameMS.header.sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); + } + break; + + /*read self raw*/ + case CMD_GETSSFRAME: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + logError(0, "%s Get 1 SS Frame\n", tag); + flushFIFO(); + /** + * delete the events related to some + * touch (allow to call this function + * while touching the sreen without + * having a flooding of the FIFO) + */ + res = getSSFrame2((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), &frameSS); + + if (res < OK) { + logError(0, + "%s Error while taking the SS frame... ERROR %02X\n", + tag, res); + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2 + 1; + + /*set res to OK because if getMSFrame is*/ + /*successful res = number of words read*/ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short(frameSS.force_data, + frameSS.header.force_node, 1), + frameSS.header.force_node, + 1); + print_frame_short("SS sense frame =", + array1dTo2d_short(frameSS.sense_data, + frameSS.header.sense_node, + frameSS.header.sense_node), + 1, + frameSS.header.sense_node); + } + break; + + case CMD_REQCOMPDATA: + /*request comp data*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) + ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, + "%s Error requesting compensation data ERROR %02X\n", + tag, res); + } else { + logError(0, + "%s Requesting Compensation Data Finished!\n", + tag); + } + break; + + case CMD_READCOMPDATAHEAD: + /*read comp data header*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData( + (u16) ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + if (res < OK) { + logError(0, "%s Error requesting:%02X\n", tag, res); + } else { + logError(0, + "%s Requesting Compensation Data Finished!\n", tag); + res = readCompensationDataHeader( + (u16)((((u8)functionToTest[1] & 0x00FF) << 8) + +((u8)functionToTest[2] & 0x00FF)), + &dataHead, + &address); + if (res < OK) { + logError(0, "%s Read Header ERROR:%02X\n", + tag, res); + } else { + logError(0, "%s Read Header OK!\n", tag); + size += (2 * sizeof(u8)) * 2; + } + } + break; + case CMD_READMSCOMPDATA: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*read mutual comp data */ + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData( + (u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), + &compData); + + if (res < OK) { + logError(0, "%s Error reading MS compe data:%02X\n", + tag, res); + } else { + logError(0, "%s MS Compensa Reading Finished!\n", + tag); + + size = ((compData.node_data_size + 9) * sizeof(u8)) * 2; + print_frame_u8("MS Data (Cx2) = ", + array1dTo2d_u8(compData.node_data, + compData.node_data_size, + compData.header.sense_node), + compData.header.force_node, + compData.header.sense_node); + } + break; + case CMD_READSSCOMPDATA: + if (numberParam != 3) { + logError(1, "%sWrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /*read self comp data*/ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData((u16) + ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), + &comData); + if (res < OK) { + logError(0, "%s Error reading SS Compensa data %02X\n", + tag, res); + } else { + logError(0, "%s SS Compensa Reading Finished!\n", tag); + size = comData.header.force_node + + comData.header.sense_node; + size = (size * 2 + 12) * sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header.force_node, + comData.header.force_node), + 1, + comData.header.force_node); + print_frame_u8("SS Data Cx2_fm = ", + array1dTo2d_u8(comData.cx2_fm, + comData.header.force_node, + comData.header.force_node), + 1, + comData.header.force_node); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", + array1dTo2d_u8(comData.cx2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + } + break; + + case CMD_READGNCOMPDATA: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*read self comp data */ + logError(0, "%s Get General Compensation Data...\n", tag); + res = readGeneralCompensationData((u16) + ((((u8)functionToTest[1] + & 0x00FF) << 8) + ((u8)functionToTest[2] + & 0x00FF)), &gnData); + if (res < OK) { + logError(0, + "%s Reading General compensa data ERROR %02X\n", + tag, res); + } else { + logError(0, "%s:General compensa Reading Finished!\n", + tag); + size = (14) * sizeof(u8) * 2; + } + break; + case CMD_GETFWVER: + res = getFirmwareVersion(&fw_version, &config_id); + if (res < OK) { + logError(1, "%s Reading firmware version ERROR %02X\n", + tag, res); + } else { + logError(0, "%s getFirmware Version Finished!\n", tag); + size += (4) * sizeof(u8) * 2; + } + break; +#ifdef FTM3_CHIP + case CMD_FLASHSTATUS: + res = flash_status(); + /*return 0 = flash ready, 1 = flash busy, <0 error*/ + if (res < OK) { + logError(1, "%s Reading flash status ERROR %02X\n", + tag, res); + } else { + logError(0, "%s Flash Status: %d\n", tag, res); + size += (1 * sizeof(u8)) * 2; + /*need to store the value for further display */ + temp = res; + + /*set res =ok for returning code*/ + res = OK; + } + break; +#endif + case CMD_FLASHUNLOCK: + res = flash_unlock(); + if (res < OK) { + logError(1, "%s:Impossible Unlock Flash ERROR %02X\n", + tag, res); + } else { + logError(0, "%s Flash Unlock OK!\n", tag); + } + break; + case CMD_READFWFILE: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*read fw file */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, functionToTest[1]); + if (res < OK) { + logError(0, "%s Error reading FW File:%02X\n", + tag, res); + } else { + logError(0, "%s Read FW File Finished!\n", tag); + } + kfree(fw.data); + break; + case CMD_FLASHPROCEDURE: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*flashing procedure*/ + logError(0, "%s Starting Flashing Procedure\n", tag); + res = flashProcedure(PATH_FILE_FW, + functionToTest[1], functionToTest[2]); + if (res < OK) { + logError(0, "%s During flash procedure ERROR %02X", + tag, res); + } else { + logError(0, "%s Flash Procedure Finished!\n", tag); + } + break; + + /*ITO TEST*/ + case CMD_ITOTEST: + res = production_test_ito(); + break; + + /*Initialization*/ + case CMD_INITTEST: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if if save value on Flash*/ + if (functionToTest[1] == 0x01) + res = production_test_initialization(); + else + res = production_test_split_initialization(false); + break; + + case CMD_MSRAWTEST: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /* MS Raw DATA TEST*/ + /* need to specify if stopOnFail */ + res = production_test_ms_raw(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_MSINITDATATEST: + /*MS CX DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail*/ + res = production_test_ms_cx(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_SSRAWTEST: + /*SS RAW DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail*/ + res = production_test_ss_raw(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_SSINITDATATEST: + /*SS IX CX DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail*/ + res = production_test_ss_ix_cx(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_MAINTEST: + /*PRODUCTION TEST*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail and saveInit*/ + res = production_test_main(LIMITS_FILE, functionToTest[1], + functionToTest[2], &todoDefault, INIT_FIELD); + break; + + case CMD_POWERCYCLE: + res = fts_chip_powercycle(info); + break; + + default: + logError(1, "%s COMMAND ID NOT VALID!!\n", tag); + logError(1, "%s Inset a value between 00 and 1E.\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + +END: + /** + * here start the reporting phase, + * assembling the data to send in the file node + */ + all_strbuff = kmalloc(size, GFP_KERNEL); + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already*/ + /*fine printing only the res.*/ + switch (functionToTest[0]) { + case CMD_READ: + case CMD_READU16: + case CMD_READB2: + case CMD_READB2U16: + case CMD_POLLFOREVENT: + for (j = 0; j < byteToRead; j++) { + snprintf(buff, sizeof(buff), "%02X", + readData[j]); + strlcat(all_strbuff, buff, size); + } + break; + + case CMD_GETFORCELEN: + case CMD_GETSENSELEN: + case CMD_FLASHSTATUS: + snprintf(buff, sizeof(buff), "%02X", (u8)temp); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETMSFRAME: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8)frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + kfree(frameMS.node_data); + break; + + case CMD_GETSSFRAME: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8)frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case CMD_READMSCOMPDATA: + snprintf(buff, sizeof(buff), "%02X", + (u8)compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8)compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof(buff), "%02X", + compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%02X", + *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + kfree(compData.node_data); + break; + + case CMD_READSSCOMPDATA: + snprintf(buff, sizeof(buff), "%02X", + comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + /* Copying IX2 Sense*/ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + case CMD_READGNCOMPDATA: + snprintf(buff, sizeof(buff), "%02X", + gnData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal2); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal3); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsa_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsa_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETFWVER: + snprintf(buff, sizeof(buff), "%04X", fw_version); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%04X", config_id); + strlcat(all_strbuff, buff, size); + break; + + case CMD_READCOMPDATAHEAD: + snprintf(buff, sizeof(buff), "%02X", + dataHead.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + dataHead.sense_node); + strlcat(all_strbuff, buff, size); + break; + + default: + break; + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParam = 0; + /** + * need to reset the number of parameters + * in order to wait the next command, + * comment if you want to repeat + * the last command sent just doing a cat + */ + + kfree(readData); + kfree(all_strbuff); + return count; +} + +static DEVICE_ATTR_RW(stm_driver_test); + +static struct attribute *test_cmd_attributes[] = { + &dev_attr_stm_driver_test.attr, + NULL, +}; + +struct attribute_group test_cmd_attr_group = { + .attrs = test_cmd_attributes, +}; +#endif diff --git a/drivers/input/touchscreen/st/fts_gui.c b/drivers/input/touchscreen/st/fts_gui.c new file mode 100644 index 000000000000..4adec7d96ef3 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_gui.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include "fts.h" +#include "fts_lib/ftsIO.h" + +#ifdef SCRIPTLESS + +static unsigned int fts_data[CMD_RESULT_STR_LEN] = {0}; +static unsigned char fts_pAddress_i2c[CMD_RESULT_STR_LEN] = {0}; +static int byte_count_read; +static char Out_buff[TSP_BUF_SIZE]; + + +/*I2C CMd functions: functions to interface with GUI without script */ + +ssize_t fts_i2c_wr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i; + char buff[16]; + + memset(Out_buff, 0x00, sizeof(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}\n"); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ {\n", __func__); + for (i = 0; i < byte_count_read; i++) { + pr_err(" %02X\n", (unsigned int)info->cmd_wr_result[i]); + if (i < (byte_count_read - 1)) + pr_err("\n"); + } + pr_err("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + + } else { + snprintf(buff, sizeof(buff), "%02X", + info->cmd_wr_result[i-2]); + } + //snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i]); + strlcat(Out_buff, buff, sizeof(Out_buff)); + if (i < (byte_count_read + 1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, sizeof(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_wr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[9]; + unsigned int byte_count = 0; + int i; + unsigned int data[9]; + + memset(data, 0x00, sizeof(data)); + memset(pAddress, 0x00, sizeof(pAddress)); + memset(info->cmd_wr_result, 0x00, CMD_RESULT_STR_LEN); + ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x ", + (data + 8), (data), (data + 1), (data + 2), (data + 3), + (data + 4), (data + 5), (data + 6), (data + 7)); + + byte_count = data[8]; + + /** + * if(sizeof(buf) != byte_count ) + * { + * printk("%s : Byte count is wrong\n",__func__); + * return count; + * } + */ + + if (byte_count > sizeof(pAddress)) + return -EINVAL; +#ifdef SCRIPTLESS_DEBUG + + pr_err("%s: Input Data 1:\n", __func__); + + for (i = 0 ; i < byte_count; i++) { + pr_err(" %02X\n", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + pr_err("\n"); +#else + for (i = 0 ; i < byte_count; i++) + pAddress[i] = (unsigned char)data[i]; +#endif + byte_count_read = (((unsigned int)data[byte_count - 2]) << 8) + | data[byte_count - 1]; + ret = fts_writeCmd(pAddress, 3); + msleep(20); + ret = fts_readCmd(&pAddress[3], (byte_count - 5), + info->cmd_wr_result, byte_count_read); +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ {\n", __func__); + for (i = 0; i < (2 + byte_count_read); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + + } else { + pr_err("%02X\n", + (unsigned int)info->cmd_read_result[i - 2]); + } + + if (i < (byte_count_read + 1)) + pr_err("\n"); + + } + pr_err("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + +ssize_t fts_i2c_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i; + char buff[16]; + + memset(Out_buff, 0x00, sizeof(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ {\n", __func__); + for (i = 0; i < byte_count_read; i++) { + pr_err("%02X\n", (unsigned int)info->cmd_read_result[i]); + if (i < (byte_count_read - 1)) + pr_err("\n"); + } + pr_err("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else { + snprintf(buff, sizeof(buff), "%02X", + info->cmd_read_result[i - 2]); + } + strlcat(Out_buff, buff, sizeof(Out_buff)); + if (i < (byte_count_read + 1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, sizeof(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_read_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[9]; + unsigned int byte_count = 0; + int i; + unsigned int data[9]; + + byte_count_read = 0; + memset(data, 0x00, sizeof(data)); + memset(pAddress, 0x00, sizeof(pAddress)); + memset(info->cmd_read_result, 0x00, CMD_RESULT_STR_LEN); + ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x ", + (data + 8), (data), (data + 1), (data + 2), (data + 3), + (data + 4), (data + 5), (data + 6), (data + 7)); + + byte_count = data[8]; + + if (byte_count > 8) { +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:Byte count is more than 8\n", __func__); +#endif + return count; + } + /*if(sizeof(buf) != byte_count )*/ + /*{*/ + /* printk("%s : Byte count is wrong\n",__func__);*/ + /* return count;*/ + /*}*/ +#ifdef SCRIPTLESS_DEBUG + pr_err("%s: Input Data 1:\n", __func__); + for (i = 0 ; i < byte_count; i++) { + pr_err("%02X\n", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + pr_err("\n"); +#else + for (i = 0 ; i < byte_count; i++) + pAddress[i] = (unsigned char)data[i]; +#endif + byte_count_read = (((unsigned int)data[byte_count - 2]) << 8) + | data[byte_count - 1]; + ret = fts_readCmd(pAddress, (byte_count - 2), info->cmd_read_result, + byte_count_read); +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ\n{\n", __func__); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + + } else { + pr_err("%02X\n", + (unsigned int)info->cmd_read_result[i - 2]); + } + if (i < (byte_count_read + 1)) + pr_err("\n"); + } + pr_err("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + + +ssize_t fts_i2c_write_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + return snprintf(buf, TSP_BUF_SIZE, "%s", info->cmd_write_result); + +} + +ssize_t fts_i2c_write_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned int byte_count = 0; + int i; + unsigned int *data = &fts_data[0]; + + memset(fts_data, 0x00, sizeof(fts_data)); + memset(fts_pAddress_i2c, 0x00, sizeof(fts_pAddress_i2c)); + memset(info->cmd_write_result, 0x00, sizeof(info->cmd_write_result)); + ret = sscanf(buf, "%x %x", data, (data + 1)); + if (ret != 2) + return -EINVAL; + byte_count = data[0] << 8 | data[1]; + + if (byte_count <= sizeof(fts_pAddress_i2c)) { + for (i = 0; i < (byte_count); i++) { + ret = sscanf(&buf[3 * (i + 2)], "%x ", (data + i)); + if (ret != 1) + return -EINVAL; + } + } else { +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:message size is > allowed limit of 512 bytes\n", + __func__); +#endif + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write NOT OK}\n"); + return -EINVAL; + } +#ifdef SCRIPTLESS_DEBUG + pr_err("\n"); + pr_err("%s:Byte_count = %02d|Count = %02d |size of buf:%02d\n", + __func__, byte_count, (int)count, (int)sizeof(buf)); + pr_err("%s: Input Data 1:\n", __func__); + for (i = 0 ; i < byte_count; i++) { + pr_err(" %02X\n", data[i]); + fts_pAddress_i2c[i] = (unsigned char)data[i]; + } + pr_err("\n"); +#else + for (i = 0 ; i < byte_count; i++) + fts_pAddress_i2c[i] = (unsigned char)data[i]; +#endif + if ((fts_pAddress_i2c[0] == 0xb3) && (fts_pAddress_i2c[3] == 0xb1)) { + ret = fts_writeCmd(fts_pAddress_i2c, 3); + msleep(20); + ret = fts_writeCmd(&fts_pAddress_i2c[3], byte_count-3); + } else + ret = fts_writeCmd(fts_pAddress_i2c, byte_count); + +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA :\n", __func__); + for (i = 0; i < byte_count; i++) + pr_err(" %02X\n", (unsigned int)fts_pAddress_i2c[i]); + pr_err(" byte_count: %02X\n", byte_count); +#endif + if (ret < 0) { + dev_err(dev, "{Write NOT OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write NOT OK}\n"); + } else { + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write OK}\n"); +#ifdef SCRIPTLESS_DEBUG + pr_err("%s : {Write OK}\n", __func__); +#endif + } + return count; +} + +static DEVICE_ATTR_RW(fts_i2c_read); +static DEVICE_ATTR_RW(fts_i2c_wr); +static DEVICE_ATTR_RW(fts_i2c_write); + +static struct attribute *i2c_cmd_attributes[] = { + &dev_attr_fts_i2c_read.attr, + &dev_attr_fts_i2c_wr.attr, + &dev_attr_fts_i2c_write.attr, + NULL, +}; + +struct attribute_group i2c_cmd_attr_group = { + .attrs = i2c_cmd_attributes, +}; + + #endif diff --git a/drivers/input/touchscreen/st/fts_lib/Makefile b/drivers/input/touchscreen/st/fts_lib/Makefile new file mode 100644 index 000000000000..b37f3cbfd6e9 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the FTS touchscreen driver. +# + +obj-$(CONFIG_TOUCHSCREEN_ST) += ftsCompensation.o \ + ftsCrossCompile.o ftsError.o ftsFrame.o ftsIO.o ftsTest.o \ + ftsTime.o ftsTool.o ftsFlash.o ftsGesture.o + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c new file mode 100644 index 000000000000..2bffaa90607e --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting Initialization Data * + * * + ************************************************************************** + ************************************************************************** + */ + +#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 "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" + + +static char tag[8] = "[ FTS ]\0"; +struct chipInfo ftsInfo; + +int requestCompensationData(u16 type) +{ + int retry = 0; + int ret; + char *temp = NULL; + u16 answer; + + int event_to_search[3]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_COMP_DATA, 0x00, 0x00}; + /* B8 is the command for asking compensation data*/ + u16ToU8(type, &cmd[1]); + + event_to_search[0] = (int)EVENTID_COMP_DATA_READ; + event_to_search[1] = cmd[1]; + event_to_search[2] = cmd[2]; + + while (retry < COMP_DATA_READ_RETRY) { + temp = printHex("Command = ", cmd, 3); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + ret = fts_writeFwCmd(cmd, 3); + /*send the request to the chip to load*/ + /*in memory the Compensation Data*/ + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, 3, readEvent, + TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp!\n", + tag, retry + 1); + retry += 1; + } else { + retry = 0; + break; + } + } + + if (retry == COMP_DATA_READ_RETRY) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + + logError(1, "%sThe event found has a different type of ", tag); + logError(1, "Compensation data %02X\n", ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + +int readCompensationDataHeader(u16 type, struct DataHeader *header, + u16 *address) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[COMP_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, COMP_DATA_HEADER, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s %s:%02X The Header Signature was wrong!", + tag, __func__, ERROR_WRONG_COMP_SIGN); + logError(1, "%02X != %02X\n", data[0], HEADER_SIGNATURE); + + return ERROR_WRONG_COMP_SIGN; + } + + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Compensation data OK!\n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + *address = offset + COMP_DATA_HEADER; + + return OK; + +} + +int readMutualSenseGlobalData(u16 *address, struct MutualSenseData *global) +{ + + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X\n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + + return ERROR_I2C_R; + } + logError(0, "%s Global data Read!\n", tag); + + global->tuning_ver = data[0]; + global->cx1 = data[1]; + + logError(0, "%s tuning_ver = %d CX1 = %d\n", + tag, global->tuning_ver, global->cx1); + + *address += COMP_DATA_GLOBAL; + return OK; +} + + + +int readMutualSenseNodeData(u16 address, struct MutualSenseData *node) +{ + int size = node->header.force_node*node->header.sense_node; + + logError(0, "%s Address for Node data = %02X\n", tag, address); + + node->node_data = (u8 *)kmalloc_array(size, (sizeof(u8)), GFP_KERNEL); + + if (node->node_data == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, node->node_data, + size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_I2C_R); + kfree(node->node_data); + return ERROR_I2C_R; + } + node->node_data_size = size; + + logError(0, "%s Read node data ok!\n", tag); + + return size; +} + + +int readMutualSenseCompensationData(u16 type, struct MutualSenseData *data) +{ + int ret; + u16 address; + + data->node_data = NULL; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER + || type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s %s: Choose a MS type of compensation data ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret|ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + ret = readMutualSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return (ret|ERROR_COMP_DATA_GLOBAL); + } + + ret = readMutualSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_NODE); + return (ret | ERROR_COMP_DATA_NODE); + } + + return OK; +} + + +int readSelfSenseGlobalData(u16 *address, struct SelfSenseData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X\n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + logError(0, "%s Global data Read!\n", tag); + + global->tuning_ver = data[0]; + global->f_ix1 = data[1]; + global->s_ix1 = data[2]; + global->f_cx1 = data[3]; + global->s_cx1 = data[4]; + global->f_max_n = data[5]; + global->s_max_n = data[6]; + + logError(0, + "%stuning_ver = %df_ix1 = %ds_ix1 = %df_cx1 = %d s_cx1 = %d\n", + tag, global->tuning_ver, global->f_ix1, + global->s_ix1, global->f_cx1, global->s_cx1); + logError(0, "%s max_n = %d s_max_n = %d\n", + tag, global->f_max_n, global->s_max_n); + + *address += COMP_DATA_GLOBAL; + return OK; +} + +int readSelfSenseNodeData(u16 address, struct SelfSenseData *node) +{ + int size = node->header.force_node * 2 + node->header.sense_node * 2; + u8 *data; + + node->ix2_fm = (u8 *)kmalloc_array(node->header.force_node, + sizeof(u8), GFP_KERNEL); + if (node->ix2_fm == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + node->cx2_fm = (u8 *)kmalloc_array(node->header.force_node, + sizeof(u8), GFP_KERNEL); + if (node->cx2_fm == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + return ERROR_ALLOC; + } + node->ix2_sn = (u8 *)kmalloc_array(node->header.sense_node, + sizeof(u8), GFP_KERNEL); + if (node->ix2_sn == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + return ERROR_ALLOC; + } + node->cx2_sn = (u8 *)kmalloc_array(node->header.sense_node, + sizeof(u8), GFP_KERNEL); + if (node->cx2_sn == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + return ERROR_ALLOC; + } + + logError(0, "%s Address for Node data = %02X\n", tag, address); + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + data = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (data == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(node->cx2_sn); + return ERROR_ALLOC; + } + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(node->cx2_sn); + kfree(data); + return ERROR_I2C_R; + } + + logError(0, "%s Read node data ok!\n", tag); + + memcpy(node->ix2_fm, data, node->header.force_node); + memcpy(node->ix2_sn, &data[node->header.force_node], + node->header.sense_node); + memcpy(node->cx2_fm, + &data[node->header.force_node + node->header.sense_node], + node->header.force_node); + memcpy(node->cx2_sn, + &data[node->header.force_node * 2 + node->header.sense_node], + node->header.sense_node); + + kfree(data); + + return OK; +} + +int readSelfSenseCompensationData(u16 type, struct SelfSenseData *data) +{ + + int ret; + u16 address; + + data->ix2_fm = NULL; + data->cx2_fm = NULL; + data->ix2_sn = NULL; + data->cx2_sn = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER + || type == SS_PROXIMITY)) { + logError(1, "%s %s:Choose a SS type of compensation data ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret|ERROR_COMP_DATA_HEADER); + } + + ret = readSelfSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return (ret | ERROR_COMP_DATA_GLOBAL); + } + + ret = readSelfSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_NODE); + return (ret | ERROR_COMP_DATA_NODE); + } + + return OK; +} + + +int readGeneralGlobalData(u16 address, struct GeneralData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, COMP_DATA_GLOBAL, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + global->ftsd_lp_timer_cal0 = data[0]; + global->ftsd_lp_timer_cal1 = data[1]; + global->ftsd_lp_timer_cal2 = data[2]; + global->ftsd_lp_timer_cal3 = data[3]; + global->ftsa_lp_timer_cal0 = data[4]; + global->ftsa_lp_timer_cal1 = data[5]; + + return OK; +} + + +int readGeneralCompensationData(u16 type, struct GeneralData *data) +{ + int ret; + u16 address; + + if (!(type == GENERAL_TUNING)) { + logError(1, "%s %s:Choose a GENERAL type of compensation data ", + tag); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return ERROR_COMP_DATA_HEADER; + } + + ret = readGeneralGlobalData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return ERROR_COMP_DATA_GLOBAL; + } + + return OK; + +} + + +int defaultChipInfo(int i2cError) +{ + int i; + + logError(0, "%s Setting default Chip Info...\n", tag); + ftsInfo.u32_echoEn = 0x00000000; + ftsInfo.u8_msScrConfigTuneVer = 0; + ftsInfo.u8_ssTchConfigTuneVer = 0; + ftsInfo.u8_msScrCxmemTuneVer = 0; + ftsInfo.u8_ssTchCxmemTuneVer = 0; + if (i2cError == 1) { + ftsInfo.u16_fwVer = 0xFFFF; + ftsInfo.u16_cfgId = 0xFFFF; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) + ftsInfo.u8_extReleaseInfo[i] = 0xFF; + } else { + ftsInfo.u16_fwVer = 0x0000; + ftsInfo.u16_cfgId = 0x0000; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) + ftsInfo.u8_extReleaseInfo[i] = 0x00; + } + ftsInfo.u32_mpPassFlag = INIT_FIELD; + ftsInfo.u16_errOffset = INVALID_ERROR_OFFS; + logError(0, "%s default Chip Info DONE!\n", tag); + return OK; +} + +int readChipInfo(int doRequest) +{ + int ret, i; + u16 answer; + u8 data[CHIP_INFO_SIZE + 3]; + /*+3 because need to read all the field of*/ + /*the struct plus the signature and 2 address bytes*/ + int index = 0; + + logError(0, "%s Starting Read Chip Info...\n", tag); + if (doRequest == 1) { + ret = requestCompensationData(CHIP_INFO); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + ret = (ret | ERROR_REQU_COMP_DATA); + goto FAIL; + } + } + + logError(0, "%s Byte to read = %d bytes\n", tag, CHIP_INFO_SIZE + 3); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, ADDR_FRAMEBUFFER_DATA, data, + CHIP_INFO_SIZE + 3, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + ret = ERROR_I2C_R; + goto FAIL; + } + + logError(0, "%s Read data ok!\n", tag); + + logError(0, "%s Starting parsing of data...\n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s %s:ERROR ", tag, __func__); + logError(1, "%02X The Header Signature is wrong!%02X != %02X\n", + ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + ret = ERROR_WRONG_COMP_SIGN; + goto FAIL; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != CHIP_INFO) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + ret = ERROR_DIFF_COMP_TYPE; + goto FAIL; + } + + index += 3; + ftsInfo.u8_loadCnt = data[index++]; + ftsInfo.u8_infoVer = data[index++]; + u8ToU16(&data[index], &ftsInfo.u16_ftsdId); + index += 2; + ftsInfo.u8_ftsdVer = data[index++]; + ftsInfo.u8_ftsaId = data[index++]; + ftsInfo.u8_ftsaVer = data[index++]; + ftsInfo.u8_tchRptVer = data[index++]; + + logError(0, "%s External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = data[index++]; + logError(0, "%02X ", ftsInfo.u8_extReleaseInfo[i]); + } + logError(0, "\n"); + + for (i = 0; i < sizeof(ftsInfo.u8_custInfo); i++) + ftsInfo.u8_custInfo[i] = data[index++]; + + u8ToU16(&data[index], &ftsInfo.u16_fwVer); + index += 2; + logError(1, "%s FW VERSION = %04X\n", tag, ftsInfo.u16_fwVer); + + u8ToU16(&data[index], &ftsInfo.u16_cfgId); + index += 2; + logError(1, "%s CONFIG ID = %04X\n", tag, ftsInfo.u16_cfgId); + + ftsInfo.u32_projId = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + + u8ToU16(&data[index], &ftsInfo.u16_scrXRes); + index += 2; + + u8ToU16(&data[index], &ftsInfo.u16_scrYRes); + index += 2; + + ftsInfo.u8_scrForceLen = data[index++]; + logError(0, "%s Force Len = %d\n", tag, ftsInfo.u8_scrForceLen); + + ftsInfo.u8_scrSenseLen = data[index++]; + logError(0, "%s Sense Len = %d\n", tag, ftsInfo.u8_scrSenseLen); + + for (i = 0; i < 8; i++) + ftsInfo.u64_scrForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_scrSenseEn[i] = data[index++]; + + ftsInfo.u8_msKeyLen = data[index++]; + logError(0, "%s MS Key Len = %d\n", tag, ftsInfo.u8_msKeyLen); + + for (i = 0; i < 8; i++) + ftsInfo.u64_msKeyForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_msKeySenseEn[i] = data[index++]; + + ftsInfo.u8_ssKeyLen = data[index++]; + logError(0, "%s SS Key Len = %d\n", tag, ftsInfo.u8_ssKeyLen); + + for (i = 0; i < 8; i++) + ftsInfo.u64_ssKeyForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_ssKeySenseEn[i] = data[index++]; + + ftsInfo.u8_frcTchXLen = data[index++]; + + ftsInfo.u8_frcTchYLen = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_frcTchForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_frcTchSenseEn[i] = data[index++]; + + + ftsInfo.u8_msScrConfigTuneVer = data[index++]; + logError(0, "%s CFG MS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_msScrConfigTuneVer); + ftsInfo.u8_msScrLpConfigTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpConfigTuneVer = data[index++]; + ftsInfo.u8_msKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssTchConfigTuneVer = data[index++]; + logError(0, "%s CFG SS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_ssTchConfigTuneVer); + ftsInfo.u8_ssKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssHvrConfigTuneVer = data[index++]; + ftsInfo.u8_frcTchConfigTuneVer = data[index++]; + ftsInfo.u8_msScrCxmemTuneVer = data[index++]; + logError(0, "%s CX MS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_msScrCxmemTuneVer); + ftsInfo.u8_msScrLpCxmemTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpCxmemTuneVer = data[index++]; + ftsInfo.u8_msKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssTchCxmemTuneVer = data[index++]; + logError(0, "%s CX SS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_ssTchCxmemTuneVer); + ftsInfo.u8_ssKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssHvrCxmemTuneVer = data[index++]; + ftsInfo.u8_frcTchCxmemTuneVer = data[index++]; + ftsInfo.u32_mpPassFlag = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + logError(0, "%s MP SIGNATURE = %08X\n", tag, ftsInfo.u32_mpPassFlag); + ftsInfo.u32_featEn = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + ftsInfo.u32_echoEn = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + logError(0, "%s FEATURES = %08X\n", tag, ftsInfo.u32_echoEn); + ftsInfo.u8_sideTchConfigTuneVer = data[index++]; + ftsInfo.u8_sideTchCxmemTuneVer = data[index++]; + ftsInfo.u8_sideTchForceLen = data[index++]; + logError(0, "%s Side Touch Force Len = %d\n", + tag, ftsInfo.u8_sideTchForceLen); + ftsInfo.u8_sideTchSenseLen = data[index++]; + logError(0, "%s Side Touch Sense Len = %d\n", + tag, ftsInfo.u8_sideTchSenseLen); + for (i = 0; i < 8; i++) + ftsInfo.u64_sideTchForceEn[i] = data[index++]; + for (i = 0; i < 8; i++) + ftsInfo.u64_sideTchSenseEn[i] = data[index++]; + ftsInfo.u8_errSign = data[index++]; + logError(0, "%s ERROR SIGNATURE = %02X\n", tag, ftsInfo.u8_errSign); + if (ftsInfo.u8_errSign == ERROR_SIGN_HEAD) { + logError(0, "%s Correct Error Signature found!\n", tag); + u8ToU16(&data[index], &ftsInfo.u16_errOffset); + } else { + logError(1, "%s Error Signature NOT FOUND!\n", tag); + ftsInfo.u16_errOffset = INVALID_ERROR_OFFS; + } + logError(0, "%s ERROR OFFSET = %04X\n", tag, ftsInfo.u16_errOffset); + index += 2; + logError(0, "%s Parsed %d bytes!\n", tag, index); + + + if (index != CHIP_INFO_SIZE + 3) { + logError(1, "%s %s: index = %d different from %d ERROR %02X\n", + tag, __func__, index, CHIP_INFO_SIZE + 3, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(0, "%s Chip Info Read DONE!\n", tag); + return OK; + +FAIL: + defaultChipInfo(isI2cError(ret)); + return ret; +} + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h new file mode 100644 index 000000000000..fcd1cccfe5bc --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_COMPENSATION_H +#define __FTS_COMPENSATION_H + + +#include "ftsCrossCompile.h" +#include "ftsSoftware.h" + + +#define COMP_DATA_READ_RETRY 2 + +//Bytes dimension of Compensation Data Format + +#define COMP_DATA_HEADER 8 +#define COMP_DATA_GLOBAL 8 + + +#define HEADER_SIGNATURE 0xA5 +#define INVALID_ERROR_OFFS 0xFFFF + +//Possible Compensation/Frame Data Type +#define GENERAL_TUNING 0x0100 +#define MS_TOUCH_ACTIVE 0x0200 +#define MS_TOUCH_LOW_POWER 0x0400 +#define MS_TOUCH_ULTRA_LOW_POWER 0x0800 +#define MS_KEY 0x1000 +#define SS_TOUCH 0x2000 +#define SS_KEY 0x4000 +#define SS_HOVER 0x8000 +#define SS_PROXIMITY 0x0001 +#define CHIP_INFO 0xFFFF + + +#define TIMEOUT_REQU_COMP_DATA 1000 //ms + +//CHIP INFO +#define CHIP_INFO_SIZE 161 +/*bytes to read from framebuffer (exclude the signature and the type*/ +/*because already checked during the reading)*/ +#define EXTERNAL_RELEASE_INFO_SIZE 8 //bytes + +struct DataHeader { + int force_node, sense_node; + u16 type; +}; + + +struct MutualSenseData { + struct DataHeader header; + u8 tuning_ver; + u8 cx1; + u8 *node_data; + int node_data_size; +}; + + +struct SelfSenseData { + struct DataHeader header; + u8 tuning_ver; + u8 f_ix1, s_ix1; + u8 f_cx1, s_cx1; + u8 f_max_n, s_max_n; + + u8 *ix2_fm; + u8 *ix2_sn; + u8 *cx2_fm; + u8 *cx2_sn; +}; + + +struct GeneralData { + struct DataHeader header; + u8 ftsd_lp_timer_cal0; + u8 ftsd_lp_timer_cal1; + u8 ftsd_lp_timer_cal2; + + u8 ftsd_lp_timer_cal3; + u8 ftsa_lp_timer_cal0; + u8 ftsa_lp_timer_cal1; +}; + +struct chipInfo { + u8 u8_loadCnt; ///< 03 - Load Counter + u8 u8_infoVer; ///< 04 - New chip info version + u16 u16_ftsdId; ///< 05 - FTSD ID + u8 u8_ftsdVer; ///< 07 - FTSD version + u8 u8_ftsaId; ///< 08 - FTSA ID + u8 u8_ftsaVer; ///< 09 - FTSA version + u8 u8_tchRptVer; ///< 0A - Touch report version (e.g. ST, Samsung etc) + + ///< 0B - External release information + u8 u8_extReleaseInfo[EXTERNAL_RELEASE_INFO_SIZE]; + u8 u8_custInfo[12]; ///< 13 - Customer information + u16 u16_fwVer; ///< 1F - Firmware version + u16 u16_cfgId; ///< 21 - Configuration ID + u32 u32_projId; ///< 23 - Project ID + u16 u16_scrXRes; ///< 27 - X resolution on main screen + u16 u16_scrYRes; ///< 29 - Y resolution on main screen + u8 u8_scrForceLen; ///< 2B - Number of force channel on main screen + u8 u8_scrSenseLen; ///< 2C - Number of sense channel on main screen + u8 u64_scrForceEn[8]; ///< 2D - Force channel enabled on main screen + u8 u64_scrSenseEn[8]; ///< 35 - Sense channel enabled on main screen + u8 u8_msKeyLen; ///< 3D - Number of MS Key channel + u8 u64_msKeyForceEn[8]; ///< 3E - MS Key force channel enable + u8 u64_msKeySenseEn[8]; ///< 46 - MS Key sense channel enable + u8 u8_ssKeyLen; ///< 4E - Number of SS Key channel + u8 u64_ssKeyForceEn[8]; ///< 4F - SS Key force channel enable + u8 u64_ssKeySenseEn[8]; ///< 57 - SS Key sense channel enable + u8 u8_frcTchXLen; ///< 5F - Number of force touch force channel + u8 u8_frcTchYLen; ///< 60 - Number of force touch sense channel + u8 u64_frcTchForceEn[8];///< 61 - Force touch force channel enable + u8 u64_frcTchSenseEn[8];///< 69 - Force touch sense channel enable + u8 u8_msScrConfigTuneVer; ///< 71 - MS screen tuning version in config + + ///< 72 - MS screen LP mode tuning version in config + u8 u8_msScrLpConfigTuneVer; + + ///< 73 - MS screen ultra low power mode tuning version in config + u8 u8_msScrHwulpConfigTuneVer; + u8 u8_msKeyConfigTuneVer; ///< 74 - MS Key tuning version in config + u8 u8_ssTchConfigTuneVer; ///< 75 - SS touch tuning version in config + u8 u8_ssKeyConfigTuneVer; ///< 76 - SS Key tuning version in config + u8 u8_ssHvrConfigTuneVer; ///< 77 - SS hover tuning version in config + + ///< 78 - Force touch tuning version in config + u8 u8_frcTchConfigTuneVer; + u8 u8_msScrCxmemTuneVer; ///< 79 - MS screen tuning version in cxmem + + ///< 7A - MS screen LP mode tuning version in cxmem + u8 u8_msScrLpCxmemTuneVer; + + ///< 7B - MS screen ultra low power mode tuning version in cxmem + u8 u8_msScrHwulpCxmemTuneVer; + u8 u8_msKeyCxmemTuneVer; ///< 7C - MS Key tuning version in cxmem + u8 u8_ssTchCxmemTuneVer; ///< 7D - SS touch tuning version in cxmem + u8 u8_ssKeyCxmemTuneVer; ///< 7E - SS Key tuning version in cxmem + u8 u8_ssHvrCxmemTuneVer; ///< 7F - SS hover tuning version in cxmem + + ///< 80 - Force touch tuning version in cxmem + u8 u8_frcTchCxmemTuneVer; + u32 u32_mpPassFlag; ///< 81 - Mass production pass flag + u32 u32_featEn; ///< 85 - Supported features + + ///< 89 - enable of particular features: first bit is Echo Enables + u32 u32_echoEn; + + ///< 8D - Side Touch tuning version in config + u8 u8_sideTchConfigTuneVer; + u8 u8_sideTchCxmemTuneVer; ///< 8E - Side Touch tuning version in cxmem + u8 u8_sideTchForceLen; ///< 8F - Number of force channel on side touch + u8 u8_sideTchSenseLen; ///< 90 - Number of sense channel on side touch + u8 u64_sideTchForceEn[8];///< 91 - Side touch force channel enable + u8 u64_sideTchSenseEn[8];///< 99 - Side touch sense channel enable + u8 u8_errSign; ///< A1 - Signature for error field + u16 u16_errOffset; ///< A2 - Error Offset +}; + +int requestCompensationData(u16 type); +int readCompensationDataHeader(u16 type, struct DataHeader *header, + u16 *address); +int readMutualSenseGlobalData(u16 *address, struct MutualSenseData *global); +int readMutualSenseNodeData(u16 address, struct MutualSenseData *node); +int readMutualSenseCompensationData(u16 type, struct MutualSenseData *data); +int readSelfSenseGlobalData(u16 *address, struct SelfSenseData *global); +int readSelfSenseNodeData(u16 address, struct SelfSenseData *node); +int readSelfSenseCompensationData(u16 type, struct SelfSenseData *data); +int readGeneralGlobalData(u16 address, struct GeneralData *global); +int readGeneralCompensationData(u16 type, struct GeneralData *data); +int defaultChipInfo(int i2cError); +int readChipInfo(int doRequest); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c new file mode 100644 index 000000000000..e44899c0a6b5 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Cross Compile * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#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 "ftsCrossCompile.h" +#include "ftsError.h" + +void *stmalloc(size_t size) +{ + return kmalloc(size, GFP_KERNEL); +} + +void stfree(void *ptr) +{ + kfree(ptr); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h new file mode 100644 index 000000000000..15f51b3d72af --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS cross compile * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_CROSS_COMPILE_H +#define __FTS_CROSS_COMPILE_H + +//#define NDK +//#define DEBUG + +#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 + +void *stmalloc(size_t size); +void stfree(void *ptr); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.c b/drivers/input/touchscreen/st/fts_lib/ftsError.c new file mode 100644 index 000000000000..80919322e212 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#include +#include +#include + +#include "../fts.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsIO.h" +#include "ftsTool.h" +#include "ftsCompensation.h" + +static char tag[8] = "[ FTS ]\0"; + + +void logError(int force, const char *msg, ...) +{ + + if (force == 1 +#ifdef DEBUG + || 1 +#endif + ) { + va_list args; + + va_start(args, msg); + vprintk(msg, args); + va_end(args); + } +} + +int isI2cError(int error) +{ + if (((error & 0x000000FF) >= (ERROR_I2C_R & 0x000000FF)) + && ((error & 0x000000FF) <= (ERROR_I2C_O & 0x000000FF))) + return 1; + else + return 0; +} + +int dumpErrorInfo(void) +{ + int ret, i; + u8 data[ERROR_INFO_SIZE] = {0}; + u32 sign = 0; + + logError(0, "%s %s: Starting dump of error info...\n", tag, __func__); + if (ftsInfo.u16_errOffset == INVALID_ERROR_OFFS) { + logError(1, "%s %s: Invalid error offset ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, ftsInfo.u16_errOffset, + data, ERROR_INFO_SIZE, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: reading data ERROR %02X\n", + tag, __func__, ret); + return ret; + } + logError(0, "%s %s: Error Info =\n", tag, __func__); + u8ToU32(data, &sign); + if (sign != ERROR_SIGNATURE) + logError(1, "%s %s:Wrong Signature! Data may be invalid!\n", + tag, __func__); + else + logError(1, "%s %s: Error Signature OK! Data are valid!\n", + tag, __func__); + + for (i = 0; i < ERROR_INFO_SIZE; i++) { + if (i % 4 == 0) + logError(1, KERN_ERR "\n%s %s: %d) ", + tag, __func__, i / 4); + logError(1, "%02X ", data[i]); + } + logError(1, "\n"); + + logError(0, "%s %s: dump of error info FINISHED!\n", tag, __func__); + return OK; + +} + +int errorHandler(u8 *event, int size) +{ + int res = OK; + struct fts_ts_info *info = NULL; + + if (getClient() != NULL) + info = i2c_get_clientdata(getClient()); + + if (info == NULL || event == NULL || size <= 1 || event[0] != + EVENTID_ERROR_EVENT) { + logError(1, "%s %s: event Null or not correct size! ", + tag, __func__, ERROR_OP_NOT_ALLOW); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(0, "%s %s: Starting handling...\n", tag, __func__); + //TODO: write an error log for undefinied command subtype 0xBA + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: //esd + res = fts_chip_powercycle(info); + if (res < OK) { + logError(1, "%s %s: ", tag, res); + logError(1, "Error performing powercycle ERROR %08X\n"); + } + + res = fts_system_reset(); + if (res < OK) + logError(1, "%s %s:Cannot reset device ERROR%08X\n", + tag, __func__, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + + case EVENT_TYPE_WATCHDOG_ERROR: //watchdog + dumpErrorInfo(); + res = fts_system_reset(); + if (res < OK) + logError(1, "%s %s:Cannot reset device:ERROR%08X\n", + tag, __func__, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + case EVENT_TYPE_CHECKSUM_ERROR: //CRC ERRORS + switch (event[2]) { + case CRC_CONFIG_SIGNATURE: + logError(1, "%s %s: Config Signature ERROR!\n", + tag, __func__); + break; + case CRC_CONFIG: + logError(1, "%s %s:Config CRC ERROR!\n", tag, __func__); + break; + case CRC_CX_MEMORY: + logError(1, "%s %s: CX CRC ERROR!\n", tag, __func__); + break; + } + break; + case EVENT_TYPE_LOCKDOWN_ERROR: + //res = (ERROR_HANDLER_STOP_PROC|res); + //stop lockdown code routines in order to retry + switch (event[2]) { + case 0x01: + logError(1, "%s %s:Lockdown code alredy ", + tag, __func__); + logError(1, "written into the IC!\n"); + break; + case 0x02: + logError(1, "%s %s:Lockdown CRC ", tag, __func__); + logError(1, "check fail during a WRITE!\n"); + break; + + case 0x03: + logError(1, + "%s %s:Lockdown WRITE command format wrong!\n", + tag, __func__); + break; + case 0x04: + pr_err("Lockdown Memory Corrupted!\n"); + logError(1, "%s %s:Please contact ST for support!\n", + tag, __func__); + break; + case 0x11: + logError(1, + "%s %s:NO Lockdown code to READ into the IC!\n", + tag, __func__); + break; + case 0x12: + logError(1, + "%s %s:Lockdown code data corrupted\n", + tag, __func__); + break; + case 0x13: + logError(1, + "%s %s:Lockdown READ command format wrong!\n", + tag, __func__); + break; + case 0x21: + pr_err("Exceeded maximum number of\n"); + logError(1, + "%s %s:Lockdown code REWRITE into IC!\n", + tag, __func__); + break; + case 0x22: + logError(1, "%s %s:Lockdown CRC check", tag, __func__); + logError(1, " fail during a REWRITE!\n"); + break; + case 0x23: + logError(1, "%s %s:", tag, __func__); + logError(1, "Lockdown REWRITE command format wrong!\n"); + break; + case 0x24: + pr_err("Lockdown Memory Corrupted!\n"); + logError(1, "%s %s:Please contact ST for support!\n", + tag, __func__); + break; + default: + logError(1, "%s %s:No valid error type for LOCKDOWN!\n", + tag, __func__); + } + break; + + default: + logError(0, "%s %s: No Action taken!\n", tag, __func__); + break; + } + logError(0, "%s %s: handling Finished! res = %08X\n", + tag, __func__, res); + return res; +} + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.h b/drivers/input/touchscreen/st/fts_lib/ftsError.h new file mode 100644 index 000000000000..c96a5876918c --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_ERROR_H +#define __FTS_ERROR_H + + +//FIRST LEVEL ERROR CODE +#define OK (int)(0x00000000)/* No ERROR*/ +/*allocation of memory failed*/ +#define ERROR_ALLOC (int)(0x80000001) +#define ERROR_I2C_R (int)(0x80000002)//i2c read failed +#define ERROR_I2C_W (int)(0x80000003)//i2c write failed +#define ERROR_I2C_WR (int)(0x80000004)//i2c write/read failed + +//error during opening i2c device +#define ERROR_I2C_O (int)(0x80000005) +#define ERROR_OP_NOT_ALLOW (int)(0x80000006)//operation not allowed + +//timeout expired! exceed the max number of +//retries or the max waiting time +#define ERROR_TIMEOUT (int)(0x80000007) + +//the file that i want to open is not found +#define ERROR_FILE_NOT_FOUND (int)(0x80000008) +//error during parsing the file +#define ERROR_FILE_PARSE (int)(0x80000009) +//error during reading the file +#define ERROR_FILE_READ (int)(0x8000000A) +#define ERROR_LABEL_NOT_FOUND (int)(0x8000000B)//label not found + +//fw in the chip newer than the one in the memmh +#define ERROR_FW_NO_UPDATE (int)(0x8000000C) +//flash status busy or unknown +#define ERROR_FLASH_UNKNOWN (int)(0x8000000D) + +//SECOND LEVEL ERROR CODE +//unable to disable the interrupt +#define ERROR_DISABLE_INTER (int)(0x80000200) + +//unable to activate the interrupt +#define ERROR_ENABLE_INTER (int)(0x80000300) + +#define ERROR_READ_B2 (int)(0x80000400)//B2 command failed + +//unable to read an offset from memory +#define ERROR_GET_OFFSET (int)(0x80000500) + +//unable to retrieve the data of a required frame +#define ERROR_GET_FRAME_DATA (int)(0x80000600) + +//FW answers with an event that has a +//different address respect the request done +#define ERROR_DIFF_COMP_TYPE (int)(0x80000700) + +//the signature of the compensation data is not A5 +#define ERROR_WRONG_COMP_SIGN (int)(0x80000800) +//the command Sense On failed +#define ERROR_SENSE_ON_FAIL (int)(0x80000900) +//the command Sense Off failed +#define ERROR_SENSE_OFF_FAIL (int)(0x80000A00) + +//the command SYSTEM RESET failed +#define ERROR_SYSTEM_RESET_FAIL (int)(0x80000B00) + +//flash status not ready within a timeout +#define ERROR_FLASH_NOT_READY (int)(0x80000C00) + +//unable to retrieve fw_vers or the config_id +#define ERROR_FW_VER_READ (int)(0x80000D00) + +//unable to enable/disable the gesture +#define ERROR_GESTURE_ENABLE_FAIL (int)(0x80000E00) + +//unable to start to add custom gesture +#define ERROR_GESTURE_START_ADD (int)(0x80000F00) + +//unable to finish to add custom gesture +#define ERROR_GESTURE_FINISH_ADD (int)(0x80001000) + +//unable to add custom gesture data +#define ERROR_GESTURE_DATA_ADD (int)(0x80001100) + +//unable to remove custom gesture data +#define ERROR_GESTURE_REMOVE (int)(0x80001200) + +//unable to enable/disable a feature mode in the IC +#define ERROR_FEATURE_ENABLE_DISABLE (int)(0x80001300) + +//unable to set/read noise parameter in the IC +#define ERROR_NOISE_PARAMETERS (int)(0x80001400) + +//unable to write/rewrite/read lockdown code in the IC +#define ERROR_LOCKDOWN_CODE (int)(0x80001500) + +//THIRD LEVEL ERROR CODE +//unable to retrieve the force and/or sense length +#define ERROR_CH_LEN (int)(0x80010000) + +//compensation data request failed +#define ERROR_REQU_COMP_DATA (int)(0x80020000) + +//unable to retrieve the compensation data header +#define ERROR_COMP_DATA_HEADER (int)(0x80030000) + +//unable to retrieve the global compensation data +#define ERROR_COMP_DATA_GLOBAL (int)(0x80040000) + +//unable to retrieve the compensation data for each node +#define ERROR_COMP_DATA_NODE (int)(0x80050000) + +//check of production limits or of fw answers failed +#define ERROR_TEST_CHECK_FAIL (int)(0x80060000) +#define ERROR_MEMH_READ (int)(0x80070000)//memh reading failed +#define ERROR_FLASH_BURN_FAILED (int)(0x80080000)//flash burn failed +#define ERROR_MS_TUNING (int)(0x80090000)//ms tuning failed +#define ERROR_SS_TUNING (int)(0x800A0000)//ss tuning failed +//lp timer calibration failed +#define ERROR_LP_TIMER_TUNING (int)(0x800B0000) +//save cx data to flash failed +#define ERROR_SAVE_CX_TUNING (int)(0x800C0000) + +//stop the poll of the FIFO if particular errors are found +#define ERROR_HANDLER_STOP_PROC (int)(0x800D0000) +//unable to retrieve echo event +#define ERROR_CHECK_ECHO_FAIL (int)(0x800E0000) + +//FOURTH LEVEL ERROR CODE +//production data test failed +#define ERROR_PROD_TEST_DATA (int)(0x81000000) + +//complete flash procedure failed +#define ERROR_FLASH_PROCEDURE (int)(0x82000000) +//production ito test failed +#define ERROR_PROD_TEST_ITO (int)(0x83000000) + +//production initialization test failed +#define ERROR_PROD_TEST_INITIALIZATION (int)(0x84000000) + +//mismatch of the MS or SS tuning_version +#define ERROR_GET_INIT_STATUS (int)(0x85000000) + + +void logError(int force, const char *msg, ...); +int isI2cError(int error); +int dumpErrorInfo(void); +int errorHandler(u8 *event, int size); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c new file mode 100644 index 000000000000..9fc3926dec64 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#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 "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFlash.h" +#include "ftsFrame.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h"//needed for including the define FW_H_FILE + +#ifdef FW_H_FILE +#include <../fts_fw.h> +#define LOAD_FW_FROM 1 +#else +#define LOAD_FW_FROM 0 +#endif + +#define FTS_LATEST_VERSION 0x1101 + +static char tag[8] = "[ FTS ]\0"; + +int getFirmwareVersion(u16 *fw_vers, u16 *config_id) +{ + u8 fwvers[DCHIP_FW_VER_BYTE]; + u8 confid[CONFIG_ID_BYTE]; + int res; + + res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR, + fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG); + if (res < OK) { + logError(1, + "%s %s:unable to read fw_version ERROR %02X\n", + tag, __func__, ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + + u8ToU16(fwvers, fw_vers); //fw version use big endian + if (*fw_vers != 0) { + // if fw_version is 00 00 means that there is + //no firmware running in the chip therefore will be + //impossible find the config_id + res = readB2(CONFIG_ID_ADDR, confid, CONFIG_ID_BYTE); + if (res < OK) { + logError(1, "%s %s:unable to read config_id ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + u8ToU16(confid, config_id); //config id use little endian + } else { + *config_id = 0x0000; + } + + logError(0, "%s FW VERS = %04X\n", tag, *fw_vers); + logError(0, "%s CONFIG ID = %04X\n", tag, *config_id); + return OK; +} + +int getFWdata_nocheck(const char *pathToFile, u8 **data, int *size, int from) +{ + const struct firmware *fw = NULL; + struct device *dev = getDev(); + int res; + + if (dev == NULL) + return ERROR_OP_NOT_ALLOW; + + logError(0, "%s Read FW from BIN file!\n", tag); + + res = firmware_request_nowarn(&fw, pathToFile, dev); + if (res) { + logError(1, "%s %s:No File found! ERROR %08X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + + *size = fw->size; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to allocate! %08X\n", __func__); + release_firmware(fw); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)fw->data, (*size)); + release_firmware(fw); + + logError(0, "%s %s:Finshed!\n", tag, __func__); + return OK; +} + +int getFWdata(const char *pathToFile, u8 **data, int *size, int from) +{ + const struct firmware *fw = NULL; + struct device *dev = NULL; + int res; + + logError(0, "%s %s starting...\n", tag, __func__); + switch (from) { +#ifdef FW_H_FILE + case 1: + logError(1, "%s Read FW from .h file!\n", tag); + *size = FW_SIZE_NAME; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to allocate memory! ", + tag, __func__); + logError(1, "ERROR %08X\n", ERROR_ALLOC); + + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size)); + break; +#endif + default: + logError(0, "%s Read FW from BIN file!\n", tag); + + if (ftsInfo.u16_fwVer >= FTS_LATEST_VERSION) + return ERROR_FW_NO_UPDATE; + + dev = getDev(); + + if (dev != NULL) { + res = firmware_request_nowarn(&fw, pathToFile, dev); + if (res == 0) { + *size = fw->size; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), + GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to ", + tag, __func__); + logError(1, "%allocate! %08X\n", + ERROR_ALLOC); + release_firmware(fw); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)fw->data, (*size)); + release_firmware(fw); + } else { + logError(0, "%s %s:No File found! ERROR %08X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + } else { + logError(1, "%s %s:No device found! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + /* break; */ + } + + logError(0, "%s %s:Finshed!\n", tag, __func__); + return OK; +} + +int readFwFile(const char *path, struct Firmware *fw, int keep_cx) +{ + int res; + int orig_size; + u8 *orig_data = NULL; + + + res = getFWdata(path, &orig_data, &orig_size, LOAD_FW_FROM); + if (res < OK) { + logError(0, "%s %s:impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + + return (res | ERROR_MEMH_READ); + } + res = parseBinFile(orig_data, orig_size, fw, keep_cx); + + if (res < OK) { + logError(1, "%s %s:impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + + return OK; +} + +int flashProcedure(const char *path, int force, int keep_cx) +{ + struct Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(&fw, force, keep_cx); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + + return res; +} + +#ifdef FTM3_CHIP +int flash_status(void) +{ + u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00}; + u8 readData = 0; + + logError(0, "%s %s:Reading ...\n", tag, __func__); + if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + readData &= 0x01; + logError(0, "%s %s = %d\n", tag, __func__, readData); + return (int) readData; +} + +int flash_status_ready(void) +{ + + int status = flash_status(); + + if (status == ERROR_I2C_R) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + if (status != FLASH_READY) { + //logError(1, + //"%s %s:flash busy or unknown STATUS = % 02X\n", + //tag, status); + return ERROR_FLASH_UNKNOWN; + } + + return FLASH_READY; +} + +int wait_for_flash_ready(void) +{ + int status; + int (*code)(void); + + code = flash_status_ready; + + logError(0, "%s Waiting for flash ready...\n", tag); + status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY, + FLASH_RETRY_COUNT); + + if (status != FLASH_READY) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Flash ready!\n", tag); + return OK; +} + +int flash_unlock(void) +{ + int status; + //write the command to perform the unlock + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, + FLASH_UNLOCK_CODE1}; + + logError(0, "%s Try to unlock flash...\n", tag); + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, better exit! + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; +} + +int parseBinFile(u8 *fw_data, int fw_size, Firmware *fwData, int keep_cx) +{ + int dimension; + + if (keep_cx) { + dimension = FW_SIZE - FW_CX_SIZE; + logError(1, "%s %s: Selected 124k Configuration!\n", + tag, __func__); + } else { + dimension = FW_SIZE; + logError(1, "%s %s: Selected 128k Configuration!\n", + tag, __func__); + } + + if (fw_size - FW_HEADER_SIZE != FW_SIZE || fw_data == NULL) { + logError(1, "%s %s:Read only %d instead of %d... ERROR %02X\n", + tag, __func__, + fw_size - FW_HEADER_SIZE, + FW_SIZE, ERROR_FILE_PARSE); + kfree(fw_data); + return ERROR_FILE_PARSE; + } + + fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + + kfree(fw_data); + return ERROR_ALLOC; + } + + memcpy(fwData->data, ((u8 *)(fw_data) + FW_HEADER_SIZE), + dimension); + fwData->data_size = dimension; + + fwData->fw_ver = (u16)(((fwData->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8) + + (fwData->data[FW_VER_MEMH_BYTE0] & 0x00FF)); + + fwData->config_id = (u16)(((fwData->data[(FW_CODE_SIZE) + + FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8) + + (fwData->data[(FW_CODE_SIZE) + + FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF)); + + logError(0, "%s %s: FW VERS File = %04X\n", + tag, __func__, fwData->fw_ver); + logError(0, "%s %s: CONFIG ID File = %04X\n", + tag, __func__, fwData->config_id); + + logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size); + + kfree(fw_data); + return OK; +} + +int fillMemory(u32 address, u8 *data, int size) +{ + int remaining = size; + int toWrite = 0; + int delta; + + u8 *buff = (u8 *)kmalloc_array((MEMORY_CHUNK + 3), sizeof(u8), + GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= MEMORY_CHUNK) { + if ((address + MEMORY_CHUNK) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + delta = FLASH_ADDR_SWITCH_CMD - address; + + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } + } + } else { + if ((address + remaining) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = remaining; + remaining = 0; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + delta = FLASH_ADDR_SWITCH_CMD - address; + + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = remaining; + remaining = 0; + } + } + } + + buff[1] = (u8) ((address & 0x0000FF00) >> 8); + buff[2] = (u8) (address & 0x000000FF); + memcpy(buff + 3, data, toWrite); + //logError(0, + //"%s Command = %02X , address = %02X %02X, bytes = %d\n", + //tag, buff[0], buff[1], buff[2], toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + address += toWrite; + data += toWrite; + } + kfree(buff); + return OK; +} + +int flash_burn(Firmware *fw, int force_burn, int keep_cx) +{ + u8 cmd; + int res; + + if (!force_burn && (ftsInfo.u16_fwVer >= fw->fw_ver) + && (ftsInfo.u16_cfgId >= fw->config_id)) { + logError(0, "Firmware in the chip newer"); + logError(0, " or equal to the one to burn! "); + logError(0, "%s %s:NO UPDATE ERROR %02X\n", + tag, __func__, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + //programming procedure start + + logError(0, "%s Programming Procedure for flashing started:\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + //if there is no firmware i will not + //get the controller ready event and + //there will be a timeout but i can + //keep going, but if there is + //an I2C error i have to exit + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 2) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + + //Write the lower part of the Program RAM + logError(0, "%s 3) PREPARING DATA FOR FLASH BURN:\n", tag); + + res = fillMemory(FLASH_ADDR_CODE, fw->data, fw->data_size); + if (res < 0) { + logError(1, "%s Error During filling the memory!%02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s Data copy COMPLETED!\n\n", tag); + + logError(0, "%s 4) ERASE FLASH:\n", tag); + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Command erase ...\n", tag); + cmd = FLASH_CMD_ERASE; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during erasing flash! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready 2! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash erase COMPLETED!\n\n", tag); + + logError(0, "%s 5) BURN FLASH:\n", tag); + logError(0, "%s Command burn ...\n", tag); + cmd = FLASH_CMD_BURN; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during burning data! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 6) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + + logError(0, "%s 7) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s %s:Unable to retrieve Chip INFO!%02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + if ((ftsInfo.u16_fwVer != fw->fw_ver) + && (ftsInfo.u16_cfgId != fw->config_id)) { + logError(1, "Firmware in the chip different"); + logError(1, " from the one that was burn!"); + logError(1, "%s fw: %x != %x , conf: %x != %x\n", + tag, ftsInfo.u16_fwVer, + fw->fw_ver, + ftsInfo.u16_cfgId, + fw->config_id); + return ERROR_FLASH_BURN_FAILED; + } + + logError(0, "%s Final check OK! fw: %02X, conf: %02X\n", + tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +#else + +int wait_for_flash_ready(u8 type) +{ + u8 cmd[2] = {FLASH_CMD_READ_REGISTER, type}; + u8 readData = 0; + int i, res = -1; + + logError(0, "%s Waiting for flash ready ...\n", tag); + for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) { + if (fts_readCmd(cmd, sizeof(cmd), &readData, 1) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_R); + } else { + res = readData & 0x80; + //logError(0, "%s flash status = %d\n", tag, res); + } + msleep(FLASH_WAIT_BEFORE_RETRY); + } + + if (i == FLASH_RETRY_COUNT && res != 0) { + logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n", + tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + logError(0, "%s Flash READY!\n", tag); + return OK; +} + +int fts_warm_boot(void) +{ + //write the command to perform the warm boot + u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE}; + + u16ToU8_be(ADDR_WARM_BOOT, &cmd[1]); + + logError(0, "%s Command warm boot ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Warm boot DONE!\n", tag); + + return OK; +} + +int parseBinFile(u8 *data, int fw_size, + struct Firmware *fwData, int keep_cx) +{ + int dimension, index = 0; + u32 temp; + int res, i; + + //the file should contain at least the header plus the content_crc + if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN || data == NULL) { + logError(1, "%s %s:Read only %d instead of %d...ERROR %02X\n", + tag, __func__, fw_size, + FW_HEADER_SIZE + FW_BYTES_ALIGN, + ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } else { + //start parsing of bytes + u8ToU32(&data[index], &temp); + if (temp != FW_HEADER_SIGNATURE) { + logError(1, "%s %s:Wrong Signature %08X...ERROR %02X\n", + tag, __func__, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + + logError(0, "%s %s: Fw Signature OK!\n", tag, __func__); + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + if (temp != FW_FTB_VER) { + logError(1, "%s %s:Wrong ftb_version %08X.ERROR %02X\n", + tag, __func__, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s %s:ftb_version OK!\n", __func__, tag); + index += FW_BYTES_ALIGN; + if (data[index] != DCHIP_ID_0 || data[index+1] != DCHIP_ID_1) { + logError(1, "%s %s:Wrong target %02X != %02X ", + tag, __func__, data[index]); + logError(1, "%%02X != %02X:%08X\n", + DCHIP_ID_0, data[index+1], + DCHIP_ID_1, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(0, "%s %s: Fw ID = %08X\n", tag, __func__, temp); + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->fw_ver = temp; + logError(0, "%s %s:FILE Fw Version = %04X\n", + tag, __func__, fwData->fw_ver); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->config_id = temp; + logError(0, "%s %s:FILE Config ID = %04X\n", + tag, __func__, fwData->config_id); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(0, "%s %s:Config Version = %08X\n", + tag, __func__, temp); + //skip reserved data + index += FW_BYTES_ALIGN * 2; + index += FW_BYTES_ALIGN; + logError(0, "%s %s:File External Release = ", + tag, __func__); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + fwData->externalRelease[i] = data[index++]; + logError(0, "%02X", fwData->externalRelease[i]); + } + logError(0, "\n"); + + //index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec0_size = temp; + logError(0, "%s %s:sec0_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec0_size, fwData->sec0_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec1_size = temp; + logError(0, "%s %s:sec1_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec1_size, fwData->sec1_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec2_size = temp; + logError(0, "%s %s:sec2_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec2_size, fwData->sec2_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec3_size = temp; + logError(0, "%s %s:sec3_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec3_size, fwData->sec3_size); + + //skip header crc + index += FW_BYTES_ALIGN; + if (!keep_cx) { + dimension = fwData->sec0_size + fwData->sec1_size + + fwData->sec2_size + fwData->sec3_size; + temp = fw_size; + } else { + //sec2 may contain cx data (future implementation) + //sec3 atm not used + dimension = fwData->sec0_size + fwData->sec1_size; + temp = fw_size - fwData->sec2_size - fwData->sec3_size; + fwData->sec2_size = 0; + fwData->sec3_size = 0; + } + if (dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN != temp) { + logError(1, "%s %s:Read only %d instead of %d...", + tag, __func__, fw_size, + dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN); + logError(1, "ERROR %02X\n", ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), + GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + res = ERROR_ALLOC; + goto END; + } + index += FW_BYTES_ALIGN; + memcpy(fwData->data, &data[index], dimension); + fwData->data_size = dimension; + logError(0, "%s READ FW DONE %d bytes!\n", + tag, fwData->data_size); + res = OK; + goto END; + } +END: + kfree(data); + return res; +} + +int flash_unlock(void) +{ + //write the command to perform the unlock + u8 cmd[3] = {FLASH_CMD_UNLOCK, + FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; +} + +int flash_erase_unlock(void) +{ + //write the command to perform + //the unlock for erasing the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0, + FLASH_ERASE_UNLOCK_CODE1}; + + logError(0, "%s Try to erase unlock flash...\n", tag); + + logError(0, "%s Command erase unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Erase Unlock flash DONE!\n", tag); + + return OK; +} + +int flash_full_erase(void) +{ + int status; + //write the command to erase the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, + FLASH_ERASE_CODE1}; + + logError(0, "%s Command full erase sent...\n", + tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + + if (status != OK) { + logError(1, "%s %s:ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Full Erase flash DONE!\n", tag); + + return OK; +} + +int flash_erase_page_by_page(int keep_cx) +{ + u8 status, i = 0; + //write the command to erase the flash + u8 cmd[4] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, 0x00, 0x00}; + + for (i = 0; i < FLASH_NUM_PAGE; i++) { + if (i >= FLASH_CX_PAGE_START && i <= FLASH_CX_PAGE_END + && keep_cx == 1) { + logError(0, "%s Skipping erase page %d!\n", tag, i); + continue; + } + cmd[2] = (0x3F & i) | FLASH_ERASE_START; + logError(0, "Command erase page %d sent", i); + logError(0, "%s:%02X %02X %02X %02X\n", + tag, i, cmd[0], cmd[1], cmd[2], cmd[3]); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, + "%s %s:ERROR % 08X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + if (status != OK) { + logError(1, "%s %s:ERROR % 08X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + } + + logError(0, "%s Erase flash page by page DONE!\n", tag); + + return OK; +} + +int start_flash_dma(void) +{ + int status; + //write the command to erase the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0, + FLASH_DMA_CODE1}; + + logError(0, "%s Command flash DMA ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_DMA_CODE0); + + if (status != OK) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, better exit! + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s flash DMA DONE!\n", tag); + + return OK; +} + +int fillFlash(u32 address, u8 *data, int size) +{ + int remaining = size; + int toWrite = 0; + int byteBlock = 0; + int wheel = 0; + u32 addr = 0; + int res; + int delta; + u8 *buff = NULL; + u8 buff2[9] = {0}; + + + buff = (u8 *)kmalloc_array((DMA_CHUNK + 3), sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + byteBlock = 0; + addr = 0; + while (byteBlock < FLASH_CHUNK && remaining > 0) { + buff[0] = FLASH_CMD_WRITE_64K; + if (remaining >= DMA_CHUNK) { + if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) { + //logError(1, "%s fillFlash:1\n", tag); + toWrite = DMA_CHUNK; + remaining -= DMA_CHUNK; + byteBlock += DMA_CHUNK; + } else { + //logError(1, "%s fillFlash:2\n", tag); + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } else { + if ((byteBlock + remaining) <= FLASH_CHUNK) { + //logError(1, "%s fillFlash:3\n", tag); + toWrite = remaining; + byteBlock += remaining; + remaining = 0; + } else { + //logError(1, "%s fillFlash:4\n", tag); + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } + + buff[1] = (u8) ((addr & 0x0000FF00) >> 8); + buff[2] = (u8) (addr & 0x000000FF); + memcpy(&buff[3], data, toWrite); + //logError(0, + //"%s Command = %02X, address = %02X %02X, + //bytes = %d, data = %02X %02X, %02X %02X\n", + //tag, buff[0], buff[1], buff[2], toWrite, + //buff[3], buff[4], buff[3 + toWrite-2], + //buff[3 + toWrite-1]); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + + addr += toWrite; + data += toWrite; + } + //configuring the DMA + byteBlock = byteBlock / 4 - 1; + + buff2[0] = FLASH_CMD_WRITE_REGISTER; + buff2[1] = FLASH_DMA_CONFIG; + buff2[2] = 0x00; + buff2[3] = 0x00; + + addr = address + ((wheel * FLASH_CHUNK)/4); + buff2[4] = (u8) ((addr & 0x000000FF)); + buff2[5] = (u8) ((addr & 0x0000FF00) >> 8); + buff2[6] = (u8) (byteBlock & 0x000000FF); + buff2[7] = (u8) ((byteBlock & 0x0000FF00) >> 8); + buff2[8] = 0x00; + + logError(0, "%s:Command:%02X, address:%02X %02X, ", + tag, buff2[0], buff2[5], buff2[4]); + logError(0, "words:%02X %02X\n", buff2[7], buff2[6]); + if (fts_writeCmd(buff2, 9) < OK) { + logError(1, "%s Error during filling Flash!:%02X\n", + tag, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + //mdelay(FLASH_WAIT_TIME); + res = start_flash_dma(); + if (res < OK) { + logError(1, "%s Error during flashing DMA!:%02X\n", + tag, res); + kfree(buff); + return res; + } + wheel++; + } + kfree(buff); + return OK; +} + +int flash_burn(struct Firmware *fw, int force_burn, int keep_cx) +{ + int res; + + if (!force_burn && (ftsInfo.u16_fwVer >= fw->fw_ver) + && (ftsInfo.u16_cfgId >= fw->config_id)) { + for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) { + if (fw->externalRelease[res] > + ftsInfo.u8_extReleaseInfo[res]) + goto start; + } + + logError(0, "Firmware in the chip newer or "); + logError(0, "equal to the one to burn!"); + logError(0, "%s %s:NO UPDATE ERROR %02X\n", + tag, __func__, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + //programming procedure start +start: + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + + logError(0, "%s 2) WARM BOOT:\n", tag); + res = fts_warm_boot(); + if (res < OK) { + logError(1, "%s warm boot FAILED!\n", tag); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s warm boot COMPLETED!\n\n", tag); + + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s 3) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < OK) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + //mdelay(200); + logError(0, "%s 4) FLASH ERASE UNLOCK:\n", tag); + res = flash_erase_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s 5) FLASH ERASE:\n", tag); + if (keep_cx == 1) + res = flash_erase_page_by_page(keep_cx); + else + res = flash_full_erase(); + if (res < 0) { + logError(1, "%s flash erase FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash erase COMPLETED!\n\n", tag); + + + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s 6) LOAD PROGRAM:\n", tag); + res = fillFlash(FLASH_ADDR_CODE, (u8 *)(&fw->data[0]), + fw->sec0_size); + if (res < OK) { + logError(1, "%s load program ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s load program DONE!\n", tag); + logError(0, "%s 7) LOAD CONFIG:\n", tag); + res = fillFlash(FLASH_ADDR_CONFIG, + &(fw->data[fw->sec0_size]), fw->sec1_size); + if (res < OK) { + logError(1, "%s load config ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s load config DONE!\n", tag); + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 8) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + + logError(0, "%s 9) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s %s:Unable to retrieve Chip INFO!:%02X\n", + tag, __func__, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + if ((ftsInfo.u16_fwVer != fw->fw_ver) + && (ftsInfo.u16_cfgId != fw->config_id)) { + pr_err("Firmware is different from the old!\n"); + logError(1, "%s fw: %x != %x, conf: %x != %x\n", + tag, ftsInfo.u16_fwVer, fw->fw_ver, + ftsInfo.u16_cfgId, fw->config_id); + return ERROR_FLASH_BURN_FAILED; + } + + logError(0, "%s Final check OK! fw: %02X , conf: %02X\n", + tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.h b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h new file mode 100644 index 000000000000..844c5da53280 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_FLASH_H +#define __FTS_FLASH_H + + +#include "ftsSoftware.h" + +//Flash possible status +#define FLASH_READY 0 +#define FLASH_BUSY 1 +#define FLASH_UNKNOWN -1 +#define FLASH_STATUS_BYTES 1 + + +//Flash timing parameters +#define FLASH_RETRY_COUNT 1000 +#define FLASH_WAIT_BEFORE_RETRY 50 //ms +#define FLASH_WAIT_TIME 200 //ms + + +//PATHS FW FILES +//#define PATH_FILE_FW "fw.memh" +#ifdef FTM3_CHIP +#define PATH_FILE_FW "st_fts.bin" +#else +#define PATH_FILE_FW "st_fts.ftb"//new bin file structure +#endif + +#ifndef FTM3_CHIP +#define FLASH_CHUNK (64 * 1024) +#define DMA_CHUNK (2 * 1024) +#endif + + +struct Firmware { + u8 *data; + u16 fw_ver; + u16 config_id; + u8 externalRelease[EXTERNAL_RELEASE_INFO_SIZE]; + int data_size; +#ifndef FTM3_CHIP + u32 sec0_size; + u32 sec1_size; + u32 sec2_size; + u32 sec3_size; +#endif +}; + +#ifdef FTM3_CHIP +int flash_status(void); +int flash_status_ready(void); +int wait_for_flash_ready(void); +#else +int wait_for_flash_ready(u8 type); +int fts_warm_boot(void); +int flash_erase_unlock(void); +int flash_full_erase(void); +int flash_erase_page_by_page(int keep_cx); +//int flash_erase_page_by_page_info(int page); +int start_flash_dma(void); +int fillFlash(u32 address, u8 *data, int size); +#endif + +int flash_unlock(void); +int fillMemory(u32 address, u8 *data, int size); +int getFirmwareVersion(u16 *fw_vers, u16 *config_id); +int getFWdata(const char *pathToFile, u8 **data, int *size, int from); +int getFWdata_nocheck(const char *pathToFile, u8 **data, int *size, int from); +int parseBinFile(u8 *fw_data, int fw_size, struct Firmware *fw, int keep_cx); +int readFwFile(const char *path, struct Firmware *fw, int keep_cx); +int flash_burn(struct Firmware *fw, int force_burn, int keep_cx); +int flashProcedure(const char *path, int force, int keep_cx); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.c b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c new file mode 100644 index 000000000000..ffcf34925eea --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + */ + +#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 "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" +#include "ftsTime.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; +static int sense_len, force_len; + +int getOffsetFrame(u16 address, u16 *offset) +{ + u8 data[2]; + u8 cmd = { FTS_CMD_FRAMEBUFFER_R }; + char *temp = NULL; + + if (readCmdU16(cmd, address, data, OFFSET_LENGTH, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %S: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + u8ToU16(data, offset); + temp = printHex("Offest = ", data, OFFSET_LENGTH); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + return OK; +} + +int getChannelsLength(void) +{ + int ret; + u8 *data = (u8 *)kmalloc_array(2, sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = readB2(ADDR_SENSE_LEN, data, 2); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_READ_B2); + kfree(data); + return (ret|ERROR_READ_B2); + } + + sense_len = (int)data[0]; + force_len = (int)data[1]; + + logError(0, "%s Force_len = %d Sense_Len = %d\n", + tag, force_len, sense_len); + kfree(data); + + return OK; +} + + +int getFrameData(u16 address, int size, short **frame) +{ + int i, j, ret; + u8 *data = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, + data, size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + kfree(data); + return ERROR_I2C_R; + } + j = 0; + for (i = 0; i < size; i += 2) { + (*frame)[j] = (short)((data[i + 1] << 8) + data[i]); + j++; + } + kfree(data); + return OK; +} + + +int getMSFrame(u16 type, struct MutualSenseFrame *frame, int keep_first_row) +{ + u16 offset; + int ret; + + if (getSenseLen() == 0 || getForceLen() == 0) { + ret = getChannelsLength(); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_CH_LEN); + return (ret|ERROR_CH_LEN); + } + } + + ret = getOffsetFrame(type, &offset); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + switch (type) { + case ADDR_RAW_TOUCH: + case ADDR_FILTER_TOUCH: + case ADDR_NORM_TOUCH: + case ADDR_CALIB_TOUCH: + if (keep_first_row == 1) { + frame->node_data_size = ((force_len + 1) * sense_len); + frame->header.force_node = force_len + 1; + } else { + frame->node_data_size = ((force_len) * sense_len); + offset += (sense_len * BYTES_PER_NODE); + frame->header.force_node = force_len; + } + frame->header.sense_node = sense_len; + break; + case ADDR_NORM_MS_KEY: + case ADDR_RAW_MS_KEY: + frame->header.force_node = 1; + frame->header.sense_node = ftsInfo.u8_msKeyLen; + frame->node_data_size = ftsInfo.u8_msKeyLen; + break; + default: + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + frame->node_data = (short *)kmalloc_array(frame->node_data_size, + sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, + frame->node_data_size * BYTES_PER_NODE, + &(frame->node_data)); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(frame->node_data); + return (ret | ERROR_GET_FRAME_DATA); + } + // if you want to access one node i,j, + //you should compute the offset like: + //offset = i * columns + j => frame[i, j] + logError(0, "%s Frame acquired!\n", tag); + //return the number of data put inside frame + return frame->node_data_size; +} + +int getSenseLen(void) +{ + int ret; + + if (sense_len != 0) + return sense_len; + + if (ftsInfo.u8_scrSenseLen != 0) { + sense_len = ftsInfo.u8_scrSenseLen; + } else { + ret = getChannelsLength(); + if (ret < OK) + return ret; + } + return sense_len; +} + +int getForceLen(void) +{ + int ret; + + if (force_len != 0) + return force_len; + + if (ftsInfo.u8_scrForceLen != 0) { + force_len = ftsInfo.u8_scrForceLen; + } else { + ret = getChannelsLength(); + if (ret < OK) + return ret; + } + return force_len; +} + +int requestFrame(u16 type) +{ + int retry = 0; + int ret; + u16 answer; + char *temp = NULL; + + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_FRAME_DATA, 0x00, 0x00}; + // B7 is the command for asking frame data + event_to_search[0] = (int)EVENTID_FRAME_DATA_READ; + + + u16ToU8(type, &cmd[1]); + + while (retry < FRAME_DATA_READ_RETRY) { + temp = printHex("Command = ", cmd, 3); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + + //send the request to the chip to load in memory the Frame Data + ret = fts_writeFwCmd(cmd, 3); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, + 1, + readEvent, + TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp!\n", + tag, retry + 1); + retry += 1; + } else { + retry = 0; + break; + } + } + if (retry == FRAME_DATA_READ_RETRY) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + + logError(1, "%s The event found has a different type of ", tag); + logError(1, "Frame data:%02X\n", ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + + +int readFrameDataHeader(u16 type, struct DataHeader *header) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[FRAME_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, + FRAME_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != FRAME_HEADER_SIGNATURE) { + logError(1, "%s %s %02X Wrong Header Signature !%02X != %02X\n", + tag, __func__, ERROR_WRONG_COMP_SIGN, data[0], + HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Frame data OK!\n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + return OK; +} + +int getMSFrame2(u16 type, struct MutualSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA+FRAME_DATA_HEADER; + int size, ret; + + frame->node_data = NULL; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER + || type == MS_TOUCH_ULTRA_LOW_POWER + || type == MS_KEY)) { + logError(1, "%s %s:Choose a MS type of frame data ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensation:ERROR %02X\n", + tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData:ERROR %02X\n", + tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case MS_TOUCH_ACTIVE: + case MS_TOUCH_LOW_POWER: + case MS_TOUCH_ULTRA_LOW_POWER: + size = frame->header.force_node * frame->header.sense_node; + break; + case MS_KEY: + //or use directly the number in the ftsChip + if (frame->header.force_node > frame->header.sense_node) + size = frame->header.force_node; + else + size = frame->header.sense_node; + frame->header.force_node = 1; + frame->header.sense_node = size; + break; + + default: + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + frame->node_data = (short *)kmalloc_array(size, + sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &(frame->node_data)); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(frame->node_data); + return (ret | ERROR_GET_FRAME_DATA); + } + // if you want to access one node i,j, + //you should compute the offset like: + //offset = i * columns + j = > frame[i, j] + logError(0, "%s Frame acquired!\n", tag); + frame->node_data_size = size; + return size;//return the number of data put inside frame + +} + +int getSSFrame2(u16 type, struct SelfSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA + FRAME_DATA_HEADER; + int size, ret; + short *temp = NULL; + + frame->force_data = NULL; + frame->sense_data = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER + || type == SS_PROXIMITY)) { + logError(1, "%s %s:Choose a SS type of frame data ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case SS_TOUCH: + case SS_HOVER: + case SS_PROXIMITY: + size = frame->header.force_node + frame->header.sense_node; + break; + + default: + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + + temp = (short *)kmalloc_array(size, sizeof(short), GFP_KERNEL); + if (temp == NULL) { + logError(1, "%s %s: temp ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &temp); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(temp); + return (ret | ERROR_GET_FRAME_DATA); + } + + frame->force_data = (short *)kmalloc_array(frame->header.force_node, + sizeof(short), GFP_KERNEL); + if (frame->force_data == NULL) { + logError(1, "%s %s: frame->force_data ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + kfree(temp); + return ERROR_ALLOC; + } + + memcpy(frame->force_data, temp, + frame->header.force_node * sizeof(short)); + + frame->sense_data = (short *)kmalloc_array(frame->header.sense_node, + sizeof(short), GFP_KERNEL); + if (frame->sense_data == NULL) { + logError(1, "%s %s: frame->sense_data ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + kfree(temp); + kfree(frame->force_data); + return ERROR_ALLOC; + } + + memcpy(frame->sense_data, &temp[frame->header.force_node], + frame->header.sense_node * sizeof(short)); + + logError(0, "%s Frame acquired!\n", tag); + kfree(temp); + return size; //return the number of data put inside frame +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.h b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h new file mode 100644 index 000000000000..6945a619eb17 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_FRAME_H +#define __FTS_FRAME_H + +#include "ftsSoftware.h" + +//Number of data bytes for each node +#define BYTES_PER_NODE 2 +#define OFFSET_LENGTH 2 +#define FRAME_DATA_HEADER 8 +#define FRAME_HEADER_SIGNATURE 0xB5 +#define FRAME_DATA_READ_RETRY 2 + +struct MutualSenseFrame { + struct DataHeader header; + short *node_data; + int node_data_size; +}; + +struct SelfSenseFrame { + struct DataHeader header; + short *force_data; + short *sense_data; +}; + +int getOffsetFrame(u16 address, u16 *offset); +int getChannelsLength(void); +int getFrameData(u16 address, int size, short **frame); +int getMSFrame(u16 type, struct MutualSenseFrame *frame, int keep_first_row); +//int getMSKeyFrame(u16 type, short **frame); +//int getSSFrame(u16 type, short **frame); +//int getNmsFrame(u16 type, short ***frames, int * sizes, +//int keep_first_row, int fs, int n); +int getSenseLen(void); +int getForceLen(void); +int requestFrame(u16 type); +int readFrameDataHeader(u16 type, struct DataHeader *header); +int getMSFrame2(u16 type, struct MutualSenseFrame *frame); +int getSSFrame2(u16 type, struct SelfSenseFrame *frame); + +#endif + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c new file mode 100644 index 000000000000..dfd2dae00cb8 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + */ + +#include "ftsSoftware.h" +#include "ftsError.h" +#include "ftsGesture.h" +#include "ftsIO.h" +#include "ftsTool.h" + + +static char tag[8] = "[ FTS ]\0"; + +static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 }; +static u8 custom_gestures[GESTURE_CUSTOM_NUMBER][GESTURE_CUSTOM_POINTS]; +static u8 custom_gesture_index[GESTURE_CUSTOM_NUMBER] = { 0 }; +static int refreshGestureMask; + +u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX] = {0}; +u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX] = {0}; +int gesture_coords_reported = ERROR_OP_NOT_ALLOW; +struct mutex gestureMask_mutex; + + +int updateGestureMask(u8 *mask, int size, int en) +{ + u8 temp; + int i; + + if (mask == NULL) { + logError(1, "%s %s: Mask NULL! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s:Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + + if (en == FEAT_ENABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, "%s %s:setting gesture mask to enable..\n", + tag, __func__); + if (mask != NULL) { + for (i = 0; i < size; i++) { + //back up the gesture enabled + gesture_mask[i] = gesture_mask[i] | mask[i]; + } + } + refreshGestureMask = 1; + logError(0, "%s %s:gesture mask to enable SET!\n", + tag, __func__); + mutex_unlock(&gestureMask_mutex); + return OK; + } + if (en == FEAT_DISABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, "%s %s:setting gesture ", tag, __func__); + logError(0, "mask to disable...\n"); + for (i = 0; i < size; i++) { + // enabled XOR disabled + temp = gesture_mask[i] ^ mask[i]; + gesture_mask[i] = temp & gesture_mask[i]; + } + logError(0, "%s %s:gesture mask to disable SET!\n", + tag, __func__); + refreshGestureMask = 1; + mutex_unlock(&gestureMask_mutex); + return OK; + } + logError(1, "%s%s:Enable parameter Invalid%d!=%d or%d%:08X", + tag, __func__, FEAT_DISABLE, + FEAT_ENABLE, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + +} + +int enableGesture(u8 *mask, int size) +{ + u8 cmd[GESTURE_MASK_SIZE + 2]; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int i, res; + int event_to_search[4] = { EVENTID_GESTURE, + EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE }; + + logError(0, "%s Trying to enable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_ENABLE; + + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s: Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + + mutex_lock(&gestureMask_mutex); + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + //back up of the gesture enabled + gesture_mask[i] = gesture_mask[i] | mask[i]; + } + while (i < GESTURE_MASK_SIZE) { + cmd[i + 2] = gesture_mask[i]; + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) + cmd[i + 2] = gesture_mask[i]; + } + + res = fts_writeFwCmd(cmd, GESTURE_MASK_SIZE + 2); + if (res < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, res); + goto END; + } + + res = pollForEvent(event_to_search, 4, + readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: pollForEvent ERROR %08X\n", + tag, __func__, res); + goto END; + } + + if (readData[4] != 0x00) { + logError(1, "%s %s: ERROR %08X\n", + tag, __func__, ERROR_GESTURE_ENABLE_FAIL); + res = ERROR_GESTURE_ENABLE_FAIL; + goto END; + } + + logError(0, "%s %s: DONE!\n", tag, __func__); + res = OK; + +END: + mutex_unlock(&gestureMask_mutex); + return res; +} + + +int disableGesture(u8 *mask, int size) +{ + u8 cmd[2 + GESTURE_MASK_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; + u8 temp; + int i, res; + int event_to_search[4] = { EVENTID_GESTURE, + EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; + + logError(0, "%s Trying to disable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DISABLE; + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s: Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + mutex_lock(&gestureMask_mutex); + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + // enabled XOR disabled + temp = gesture_mask[i] ^ mask[i]; + gesture_mask[i] = temp & gesture_mask[i]; + } + while (i < GESTURE_MASK_SIZE) { + //cmd[i + 2] = gesture_mask[i]; + //gesture_mask[i] = 0x00; + + cmd[i + 2] = 0x00; + //leave untouched the gestures not specified + + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) { + //cmd[i + 2] = gesture_mask[i]; + cmd[i + 2] = 0xFF; + } + } + + res = fts_writeFwCmd(cmd, 2 + GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s %s:ERROR %08X\n", tag, __func__, res); + goto END; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: pollForEvent ERROR %08X\n", + tag, __func__, res); + goto END; + } + + if (readData[4] != 0x00) { + logError(1, "%s %s:ERROR %08X\n", + tag, __func__, ERROR_GESTURE_ENABLE_FAIL); + res = ERROR_GESTURE_ENABLE_FAIL; + goto END; + } + + logError(0, "%s %s: DONE!\n", tag, __func__); + res = OK; +END: + mutex_unlock(&gestureMask_mutex); + return res; + +} + +int startAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_START_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, + "%s%s:Impossible to start adding custom gesture ID:%02X %08X\n", + tag, __func__, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s:start add event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:start add event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); + return ERROR_GESTURE_START_ADD; + } + + return OK; +} + +int finishAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, + GESTURE_FINISH_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_FINISH_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, + "%s%s:Impossible to finish adding custom gestureID:%02X %08X\n", + tag, __func__, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: finish add event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, + "%s %s:finish add event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); + return ERROR_GESTURE_FINISH_ADD; + } + + return OK; +} + +int loadCustomGesture(u8 *template, u8 gestureID) +{ + int res, i, wheel; + int remaining = GESTURE_CUSTOM_POINTS; + int toWrite, offset = 0; + u8 cmd[TEMPLATE_CHUNK + 5]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_DATA_ADD }; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + logError(0, "%s Starting adding custom gesture procedure...\n", tag); + + res = startAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s %s:unable to start adding procedure %08X\n", + tag, __func__, res); + return res; + } + + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DATA_ADD; + cmd[2] = gestureID; + wheel = 0; + while (remaining > 0) { + if (remaining > TEMPLATE_CHUNK) + toWrite = TEMPLATE_CHUNK; + else + toWrite = remaining; + + cmd[3] = toWrite; + cmd[4] = offset; + for (i = 0; i < toWrite; i++) + cmd[i + 5] = template[wheel++]; + + res = fts_writeFwCmd(cmd, toWrite + 5); + if (res < OK) { + logError(1, "%s %s:unable to start ", tag, __func__); + logError(1, "adding procedure %08X\n", res); + return res; + } + + res = pollForEvent(event_to_search, + 4, + readData, + GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: add event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:add event status not OK! ", + tag, __func__); + logError(1, "ERROR %08X\n", readData[4]); + return ERROR_GESTURE_DATA_ADD; + } + + remaining -= toWrite; + offset += toWrite / 2; + } + + res = finishAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s %s:unable to finish adding procedure! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + return res; + } + + logError(0, "%s Adding custom gesture procedure DONE!\n", tag); + return OK; + +} + + +int reloadCustomGesture(void) +{ + int res, i; + + logError(0, "%s Starting reload Gesture Template...\n", tag); + + for (i = 0; i < GESTURE_CUSTOM_NUMBER; i++) { + if (custom_gesture_index[i] == 1) { + res = loadCustomGesture(custom_gestures[i], + GESTURE_CUSTOM_OFFSET + i); + if (res < OK) { + logError(1, "%s %s:Impossible load gesture ", + tag, __func__); + logError(1, "D:%02X %08X\n", + GESTURE_CUSTOM_OFFSET + i, res); + return res; + } + } + } + + logError(0, "%s Reload Gesture Template DONE!\n", tag); + return OK; + +} + +int enterGestureMode(int reload) +{ + u8 cmd = FTS_CMD_GESTURE_MODE; + int res, ret; + + res = fts_disableInterrupt(); + if (res < OK) { + logError(1, "%s %s: ERROR %08X\n", + tag, __func__, res | ERROR_DISABLE_INTER); + return res | ERROR_DISABLE_INTER; + } + + if (reload == 1 || refreshGestureMask == 1) { + if (reload == 1) { + res = reloadCustomGesture(); + if (res < OK) { + logError(1, "%s %s:impossible reload ", + tag, __func__); + logError(1, "custom gesture %08X\n", res); + goto END; + } + } + + /** + * mandatory steps to set the correct gesture + * mask defined by the user + */ + res = disableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s %s:disableGesture ERROR %08X\n", + tag, res); + goto END; + } + + res = enableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s %s:enableGesture ERROR %08X\n", + tag, __func__, res); + goto END; + } + + refreshGestureMask = 0; + /**************************************************/ + } + + res = fts_writeFwCmd(&cmd, 1); + if (res < OK) { + logError(1, "%s %s:enter gesture mode ERROR %08X\n", + tag, __func__, res); + goto END; + } + + res = OK; +END: + ret = fts_enableInterrupt(); + if (ret < OK) { + logError(1, "%s %s:fts_enableInterrupt ERROR %08X\n", + tag, __func__, res | ERROR_ENABLE_INTER); + res |= ret | ERROR_ENABLE_INTER; + } + + return res; +} + +int addCustomGesture(u8 *data, int size, u8 gestureID) +{ + int index, res, i; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Adding procedure...\n", tag); + if (size != GESTURE_CUSTOM_POINTS || (gestureID != GES_ID_CUST1 + && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 + && gestureID != GES_ID_CUST4 && gestureID != GES_ID_CUST5)) { + logError(1, "%s %s:Invalid size(%d) or Custom GestureID ", + tag, __func__, size); + logError(1, "(%02X)!ERROR%08X\n", + size, gestureID, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + for (i = 0; i < GESTURE_CUSTOM_POINTS; i++) + custom_gestures[index][i] = data[i]; + + res = loadCustomGesture(custom_gestures[index], gestureID); + if (res < OK) { + logError(1, "%s %s:impossible to load the custom gesture! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + return res; + } + + custom_gesture_index[index] = 1; + logError(0, "%s Custom Gesture Adding procedure DONE!\n", tag); + return OK; +} + +int removeCustomGesture(u8 gestureID) +{ + int res, index; + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GETURE_REMOVE_CUSTOM, gestureID }; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GETURE_REMOVE_CUSTOM }; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Removing procedure...\n", tag); + if (gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && + gestureID != GES_ID_CUST3 && gestureID != + GES_ID_CUST4 && gestureID != GES_ID_CUST5) { + logError(1, "%s %s:Invalid Custom GestureID (%02X)! ", + tag, __func__, gestureID); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + //when a gesture is removed, it is also disabled automatically + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, "%s %s:Impossible to remove custom ", + tag, __func__); + logError(1, "%gesture ID:%02X %08X\n", gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s:remove event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:remove event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); + return ERROR_GESTURE_REMOVE; + } + + custom_gesture_index[index] = 0; + logError(0, "%s Custom Gesture Remove procedure DONE!\n", tag); + return OK; + +} + +int isAnyGestureActive(void) +{ + int res = 0; + /*-1 because in any case the last gesture mask byte will*/ + /*be evaluated with the following if*/ + while (res < (GESTURE_MASK_SIZE - 1) && gesture_mask[res] == 0) + res++; + + if (gesture_mask[res] == 0) { + logError(0, "%s %s: All Gestures Disabled!\n", tag, __func__); + return FEAT_DISABLE; + } + + logError(0, "%s %s:Active Gestures Found! gesture_mask[%d] = %02X!\n", + tag, __func__, res, gesture_mask[res]); + return FEAT_ENABLE; +} + +int gestureIDtoGestureMask(u8 id, u8 *mask) +{ + logError(0, "%s %s: Index = %d Position = %d!\n", + tag, __func__, ((int)((id) / 8)), (id % 8)); + mask[((int)((id) / 8))] |= 0x01 << (id % 8); + return OK; +} + + +int readGestureCoords(u8 *event) +{ + int i = 0; + u8 rCmd[3] = {FTS_CMD_FRAMEBUFFER_R, 0x00, 0x00 }; + int res; + unsigned char val[GESTURE_COORDS_REPORT_MAX * 4 + 1]; + + //the max coordinates to read are GESTURE_COORDS_REPORT_MAX*4 + //(because each coordinate is a short(*2) and we have x and y) + //+ dummy byte + if (event[0] != EVENTID_GESTURE + || event[1] != EVENT_TYPE_GESTURE_DTC2) { + + logError(1, "%s %s:The event passsed as argument is invalid! ", + tag, __func__); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + rCmd[1] = event[4]; // Offset address L + rCmd[2] = event[3]; // Offset address H + //number of coords reported L + gesture_coords_reported = event[6]; + //number of coords reported H + gesture_coords_reported = (gesture_coords_reported << 8) | event[5]; + if (gesture_coords_reported > GESTURE_COORDS_REPORT_MAX) { + logError(1, "%s%s:FW reported more than:%d points ", + tag, __func__, gesture_coords_reported); + logError(1, "for gestures!\n"); + logError(1, " Decreasing to %d\n", GESTURE_COORDS_REPORT_MAX); + gesture_coords_reported = GESTURE_COORDS_REPORT_MAX; + } + + logError(1, "%s %s: Offset: %02X %02X points = %d\n", tag, + __func__, rCmd[1], rCmd[2], gesture_coords_reported); + res = fts_readCmd(rCmd, 3, (unsigned char *)val, + 1 + (gesture_coords_reported * 2)); + if (res < OK) { + logError(1, "%s %s: Cannot read the coordinates! ERROR %08X\n", + tag, __func__, res); + gesture_coords_reported = ERROR_OP_NOT_ALLOW; + return res; + } + //all the points of the gesture are stored in val + for (i = 0; i < gesture_coords_reported; i++) { + //ignore first byte data because it is a dummy byte + gesture_coordinates_x[i] = (((u16) val[i * 2 + 1 + 1]) + & 0x0F) << 8 | (((u16) val[i * 2 + 1]) & 0xFF); + gesture_coordinates_y[i] = + (((u16)val[gesture_coords_reported * 2 + + i * 2 + 1 + 1]) & 0x0F) << 8 + | (((u16)val[gesture_coords_reported * 2 + + i * 2 + 1]) & 0xFF); + } + + logError(1, "%s %s: Reading Gesture Coordinates DONE!\n", + tag, __func__); + return OK; +} + +int getGestureCoords(u16 *x, u16 *y) +{ + x = gesture_coordinates_x; + y = gesture_coordinates_y; + logError(1, "%s %s:Number of gesture coordinates returned = %d\n", + tag, __func__, gesture_coords_reported); + return gesture_coords_reported; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.h b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h new file mode 100644 index 000000000000..c5c2a6abbf3d --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef _FTS_GESTURE_H_ +#define _FTS_GESTURE_H_ + +#include "ftsHardware.h" + +#define GESTURE_MASK_SIZE 8 + +//max number of gestures coordinates reported +#define GESTURE_COORDS_REPORT_MAX 32 + +// for each custom gesture should be provided 30 +//points (each point is a couple of x,y) +#define GESTURE_CUSTOM_POINTS (30 * 2) + +//fw support up to 5 custom gestures +#define GESTURE_CUSTOM_NUMBER 5 + +#ifdef FTM3 + +//number of points to transfer with each I2C transaction +#define TEMPLATE_CHUNK (10 * 2) +#else +//number of points to transfer with each I2C transaction +#define TEMPLATE_CHUNK (5 * 2) +#endif + + +//Gesture IDs +#define GES_ID_UNKNOWN 0x00 //no meaningful gesture +#define GES_ID_DBLTAP 0x01 //Double Tap +#define GES_ID_O 0x02 //'O' +#define GES_ID_C 0x03 //'C' +#define GES_ID_M 0x04 //'M' +#define GES_ID_W 0x05 //'W' +#define GES_ID_E 0x06 //'e' +#define GES_ID_HFLIP_L2R 0x07 //Left to right line +#define GES_ID_HFLIP_R2L 0x08 //Right to left line +#define GES_ID_VFLIP_T2D 0x09 //Top to bottom line +#define GES_ID_VFLIP_D2T 0x0A //Bottom to Top line +#define GES_ID_L 0x0B //'L' +#define GES_ID_F 0x0C //'F' +#define GES_ID_V 0x0D //'V' +#define GES_ID_AT 0x0E //'@' +#define GES_ID_S 0x0F //'S' +#define GES_ID_Z 0x10 //'Z' +#define GES_ID_CUST1 0x11 //Custom gesture 1 +#define GES_ID_CUST2 0x12 //Custom gesture 2 +#define GES_ID_CUST3 0x13 //Custom gesture 3 +#define GES_ID_CUST4 0x14 //Custom gesture 4 +#define GES_ID_CUST5 0x15 //Custom gesture 5 +#define GES_ID_LEFTBRACE 0x20 //'<' +#define GES_ID_RIGHTBRACE 0x21 //'>' + +#define GESTURE_CUSTOM_OFFSET GES_ID_CUST1 + +//Command sub-type +#define GESTURE_ENABLE 0x01 +#define GESTURE_DISABLE 0x02 +#define GESTURE_ENB_CHECK 0x03 +#define GESTURE_START_ADD 0x10 +#define GESTURE_DATA_ADD 0x11 +#define GESTURE_FINISH_ADD 0x12 +#define GETURE_REMOVE_CUSTOM 0x13 +#define GESTURE_CHECK_CUSTOM 0x14 + + +//Event sub-type +#define EVENT_TYPE_ENB 0x04 +#define EVENT_TYPE_CHECK_ENB 0x03 +#define EVENT_TYPE_GESTURE_DTC1 0x01 +#define EVENT_TYPE_GESTURE_DTC2 0x02 + +int updateGestureMask(u8 *mask, int size, int en); +int disableGesture(u8 *mask, int size); +int enableGesture(u8 *mask, int size); +int startAddCustomGesture(u8 gestureID); +int finishAddCustomGesture(u8 gestureID); +int loadCustomGesture(u8 *template, u8 gestureID); +int reloadCustomGesture(void); +int enterGestureMode(int reload); +int addCustomGesture(u8 *data, int size, u8 gestureID); +int removeCustomGesture(u8 gestureID); +int isAnyGestureActive(void); +int gestureIDtoGestureMask(u8 id, u8 *mask); +int readGestureCoords(u8 *event); +int getGestureCoords(u16 *x, u16 *y); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsHardware.h b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h new file mode 100644 index 000000000000..553cf6d5762f --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * HW related data ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_HARDWARE_H +#define __FTS_HARDWARE_H + +//DUMMY BYTES DATA +#define DUMMY_HW_REG 1 +#define DUMMY_FRAMEBUFFER 1 +#define DUMMY_MEMORY 1 + +//DIGITAL CHIP INFO +#ifdef FTM3_CHIP +#define DCHIP_ID_0 0x39 +#define DCHIP_ID_1 0x6C +#else +#define DCHIP_ID_0 0x36 +#define DCHIP_ID_1 0x70 +#endif + +#ifdef FTM3_CHIP +#define DCHIP_ID_ADDR 0x0007 +#define DCHIP_FW_VER_ADDR 0x000A +#else +#define DCHIP_ID_ADDR 0x0004 +#define DCHIP_FW_VER_ADDR 0x0008 +#endif + +#define DCHIP_FW_VER_BYTE 2 + +//CHUNKS +#define READ_CHUNK (2 * 1024) +#define WRITE_CHUNK (2 * 1024) +#define MEMORY_CHUNK (2 * 1024) + +//PROTOCOL INFO +#ifdef FTM3_CHIP +#define I2C_SAD 0x49 +#else +#define I2C_SAD 0x49 +#endif + +#define I2C_INTERFACE //comment if the chip use SPI +#define ICR_ADDR 0x0024 +#define ICR_SPI_VALUE 0x02 + +//SYSTEM RESET INFO +#ifdef FTM3_CHIP +#define SYSTEM_RESET_ADDRESS 0x0023 +#define SYSTEM_RESET_VALUE 0x01 +#else +#define SYSTEM_RESET_ADDRESS 0x0028 +#define SYSTEM_RESET_VALUE 0x80 +#endif + +//INTERRUPT INFO +#ifdef FTM3_CHIP +#define IER_ADDR 0x001C +#else +#define IER_ADDR 0x002C +#endif + +#define IER_ENABLE 0x41 +#define IER_DISABLE 0x00 + +//FLASH COMMAND + +#define FLASH_CMD_UNLOCK 0xF7 + + +#ifdef FTM3_CHIP +#define FLASH_CMD_WRITE_LOWER_64 0xF0 +#define FLASH_CMD_WRITE_UPPER_64 0xF1 +#define FLASH_CMD_BURN 0xF2 +#define FLASH_CMD_ERASE 0xF3 +#define FLASH_CMD_READSTATUS 0xF4 +#else +#define FLASH_CMD_WRITE_64K 0xF8 +#define FLASH_CMD_READ_REGISTER 0xF9 +#define FLASH_CMD_WRITE_REGISTER 0xFA +#endif + +//FLASH UNLOCK PARAMETER +#define FLASH_UNLOCK_CODE0 0x74 +#define FLASH_UNLOCK_CODE1 0x45 + +#ifndef FTM3_CHIP +//FLASH ERASE and DMA PARAMETER +#define FLASH_ERASE_UNLOCK_CODE0 0x72 +#define FLASH_ERASE_UNLOCK_CODE1 0x03 +#define FLASH_ERASE_UNLOCK_CODE2 0x02 +#define FLASH_ERASE_CODE0 0x02 +#define FLASH_ERASE_CODE1 0xC0 +#define FLASH_DMA_CODE0 0x05 +#define FLASH_DMA_CODE1 0xC0 +#define FLASH_DMA_CONFIG 0x06 +#define FLASH_ERASE_START 0x80 +#define FLASH_NUM_PAGE 64//number of pages +#define FLASH_CX_PAGE_START 61 +#define FLASH_CX_PAGE_END 62 +#endif + + +//FLASH ADDRESS +#ifdef FTM3_CHIP +#define FLASH_ADDR_SWITCH_CMD 0x00010000 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0001E800 +#define FLASH_ADDR_CX 0x0001F000 +#else +#define ADDR_WARM_BOOT 0x001E +#define WARM_BOOT_VALUE 0x38 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0000FC00 +#endif + + +//CRC ADDR +#ifdef FTM3_CHIP +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x86 +#define CRC_MASK 0x02 +#else +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x74 +#define CRC_MASK 0x03 +#endif + +//SIZES FW, CODE, CONFIG, MEMH +#ifdef FTM3_CHIP +#define FW_HEADER_SIZE 32 +#define FW_SIZE (int)(128*1024) +#define FW_CODE_SIZE (int)(122*1024) +#define FW_CONFIG_SIZE (int)(2*1024) +#define FW_CX_SIZE (int)(FW_SIZE-FW_CODE_SIZE-FW_CONFIG_SIZE) +#define FW_VER_MEMH_BYTE1 193 +#define FW_VER_MEMH_BYTE0 192 +#define FW_OFF_CONFID_MEMH_BYTE1 2 +#define FW_OFF_CONFID_MEMH_BYTE0 1 +#define FW_BIN_VER_OFFSET 4 +#define FW_BIN_CONFIG_VER_OFFSET (FW_HEADER_SIZE+FW_CODE_SIZE+1) +#else +#define FW_HEADER_SIZE 64 +#define FW_HEADER_SIGNATURE 0xAA55AA55 +#define FW_FTB_VER 0x00000001 +#define FW_BYTES_ALIGN 4 +#define FW_BIN_VER_OFFSET 16 +#define FW_BIN_CONFIG_VER_OFFSET 20 +#endif + +//FIFO +#define FIFO_EVENT_SIZE 8 + +#ifdef FTM3_CHIP +#define FIFO_DEPTH 32 +#else +#define FIFO_DEPTH 64 +#endif + +#define FIFO_CMD_READONE 0x85 +#define FIFO_CMD_READALL 0x86 +#define FIFO_CMD_LAST 0x87 +#define FIFO_CMD_FLUSH 0xA1 + + +//CONSTANT TOTAL CX +#ifdef FTM3_CHIP +#define CX1_WEIGHT 4 +#define CX2_WEIGHT 1 +#else +#define CX1_WEIGHT 8 +#define CX2_WEIGHT 1 +#endif + +//OP CODES FOR MEMORY (based on protocol) + +#define FTS_CMD_HW_REG_R 0xB6 +#define FTS_CMD_HW_REG_W 0xB6 +#define FTS_CMD_FRAMEBUFFER_R 0xD0 +#define FTS_CMD_FRAMEBUFFER_W 0xD0 + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.c b/drivers/input/touchscreen/st/fts_lib/ftsIO.c new file mode 100644 index 000000000000..ba0fb18e3307 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication ** + * * + ************************************************************************** + ************************************************************************** + * + */ + +#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 "ftsSoftware.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsTool.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; +static struct i2c_client *client; +static u16 I2CSAD; + +int openChannel(struct i2c_client *clt) +{ + client = clt; + I2CSAD = clt->addr; + logError(0, "%s %s: SAD: %02X\n", tag, __func__, I2CSAD); + return OK; +} + +struct device *getDev() +{ + if (client != NULL) + return &(client->dev); + else + return NULL; +} + +struct i2c_client *getClient() +{ + if (client != NULL) + return client; + else + return NULL; +} + +int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < cmdLength + byteToRead + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(cmdLength + byteToRead + 1, + GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = cmdLength + byteToRead + 1; + buf = info->i2c_data; + } + + //write msg + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = buf; + + //read msg + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = I2C_M_RD; + I2CMsg[1].len = byteToRead; + I2CMsg[1].buf = buf + cmdLength; + + memcpy(buf, cmd, cmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 2); + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + memcpy(outBuf, buf + cmdLength, byteToRead); + + return OK; +} + +int fts_writeCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[1]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < cmdLength + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(cmdLength + 1, GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = cmdLength + 1; + buf = info->i2c_data; + } + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = buf; + + memcpy(buf, cmd, cmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + return OK; +} + +int fts_writeFwCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int ret2 = -1; + int retry = 0; + struct i2c_msg I2CMsg[1]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < cmdLength + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(cmdLength + 1, GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = cmdLength + 1; + buf = info->i2c_data; + } + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = buf; + + memcpy(buf, cmd, cmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + retry++; + if (ret >= 0) + ret2 = checkEcho(cmd, cmdLength); + if (ret < OK || ret2 < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + if (ret2 < OK) { + logError(1, "%s %s: check echo ERROR %02X\n", + tag, __func__, ret2); + return (ret | ERROR_I2C_W); + } + return OK; +} + + +int writeReadCmd(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, + int readCmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[3]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + uint8_t wr_len = writeCmdLength + readCmdLength + byteToRead; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < wr_len + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(wr_len + 1, GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = wr_len + 1; + buf = info->i2c_data; + } + + //write msg + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)writeCmdLength; + I2CMsg[0].buf = buf; + + //write msg + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = (__u16)0; + I2CMsg[1].len = (__u16)readCmdLength; + I2CMsg[1].buf = buf + writeCmdLength; + + //read msg + I2CMsg[2].addr = (__u16)I2CSAD; + I2CMsg[2].flags = I2C_M_RD; + I2CMsg[2].len = byteToRead; + I2CMsg[2].buf = buf + writeCmdLength + readCmdLength; + + memcpy(buf, writeCmd1, writeCmdLength); + memcpy(buf + writeCmdLength, readCmd1, readCmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 3); + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + } + + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_WR); + return ERROR_I2C_WR; + } + + memcpy(outBuf, buf + writeCmdLength + readCmdLength, byteToRead); + + return OK; +} + + +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, + int hasDummyByte) +{ + int remaining = byteToRead; + int toRead = 0; + u8 rCmd[3] = { cmd, 0x00, 0x00 }; + + u8 *buff = (u8 *)kmalloc_array(READ_CHUNK + 1, sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + rCmd[1] = (u8)((address & 0xFF00) >> 8); + rCmd[2] = (u8)(address & 0xFF); + + if (hasDummyByte) { + if (fts_readCmd(rCmd, 3, buff, toRead + 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + kfree(buff); + return ERROR_I2C_R; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (fts_readCmd(rCmd, 3, buff, toRead) < 0) + return ERROR_I2C_R; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + outBuf += toRead; + } + kfree(buff); + + return OK; +} + + +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite) +{ + int remaining = byteToWrite; + int toWrite = 0; + + u8 *buff = (u8 *)kmalloc_array(WRITE_CHUNK + 3, sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + buff[0] = WriteCmd; + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff[1] = (u8)((address & 0xFF00) >> 8); + buff[2] = (u8)(address & 0xFF); + memcpy(buff + 3, dataToWrite, toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + address += toWrite; + dataToWrite += toWrite; + } + + kfree(buff); + return OK; +} + +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, + int byteToWrite) +{ + int remaining = byteToWrite; + int toWrite = 0; + int ret; + + u8 buff1[3] = { writeCmd1, 0x00, 0x00 }; + u8 *buff2 = (u8 *)kmalloc_array(WRITE_CHUNK + 3, + sizeof(u8), GFP_KERNEL); + + if (buff2 == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + buff2[0] = writeCmd2; + + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff1[1] = (u8)((address & 0xFF000000) >> 24); + buff1[2] = (u8)((address & 0x00FF0000) >> 16); + buff2[1] = (u8)((address & 0x0000FF00) >> 8); + buff2[2] = (u8)(address & 0xFF); + memcpy(buff2 + 3, dataToWrite, toWrite); + + if (fts_writeCmd(buff1, 3) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + ret = ERROR_I2C_W; + goto END; + } + if (fts_writeCmd(buff2, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + ret = ERROR_I2C_W; + goto END; + } + + address += toWrite; + dataToWrite += toWrite; + } + + ret = OK; +END: + kfree(buff2); + return ret; +} + +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, + int byteToRead, int hasDummyByte) +{ + int remaining = byteToRead; + int toRead = 0; + u8 reaCmd[3]; + u8 wriCmd[3]; + + u8 *buff = (u8 *)kmalloc_array(READ_CHUNK + 1, sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s writereadCmd32: ERROR %02X\n", + tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + reaCmd[0] = rCmd; + wriCmd[0] = wCmd; + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + wriCmd[1] = (u8)((address & 0xFF000000) >> 24); + wriCmd[2] = (u8)((address & 0x00FF0000) >> 16); + + reaCmd[1] = (u8)((address & 0x0000FF00) >> 8); + reaCmd[2] = (u8)(address & 0x000000FF); + + if (hasDummyByte) { + if (writeReadCmd(wriCmd, 3, reaCmd, 3, + buff, toRead + 1) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", + tag, ERROR_I2C_WR); + kfree(buff); + return ERROR_I2C_WR; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (writeReadCmd(wriCmd, 3, reaCmd, + 3, buff, toRead) < 0) + return ERROR_I2C_WR; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + outBuf += toRead; + } + + kfree(buff); + return OK; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.h b/drivers/input/touchscreen/st/fts_lib/ftsIO.h new file mode 100644 index 000000000000..efba899ea2bf --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_IO_H +#define __FTS_IO_H + +#include +#include + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#define I2C_RETRY 3 //number of retry +#define I2C_WAIT_BEFORE_RETRY 2 //ms + +int openChannel(struct i2c_client *clt); +struct device *getDev(void); +struct i2c_client *getClient(void); +int fts_readCmd(u8 *cmd, int cmdLenght, u8 *outBuf, int byteToRead); +int fts_writeCmd(u8 *cmd, int cmdLenght); +int fts_writeFwCmd(u8 *cmd, int cmdLenght); +int writeReadCmd(u8 *writeCmd, int writeCmdLenght, u8 *readCmd, + int readCmdLenght, u8 *outBuf, int byteToRead); +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, + int hasDummyByte); +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite); +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, + int byteToWrite); +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, + int hasDummyByte); + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h new file mode 100644 index 000000000000..0c52982856c3 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************* + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FW related data * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_SOFTWARE_H +#define __FTS_SOFTWARE_H + +#include + +#include "ftsHardware.h" + +#define ECHO_ENABLED 0x00000001 + +//FTS FW COMMAND +#define FTS_CMD_MS_MT_SENSE_OFF 0x92 +#define FTS_CMD_MS_MT_SENSE_ON 0x93 +#define FTS_CMD_SS_HOVER_OFF 0x94 +#define FTS_CMD_SS_HOVER_ON 0x95 +#define FTS_CMD_LP_TIMER_CALIB 0x97 +#define FTS_CMD_MS_KEY_OFF 0x9A +#define FTS_CMD_MS_KEY_ON 0x9B +#define FTS_CMD_MS_COMP_TUNING 0xA3 +#define FTS_CMD_SS_COMP_TUNING 0xA4 +#define FTS_CMD_FULL_INITIALIZATION 0xA5 +#define FTS_CMD_ITO_CHECK 0xA7 +#define FTS_CMD_RELEASE_INFO 0xAA +#define FTS_CMD_GESTURE_MODE 0xAD +#define FTS_CMD_REQU_FW_CONF 0xB2 +#define FTS_CMD_REQU_FRAME_DATA 0xB7 +#define FTS_CMD_REQU_COMP_DATA 0xB8 +#define FTS_CMD_WRITE_MP_FLAG 0xC0 +#define FTS_CMD_FEATURE_ENABLE 0xC1 +#define FTS_CMD_FEATURE_DISABLE 0xC2 +#define FTS_CMD_GESTURE_CMD 0xC3 +#define FTS_CMD_LOCKDOWN_CMD 0xC4 +#define FTS_CMD_NOISE_WRITE 0xC7 +#define FTS_CMD_NOISE_READ 0xC8 +#define FTS_CMD_LOCKDOWN_FILL 0xCA +#define FTS_CMD_LOCKDOWN_WRITE 0xCB +#define FTS_CMD_LOCKDOWN_READ 0xCC +#define FTS_CMD_SAVE_CX_TUNING 0xFC + +//Event ID +#define EVENTID_NO_EVENT 0x00 +#define EVENTID_ERROR_EVENT 0x0F +#define EVENTID_CONTROL_READY 0x10 +#define EVENTID_FW_CONFIGURATION 0x12 +#define EVENTID_COMP_DATA_READ 0x13 +#define EVENTID_STATUS_UPDATE 0x16 +#define EVENTID_RELEASE_INFO 0x1C +#define EVENTID_LOCKDOWN_INFO 0x1E +#define EVENTID_ENTER_POINTER 0x03 +#define EVENTID_LEAVE_POINTER 0x04 +#define EVENTID_MOTION_POINTER 0x05 +#define EVENTID_HOVER_ENTER_POINTER 0x07 +#define EVENTID_HOVER_LEAVE_POINTER 0x08 +#define EVENTID_HOVER_MOTION_POINTER 0x09 +#define EVENTID_PROXIMITY_ENTER 0x0B +#define EVENTID_PROXIMITY_LEAVE 0x0C +#define EVENTID_KEY_STATUS 0x0E +#define EVENTID_LOCKDOWN_INFO_READ 0x1E +#define EVENTID_NOISE_READ 0x17 +#define EVENTID_NOISE_WRITE 0x18 +#define EVENTID_GESTURE 0x22 +#define EVENTID_FRAME_DATA_READ 0x25 +#define EVENTID_ECHO 0xEC +#define EVENTID_LAST (EVENTID_FRAME_DATA_READ+1) + +//EVENT TYPE +#define EVENT_TYPE_MS_TUNING_CMPL 0x01 +#define EVENT_TYPE_SS_TUNING_CMPL 0x02 +#define EVENT_TYPE_COMP_DATA_SAVED 0x04 +#define EVENT_TYPE_ITO 0x05 +#define EVENT_TYPE_FULL_INITIALIZATION 0x07 +#define EVENT_TYPE_LPTIMER_TUNING_CMPL 0x20 +#define EVENT_TYPE_ESD_ERROR 0x0A +#define EVENT_TYPE_WATCHDOG_ERROR 0x01 +#define EVENT_TYPE_CHECKSUM_ERROR 0x03 +#define EVENT_TYPE_LOCKDOWN 0x0A +#define EVENT_TYPE_LOCKDOWN_WRITE 0x08 +#define EVENT_TYPE_LOCKDOWN_ERROR 0x0B + +//CRC type +#define CRC_CONFIG_SIGNATURE 0x01 +#define CRC_CONFIG 0x02 +#define CRC_CX_MEMORY 0x03 + +// CONFIG ID INFO +#define CONFIG_ID_ADDR 0x0001 +#define CONFIG_ID_BYTE 2 + +//ADDRESS OFFSET IN SYSINFO +#define ADDR_RAW_TOUCH 0x0000 +#define ADDR_FILTER_TOUCH 0x0002 +#define ADDR_NORM_TOUCH 0x0004 +#define ADDR_CALIB_TOUCH 0x0006 +#define ADDR_RAW_HOVER_FORCE 0x000A +#define ADDR_RAW_HOVER_SENSE 0x000C +#define ADDR_FILTER_HOVER_FORCE 0x000E +#define ADDR_FILTER_HOVER_SENSE 0x0010 +#define ADDR_NORM_HOVER_FORCE 0x0012 +#define ADDR_NORM_HOVER_SENSE 0x0014 +#define ADDR_CALIB_HOVER_FORCE 0x0016 +#define ADDR_CALIB_HOVER_SENSE 0x0018 +#define ADDR_RAW_PRX_FORCE 0x001A +#define ADDR_RAW_PRX_SENSE 0x001C +#define ADDR_FILTER_PRX_FORCE 0x001E +#define ADDR_FILTER_PRX_SENSE 0x0020 +#define ADDR_NORM_PRX_FORCE 0x0022 +#define ADDR_NORM_PRX_SENSE 0x0024 +#define ADDR_CALIB_PRX_FORCE 0x0026 +#define ADDR_CALIB_PRX_SENSE 0x0028 +#define ADDR_RAW_MS_KEY 0x0032 +#define ADDR_NORM_MS_KEY 0x0036 +#define ADDR_COMP_DATA 0x0050 +#define ADDR_FRAMEBUFFER_DATA 0x8000 + +//ADDRESS FW REGISTER +#define ADDR_SENSE_LEN 0x0014 +#define ADDR_FORCE_LEN 0x0015 +#define ADDR_MS_TUNING_VER 0x0729 +#define ADDR_SS_TUNING_VER 0x074E + +//B2 INFO +#define B2_DATA_BYTES 4 +//number of bytes +#define B2_CHUNK ((FIFO_DEPTH/2)*B2_DATA_BYTES) + +//FEATURES +#define FEAT_GLOVE 0x00000001 +#define FEAT_STYLUS 0x00000002 +#define FEAT_COVER 0x00000004 +#define FEAT_CHARGER 0x00000008 +#define FEAT_VR 0x00000010 +#define FEAT_EDGE_REJECTION 0x00000020 +#define FEAT_CORNER_REJECTION 0x00000040 +#define FEAT_EDGE_PALM_REJECTION 0x00000100 + +//TOUCH SIZE +#define STYLUS_SIZE 0x01 +#define FINGER_SIZE 0x02 +#define LARGE_SIZE 0x03 + +//C7/C8 parameters +#define NOISE_PARAMETERS 0x01 + +//MP_FLAG_VALUE +#define INIT_MP 0xA5A5A501 +#define INIT_FIELD 0xA5A5A502 + +//NOISE PARAMS +#define NOISE_PARAMETERS_SIZE 4 //bytes + +//ERROR INFO +#define ERROR_INFO_SIZE (20*4) // bytes +#define ERROR_SIGNATURE 0xFA5005AF +#define ERROR_SIGN_HEAD 0xA5 + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.c b/drivers/input/touchscreen/st/fts_lib/ftsTest.c new file mode 100644 index 000000000000..baa0772cdb18 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.c @@ -0,0 +1,3844 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test *** + * * + ************************************************************************** + ************************************************************************** + * + */ + +#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 "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" + +#ifdef LIMITS_H_FILE +#include <../fts_limits.h> +#endif + +static char tag[8] = "[ FTS ]\0"; + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *) kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - + data[i * column + (j - 1)]); + } + } + return OK; +} + +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - + data[i * column + (j - 1)]); + } + } + + return OK; +} + +int computeAdjVert(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - + data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - + data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeTotal(u8 *data, u8 main, int row, int column, + int m, int n, u16 **result) +{ + int i, j; + int size = (row) * (column); + + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s : ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + (i * column + j)) = + m * main + n * data[i * column + j]; + } + } + + return OK; +} + +int checkLimitsMinMax(short *data, int row, int column, int min, int max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min + || data[i * column + j] > max) { + logError(1, "%s %s:Node[%d,%d] = %d ", tag, + __func__, i, j, data[i * column + j]); + logError(1, "exceed limit [%d,%d]\n", min, max); + count++; + } + } + } + + return count;//if count is 0 = OK, test completed successfully +} + +int checkLimitsGap(short *data, int row, int column, int threshold) +{ + int i, j; + int min_node; + int max_node; + + if (row == 0 || column == 0) { + logError(1, "%s %s:invalid number of rows = %d ", + tag, __func__, row); + logError(1, "or columns = %d %02\n", + column, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + min_node = data[0]; + max_node = data[0]; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min_node) { + min_node = data[i * column + j]; + } else { + if (data[i * column + j] > max_node) + max_node = data[i * column + j]; + } + } + } + + if (max_node - min_node > threshold) { + logError(1, "%s %s: GAP = %d exceed limit %d\n", + tag, __func__, max_node - min_node, threshold); + return ERROR_TEST_CHECK_FAIL; + } + return OK; +} + +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] + || data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s: Node[%d,%d] = %d ", + tag, __func__, + i, j, + data[i * column + j]); + logError(1, "exceed limit [%d, %d]\n", + min[i * column + j], + max[i * column + j]); + count++; + } + } + } + + return count; //if count is 0 = OK, test completed successfully +} + +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] + || data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d\n", + tag, __func__, i, j, + data[i * column + j]); + logError(1, "exceed limit [%d, %d]\n", + min[i * column + j], + max[i * column + j]); + count++; + } + } + } + + return count; //if count is 0 = OK, test completed successfully +} + +int checkLimitsMapAdj(u8 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d ", + tag, __func__, i, j); + logError(1, "exceed limit > %d\n", + data[i * column + j], + max[i * column + j]); + count++; + } + } + } + //if count is 0 = OK, test completed successfully + return count; +} + +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d ", + tag, __func__, i, j); + logError(1, "exceed limit > %d\n", + data[i * column + j], + max[i * column + j]); + count++; + } + } + } + //if count is 0 = OK, test completed successfully + return count; +} + +int production_test_ito(void) +{ + int res = OK; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + //look for ito event + int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; + + logError(0, "%s ITO Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + cmd = FTS_CMD_ITO_CHECK; + + logError(0, "%s ITO Check command sent...\n", tag); + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_PROD_TEST_ITO)); + return (ERROR_I2C_W | ERROR_PROD_TEST_ITO); + } + + logError(0, "%s Looking for ITO Event...\n", tag); + res = pollForEvent(eventToSearch, 2, + readData, TIMEOUT_ITO_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s: ITO Production test failed ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s ITO Production testes finished! ERROR %02X\n", + tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO); + } else { + logError(0, "%s ITO Production test finished!..OK\n", tag); + res = OK; + } + + res |= fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + res = (res | ERROR_PROD_TEST_ITO); + } + return res; +} + +int production_test_initialization(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_FULL_INITIALIZATION}; + logError(0, "%s INITIALIZATION Production test is starting\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_FULL_INITIALIZATION; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION)); + return (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s Looking for INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s: INITIALIZATION Production ", tag, __func__); + logError(1, "test failed %02X\n", + ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + if (readData[2] != 0x00) { + logError(0, "%sINITIALIZATION Production ", tag); + logError(0, "testes finished! FAILED %02X\n", + (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s INITIALIZATION Production test...OK\n", tag); + res = OK; + } + + logError(0, "%s Refresh Chip Info...\n", tag); + //need to update the chipInfo in order to refresh the tuning_versione + res |= readChipInfo(1); + + if (res < 0) { + logError(1, "%s %s: read chip info ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } + + return res; +} + +int ms_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_MS_TUNING_CMPL}; + + logError(0, "%s MS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_MS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_MS_TUNING)); + return (ERROR_I2C_W | ERROR_MS_TUNING); + } + + logError(0, "%s Looking for MS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s:MS INITIALIZATION Production\n", + tag, __func__); + logError(1, "test failed %02X\n", ERROR_MS_TUNING); + return (res | ERROR_MS_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s MS INITIALIZATION Production ", tag); + logError(0, "test finished! FAILED %02X\n", ERROR_MS_TUNING); + res = ERROR_MS_TUNING; + } else { + logError(0, + "%s MS INITIALIZATION Production test finished! OK\n", + tag); + res = OK; + } + + return res; +} + +int ss_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_SS_TUNING_CMPL}; + + logError(0, "%s SS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_SS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_SS_TUNING)); + return (ERROR_I2C_W | ERROR_SS_TUNING); + } + + logError(0, "%s Looking for SS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s:SS INITIALIZATION Production ", + tag, __func__); + logError(1, "test failed %02X\n", ERROR_SS_TUNING); + return (res | ERROR_SS_TUNING); + } + logError(0, "%s SS INITIALIZATION Production test finished!", tag); + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s.................FAILED ERROR %02X\n", + tag, ERROR_SS_TUNING); + res = ERROR_SS_TUNING; + } else { + logError(0, "%s.................OK\n", tag); + res = OK; + } + + return res; +} + +int lp_timer_calibration(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LPTIMER_TUNING_CMPL}; + + logError(0, "%s LP TIMER CALIBRATION command sent...\n", tag); + cmd = FTS_CMD_LP_TIMER_CALIB; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2:ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_LP_TIMER_TUNING)); + return (ERROR_I2C_W | ERROR_LP_TIMER_TUNING); + } + + logError(0, "%s Looking for LP TIMER CALIBRATION Event...\n", tag); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + + if (res < 0) { + logError(1, "%s:LP TIMER CALIBRATION Production test failed\n", + tag); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_LP_TIMER_TUNING); + return (res | ERROR_LP_TIMER_TUNING); + } + + logError(0, "LP TIMER CALIBRATION Production test finished!"); + if (readData[2] != 0x00 || readData[3] != 0x01) { + logError(0, "%s........FAILED ERROR %02X\n", + tag, ERROR_LP_TIMER_TUNING); + res = ERROR_LP_TIMER_TUNING; + } else { + logError(0, "%s.................OK\n", tag); + res = OK; + } + + return res; +} + +int save_cx_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_COMP_DATA_SAVED}; + + logError(0, "%s SAVE CX command sent...\n", tag); + cmd = FTS_CMD_SAVE_CX_TUNING; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2:ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_SAVE_CX_TUNING)); + return (ERROR_I2C_W | ERROR_SAVE_CX_TUNING); + } + + logError(0, "%s Looking for SAVE CX Event...\n", tag); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s: SAVE CX failed... ERROR %02X\n", + tag, ERROR_SAVE_CX_TUNING); + return (res | ERROR_SAVE_CX_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s SAVE CX finished! FAILED ERROR %02X\n", + tag, ERROR_SAVE_CX_TUNING); + res = ERROR_SAVE_CX_TUNING; + } else { + logError(0, "%s SAVE CX finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int production_test_split_initialization(int saveToFlash) +{ + int res; + + logError(0, "%s Split Initialization test is starting...\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, + ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s MS INITIALIZATION TEST:\n", tag); + res = ms_compensation_tuning(); + if (res < 0) { + logError(0, "%s %s:MS INITIALIZATION TEST FAILED! ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s MS INITIALIZATION TEST OK!\n", tag); + logError(0, "%s\n", tag); + logError(0, "%s SS INITIALIZATION TEST:\n", tag); + res = ss_compensation_tuning(); + if (res < 0) { + logError(0, "%s %s: SS INITIALIZATION TEST FAILED! ", + tag, __func__); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s SS INITIALIZATION TEST OK!\n", tag); + logError(0, "%s\n", tag); + logError(0, "%s LP INITIALIZATION TEST:\n", tag); + res = lp_timer_calibration(); + if (res < 0) { + logError(0, "%s %s: LP INITIALIZATION TEST FAILED! ", + tag, __func__); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s LP INITIALIZATION TEST OK!\n", tag); + if (saveToFlash) { + logError(0, "%s\n", tag); + logError(0, "%s SAVE CX TEST:\n", tag); + res = save_cx_tuning(); + if (res < 0) { + logError(0, "%s %s: SAVE CX TEST FAILED! ERROR %02X\n", + tag, __func__, res); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s SAVE CX TEST OK!\n", tag); + } + + logError(0, "%s Refresh Chip Info...\n", tag); + res |= readChipInfo(1); + if (res < 0) { + logError(1, "%s %s: read chip info ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s Split Initialization test finished! OK\n", tag); + } + return res; + +} + +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + struct TestToDo *todo, u32 signature) +{ + int res, ret; + + logError(0, "%s MAIN Production test is starting...\n", tag); + logError(0, "%s\n", tag); + logError(0, "%s ITO TEST:\n", tag); + + res = production_test_ito(); + if (res < 0) { + logError(0, "%s Error during ITO TEST! ERROR %08X\n", tag, res); + //in case of ITO TEST failure is no sense keep going + goto END; + } + logError(0, "%s ITO TEST OK!\n", tag); + + logError(0, "%s:\n", tag); + logError(0, "%s INITIALIZATION TEST:\n", tag); + + if (saveInit == 1) { + res = production_test_initialization(); + if (res < 0) { + logError(0, "%s Error during INITIALIZATION TEST!", + tag); + logError(0, "ERROR %08X\n", res); + if (stop_on_fail) + goto END; + } else { + logError(0, "%s INITIALIZATION TEST OK!\n", tag); + } + } else + logError(0, "%s INITIALIZATION TEST:..SKIPPED\n", tag); + + logError(0, "%s\n", tag); + if (saveInit == 1) { + logError(0, "%s Cleaning up...\n", tag); + ret = cleanUp(0); + if (ret < 0) { + logError(1, "%s %s: clean up ERROR %02X\n", + tag, __func__, ret); + res |= ret; + if (stop_on_fail) + goto END; + } + logError(0, "%s\n", tag); + } + + logError(0, "%s PRODUCTION DATA TEST:\n", tag); + ret = production_test_data(pathThresholds, stop_on_fail, todo); + if (ret < 0) { + logError(0, "%sError during PRODUCTION DATA TEST %08X\n", + tag, ret); + } else { + logError(0, "%s PRODUCTION DATA TEST OK!\n", tag); + } + res |= ret; + // the OR is important because if + //the data test is OK but the inizialization + //test fail, the main production + //test result should = FAIL + + if (ret == OK && saveInit == 1) { + logError(0, "%s SAVE FLAG:\n", tag); + ret = save_mp_flag(signature); + if (ret < OK) + logError(0, "%s SAVE FLAG:FAIL! ERROR %08X\n", + tag, ret); + else + logError(0, "%s SAVE FLAG:OK!\n", tag); + res |= ret; + // need to update the MP Flag + ret = readChipInfo(1); + if (ret < OK) + logError(1, "%s %s:read chip info ERROR %08X\n", + tag, __func__, ret); + res |= ret; + } + logError(0, "%s\n", tag); +END: + if (res < 0) { + logError(0, "%s MAIN Production test finished..FAILED\n", tag); + return res; + } + logError(0, "%s MAIN Production test finished..OK\n", tag); + return OK; +} + +int production_test_ms_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int ret, count_fail = 0; + struct MutualSenseFrame msRawFrame = {0}; + + int *thresholds = NULL; + int trows, tcolumns; + + //****** Mutual Sense Test ************/ + logError(0, "%s\n", tag); + logError(0, "%s MS RAW DATA TEST is starting...\n", tag); + if (todo->MutualRaw == 1 || todo->MutualRawGap == 1) { + ret = getMSFrame2(MS_TOUCH_ACTIVE, &msRawFrame); + if (ret < 0) { + logError(1, "%s %s:getMSFrame failed... ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS RAW MIN MAX TEST:\n", tag); + if (todo->MutualRaw == 1) { + ret = parseProductionTestLimits(path_limits, + MS_RAW_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(0, "%s %s:MS_RAW_MIN_MAX failed...", + tag, __func__); + logError(0, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(0, "%s %s:MS RAW failed...", + tag, __func__); + logError(0, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS RAW MIN MAX TEST:...", tag); + logError(0, "FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, "%s MS RAW MIN MAX TEST:OK\n", tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, "%s MS RAW MIN MAX TEST:SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW GAP TEST:\n", tag); + if (todo->MutualRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + MS_RAW_GAP, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s %s: MS_RAW_GAP failed... ", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0]); + if (ret != OK) { + logError(1, "%s %s:checkLimitsGap MS RAW ", + tag, __func__); + logError(1, "failed ERROR:%02X\n", ret); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, "%s MS RAW GAP TEST:....OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, "%s MS RAW GAP TEST:..SKIPPED\n", tag); + } else + logError(0, "%s MS RAW FRAME TEST:.SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS KEY RAW TEST:\n", tag); + if (todo->MutualKeyRaw == 1) { + ret = production_test_ms_key_raw(path_limits); + if (ret < 0) { + logError(1, "%s %s:production_test_ms_key_raw ", + tag, __func__); + logError(1, "failed ERROR:%02X\n", ret); + count_fail += 1; + if (count_fail == 1) { + logError(0, "%s MS RAW DATA TEST:FAIL ", tag); + logError(0, "fails_count:%d\n\n", count_fail); + goto ERROR_LIMITS; + } + } + } else + logError(0, "%s MS KEY RAW TEST:....SKIPPED\n", tag); +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + logError(0, "%s MS RAW DATA TEST finished!.OK\n", tag); + return OK; + } + print_frame_short("MS Raw frame =", + array1dTo2d_short(msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + kfree(msRawFrame.node_data); + kfree(thresholds); + logError(0, "%s MS RAW DATA TEST: FAIL fails_count = %d\n\n", + tag, count_fail); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + +ERROR_LIMITS: + kfree(msRawFrame.node_data); + kfree(thresholds); + return ret; +} + +int production_test_ms_key_raw(char *path_limits) +{ + int ret; + struct MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + //************* Mutual Sense Test ************/ + logError(0, "%s MS KEY RAW DATA TEST is starting...\n", tag); + + ret = getMSFrame2(MS_KEY, &msRawFrame); + if (ret < 0) { + logError(1, "%s %s:getMSKeyFrame failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, + MS_KEY_RAW_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s %s: MS_KEY_RAW_MIN_MAX failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMinMax failed..ERROR COUNT:%d\n", + tag, __func__, ret); + goto ERROR; + } else + logError(0, "%s MS KEY RAW TEST:.................OK\n\n", tag); + + kfree(thresholds); + thresholds = NULL; + + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + return OK; +ERROR: + print_frame_short("MS Key Raw frame =", + array1dTo2d_short(msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + kfree(msRawFrame.node_data); + kfree(thresholds); + logError(0, "%s MS KEY RAW TEST:......FAIL\n\n", tag); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); +ERROR_LIMITS: + kfree(msRawFrame.node_data); + kfree(thresholds); + return ret; +} + +int production_test_ms_cx(char *path_limits, + int stop_on_fail, struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + struct MutualSenseData msCompData; + + u8 *adjhor = NULL; + + u8 *adjvert = NULL; + + u16 container; + u16 *total_cx = NULL; + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + //MS CX TEST + logError(0, "%s\n", tag); + logError(0, "%s MS CX Testes are starting...\n", tag); + //read MS compensation data + ret = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &msCompData); + if (ret < 0) { + logError(1, "%s %s:readMutualSenseCompensationData ", + tag, __func__); + logError(1, "failed %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS CX1 TEST:\n", tag); + if (todo->MutualCx1 == 1) { + ret = parseProductionTestLimits(path_limits, + MS_CX1_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s %s:parseProductionTestLimits failed ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16)msCompData.cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits + if (ret != OK) { + logError(1, "%s %s:checkLimitsMinMax MS CX1 failed ", + tag, __func__); + logError(1, "ERROR COUNT:%d\n", ret); + logError(0, "%s MS CX1 TEST:.........FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX1 TEST:..........OK\n\n", tag); + } else + logError(0, "%s MS CX1 TEST:.......SKIPPED\n\n", tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS CX2 MIN MAX TEST:\n", tag); + if (todo->MutualCx2 == 1) { + ret = parseProductionTestLimits(path_limits, + MS_CX2_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); //load min thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + MS_CX2_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load max thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s: MS_CX2_MAP_MAX failed ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, + thresholds_max);//check the limits + if (ret != OK) { + logError(1, "%s %s:checkLimitsMap MS CX2 MIN MAX ", + tag, __func__); + logError(1, "failed ERR_COUNT:%d\n", ret); + logError(0, "%s MS CX2 MIN MAX TEST:......FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 MIN MAX TEST:....OK\n\n", tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS CX2 MIN MAX TEST:....SKIPPED\n\n", tag); + + logError(0, "%s MS CX2 ADJ TEST:\n", tag); + if (todo->MutualCx2Adj == 1) { + //MS CX2 ADJ HORIZ + logError(0, "%s MS CX2 ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHoriz(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, "%s %s:computeAdjHoriz failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS CX2 ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + MS_CX2_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node - 1)) { + + logError(1, "%s %s: MS_CX2_ADJH_MAP_MAX failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, + msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMapAdj CX2 ADJH failed ", + tag, __func__); + logError(1, "ERROR COUNT:%d\n", ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:..FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ HORIZ TEST:..OK\n\n", tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + + //MS CX2 ADJ VERT + logError(0, "%s MS CX2 ADJ VERT TEST:\n", tag); + ret = computeAdjVert(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjvert); + if (ret < 0) { + logError(1, "%s %s:computeAdjVert failed... ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS CX2 ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + MS_CX2_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:MS_CX2_ADJV_MAP_MAX failed ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, + msCompData.header.force_node - 1, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMapAdj CX2 ADJV failed ", + tag, __func__); + logError(1, "COUNT:%d\n", ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ VERT TEST:OK\n\n", tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, "%s MS CX2 ADJ TEST:SKIPPED\n\n", tag); + + //START OF TOTAL CHECK + logError(0, "%s MS TOTAL CX TEST:\n", tag); + + if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) { + ret = computeTotal(msCompData.node_data, + msCompData.cx1, + msCompData.header.force_node, + msCompData.header.sense_node, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s %s:computeTotalCx failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX MIN MAX TEST:\n", tag); + if (todo->MutualCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns);//load min thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:MS_TOTAL_CX_MAP_MAX failed", + tag, __func__); + logError(1, "...ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, + thresholds_max);//check the limits + if (ret != OK) { + logError(1, "%s %s:MS TOTAL CX TEST failed ", + tag, __func__); + logError(1, "COUNT:%d\n", ret); + logError(0, "%s MS TOTAL CX MIN MAX ", tag); + logError(0, "TEST:FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS TOTAL CX MIN MAX TEST", tag); + logError(0, ":OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS TOTAL CX MIN MAX TEST:SKIPPED\n\n", + tag); + + + logError(0, "%s MS TOTAL CX ADJ TEST:\n", tag); + if (todo->MutualCxTotalAdj == 1) { + //MS TOTAL CX ADJ HORIZ + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:\n", tag); + + //thresholds_max = NULL; + ret = computeAdjHorizTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", + tag); + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_TOTAL_CX_ADJH_MAP_MAX "); + logError(1, "failed...RROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, + msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj MS TOTAL "); + logError(1, "CX ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + + //MS TOTAL CX ADJ VERT + logError(0, "%s MS TOTAL CX ADJ VERT TEST:\n", tag); + + ret = computeAdjVertTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || + (trows != msCompData.header.force_node - 1 + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_TOTAL_CX_ADJV_MAP_MAX failed"); + logError(1, "... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + msCompData.header.force_node - 1, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj MS TOTAL "); + logError(1, "CX ADJV failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................FAIL\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS TOTAL CX ADJ VERT ", tag); + logError(0, "TEST:.................OK\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else { + logError(0, "%s MS TOTAL CX ADJ ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + + kfree(total_cx); + total_cx = NULL; + } else + logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", + tag); + + if ((todo->MutualKeyCx1 + | todo->MutualKeyCx2 | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, + stop_on_fail, + todo); + if (ret < 0) { + count_fail += 1; + logError(1, "%s production_test_data: ", tag); + logError(1, "production_test_ms_key_cx failed..."); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s MS CX testes finished!", tag); + logError(0, ".................FAILED "); + logError(0, "fails_count = %d\n\n", count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", + tag); + + if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 + | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, + stop_on_fail, + todo); + + if (ret < 0) { + count_fail += 1; + logError(1, "%s %s:production_test_ms_key_cx ", + tag, __func__); + logError(1, "failed :%02X\n", ret); + logError(0, "%s MS CX testes finished! ", tag); + logError(0, "fails_count = %d\n\n", count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:..SKIPPED\n", tag); +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS CX testes finished! OK\n", tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } + print_frame_u8("MS Init Data (Cx2) =", + array1dTo2d_u8(msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + msCompData.header.force_node, + msCompData.header.sense_node); + logError(0, "%s MS CX testes finished! fails_count = %d\n\n", + tag, count_fail); + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(msCompData.node_data); + + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +ERROR_LIMITS: + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(msCompData.node_data); + return ret; +} + +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + int num_keys = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + struct MutualSenseData msCompData; + + u16 container; + u16 *total_cx = NULL; + + + //MS CX TEST + logError(0, "%s MS KEY CX Testes are starting...\n", tag); + + //read MS compensation data + ret = readMutualSenseCompensationData(MS_KEY, &msCompData); + if (ret < 0) { + logError(0, "%s production_test_data: ", tag); + logError(0, "readMutualSenseCompensationData failed... "); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //the meaningful data are only in the first row, the other rows are + // only a copy of the first one + if (msCompData.header.force_node > msCompData.header.sense_node) + num_keys = msCompData.header.force_node; + else + num_keys = msCompData.header.sense_node; + + logError(0, "%s MS KEY CX1 TEST:\n", tag); + if (todo->MutualKeyCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX1_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(0, "%s production_test_data: ", tag); + logError(0, "parseProductionTestLimits "); + logError(0, "MS_KEY_CX1_MIN_MAX failed... "); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16) msCompData.cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax MS CX1 failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS KEY CX1 TEST:................", tag); + logError(0, ".FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS KEY CX1 TEST:................", tag); + logError(0, ".OK\n\n"); + } + } else + logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS KEY CX2 TEST:\n", tag); + if (todo->MutualKeyCx2 == 1) { + //load min thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX2_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX2_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX2_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX2_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the limits + ret = checkLimitsMap(msCompData.node_data, + 1, + num_keys, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap MS KEY CX2 failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS KEY CX2 TEST:................", tag); + logError(0, ".FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS KEY CX2 TEST:...............", tag); + logError(0, "..OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", + tag); + + //START OF TOTAL CHECK + logError(0, "%s MS KEY TOTAL CX TEST:\n", tag); + + if (todo->MutualKeyCxTotal == 1) { + ret = computeTotal(msCompData.node_data, + msCompData.cx1, 1, + num_keys, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotalCx failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load min thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "%parseProductionTestLimits "); + logError(1, "MS_KEY_TOTAL_CX_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_TOTAL_CX_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the limits + ret = checkLimitsMapTotal(total_cx, + 1, + num_keys, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap MS TOTAL "); + logError(1, "KEY CX TEST failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s MS KEY TOTAL CX TEST:...........", tag); + logError(0, "......FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS KEY TOTAL CX TEST:...........", tag); + logError(0, "......OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + + kfree(total_cx); + total_cx = NULL; + } else { + logError(0, "%s MS KEY TOTAL CX TEST:.................", tag); + logError(0, "SKIPPED\n"); + } + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, + "%s MS KEY CX testes finished! OK\n", tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } + print_frame_u8("MS Key Init Data (Cx2) =", + array1dTo2d_u8(msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + 1, + msCompData.header.sense_node); + logError(0, "%s MS Key CX testes finished!..............", tag); + logError(0, "...FAILED fails_count = %d\n\n", count_fail); + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(msCompData.node_data); + kfree(total_cx); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +ERROR_LIMITS: + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(msCompData.node_data); + kfree(total_cx); + return ret; +} + +int production_test_ss_raw(char *path_limits, + int stop_on_fail, struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + int rows, columns; + + //short *ssRawFrame = NULL; + struct SelfSenseFrame ssRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + //MS SS TEST + logError(0, "%s\n", tag); + logError(0, "%s SS RAW Testes are starting...\n", tag); + + //******* Self Sense Test ***************/ + logError(0, "%s Getting SS Frame...\n", tag); + ret = getSSFrame2(SS_TOUCH, &ssRawFrame); + if (ret < 0) { + logError(1, "%s %s:getSSFrame failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //SS RAW (PROXIMITY) FORCE TEST + logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:\n", tag); + + if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1) { + //there are no data for the sense + //channels due to the fact that + //the force frame is analized + columns = 1; + rows = ssRawFrame.header.force_node; + + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceRaw == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", + ERROR_PROD_TEST_DATA); + //return (ret | ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.force_data, + rows, columns, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMinMax ", + tag, __func__); + logError(1, "failed ERROR COUNT:%d\n", ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE", tag); + logError(0, " MIN MAX TEST:FAIL\n\n"); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short(ssRawFrame.force_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE MIN MAX TEST:............."); + logError(0, "....OK\n\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE MIN MAX TEST:................."); + logError(0, "SKIPPED\n\n"); + } + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:\n", tag); + if (todo->SelfForceRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_FORCE_GAP, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_FORCE_GAP failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.force_data, + rows, + columns, + thresholds[0]); + + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsGap SS RAW "); + logError(1, "(PROXIMITY) FORCE GAP failed..."); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "FAIL\n\n"); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short(ssRawFrame.force_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "OK\n\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "SKIPPED\n\n"); + } + + kfree(ssRawFrame.force_data); + ssRawFrame.force_data = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) FORCE ", tag); + logError(0, "TEST:.................SKIPPED\n\n"); + } + + logError(0, "%s\n", tag); + //SS RAW (PROXIMITY) SENSE TEST + logError(0, "%s SS RAW (PROXIMITY) SENSE TEST:\n", tag); + + if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1) { + columns = ssRawFrame.header.sense_node; + // there are no data for the force channels due + // to the fact that the sense frame is analized + rows = 1; + + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseRaw == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_SENSE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.sense_data, + rows, + columns, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS RAW "); + logError(1, "(PROXIMITY) SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE MIN MAX TEST:............."); + logError(0, "....FAIL\n"); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short(ssRawFrame.sense_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE MIN MAX TEST:............."); + logError(0, "....OK\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX", tag); + logError(0, " TEST:.................SKIPPED\n"); + } + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:\n", tag); + if (todo->SelfSenseRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_SENSE_GAP, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_SENSE_GAP failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.sense_data, + rows, + columns, + thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsGap SS RAW "); + logError(1, "(PROXIMITY) SENSE GAP failed... "); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE GAP TEST:................."); + logError(0, "FAIL\n"); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short(ssRawFrame.sense_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE", tag); + logError(0, " GAP TEST:.................OK\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + + kfree(ssRawFrame.sense_data); + ssRawFrame.sense_data = NULL; + } + + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS RAW testes finished!.................OK\n\n", + tag); + return OK; + } + logError(0, "%s SS RAW testes finished!.................", tag); + logError(0, "FAILED fails_count = %d\n\n", count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +ERROR_LIMITS: + kfree(ssRawFrame.force_data); + kfree(ssRawFrame.sense_data); + kfree(thresholds); + return ret; + +} + +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + struct SelfSenseData ssCompData; + + u8 *adjhor = NULL; + u8 *adjvert = NULL; + + u16 container; + int *ix1_w = NULL; + int *ix2_w = NULL; + u16 *total_ix = NULL; + u16 *total_cx = NULL; + + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + logError(0, "%s\n", tag); + logError(0, "%s SS IX CX testes are starting...\n", tag); + + //read the SS compensation data + ret = readSelfSenseCompensationData(SS_TOUCH, &ssCompData); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "readSelfSenseCompensationData failed... ", tag); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //********************** SS FORCE IX *********************************/ + //SS IX1 FORCE TEST + logError(0, "%s SS IX1 FORCE TEST:\n", tag); + if (todo->SelfForceIx1 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_IX1_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + container = (u16) ssCompData.f_ix1; + + //check the limits + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax "); + logError(1, "SS IX1 FORCE TEST failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX1 FORCE TEST:......OK\n\n", tag); + } + + kfree(thresholds); + thresholds = NULL; + //SS IX2 FORCE TEST + logError(0, "%s SS IX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIx2 == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits"); + logError(1, "SS_IX2_FORCE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.ix2_fm, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.........."); + logError(0, "FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.....", tag); + logError(0, "OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS IX2 FORCE MIN MAX TEST:...........", tag); + logError(0, "KIPPED\n\n"); + } + + logError(0, "%s SS IX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIx2Adj == 1) { + //SS IX2 FORCE ADJV TEST + logError(0, "%s SS IX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.ix2_fm, + ssCompData.header.force_node, + 1, + &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS IX2 FORCE ADJV "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 FORCE ADJV computed!\n", tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_FORCE_ADJV_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE failed... "); + logError(0, "FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 FORCE ADJV TEST:", tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + + } else { + logError(0, "%s SS IX2 FORCE ADJ TEST:", tag); + logError(0, ".................SKIPPED\n\n"); + } + + //SS TOTAL FORCE IX + logError(0, "%s SS TOTAL IX FORCE TEST:\n", tag); + if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX FORCE Weights...\n", tag); + + //load the IX1 weight + ret = parseProductionTestLimits(path_limits, + SS_IX1_FORCE_W, + &ix1_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_W failed... ERROR %02X\n", + tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //load the IX2 weight + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_W, + &ix2_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_W failed... ERROR %02X\n", + tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", + tag, *ix1_w, *ix2_w); + ret = computeTotal(ssCompData.ix2_fm, ssCompData.f_ix1, + ssCompData.header.force_node, + 1, + *ix1_w, + *ix2_w, + &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Ix Force failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + kfree(ix1_w); + ix1_w = NULL; + kfree(ix2_w); + ix2_w = NULL; + + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIxTotal == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_ix, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX FORCE"); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL IX FORCE MIN MAX ", + tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX FORCE MIN MAX ", + tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } + + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIxTotalAdj == 1) { + //SS TOTAL IX FORCE ADJV TEST + logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotal(total_ix, + ssCompData.header.force_node, + 1, + &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS TOTAL IX "); + logError(1, "FORCE ADJV failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", + tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 + || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_ADJV_MAP_MAX"); + logError(1, "... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX "); + logError(1, "FORCE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:", + tag); + logError(0, ".................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:", + tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:"); + logError(0, ".................SKIPPED\n"); + } + + kfree(total_ix); + total_ix = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE TEST:", tag); + logError(0, ".................SKIPPED\n\n"); + } + + + //************** SS SENSE IX *******************/ + //SS IX1 SENSE TEST + logError(0, "%s SS IX1 SENSE TEST:\n", tag); + if (todo->SelfSenseIx1 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_IX1_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16) ssCompData.s_ix1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS IX1 SENSE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX1 SENSE TEST:..............", tag); + logError(0, "...OK\n\n"); + } + } else { + logError(0, "%s SS IX1 SENSE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + kfree(thresholds); + thresholds = NULL; + //SS IX2 SENSE TEST + logError(0, "%s SS IX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIx2 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); //load the min thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "S_IX2_SENSE_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.ix2_sn, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.....", tag); + logError(0, "............FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 SENSE MIN MAX TEST:", tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS IX2 SENSE MIN MAX TEST:..............", tag); + logError(0, "...SKIPPED\n\n"); + } + + logError(0, "%s SS IX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIx2Adj == 1) { + //SS IX2 SENSE ADJH TEST + logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.ix2_sn, + 1, + ssCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS IX2 SENSE ADJH "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS IX2 SENSE ADJH "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 SENSE ADJH TEST:.......", tag); + logError(0, "..........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 SENSE ADJH TEST:........", tag); + logError(0, ".........OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else { + logError(0, "%s SS IX2 SENSE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n", tag); + } + + //SS TOTAL IX SENSE + logError(0, "%s SS TOTAL IX SENSE TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX SENSE Weights...\n", tag); + //load the IX1 weight + ret = parseProductionTestLimits(path_limits, + SS_IX1_SENSE_W, + &ix1_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_W failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the IX2 weight + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_W, + &ix2_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_W failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", + tag, *ix1_w, *ix2_w); + + ret = computeTotal(ssCompData.ix2_sn, + ssCompData.s_ix1, + 1, + ssCompData.header.sense_node, + *ix1_w, + *ix2_w, + &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Ix Sense "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + kfree(ix1_w); + ix1_w = NULL; + kfree(ix2_w); + ix2_w = NULL; + + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:\n", tag); + //load the min thresholds + if (todo->SelfSenseIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || + tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_ix, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX SENSE"); + logError(1, " failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", + tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", + tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + + + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIxTotalAdj == 1) { + //SS TOTAL IX SENSE ADJH TEST + logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(total_ix, + 1, + ssCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS TOTAL "); + logError(1, "IXSENSE ADJH failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL IX SENSE ADJ HORIZ ", tag); + logError(0, "computed!\n"); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS TOTAL "); + logError(1, "IX SENSE ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL IX SENSE ADJH ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX SENSE ADJH ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:.....", tag); + logError(0, "............SKIPPED\n"); + } + kfree(total_ix); + total_ix = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE TEST:............", tag); + logError(0, ".....SKIPPED\n"); + } + + //************************ SS SENSE CX *******************************/ + //SS CX1 FORCE TEST + logError(0, "%s SS CX1 FORCE TEST:\n", tag); + if (todo->SelfForceCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, + SS_CX1_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX1_FORCE_MIN_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the limits + container = (u16) ssCompData.f_cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS CX1 FORCE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX1 FORCE TEST:.............", tag); + logError(0, "....OK\n\n"); + } + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS CX1 FORCE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + //SS CX2 FORCE TEST + logError(0, "%s SS CX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCx2 == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "SS_CX2_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.cx2_fm, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "%checkLimitsMap SS CX2 FORCE "); + logError(1, "%failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.....", tag); + logError(0, "............FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 FORCE MIN MAX TEST:......", tag); + logError(0, "...........OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS CX2 FORCE MIN MAX TEST:..............", tag); + logError(0, "...SKIPPED\n"); + } + + logError(0, "%s SS CX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCx2Adj == 1) { + //SS CX2 FORCE ADJV TEST + logError(0, "%s SS CX2 FORCE ADJVERT TEST:\n", tag); + //comepute the ADJV for CX2 FORCE + ret = computeAdjVert(ssCompData.cx2_fm, + ssCompData.header.force_node, + 1, + &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS CX2 FORCE ADJV "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 FORCE ADJV computed!\n", tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_FORCE_ADJV_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 FORCE ADJV TEST:......", tag); + logError(0, "...........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 FORCE ADJV TEST:.....", tag); + logError(0, "............OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else { + logError(0, "%s SS CX2 FORCE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + //SS TOTAL CX FORCE + logError(0, "%s SS TOTAL CX FORCE TEST:\n", tag); + if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_fm, + ssCompData.f_cx1, + ssCompData.header.force_node, + 1, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Cx Force failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:\n", tag); + //load the min thresholds + if (todo->SelfForceCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_cx, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL FORCE "); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL FORCE MIN MAX ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL FORCE MIN MAX ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } + + //SS TOTAL CX FORCE ADJV TEST + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX FORCE ADJVERT ", tag); + logError(0, "TEST:\n"); + + //comepute the ADJV for CX2 FORCE + ret = computeAdjVertTotal(total_cx, + ssCompData.header.force_node, + 1, + &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS TOTAL CX FORCE"); + logError(1, " ADJV failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 + || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL CX FORCE"); + logError(1, " failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL CX FORCE ADJV ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX FORCE ADJV ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + + } else { + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:......", + tag); + logError(0, "..........SKIPPED\n"); + } + kfree(total_cx); + total_cx = NULL; + } else { + logError(0, "%s SS TOTAL CX FORCE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + + //**************** SS SENSE CX **************************************/ + //SS CX1 SENSE TEST + logError(0, "%s SS CX1 SENSE TEST:\n", tag); + if (todo->SelfSenseCx1 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_CX1_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX1_SENSE_MIN_MAX failed"); + logError(1, "... ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16) ssCompData.s_cx1; + //check the limits + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS CX1 SENSE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX1 SENSE TEST:..............", tag); + logError(0, "...OK\n\n"); + } + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS CX1 SENSE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + + //SS CX2 SENSE TEST + logError(0, "%s SS CX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCx2 == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_SENSE_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.cx2_sn, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS CX2 SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 SENSE MIN MAX TEST:......", tag); + logError(0, "...........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 SENSE MIN MAX TEST:", tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.........", tag); + logError(0, "........SKIPPED\n"); + } + logError(0, "%s SS CX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCx2Adj == 1) { + //SS CX2 SENSE ADJH TEST + logError(0, "%s SS CX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.cx2_sn, + 1, + ssCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS CX2 SENSE ADJH "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 SENSE ADJH computed!\n", tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS CX2 SENSE ADJH "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 SENSE ADJH TEST:.........", tag); + logError(0, "........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 SENSE ADJH TEST:.........", tag); + logError(0, "........OK\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else { + logError(0, "%s SS CX2 SENSE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + //SS TOTAL CX SENSE + logError(0, "%s SS TOTAL CX SENSE TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_sn, + ssCompData.s_cx1, + 1, + ssCompData.header.sense_node, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Cx Sense failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:\n", tag); + //load the min thresholds + if (todo->SelfSenseCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_cx, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "heckLimitsMap SS TOTAL CX SENSE "); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL CX SENSE MIN ", tag); + logError(0, "MAX TEST:................."); + logError(0, "FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX SENSE MIN ", tag); + logError(0, "MAX TEST:................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } + + + //SS TOTAL IX SENSE ADJH TEST + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(total_cx, + 1, + ssCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS TOTAL CX "); + logError(1, "SENSE ADJH failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL CX SENSE ADJ HORIZ ", tag); + logError(0, "computed!\n"); + + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 || + tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS TOTAL "); + logError(1, "CX SENSE ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL CX SENSE ADJH ", tag); + logError(0, "TEST:...FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:", + tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else { + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:.", tag); + logError(0, "SKIPPED\n"); + } + kfree(total_cx); + total_cx = NULL; + } else + logError(0, "%s SS TOTAL CX SENSE TEST:.....SKIPPED\n", tag); + + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + kfree(ssCompData.ix2_fm); + ssCompData.ix2_fm = NULL; + kfree(ssCompData.ix2_sn); + ssCompData.ix2_sn = NULL; + kfree(ssCompData.cx2_fm); + ssCompData.cx2_fm = NULL; + kfree(ssCompData.cx2_sn); + ssCompData.cx2_sn = NULL; + logError(0, "%s SS IX CX testes finished!........OK\n\n", tag); + ret = OK; + } else { + //print all kind of data in just one row for readability reason + print_frame_u8("SS Init Data Ix2_fm = ", + array1dTo2d_u8(ssCompData.ix2_fm, + ssCompData.header.force_node, + ssCompData.header.force_node), + 1, + ssCompData.header.force_node); + print_frame_u8("SS Init Data Cx2_fm = ", + array1dTo2d_u8(ssCompData.cx2_fm, + ssCompData.header.force_node, + ssCompData.header.force_node), + 1, + ssCompData.header.force_node); + print_frame_u8("SS Init Data Ix2_sn = ", + array1dTo2d_u8(ssCompData.ix2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), + 1, + ssCompData.header.sense_node); + print_frame_u8("SS Init Data Cx2_sn = ", + array1dTo2d_u8(ssCompData.cx2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), + 1, + ssCompData.header.sense_node); + logError(0, "%s SS IX CX testes finished!.................", + tag); + logError(0, "FAILED fails_count = %d\n\n", count_fail); + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(ix1_w); + kfree(ix2_w); + kfree(total_ix); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + ret = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + } + return ret; +ERROR_LIMITS: + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(ix1_w); + kfree(ix2_w); + kfree(total_ix); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + return ret; +} + +int production_test_data(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int res = OK, ret; + + if (todo == NULL) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "No TestToDo specified!! "); + logError(0, "ERROR = %02X\n", + (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); + return (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s DATA Production test is starting...\n", tag); + ret = production_test_ms_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ms_raw failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ms_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ms_cx failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ss_raw failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_ix_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ss_ix_cx failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + +END: + if (res < OK) + logError(0, "%s DATA Production test failed!\n", tag); + else + logError(0, "%s DATA Production test finished!\n", tag); + return res; +} + + +int save_mp_flag(u32 signature) +{ + int res = -1; + int i; + u8 cmd[6] = {FTS_CMD_WRITE_MP_FLAG, 0x00, 0x00, 0x00, 0x00, 0x00}; + + u32ToU8(signature, &cmd[2]); + + logError(0, "%s Starting Saving Flag with signature = %08X ...\n", + tag, signature); + + for (i = 0; i < SAVE_FLAG_RETRY && res < OK; i++) { + logError(0, "%s Attempt number %d to save mp flag !\n", + tag, i+1); + logError(0, "%s Command write flag sent...\n", tag); + res = fts_writeFwCmd(cmd, 6); + if (res >= OK) + res = save_cx_tuning(); + } + + if (res < OK) { + logError(1, "%s %s: ERROR %08X ...\n", tag, __func__, res); + } else { + logError(0, "%s Saving Flag DONE!\n", tag); + res = OK; + } + return res; +} + +int parseProductionTestLimits(char *path, char *label, + int **data, int *row, int *column) +{ + int find = 0; + char *token = NULL; + int i = 0; + int j = 0; + int z = 0; + + char *line2 = NULL; + char line[800]; + int fd = -1; + char *buf = NULL; + int n, size, pointer = 0, ret = OK; + char *data_file = NULL; +#ifndef LIMITS_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + + dev = getDev(); + if (dev != NULL) + fd = request_firmware(&fw, path, dev); +#else + fd = 0; +#endif + + if (fd != 0) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +#ifndef LIMITS_H_FILE + size = fw->size; + data_file = (char *)fw->data; + logError(0, "%s Start to reading %s...\n", tag, path); +#else + size = LIMITS_SIZE_NAME; + data_file = (char *)(LIMITS_ARRAY_NAME); +#endif + logError(0, "%s The size of the limits file is %d bytes\n", tag, size); + + while (find == 0) { + //start to look for the wanted label + if (readLine(&data_file[pointer], line, size-pointer, &n) < 0) { + find = -1; + break; + } + pointer += n; + //each header row start with + // *ex. *label, n_row, n_colum + if (line[0] != '*') + continue; + + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, "%s %s:kstrdup ERR %02X\n", + tag, __func__, ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + line2 += 1; + token = strsep(&line2, ","); + //if the row is the wanted one i + //retrieve rows and columns info + if (strcmp(token, label) == 0) { + find = 1; + token = strsep(&line2, ","); + if (token != NULL) { + ret = kstrtoint(token, 10, row); + if (ret != 0) + return -EINVAL; + logError(0, "%s Row = %d\n", tag, *row); + } else { + logError(1, "%s %s 1:ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + token = strsep(&line2, ","); + if (token != NULL) { + ret = kstrtoint(token, 10, column); + if (ret != 0) + return -EINVAL; + logError(0, "%s Column = %d\n", tag, *column); + } else { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + kfree(buf); + buf = NULL; + //allocate memory for containing data + *data = (int *)kmalloc_array(((*row) * (*column)), + sizeof(int), GFP_KERNEL); + j = 0; + if (*data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + //release_firmware(fw); + //return ERROR_ALLOC; + ret = ERROR_ALLOC; + goto END; + } + //start to read the data + for (i = 0; i < *row; i++) { + //line = buf; + if (readLine(&data_file[pointer], line, + size-pointer, &n) < 0) { + logError(1, "%s %s : ERROR %02X\n", + tag, __func__, ERROR_FILE_READ); + //release_firmware(fw); + //return ERROR_FILE_READ + ret = ERROR_FILE_READ; + goto END; + } + pointer += n; + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, "%s %s: kstrdup ", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + token = strsep(&line2, ","); + for (z = 0; + (z < *column) && (token != NULL); z++) { + ret = kstrtoint(token, + 10, + ((*data) + j)); + if (ret != 0) + return -EINVAL; + j++; + token = strsep(&line2, ","); + } + kfree(buf); + buf = NULL; + } + //check that all the data are read + if (j == ((*row) * (*column))) { + logError(0, "%s READ DONE!\n", tag); + //release_firmware(fw); + //return OK; + ret = OK; + goto END; + } + logError(1, "%s %s 3:ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + kfree(buf); + buf = NULL; + + } + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_LABEL_NOT_FOUND); + ret = ERROR_LABEL_NOT_FOUND; +END: + kfree(buf); +#ifndef LIMITS_H_FILE + release_firmware(fw); +#endif + return ret; + +} + +int readLine(char *data, char *line, int size, int *n) +{ + int i = 0; + + if (size < 1) + return -EINVAL; + + while (data[i] != '\n' && i < size) { + line[i] = data[i]; + i++; + } + *n = i + 1; + line[i] = '\0'; + + return OK; +} + + diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.h b/drivers/input/touchscreen/st/fts_lib/ftsTest.h new file mode 100644 index 000000000000..0e9334d452f7 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_TEST_H +#define __FTS_TEST_H + +#include "ftsSoftware.h" + +#define LIMITS_FILE "stm_fts_production_limits.csv" + +#define WAIT_FOR_FRESH_FRAMES 100 //ms +#define WAIT_AFTER_SENSEOFF 50 //ms + +#define TIMEOUT_ITO_TEST_RESULT 200 //ms +#define TIMEOUT_INITIALIZATION_TEST_RESULT 5000 //ms + +//LABELS PRODUCTION TEST LIMITS FILE +#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" +#define MS_RAW_GAP "MS_RAW_DATA_GAP" +#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" +#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" +#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" +#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" +#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" +#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" +#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" +#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" +#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" +#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" +#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" +#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" +#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" +#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" +#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" +#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" +#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" +#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" +#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" +#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" +#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" +#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" + +// TOTAL SS +#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" + +//KEYS +#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" +#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" +#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" +#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" +#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" +#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" + +//CONSTANT TOTAL IX +#define SS_IX1_FORCE_W "IX1_FORCE_W" +#define SS_IX2_FORCE_W "IX2_FORCE_W" +#define SS_IX1_SENSE_W "IX1_SENSE_W" +#define SS_IX2_SENSE_W "IX2_SENSE_W" + + +#define SAVE_FLAG_RETRY 3 + + +struct TestToDo { + int MutualRaw; + int MutualRawGap; + int MutualCx1; + int MutualCx2; + int MutualCx2Adj; + int MutualCxTotal; + int MutualCxTotalAdj; + + int MutualKeyRaw; + int MutualKeyCx1; + int MutualKeyCx2; + int MutualKeyCxTotal; + + int SelfForceRaw; + int SelfForceRawGap; + int SelfForceIx1; + int SelfForceIx2; + int SelfForceIx2Adj; + int SelfForceIxTotal; + int SelfForceIxTotalAdj; + int SelfForceCx1; + int SelfForceCx2; + int SelfForceCx2Adj; + int SelfForceCxTotal; + int SelfForceCxTotalAdj; + + int SelfSenseRaw; + int SelfSenseRawGap; + int SelfSenseIx1; + int SelfSenseIx2; + int SelfSenseIx2Adj; + int SelfSenseIxTotal; + int SelfSenseIxTotalAdj; + int SelfSenseCx1; + int SelfSenseCx2; + int SelfSenseCx2Adj; + int SelfSenseCxTotal; + int SelfSenseCxTotalAdj; + +}; + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result); +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result); +int computeAdjVert(u8 *data, int row, int column, u8 **result); +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result); +int computeTotal(u8 *data, u8 main, int row, int column, int m, + int n, u16 **result); +int checkLimitsMinMax(short *data, int row, int column, int min, int max); +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max); +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max); +int checkLimitsMapAdj(u8 *data, int row, int column, int *max); +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max); +int production_test_ito(void); +int production_test_initialization(void); +int ms_compensation_tuning(void); +int ss_compensation_tuning(void); +int lp_timer_calibration(void); +int save_cx_tuning(void); +int production_test_split_initialization(int saveToFlash); +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + struct TestToDo *todo, u32 signature); +int production_test_ms_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ss_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_data(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_key_raw(char *path_limits); +int save_mp_flag(u32 signature); +int parseProductionTestLimits(char *path, char *label, int **data, + int *row, int *column); +int readLine(char *data, char *line, int size, int *n); +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.c b/drivers/input/touchscreen/st/fts_lib/ftsTime.c new file mode 100644 index 000000000000..07d1bf5dba8b --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + *************************************************************************** + */ + +#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 "ftsCrossCompile.h" +#include "ftsTime.h" + + +void startStopWatch(struct StopWatch *w) +{ + ktime_get_ts(&w->start); +} + +void stopStopWatch(struct StopWatch *w) +{ + ktime_get_ts(&w->end); +} + +int elapsedMillisecond(struct StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec) * 1000) + + (w->end.tv_nsec - w->start.tv_nsec) / 1000000; + return result; +} + +int elapsedNanosecond(struct StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec) * 1000000000) + + (w->end.tv_nsec - w->start.tv_nsec); + return result; +} + +char *timestamp() +{ + char *result = NULL; + + result = (char *)kmalloc_array(1, sizeof(char), GFP_KERNEL); + if (result == NULL) + return NULL; + result[0] = ' '; + return result; +} + +void stdelay(unsigned long ms) +{ + msleep(ms); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.h b/drivers/input/touchscreen/st/fts_lib/ftsTime.h new file mode 100644 index 000000000000..d98dd2a9fc30 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_TIME_H +#define __FTS_TIME_H + +#include + +#include "ftsCrossCompile.h" + +struct StopWatch { + struct timespec start, end; +}; + +void startStopWatch(struct StopWatch *w); +void stopStopWatch(struct StopWatch *w); +int elapsedMillisecond(struct StopWatch *w); +int elapsedNanosecond(struct StopWatch *w); +char *timestamp(void); +void stdelay(unsigned long ms); +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.c b/drivers/input/touchscreen/st/fts_lib/ftsTool.c new file mode 100644 index 000000000000..543682d1e3c6 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.c @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftsCompensation.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsFlash.h" +#include "ftsTool.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; +static int reset_gpio = GPIO_NOT_DEFINED; +static int system_resetted_up; +static int system_resetted_down; + +int readB2(u16 address, u8 *outBuf, int len) +{ + int remaining = len; + int toRead = 0; + int retry = 0; + int ret; + int event_to_search[3]; + char *temp = NULL; + u8 *init_outBuf = outBuf; + u16 init_addr = address; + u8 readEvent[FIFO_EVENT_SIZE] = {0}; + u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; + + if (readEvent == NULL) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + u16ToU8_be(address, &cmd[1]); + temp = printHex("Command B2 = ", cmd, 4); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + do { + remaining = len; + ret = fts_writeFwCmd(cmd, 4); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ret; + } + //ask to the FW the data + logError(0, "%s Command to FW sent!\n", tag); + event_to_search[0] = (int)EVENTID_FW_CONFIGURATION; + while (remaining > OK) { + event_to_search[1] = (int)((address & 0xFF00) >> 8); + event_to_search[2] = (int)(address & 0x00FF); + if (remaining > B2_DATA_BYTES) { + toRead = B2_DATA_BYTES; + remaining -= B2_DATA_BYTES; + } else { + toRead = remaining; + remaining = 0; + } + + ret = pollForEvent(event_to_search, 3, + readEvent, GENERAL_TIMEOUT); + if (ret >= OK) { + //start the polling for reading the reply + memcpy(outBuf, &readEvent[3], toRead); + retry = 0; + outBuf += toRead; + } else { + retry += 1; + break; + } + address += B2_DATA_BYTES; + } + logError(0, "%s %s:B2 failed...attempt = %d\n", + tag, __func__, retry); + outBuf = init_outBuf; + address = init_addr; + } while (retry < B2_RETRY && retry != 0); + + if (retry == B2_RETRY) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + logError(0, "%s B2 read %d bytes\n", tag, len); + + return OK; +} + +int readB2U16(u16 address, u8 *outBuf, int byteToRead) +{ + int remaining = byteToRead; + int toRead = 0; + int ret; + + u8 *buff = (u8 *)kmalloc_array((B2_CHUNK + 1), sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= B2_CHUNK) { + toRead = B2_CHUNK; + remaining -= B2_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + ret = readB2(address, buff, toRead); + if (ret < 0) { + kfree(buff); + return ret; + } + memcpy(outBuf, buff, toRead); + address += toRead; + outBuf += toRead; + } + kfree(buff); + return OK; +} + + +int releaseInformation(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_RELEASE_INFO }; + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + event_to_search[0] = (int)EVENTID_RELEASE_INFO; + + logError(0, "%s %s: started... Chip INFO:\n", tag, __func__); + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return ret; + } + + ret = pollForEvent(event_to_search, 1, &readEvent[0], + RELEASE_INFO_TIMEOUT); + //start the polling for reading the reply + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return ret; + } + + logError(0, "%s %s: Finished! %d\n", tag, __func__, ret); + return OK; +} + +int lockDownInfo(u8 *data, int len) +{ + int ret; + int i = 0, num_event; + u8 cmd[1] = { FTS_CMD_LOCKDOWN_CMD }; + int event_to_search[3] = {EVENTID_LOCKDOWN_INFO, + EVENT_TYPE_LOCKDOWN, 0x00}; + u8 readEvent[FIFO_EVENT_SIZE]; + + logError(0, "%s %s:started...\n", tag, __func__); + if (len <= 0) + return ERROR_OP_NOT_ALLOW; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ret); + return ret; + } + + num_event = (len + 3) / 4; + logError(0, "%s %s:num_event = %d\n", tag, __func__, num_event); + + for (i = 0; i < num_event; i++) { + ret = pollForEvent(event_to_search, 3, + &readEvent[0], GENERAL_TIMEOUT); + //start the polling for reading the reply + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ret); + return ret; + } + data[i * 4] = readEvent[3]; + data[i * 4 + 1] = readEvent[4]; + data[i * 4 + 2] = readEvent[5]; + data[i * 4 + 3] = readEvent[6]; + event_to_search[2] += 4; + //logError(0, "%02X %02X %02X %02X ", readEvent[3], + //readEvent[4], readEvent[5], readEvent[6]); + } + + logError(0, "%s %s:Finished! %d\n", tag, __func__, ret); + return OK; +} + + +int calculateCRC8(u8 *u8_srcBuff, int size, u8 *crc) +{ + u8 u8_remainder; + u8 bit; + int i = 0; + + u8_remainder = 0x00; + logError(0, "%s %s: Start CRC computing...\n", tag, __func__); + + if (size == 0 || u8_srcBuff == NULL) { + logError(1, "Arguments passed not valid!"); + logError(1, "%s %s:Data pointer = NULL ", tag, __func__); + logError(1, "or size = 0 (%d) ERROR %08X\n", + size, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + // Perform modulo-2 division, a byte at a time. + //Bring the next byte into the remainder. + for (i = 0; i < size; i++) { + //Perform modulo-2 division, a bit at a time. + u8_remainder ^= u8_srcBuff[i]; + //Try to divide the current data bit. + for (bit = 8; bit > 0; --bit) { + if (u8_remainder & (0x1 << 7)) + u8_remainder = (u8_remainder << 1) ^ 0x9B; + else + u8_remainder = (u8_remainder << 1); + } + } //The final remainder is the CRC result. + *crc = u8_remainder; + logError(0, "%s %s: CRC value = %02X\n", tag, __func__, *crc); + return OK; +} + +int writeLockDownInfo(u8 *data, int size) +{ + int ret, i, toWrite, retry = 0, offset = size; + u8 cmd[2 + LOCKDOWN_CODE_WRITE_CHUNK] = {FTS_CMD_LOCKDOWN_FILL, 0x00}; + u8 crc = 0; + int event_to_search[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LOCKDOWN_WRITE}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + logError(0, "%s %s: Writing Lockdown code into the IC...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + if (size > LOCKDOWN_CODE_MAX_SIZE) { + logError(1, "%s %s: Lockdown data to write too big! ", + tag, __func__); + logError(1, "%d>%d ERROR %08X\n", + size, LOCKDOWN_CODE_MAX_SIZE, ret); + ret = (ERROR_OP_NOT_ALLOW | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + + temp = printHex("Lockdown Code = ", data, size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + logError(0, "%s %s: Filling FW buffer...\n", tag, __func__); + i = 0; + offset = size; + cmd[0] = FTS_CMD_LOCKDOWN_FILL; + while (offset > 0) { + if (offset > LOCKDOWN_CODE_WRITE_CHUNK) + toWrite = LOCKDOWN_CODE_WRITE_CHUNK; + else + toWrite = offset; + memcpy(&cmd[2], &data[i], toWrite); + cmd[1] = i; + + temp = printHex("Commmand = ", cmd, 2 + toWrite); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 2 + toWrite); + if (ret < OK) { + logError(1, "Unable to write Lockdown data "); + logError(1, "%s %s:Lockdown data at %d ", + tag, __func__, i); + logError(1, "iteration.%08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + i += toWrite;//update the offset + offset -= toWrite; + } + logError(0, "%s %s: Compute 8bit CRC...\n", tag, __func__); + ret = calculateCRC8(data, size, &crc); + if (ret < OK) { + logError(1, "%s %s:Unable to compute CRC..ERROR %08X\n", + tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + cmd[0] = FTS_CMD_LOCKDOWN_WRITE; + cmd[1] = 0x00; + cmd[2] = (u8)size; + cmd[3] = crc; + logError(0, "%s %s: Write Lockdown data...\n", + tag, __func__); + temp = printHex("Commmand = ", cmd, 4); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 4); + if (ret < OK) { + logError(1, "%s%s:Unable to send Lockdown data ", + tag, __func__); + logError(1, "write command%08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + ret = pollForEvent(event_to_search, + 2, + &readEvent[0], + GENERAL_TIMEOUT); + //start the polling for reading the reply + + if (ret < OK) { + logError(1, "%s%s:Cann't find lockdown code ", + tag, __func__); + logError(1, "%write reply %08X\n", ret); + continue; + } + + if (readEvent[2] != 0x00) { + logError(1, "%s %s:Event check FAIL!%02X != 0x00 ", + tag, __func__, readEvent[2]); + logError(1, "%ERR%08X\n", ERROR_LOCKDOWN_CODE); + ret = ERROR_LOCKDOWN_CODE; + continue; + } else { + logError(0, "%s %s:Lockdown Code write DONE!\n", + tag, __func__); + ret = OK; + break; + } + } + +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s: Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +int rewriteLockDownInfo(u8 *data, int size) +{ + int ret, i, toWrite, retry = 0, offset = size; + u8 cmd[2 + LOCKDOWN_CODE_WRITE_CHUNK] = {FTS_CMD_LOCKDOWN_FILL, 0x00}; + u8 crc = 0; + int event_to_search[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LOCKDOWN_WRITE}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + logError(0, "%s %s: ReWriting Lockdown code into the IC start ...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + if (size > LOCKDOWN_CODE_MAX_SIZE) { + logError(1, "%s %s: Lockdown data to write too big! ", + tag, __func__); + logError(1, "%d>%d ERROR %08X\n", + size, LOCKDOWN_CODE_MAX_SIZE, ret); + ret = (ERROR_OP_NOT_ALLOW | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + + temp = printHex("Lockdown Code = ", data, size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + logError(0, "%s %s: Filling FW buffer ...\n", tag, __func__); + i = 0; + offset = size; + cmd[0] = FTS_CMD_LOCKDOWN_FILL; + while (offset > 0) { + if (offset > LOCKDOWN_CODE_WRITE_CHUNK) + toWrite = LOCKDOWN_CODE_WRITE_CHUNK; + else + toWrite = offset; + memcpy(&cmd[2], &data[i], toWrite); + cmd[1] = i; + temp = printHex("Commmand = ", cmd, 2 + toWrite); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 2 + toWrite); + if (ret < OK) { + logError(1, "Unable to rewrite Lockdown data"); + logError(1, "%s %s: at %d iteration ", + tag, __func__, i); + logError(1, "ERROR %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + i += toWrite;//update the offset + offset -= toWrite; + } + logError(0, "%s %s: Compute 8bit CRC...\n", tag, __func__); + ret = calculateCRC8(data, size, &crc); + if (ret < OK) { + logError(1, "%s %s:Unable to compute CRC.. ", + tag, __func__); + logError(1, "ERROR %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + cmd[0] = FTS_CMD_LOCKDOWN_WRITE; + cmd[1] = 0x01; + cmd[2] = (u8)size; + cmd[3] = crc; + + temp = printHex("Commmand = ", cmd, 4); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + logError(0, "%s %s: ReWrite Lockdown data...\n", tag, __func__); + ret = fts_writeFwCmd(cmd, 4); + if (ret < OK) { + logError(1, "Unable to send Lockdown data"); + logError(1, "%s %s:rewrite command... ERROR %08X\n", + tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + //start the polling for reading the reply + ret = pollForEvent(event_to_search, 2, + &readEvent[0], GENERAL_TIMEOUT); + if (ret >= OK) { + if (readEvent[2] < 0x00) { + logError(1, "%s %s:Event check FAIL! ", + tag, __func__); + logError(1, "%02X != 0x00 %08X\n", + readEvent[2], ERROR_LOCKDOWN_CODE); + ret = ERROR_LOCKDOWN_CODE; + continue; + } else { + logError(0, "%s %s: Lockdown Code ", + tag, __func__); + logError(0, "rewrite DONE!\n"); + ret = OK; + break; + } + } else { + logError(1, "Can not find lockdown code write "); + logError(1, "reply event!%s %s: ERROR %08X\n", + tag, __func__, ret); + } + } +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s: Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +int readLockDownInfo(u8 *lockData, int *size) +{ + int ret, retry = 0, toRead = 0, byteToRead; + u8 cmd = FTS_CMD_LOCKDOWN_READ; + int event_to_search[3] = {EVENTID_LOCKDOWN_INFO_READ, -1, 0x00}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + lockData = NULL; + logError(0, "%s %s: Reading Lockdown code from the IC...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + event_to_search[2] = 0x00; + logError(0, "%s %s: Read Lockdown data.(%d attempt)\n", + tag, __func__, retry + 1); + ret = fts_writeFwCmd(&cmd, 1); + + if (ret < OK) { + logError(1, "%s%s:Unable to send Lockdown data ", + tag, __func__); + logError(1, "write CMD %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + //start the polling for reading the reply + ret = pollForEvent(event_to_search, 3, + &readEvent[0], GENERAL_TIMEOUT); + + if (ret < OK) { + logError(1, "Cann't find first lockdown code read"); + logError(1, "%s %s:reply event! ERROR %08X\n", + tag, __func__, ret); + continue; + } + + byteToRead = readEvent[1]; + *size = byteToRead; + logError(0, "%s %s:Lockdown Code size = %d\n", + tag, __func__, *size); + lockData = (u8 *)kmalloc_array((byteToRead), + sizeof(u8), GFP_KERNEL); + if (lockData == NULL) { + logError(1, "%s %s:Unable to allocate lockData %08X\n", + tag, __func__, ERROR_ALLOC); + ret = (ERROR_ALLOC | ERROR_LOCKDOWN_CODE); + continue; + } + while (byteToRead > 0) { + if ((readEvent[1] - readEvent[2]) + > LOCKDOWN_CODE_READ_CHUNK) { + toRead = LOCKDOWN_CODE_READ_CHUNK; + } else { + toRead = readEvent[1] - readEvent[2]; + } + byteToRead -= toRead; + memcpy(&lockData[readEvent[2]], + &readEvent[3], toRead); + event_to_search[2] += toRead; + if (byteToRead <= 0) + continue; + + ret = pollForEvent(event_to_search, + 3, + &readEvent[0], + GENERAL_TIMEOUT); + + //start polling for reading reply + if (ret < OK) { + logError(1, "Can not find lockdow"); + logError(1, "code read reply event "); + logError(1, "%s%s:offset%02X%08X\n", + tag, __func__, event_to_search[2], ret); + ret = (ERROR_ALLOC | ERROR_LOCKDOWN_CODE); + break; + } + } + if (byteToRead != 0) { + logError(1, "%s %s:Read Lockdown code FAIL! ", + tag, __func__); + logError(1, "ERROR %08X\n", ret); + continue; + } else { + logError(0, "%s %s: Lockdown Code read DONE!\n", + tag, __func__); + ret = OK; + temp = printHex("Lockdown Code = ", lockData, *size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + break; + } + } +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always + //renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s:Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +char *printHex(char *label, u8 *buff, int count) +{ + int i, offset; + char *result = NULL; + size_t len = 0; + + offset = strlen(label); + len = (offset + 3 * count) + 2; + result = (char *)kmalloc_array(len, sizeof(char), GFP_KERNEL); + if (result != NULL) { + strlcpy(result, label, len); + for (i = 0; i < count; i++) + snprintf(&result[offset + i * 3], 4, "%02X ", buff[i]); + strlcat(result, "\n", len); + } + return result; +} + +int pollForEvent(int *event_to_search, int event_bytes, + u8 *readData, int time_to_wait) +{ + int i, find, retry, count_err; + int time_to_count; + int err_handling = OK; + struct StopWatch clock; + + u8 cmd[1] = { FIFO_CMD_READONE }; + char *temp = NULL; + + find = 0; + retry = 0; + count_err = 0; + time_to_count = time_to_wait / TIMEOUT_RESOLUTION; + + startStopWatch(&clock); + while (find != 1 && retry < time_to_count + && fts_readCmd(cmd, 1, readData, FIFO_EVENT_SIZE) >= 0) { + + if (readData[0] == EVENTID_ERROR_EVENT) { + temp = printHex("ERROR EVENT = ", + readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + count_err++; + err_handling = errorHandler(readData, FIFO_EVENT_SIZE); + if ((err_handling & 0xF0FF0000) + == ERROR_HANDLER_STOP_PROC) { + logError(1, "%s %s: forced to be stopped! ", + tag, __func__); + logError(1, "ERROR %08X\n", err_handling); + return err_handling; + } + } else { + if (readData[0] != EVENTID_NO_EVENT) { + temp = printHex("READ EVENT = ", + readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + } + if (readData[0] == EVENTID_CONTROL_READY && + event_to_search[0] != EVENTID_CONTROL_READY) { + logError(0, "Unmanned Controller Ready Event!"); + logError(0, "%s %s:Setting reset flags...\n", + tag, __func__); + setSystemResettedUp(1); + setSystemResettedDown(1); + } + } + find = 1; + + for (i = 0; i < event_bytes; i++) { + if (event_to_search[i] != -1 + && (int)readData[i] != event_to_search[i]) { + find = 0; + break; + } + } + + retry++; + msleep(TIMEOUT_RESOLUTION); + } + stopStopWatch(&clock); + if ((retry >= time_to_count) && find != 1) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + if (find == 1) { + temp = printHex("FOUND EVENT = ", readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + logError(0, "%s Event found in %d ms (%d iterations)!\n", + tag, elapsedMillisecond(&clock), retry); + logError(0, "Number of errors found = %d\n", count_err); + return count_err; + } + logError(0, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; +} + +int flushFIFO(void) +{ + u8 cmd = FIFO_CMD_FLUSH; + + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s FIFO flushed!\n", tag); + return OK; +} + +int fts_disableInterrupt(void) +{ + //disable interrupt + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_DISABLE }; + + u16ToU8_be(IER_ADDR, &cmd[1]); + + if (fts_writeCmd(cmd, 4) < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Disabled!\n", tag); + return OK; +} + + +int fts_enableInterrupt(void) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; + + u16ToU8_be(IER_ADDR, &cmd[1]); + if (fts_writeCmd(cmd, 4) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Enabled!\n", tag); + return OK; +} + +int u8ToU16n(u8 *src, int src_length, u16 *dst) +{ + int i, j; + u16 *buf; + + if (src_length % 2 != 0) + return -EINVAL; + + j = 0; + buf = (u16 *)kmalloc_array((src_length / 2), sizeof(u16), GFP_KERNEL); + if (!buf) { + dst = NULL; + return -EINVAL; + } + dst = buf; + for (i = 0; i < src_length; i += 2) { + dst[j] = ((src[i+1] & 0x00FF) << 8) + (src[i] & 0x00FF); + j++; + } + + return (src_length / 2); +} + +int u8ToU16(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF)); + return 0; +} + +int u8ToU16_le(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF)); + return 0; +} + +int u16ToU8n(u16 *src, int src_length, u8 *dst) +{ + int i, j; + u8 *buf = (u8 *)kmalloc_array(2 * src_length, sizeof(u8), GFP_KERNEL); + + if (!buf) { + dst = NULL; + return -EINVAL; + } + dst = buf; + j = 0; + for (i = 0; i < src_length; i++) { + dst[j] = (u8) (src[i] & 0xFF00) >> 8; + dst[j+1] = (u8) (src[i] & 0x00FF); + j += 2; + } + + return src_length * 2; +} + +int u16ToU8(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_be(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_le(u16 src, u8 *dst) +{ + dst[1] = (u8)((src & 0xFF00) >> 8); + dst[0] = (u8)(src & 0x00FF); + return 0; +} + +int u8ToU32(u8 *src, u32 *dst) +{ + *dst = (u32)(((src[3] & 0xFF) << 24) + ((src[2] & 0xFF) << 16) + + ((src[1] & 0xFF) << 8) + (src[0] & 0xFF)); + return 0; +} + +int u32ToU8(u32 src, u8 *dst) +{ + dst[3] = (u8)((src & 0xFF000000) >> 24); + dst[2] = (u8)((src & 0x00FF0000) >> 16); + dst[1] = (u8)((src & 0x0000FF00) >> 8); + dst[0] = (u8)(src & 0x000000FF); + return 0; +} + +int attempt_function(int(*code)(void), unsigned long wait_before_retry, + int retry_count) +{ + int result; + int count = 0; + + do { + result = code(); + count++; + msleep(wait_before_retry); + } while (count < retry_count && result < 0); + + if (count == retry_count) + result |= ERROR_TIMEOUT; + + return result; +} + +void setResetGpio(int gpio) +{ + reset_gpio = gpio; + logError(0, "%s %s: reset_gpio = %d\n", tag, __func__, reset_gpio); +} + +int fts_system_reset(void) +{ + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search; + int res = -1; + int i; + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + + event_to_search = (int)EVENTID_CONTROL_READY; + + u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); + logError(0, "%s System resetting...\n", tag); + for (i = 0; i < SYSTEM_RESET_RETRY && res < 0; i++) { + if (reset_gpio == GPIO_NOT_DEFINED) { +#ifndef FTM3_CHIP + res |= fts_warm_boot(); +#endif + res = fts_writeCmd(cmd, 4); + } else { + gpio_set_value(reset_gpio, 0); + msleep(20); + gpio_set_value(reset_gpio, 1); + res = OK; + } + if (res < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + } else { + res = pollForEvent(&event_to_search, 1, + readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, res); + } + } + } + if (res < OK) { + logError(1, "%s %s:failed after 3 attempts: ERROR %02X\n", + tag, __func__, (res | ERROR_SYSTEM_RESET_FAIL)); + res = (res | ERROR_SYSTEM_RESET_FAIL); + } else { + logError(0, "%s System reset DONE!\n", tag); + system_resetted_down = 1; + system_resetted_up = 1; + res = OK; + } + return res; +} + +int isSystemResettedDown(void) +{ + return system_resetted_down; +} + +int isSystemResettedUp(void) +{ + return system_resetted_up; +} + +void setSystemResettedDown(int val) +{ + system_resetted_down = val; +} + +void setSystemResettedUp(int val) +{ + system_resetted_up = val; +} + +int senseOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_ON_FAIL); + return (ret|ERROR_SENSE_ON_FAIL); + } + logError(0, "%s %s: SENSE ON\n", tag, __func__); + return OK; +} + +int senseOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + logError(0, "%s %s: SENSE OFF\n", tag, __func__); + return OK; +} + +int keyOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_ON_FAIL); + return (ret | ERROR_SENSE_ON_FAIL); + } + + logError(0, "%s %s: KEY ON\n", tag, __func__); + return OK; +} + +int keyOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + + logError(0, "%s %s: KEY OFF\n", tag, __func__); + return OK; +} + +int cleanUp(int enableTouch) +{ + int res; + + logError(0, "%s %s: system reset...\n", tag, __func__); + res = fts_system_reset(); + if (res < OK) + return res; + + if (enableTouch) { + logError(0, "%s %s:enabling touches...\n", tag, __func__); + res = senseOn(); + if (res < OK) + return res; +#ifdef PHONE_KEY + res = keyOn(); + if (res < OK) + return res; +#endif + logError(0, "%s %s:enabling interrupts...\n", tag, __func__); + res = fts_enableInterrupt(); + if (res < OK) + return res; + } + return OK; +} + +int checkEcho(u8 *cmd, int size) +{ + int ret, i; + int event_to_search[FIFO_EVENT_SIZE + 1]; + u8 readData[FIFO_EVENT_SIZE]; + + if ((ftsInfo.u32_echoEn & 0x00000001) != ECHO_ENABLED) { + logError(0, "%s ECHO Not Enabled!\n", tag); + return OK; + } + if (size < 1) { + logError(1, "%s:Error Size = %d not valid!", tag, size); + logError(1, " or ECHO not Enabled!%08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + if ((size + 2) > FIFO_EVENT_SIZE) + size = FIFO_EVENT_SIZE - 2; + //Echo event EC xx xx xx xx xx xx + //fifo_status therefore for command + //with more than 6 bytes will echo only the first 6 + event_to_search[0] = EVENTID_ECHO; + for (i = 1; i <= size; i++) + event_to_search[i] = cmd[i - 1]; + ret = pollForEvent(event_to_search, size + 1, + readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(1, "%s %s:Echo Event not found! ERROR %02X\n", + tag, __func__, ret); + return (ret | ERROR_CHECK_ECHO_FAIL); + } + + logError(0, "%s ECHO OK!\n", tag); + ret = OK; + return ret; +} + +int featureEnableDisable(int on_off, u32 feature) +{ + int ret; + u8 cmd[5]; + + if (on_off == FEAT_ENABLE) { + cmd[0] = FTS_CMD_FEATURE_ENABLE; + logError(0, "%s %s: Enabling feature %08X ...\n", + tag, __func__, feature); + } else { + cmd[0] = FTS_CMD_FEATURE_DISABLE; + logError(0, "%s %s: Disabling feature %08X ...\n", + tag, __func__, feature); + } + u32ToU8(feature, &cmd[1]); + + //not use writeFwCmd because this function can be + //called also during interrupt enable and should be fast + ret = fts_writeCmd(cmd, 5); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_FEATURE_ENABLE_DISABLE); + } + + logError(0, "%s %s: DONE!\n", tag, __func__); + return OK; +} + +int writeNoiseParameters(u8 *noise) +{ + int ret, i; + u8 cmd[2+NOISE_PARAMETERS_SIZE]; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[2] = {EVENTID_NOISE_WRITE, NOISE_PARAMETERS}; + + logError(0, "%s %s: Writing noise parameters to the IC ...\n", + tag, __func__); + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + cmd[0] = FTS_CMD_NOISE_WRITE; + cmd[1] = NOISE_PARAMETERS; + logError(0, "%s %s: Noise parameters = ", tag, __func__); + for (i = 0; i < NOISE_PARAMETERS_SIZE; i++) { + cmd[2 + i] = noise[i]; + logError(0, "%02X", cmd[2 + i]); + } + + logError(0, "\n"); + ret = fts_writeCmd(cmd, NOISE_PARAMETERS_SIZE + 2); + //not use writeFwCmd because this function should be fast + if (ret < OK) { + logError(0, "%s %s:impossible write command... ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + ret = pollForEvent(event_to_search, 2, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(0, "%s %s: polling FIFO ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + if (readData[2] != 0x00) { + logError(1, "%s %s:Event check FAIL! %02X != 0x00 ERROR%02X\n", + tag, __func__, readData[2], ERROR_NOISE_PARAMETERS); + ret = ERROR_NOISE_PARAMETERS; + goto ERROR; + } + + logError(0, "%s %s:DONE!\n", tag, __func__); + ret = OK; +ERROR: + ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_NOISE_PARAMETERS); + } + return ret; +} + +int readNoiseParameters(u8 *noise) +{ + int ret, i; + u8 cmd[2]; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[2] = {EVENTID_NOISE_READ, NOISE_PARAMETERS}; + + logError(0, "%s %s:Reading noise parameters from the IC ...\n", + tag, __func__); + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + cmd[0] = FTS_CMD_NOISE_READ; + cmd[1] = NOISE_PARAMETERS; + ret = fts_writeCmd(cmd, 2);//not use writeFwCmd should be fast + if (ret < OK) { + logError(0, "%s %s:impossible write command... ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + ret = pollForEvent(event_to_search, 2, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(0, "%s %s: polling FIFO ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + logError(0, "%s %s: Noise parameters = ", tag, __func__); + for (i = 0; i < NOISE_PARAMETERS_SIZE; i++) { + noise[i] = readData[2 + i]; + logError(0, "%02X ", noise[i]); + } + + logError(0, "\n"); + logError(0, "%s %s: DONE!\n", tag, __func__); + ret = OK; +ERROR: + ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_NOISE_PARAMETERS); + } + return ret; +} + +short **array1dTo2d_short(short *data, int size, int columns) +{ + int i; + int count = size / columns; + short **matrix = (short **)kmalloc_array(count, + sizeof(short *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < count; i++) { + matrix[i] = (short *)kmalloc_array(columns, + sizeof(short), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +u8 **array1dTo2d_u8(u8 *data, int size, int columns) +{ + int i; + int count = size / columns; + u8 **matrix = (u8 **)kmalloc_array(count, + sizeof(u8 *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < count; i++) { + matrix[i] = (u8 *)kmalloc_array(columns, + sizeof(u8), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +void print_frame_short(char *label, short **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +void print_frame_u8(char *label, u8 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +void print_frame_u32(char *label, u32 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +void print_frame_int(char *label, int **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.h b/drivers/input/touchscreen/st/fts_lib/ftsTool.h new file mode 100644 index 000000000000..350cbd57e1a4 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_TOOL_H +#define __FTS_TOOL_H + +#define GPIO_NOT_DEFINED -1 +#define TIMEOUT_RESOLUTION 10 //ms +#define GENERAL_TIMEOUT (50*TIMEOUT_RESOLUTION) //ms +#define RELEASE_INFO_TIMEOUT (15*TIMEOUT_RESOLUTION) //ms + + +#define FEAT_ENABLE 1 +#define FEAT_DISABLE 0 + +#define SYSTEM_RESET_RETRY 3 + +#define B2_RETRY 2 +//for FTM4 can not be greater than 13 bytes +#define LOCKDOWN_CODE_SIZE 10 + +#define LOCKDOWN_CODE_MAX_SIZE 63 +#define LOCKDOWN_CODE_WRITE_CHUNK 12 +#define LOCKDOWN_CODE_READ_CHUNK 4 +#define LOCKDOWN_CODE_RETRY 2 + +int readB2(u16 address, u8 *outBuf, int len); +int readB2U16(u16 address, u8 *outBuf, int byteToRead); +int releaseInformation(void); +int lockDownInfo(u8 *data, int len); +int calculateCRC8(u8 *u8_srcBuff, int size, u8 *crc); +int writeLockDownInfo(u8 *data, int size); +int rewriteLockDownInfo(u8 *data, int size); +int readLockDownInfo(u8 *lockData, int *size); +char *printHex(char *label, u8 *buff, int count); +int pollForEvent(int *event_to_search, int event_bytes, + u8 *readData, int time_to_wait); +int fts_disableInterrupt(void); +int fts_enableInterrupt(void); +int u8ToU16(u8 *src, u16 *dst); +int u8ToU16_le(u8 *src, u16 *dst); +int u8ToU16n(u8 *src, int src_length, u16 *dst); +int u16ToU8(u16 src, u8 *dst); +int u16ToU8_le(u16 src, u8 *dst); +int u16ToU8_be(u16 src, u8 *dst); +int u16ToU8n(u16 *src, int src_length, u8 *dst); +int u8ToU32(u8 *src, u32 *dst); +int u32ToU8(u32 src, u8 *dst); +int attempt_function(int(*code)(void), unsigned long wait_before_retry, + int retry_count); +void setResetGpio(int gpio); +int fts_system_reset(void); +int isSystemResettedUp(void); +int isSystemResettedDown(void); +void setSystemResettedUp(int val); +void setSystemResettedDown(int val); +int senseOn(void); +int senseOff(void); +int keyOn(void); +int keyOff(void); +int featureEnableDisable(int on_off, u32 feature); +int writeNoiseParameters(u8 *noise); +int readNoiseParameters(u8 *noise); +int checkEcho(u8 *cmd, int size); +void print_frame_short(char *label, short **matrix, int row, int column); +short **array1dTo2d_short(short *data, int size, int columns); +u8 **array1dTo2d_u8(u8 *data, int size, int columns); +void print_frame_u8(char *label, u8 **matrix, int row, int column); +void print_frame_u32(char *label, u32 **matrix, int row, int column); +void print_frame_int(char *label, int **matrix, int row, int column); +int cleanUp(int enableTouch); +int flushFIFO(void); + +#endif From de0dffc36729e367ac1d23017e8c05c89671c7d6 Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Thu, 16 Jan 2020 18:59:57 +0800 Subject: [PATCH 2/2] input: touchscreen: Enable FTS Touch driver Enable the FTS touch driver for Lahaina device. Change-Id: Ia576ba69ebb45c5ae6cb087607026cc812cdc1ed Signed-off-by: Fei Mao --- drivers/input/touchscreen/Kconfig | 6 ++---- drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/st/Kconfig | 9 +-------- drivers/input/touchscreen/st/Makefile | 7 ++++++- drivers/input/touchscreen/st/fts.c | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 46ad9090493b..9af100b015e9 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -278,7 +278,6 @@ config TOUCHSCREEN_CYTTSP4_SPI config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X - default y help Say Y here to enable the support for the touchscreen found on Dialog Semiconductor DA9034 PMIC. @@ -824,7 +823,6 @@ config TOUCHSCREEN_WM97XX config TOUCHSCREEN_WM9705 bool "WM9705 Touchscreen interface support" depends on TOUCHSCREEN_WM97XX - default y help Say Y here to enable support for the Wolfson Microelectronics WM9705 touchscreen controller. @@ -832,7 +830,6 @@ config TOUCHSCREEN_WM9705 config TOUCHSCREEN_WM9712 bool "WM9712 Touchscreen interface support" depends on TOUCHSCREEN_WM97XX - default y help Say Y here to enable support for the Wolfson Microelectronics WM9712 touchscreen controller. @@ -840,7 +837,6 @@ config TOUCHSCREEN_WM9712 config TOUCHSCREEN_WM9713 bool "WM9713 Touchscreen interface support" depends on TOUCHSCREEN_WM97XX - default y help Say Y here to enable support for the Wolfson Microelectronics WM9713 touchscreen controller. @@ -1314,4 +1310,6 @@ config TOUCHSCREEN_IQS5XX To compile this driver as a module, choose M here: the module will be called iqs5xx. +source "drivers/input/touchscreen/st/Kconfig" + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 94c6162409b3..518a70fd76db 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -110,3 +110,4 @@ obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o +obj-$(CONFIG_TOUCHSCREEN_ST) += st/ diff --git a/drivers/input/touchscreen/st/Kconfig b/drivers/input/touchscreen/st/Kconfig index 8cd119717c18..fc1e7abb5ce4 100644 --- a/drivers/input/touchscreen/st/Kconfig +++ b/drivers/input/touchscreen/st/Kconfig @@ -4,8 +4,7 @@ # config TOUCHSCREEN_ST - bool "STMicroelectronics Touchscreen Driver" - default n + tristate "STMicroelectronics Touchscreen Driver" depends on I2C help Say Y here if you have a STMicroelectronics Touchscreen. @@ -14,9 +13,6 @@ config TOUCHSCREEN_ST To compile this driver as a module, choose M here: the module will be called st. - -#if TOUCHSCREEN_ST - config TOUCHSCREEN_ST_I2C #tristate "STMicroelectronics i2c touchscreen" string "STMicroelectronics ts directory name" @@ -24,6 +20,3 @@ config TOUCHSCREEN_ST_I2C depends on TOUCHSCREEN_ST help This enables support for ST touch panel over I2C based touchscreens. - -#endif - diff --git a/drivers/input/touchscreen/st/Makefile b/drivers/input/touchscreen/st/Makefile index 2764bdc2c509..3894802eb314 100644 --- a/drivers/input/touchscreen/st/Makefile +++ b/drivers/input/touchscreen/st/Makefile @@ -3,4 +3,9 @@ ## Makefile for the STMicroelectronics touchscreen driver. # -obj-$(CONFIG_TOUCHSCREEN_ST) += fts.o fts_gui.o fts_driver_test.o fts_aoi_event.o fts_lib/ +obj-$(CONFIG_TOUCHSCREEN_ST) += st_fts.o + +st_fts-y := fts.o fts_gui.o fts_driver_test.o fts_aoi_event.o \ + fts_lib/ftsCompensation.o fts_lib/ftsCrossCompile.o fts_lib/ftsError.o \ + fts_lib/ftsFrame.o fts_lib/ftsIO.o fts_lib/ftsTest.o fts_lib/ftsTime.o \ + fts_lib/ftsTool.o fts_lib/ftsFlash.o fts_lib/ftsGesture.o diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 28313a9b0c1f..8cf227fc9769 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -5052,7 +5052,7 @@ static void __exit fts_driver_exit(void) i2c_del_driver(&fts_i2c_driver); } -late_initcall(fts_driver_init); +module_init(fts_driver_init); module_exit(fts_driver_exit); MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver");