diff --git a/focaltech_touch/focaltech_common.h b/focaltech_touch/focaltech_common.h new file mode 100644 index 0000000000..c9e93e1875 --- /dev/null +++ b/focaltech_touch/focaltech_common.h @@ -0,0 +1,167 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_common.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-16 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_COMMON_H__ +#define __LINUX_FOCALTECH_COMMON_H__ + +#include "focaltech_config.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_VERSION "Focaltech V3.2 20200422" + +#define BYTE_OFF_0(x) (u8)((x) & 0xFF) +#define BYTE_OFF_8(x) (u8)(((x) >> 8) & 0xFF) +#define BYTE_OFF_16(x) (u8)(((x) >> 16) & 0xFF) +#define BYTE_OFF_24(x) (u8)(((x) >> 24) & 0xFF) +#define FLAGBIT(x) (0x00000001 << (x)) +#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x))) + +#define FLAG_ICSERIALS_LEN 8 +#define FLAG_HID_BIT 10 +#define FLAG_IDC_BIT 11 + +#define IC_SERIALS(type) ((type) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC(type) (((type) & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED(type) (((type) & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FILE_NAME_LENGTH 128 +#define ENABLE 1 +#define DISABLE 0 +#define VALID 1 +#define INVALID 0 +#define FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_START_DELAY 12 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 +#define FTS_CMD_READ_FW_CONF 0xA8 +/*register address*/ +#define FTS_REG_INT_CNT 0x8F +#define FTS_REG_FLOW_WORK_CNT 0x91 +#define FTS_REG_WORKMODE 0x00 +#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40 +#define FTS_REG_WORKMODE_WORK_VALUE 0x00 +#define FTS_REG_ESDCHECK_DISABLE 0x8D +#define FTS_REG_CHIP_ID 0xA3 +#define FTS_REG_CHIP_ID2 0x9F +#define FTS_REG_POWER_MODE 0xA5 +#define FTS_REG_POWER_MODE_SLEEP 0x03 +#define FTS_REG_FW_VER 0xA6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_LCD_BUSY_NUM 0xAB +#define FTS_REG_FACE_DEC_MODE_EN 0xB0 +#define FTS_REG_FACTORY_MODE_DETACH_FLAG 0xB4 +#define FTS_REG_FACE_DEC_MODE_STATUS 0x01 +#define FTS_REG_IDE_PARA_VER_ID 0xB5 +#define FTS_REG_IDE_PARA_STATUS 0xB6 +#define FTS_REG_GLOVE_MODE_EN 0xC0 +#define FTS_REG_COVER_MODE_EN 0xC1 +#define FTS_REG_REPORT_RATE 0x88 +#define FTS_REG_CHARGER_MODE_EN 0x8B +#define FTS_REG_GESTURE_EN 0xD0 +#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3 +#define FTS_REG_MODULE_ID 0xE3 +#define FTS_REG_LIC_VER 0xE4 +#define FTS_REG_ESD_SATURATE 0xED + +#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1') +#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0') + +#define kfree_safe(pbuf) do {\ + if (pbuf) {\ + kfree(pbuf);\ + pbuf = NULL;\ + }\ +} while(0) + +/***************************************************************************** +* Alternative mode (When something goes wrong, +* the modules may be able to solve the problem.) +*****************************************************************************/ +/* + * point report check + * default: disable + */ +#define FTS_POINT_REPORT_CHECK_EN 0 + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct ft_chip_t { + u64 type; + u8 chip_idh; + u8 chip_idl; + u8 rom_idh; + u8 rom_idl; + u8 pb_idh; + u8 pb_idl; + u8 bl_idh; + u8 bl_idl; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; +}; + +/***************************************************************************** +* DEBUG function define here +*****************************************************************************/ +#if FTS_DEBUG_EN +#define FTS_DEBUG(fmt, args...) do { \ + printk("[FTS_TS]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_FUNC_ENTER() do { \ + printk("[FTS_TS]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_FUNC_EXIT() do { \ + printk("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) +#else /* #if FTS_DEBUG_EN*/ +#define FTS_DEBUG(fmt, args...) +#define FTS_FUNC_ENTER() +#define FTS_FUNC_EXIT() +#endif + +#define FTS_INFO(fmt, args...) do { \ + printk(KERN_INFO "[FTS_TS/I]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_ERROR(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/E]%s:"fmt"\n", __func__, ##args); \ +} while (0) +#endif /* __LINUX_FOCALTECH_COMMON_H__ */ diff --git a/focaltech_touch/focaltech_config.h b/focaltech_touch/focaltech_config.h new file mode 100644 index 0000000000..c2b68bcbab --- /dev/null +++ b/focaltech_touch/focaltech_config.h @@ -0,0 +1,277 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/************************************************************************ +* +* File Name: focaltech_config.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: global configurations +* +* Version: v1.0 +* +************************************************************************/ +#ifndef _LINUX_FOCLATECH_CONFIG_H_ +#define _LINUX_FOCLATECH_CONFIG_H_ + +/**************************************************/ +/****** G: A, I: B, S: C, U: D ******************/ +/****** chip type defines, do not modify *********/ +#define _FT8716 0x87160805 +#define _FT8736 0x87360806 +#define _FT8006M 0x80060807 +#define _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8006P 0x86220811 +#define _FT7251 0x72510812 +#define _FT7252 0x72520813 +#define _FT8613S 0x8613C814 +#define _FT8756 0x87560815 +#define _FT8302 0x83020816 +#define _FT8009 0x80090817 +#define _FT8656 0x86560818 +#define _FT8006S_AA 0x86320819 +#define _FT7250 0x7250081A +#define _FT7120 0x7120081B +#define _FT8720 0x8720081C +#define _FT8016 0x8016081D + + +#define _FT5416 0x54160402 +#define _FT5426 0x54260402 +#define _FT5435 0x54350402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5526I 0x5526B402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT5446I 0x5446B402 +#define _FT5346I 0x5346B402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3C47U 0x3C47D402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 +#define _FT5526_V00 0x5526C402 + +#define _FT5626 0x56260401 +#define _FT5726 0x57260401 +#define _FT5826B 0x5826B401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3D47 0x3D470401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 +#define _FT3817B 0x3817B401 +#define _FT3517U 0x3517D401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 +#define _FT6436T 0x6436E003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT3327DQQ_XXX 0x3327D482 +#define _FT5446DQS_XXX 0x5446D482 + +#define _FT3427_003 0x3427D482 +#define _FT3427G_003 0x3427A482 +#define _FT5446_003 0x5446D482 +#define _FT5446_Q03 0x5446C482 +#define _FT5446_P03 0x5446A481 +#define _FT5426_003 0x5426D482 +#define _FT5526_003 0x5526D482 + +#define _FT3518 0x35180481 +#define _FT3518U 0x3518D481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 +#define _FT5536L 0x5536E481 +#define _FT3418 0x34180481 + +#define _FT5446U 0x5446D083 +#define _FT5456U 0x5456D083 +#define _FT3417U 0x3417D083 +#define _FT5426U 0x5426D083 +#define _FT3428 0x34280083 +#define _FT3437U 0x3437D083 + +#define _FT7302 0x73020084 +#define _FT7202 0x72020084 +#define _FT3308 0x33080084 +#define _FT6446 0x64460084 + +#define _FT6346U 0x6346D085 +#define _FT6346G 0x6346A085 +#define _FT3067 0x30670085 +#define _FT3068 0x30680085 +#define _FT3168 0x31680085 +#define _FT3268 0x32680085 +#define _FT6146 0x61460085 + +#define _FT5726_003 0x5726D486 +#define _FT5726_V03 0x5726C486 + +#define _FT3618 0x36180487 +#define _FT5646 0x56460487 +#define _FT3A58 0x3A580487 +#define _FT3B58 0x3B580487 +#define _FT3D58 0x3D580487 +#define _FT5936 0x59360487 +#define _FT5A36 0x5A360487 +#define _FT5B36 0x5B360487 +#define _FT5D36 0x5D360487 +#define _FT5946 0x59460487 +#define _FT5A46 0x5A460487 +#define _FT5B46 0x5B460487 +#define _FT5D46 0x5D460487 + +#define _FT3658U 0x3658D488 + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#define FTS_DEBUG_EN 0 + +/* + * Linux MultiTouch Protocol + * 1: Protocol B(default), 0: Protocol A + */ +#define FTS_MT_PROTOCOL_B_EN 1 + +/* + * Report Pressure in multitouch + * 1:enable(default),0:disable +*/ +#define FTS_REPORT_PRESSURE_EN 1 + +/* + * Gesture function enable + * default: disable + */ +#define FTS_GESTURE_EN 0 + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + +/* + * Production test enable + * 1: enable, 0:disable(default) + */ +#define FTS_TEST_EN 0 + +/* + * Pinctrl enable + * default: disable + */ +#define FTS_PINCTRL_EN 1 + +/* + * Customer power enable + * enable it when customer need control TP power + * default: disable + */ +#define FTS_POWER_SOURCE_CUST_EN 1 + +/****************************************************/ + +/********************** Upgrade ****************************/ +/* + * auto upgrade + */ +#define FTS_AUTO_UPGRADE_EN 1 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * module_id: mean vendor_id generally, also maybe gpio or lcm_id... + * If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID + * FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them + * FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID + * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID + */ +#define FTS_MODULE_ID 0x0000 +#define FTS_MODULE2_ID 0x0000 +#define FTS_MODULE3_ID 0x0000 + +/* + * Need set the following when get firmware via firmware_request() + * For example: if module'vendor is tianma, + * #define FTS_MODULE_NAME "tianma" + * then file_name will be "focaltech_ts_fw_tianma" + * You should rename fw to "focaltech_ts_fw_tianma", and push it into + * etc/firmware or by customers + */ +#define FTS_MODULE_NAME "gvo" +#define FTS_MODULE2_NAME "" +#define FTS_MODULE3_NAME "" + +/* + * FW.i file for auto upgrade, you must replace it with your own + * define your own fw_file, the sample one to be replaced is invalid + * NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID + */ +#define FTS_UPGRADE_FW_FILE "include/firmware/fw_sample.i" + +/* + * if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_VENDOR_ID2 + * define your own fw_file, the sample one is invalid + */ +#define FTS_UPGRADE_FW2_FILE "include/firmware/fw_sample.i" + +/* + * if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_VENDOR_ID3 + * define your own fw_file, the sample one is invalid + */ +#define FTS_UPGRADE_FW3_FILE "include/firmware/fw_sample.i" + +/*********************************************************/ + +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/focaltech_touch/focaltech_core.c b/focaltech_touch/focaltech_core.c new file mode 100644 index 0000000000..9abd6cecaf --- /dev/null +++ b/focaltech_touch/focaltech_core.c @@ -0,0 +1,3364 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: entrance for focaltech ts driver +* +* Version: V1.0 +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_DRM) +#include +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" + +#if defined(CONFIG_FTS_TRUSTED_TOUCH) +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/gunyah/gh_msgq.h" +#include "linux/gunyah/gh_rm_drv.h" +#include +#include +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define INTERVAL_READ_REG 200 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#if FTS_POWER_SOURCE_CUST_EN +#define FTS_VTG_MIN_UV 3000000 +#define FTS_VTG_MAX_UV 3300000 +#define FTS_LOAD_MAX_UA 30000 +#define FTS_LOAD_AVDD_UA 10000 +#define FTS_LOAD_DISABLE_UA 0 +#define FTS_I2C_VTG_MIN_UV 1800000 +#define FTS_I2C_VTG_MAX_UV 1800000 +#endif + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_ts_data *fts_data; + +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *event, void *client_data); +#endif + +static struct ft_chip_t ctype[] = { + {0x88, 0x56, 0x52, 0x00, 0x00, 0x00, 0x00, 0x56, 0xB2}, + {0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}, +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); +static irqreturn_t fts_irq_handler(int irq, void *data); +static int fts_ts_probe_delayed(struct fts_ts_data *fts_data); +static int fts_ts_enable_reg(struct fts_ts_data *ts_data, bool enable); + +static void fts_ts_register_for_panel_events(struct device_node *dp, + struct fts_ts_data *ts_data) +{ + const char *touch_type; + int rc = 0; + void *cookie = NULL; + + rc = of_property_read_string(dp, "focaltech,touch-type", + &touch_type); + if (rc) { + dev_warn(&fts_data->client->dev, + "%s: No touch type\n", __func__); + return; + } + if (strcmp(touch_type, "primary")) { + pr_err("Invalid touch type\n"); + return; + } + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &fts_ts_panel_notifier_callback, ts_data); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + FTS_DEBUG("registered for panel notifications panel: 0x%x\n", + active_panel); + + ts_data->notifier_cookie = cookie; +} + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + +static void fts_ts_trusted_touch_abort_handler(struct fts_ts_data *fts_data, + int error); +static struct gh_acl_desc *fts_ts_vm_get_acl(enum gh_vm_names vm_name) +{ + struct gh_acl_desc *acl_desc; + gh_vmid_t vmid; + + gh_rm_get_vmid(vm_name, &vmid); + + acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]), + GFP_KERNEL); + if (!acl_desc) + return ERR_PTR(ENOMEM); + + acl_desc->n_acl_entries = 1; + acl_desc->acl_entries[0].vmid = vmid; + acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W; + + return acl_desc; +} + +static struct gh_sgl_desc *fts_ts_vm_get_sgl( + struct trusted_touch_vm_info *vm_info) +{ + struct gh_sgl_desc *sgl_desc; + int i; + + sgl_desc = kzalloc(offsetof(struct gh_sgl_desc, + sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL); + if (!sgl_desc) + return ERR_PTR(ENOMEM); + + sgl_desc->n_sgl_entries = vm_info->iomem_list_size; + + for (i = 0; i < vm_info->iomem_list_size; i++) { + sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i]; + sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i]; + } + + return sgl_desc; +} + +static int fts_ts_populate_vm_info_iomem(struct fts_ts_data *fts_data) +{ + int i, gpio, rc = 0; + int num_regs, num_sizes, num_gpios, list_size; + struct resource res; + struct device_node *np = fts_data->dev->of_node; + struct trusted_touch_vm_info *vm_info = fts_data->vm_info; + + num_regs = of_property_count_u32_elems(np, "focaltech,trusted-touch-io-bases"); + if (num_regs < 0) { + FTS_ERROR("Invalid number of IO regions specified\n"); + return -EINVAL; + } + + num_sizes = of_property_count_u32_elems(np, "focaltech,trusted-touch-io-sizes"); + if (num_sizes < 0) { + FTS_ERROR("Invalid number of IO regions specified\n"); + return -EINVAL; + } + + if (num_regs != num_sizes) { + FTS_ERROR("IO bases and sizes array lengths mismatch\n"); + return -EINVAL; + } + + num_gpios = of_gpio_named_count(np, "focaltech,trusted-touch-vm-gpio-list"); + if (num_gpios < 0) { + dev_warn(fts_data->dev, "Ignoring invalid trusted gpio list: %d\n", num_gpios); + num_gpios = 0; + } + + list_size = num_regs + num_gpios; + vm_info->iomem_list_size = list_size; + vm_info->iomem_bases = devm_kcalloc(fts_data->dev, list_size, sizeof(*vm_info->iomem_bases), + GFP_KERNEL); + if (!vm_info->iomem_bases) + return -ENOMEM; + + vm_info->iomem_sizes = devm_kcalloc(fts_data->dev, list_size, sizeof(*vm_info->iomem_sizes), + GFP_KERNEL); + if (!vm_info->iomem_sizes) + return -ENOMEM; + + for (i = 0; i < num_gpios; ++i) { + gpio = of_get_named_gpio(np, "focaltech,trusted-touch-vm-gpio-list", i); + if (gpio < 0 || !gpio_is_valid(gpio)) { + FTS_ERROR("Invalid gpio %d at position %d\n", gpio, i); + return gpio; + } + + if (!msm_gpio_get_pin_address(gpio, &res)) { + FTS_ERROR("Failed to retrieve gpio-%d resource\n", gpio); + return -ENODATA; + } + + vm_info->iomem_bases[i] = res.start; + vm_info->iomem_sizes[i] = resource_size(&res); + } + + rc = of_property_read_u32_array(np, "focaltech,trusted-touch-io-bases", + &vm_info->iomem_bases[i], list_size - i); + if (rc) { + FTS_ERROR("Failed to read trusted touch io bases:%d\n", rc); + return rc; + } + + rc = of_property_read_u32_array(np, "focaltech,trusted-touch-io-sizes", + &vm_info->iomem_sizes[i], list_size - i); + if (rc) { + FTS_ERROR("Failed to read trusted touch io sizes:%d\n", rc); + return rc; + } + + return 0; +} + +static int fts_ts_populate_vm_info(struct fts_ts_data *fts_data) +{ + int rc; + struct trusted_touch_vm_info *vm_info; + struct device_node *np = fts_data->dev->of_node; + + vm_info = devm_kzalloc(fts_data->dev, sizeof(struct trusted_touch_vm_info), GFP_KERNEL); + if (!vm_info) + return -ENOMEM; + + fts_data->vm_info = vm_info; + vm_info->vm_name = GH_TRUSTED_VM; + rc = of_property_read_u32(np, "focaltech,trusted-touch-spi-irq", &vm_info->hw_irq); + if (rc) { + pr_err("Failed to read trusted touch SPI irq:%d\n", rc); + return rc; + } + + rc = fts_ts_populate_vm_info_iomem(fts_data); + if (rc) { + pr_err("Failed to read trusted touch mmio ranges:%d\n", rc); + return rc; + } + + rc = of_property_read_string(np, "focaltech,trusted-touch-type", + &vm_info->trusted_touch_type); + if (rc) { + pr_warn("%s: No trusted touch type selection made\n", __func__); + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; + rc = 0; + } else if (!strcmp(vm_info->trusted_touch_type, "primary")) { + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; + } else if (!strcmp(vm_info->trusted_touch_type, "secondary")) { + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_SECONDARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_SECONDARY; + } + + return 0; +} + +static void fts_ts_destroy_vm_info(struct fts_ts_data *fts_data) +{ + kfree(fts_data->vm_info->iomem_sizes); + kfree(fts_data->vm_info->iomem_bases); + kfree(fts_data->vm_info); +} + +static void fts_ts_vm_deinit(struct fts_ts_data *fts_data) +{ + if (fts_data->vm_info->mem_cookie) + gh_mem_notifier_unregister(fts_data->vm_info->mem_cookie); + fts_ts_destroy_vm_info(fts_data); +} + +static int fts_ts_trusted_touch_get_vm_state(struct fts_ts_data *fts_data) +{ + return atomic_read(&fts_data->vm_info->vm_state); +} + +static void fts_ts_trusted_touch_set_vm_state(struct fts_ts_data *fts_data, + int state) +{ + atomic_set(&fts_data->vm_info->vm_state, state); +} + +#ifdef CONFIG_ARCH_QTI_VM +static int fts_ts_vm_mem_release(struct fts_ts_data *fts_data); +static void fts_ts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_data *fts_data); +static void fts_ts_trusted_touch_abort_tvm(struct fts_ts_data *fts_data); +static void fts_ts_trusted_touch_event_notify(struct fts_ts_data *fts_data, int event); + +void fts_ts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_data *fts_data) +{ + pr_err("initiating trusted touch abort due to i2c failure\n"); + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_I2C_FAILURE); +} + +static void fts_ts_trusted_touch_reset_gpio_toggle(struct fts_ts_data *fts_data) +{ + void __iomem *base; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + base = ioremap(TOUCH_RESET_GPIO_BASE, TOUCH_RESET_GPIO_SIZE); + writel_relaxed(0x1, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + writel_relaxed(0x0, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + iounmap(base); +} + +static void fts_trusted_touch_intr_gpio_toggle(struct fts_ts_data *fts_data, + bool enable) +{ + void __iomem *base; + u32 val; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + base = ioremap(TOUCH_INTR_GPIO_BASE, TOUCH_INTR_GPIO_SIZE); + val = readl_relaxed(base + TOUCH_RESET_GPIO_OFFSET); + if (enable) { + val |= BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } else { + val &= ~BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } + iounmap(base); +} + +static int fts_ts_sgl_cmp(const void *a, const void *b) +{ + struct gh_sgl_entry *left = (struct gh_sgl_entry *)a; + struct gh_sgl_entry *right = (struct gh_sgl_entry *)b; + + return (left->ipa_base - right->ipa_base); +} + +static int fts_ts_vm_compare_sgl_desc(struct gh_sgl_desc *expected, + struct gh_sgl_desc *received) +{ + int idx; + + if (expected->n_sgl_entries != received->n_sgl_entries) + return -E2BIG; + sort(received->sgl_entries, received->n_sgl_entries, + sizeof(received->sgl_entries[0]), fts_ts_sgl_cmp, NULL); + sort(expected->sgl_entries, expected->n_sgl_entries, + sizeof(expected->sgl_entries[0]), fts_ts_sgl_cmp, NULL); + + for (idx = 0; idx < expected->n_sgl_entries; idx++) { + struct gh_sgl_entry *left = &expected->sgl_entries[idx]; + struct gh_sgl_entry *right = &received->sgl_entries[idx]; + + if ((left->ipa_base != right->ipa_base) || + (left->size != right->size)) { + pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n", + left->ipa_base, right->ipa_base, + left->size, right->size); + return -EINVAL; + } + } + return 0; +} + +static int fts_ts_vm_handle_vm_hardware(struct fts_ts_data *fts_data) +{ + int rc = 0; + + if (atomic_read(&fts_data->delayed_vm_probe_pending)) { + rc = fts_ts_probe_delayed(fts_data); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + return rc; + } + atomic_set(&fts_data->delayed_vm_probe_pending, 0); + return rc; + } + + fts_irq_enable(); + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_INTERRUPT_ENABLED); + return rc; +} + +static void fts_ts_trusted_touch_tvm_vm_mode_enable(struct fts_ts_data *fts_data) +{ + + struct gh_sgl_desc *sgl_desc, *expected_sgl_desc; + struct gh_acl_desc *acl_desc; + struct irq_data *irq_data; + int rc = 0; + int irq = 0; + + if (fts_ts_trusted_touch_get_vm_state(fts_data) != TVM_ALL_RESOURCES_LENT_NOTIFIED) { + pr_err("All lend notifications not received\n"); + fts_ts_trusted_touch_event_notify(fts_data, + TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING); + return; + } + + acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("failed to populated acl data:rc=%d\n", + PTR_ERR(acl_desc)); + goto accept_fail; + } + + sgl_desc = gh_rm_mem_accept(fts_data->vm_info->vm_mem_handle, + GH_RM_MEM_TYPE_IO, + GH_RM_TRANS_TYPE_LEND, + GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | + GH_RM_MEM_ACCEPT_VALIDATE_LABEL | + GH_RM_MEM_ACCEPT_DONE, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, NULL, NULL, 0); + if (IS_ERR_OR_NULL(sgl_desc)) { + pr_err("failed to do mem accept :rc=%d\n", + PTR_ERR(sgl_desc)); + goto acl_fail; + } + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_ACCEPTED); + + /* Initiate session on tvm */ + if (fts_data->bus_type == BUS_TYPE_I2C) + rc = pm_runtime_get_sync(fts_data->client->adapter->dev.parent); + else + rc = pm_runtime_get_sync(fts_data->spi->master->dev.parent); + + if (rc < 0) { + pr_err("failed to get sync rc:%d\n", rc); + goto acl_fail; + } + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_I2C_SESSION_ACQUIRED); + + expected_sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info); + if (fts_ts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { + pr_err("IO sg list does not match\n"); + goto sgl_cmp_fail; + } + + kfree(expected_sgl_desc); + kfree(acl_desc); + + irq = gh_irq_accept(fts_data->vm_info->irq_label, -1, IRQ_TYPE_EDGE_RISING); + fts_trusted_touch_intr_gpio_toggle(fts_data, false); + if (irq < 0) { + pr_err("failed to accept irq\n"); + goto accept_fail; + } + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_ACCEPTED); + + + irq_data = irq_get_irq_data(irq); + if (!irq_data) { + pr_err("Invalid irq data for trusted touch\n"); + goto accept_fail; + } + if (!irq_data->hwirq) { + pr_err("Invalid irq in irq data\n"); + goto accept_fail; + } + if (irq_data->hwirq != fts_data->vm_info->hw_irq) { + pr_err("Invalid irq lent\n"); + goto accept_fail; + } + + pr_debug("irq:returned from accept:%d\n", irq); + fts_data->irq = irq; + + rc = fts_ts_vm_handle_vm_hardware(fts_data); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + goto accept_fail; + } + atomic_set(&fts_data->trusted_touch_enabled, 1); + pr_info("trusted touch enabled\n"); + + return; +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +accept_fail: + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE); +} +static void fts_ts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_data *fts_data = data; + + pr_debug("received irq lend request for label:%d\n", label); + if (fts_ts_trusted_touch_get_vm_state(fts_data) == TVM_IOMEM_LENT_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_LENT_NOTIFIED); +} + +static void fts_ts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_shared_payload *payload; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_data *fts_data; + + fts_data = (struct fts_ts_data *)entry_data; + vm_info = fts_data->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + if (notif_type != GH_RM_NOTIF_MEM_SHARED || + tag != vm_info->mem_tag) { + pr_err("Invalid command passed from rm\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid entry data passed from rm\n"); + return; + } + + + payload = (struct gh_rm_notif_mem_shared_payload *)notif_msg; + if (payload->trans_type != GH_RM_TRANS_TYPE_LEND || + payload->label != TRUSTED_TOUCH_MEM_LABEL) { + pr_err("Invalid label or transaction type\n"); + return; + } + + vm_info->vm_mem_handle = payload->mem_handle; + pr_debug("received mem lend request with handle:%d\n", + vm_info->vm_mem_handle); + if (fts_ts_trusted_touch_get_vm_state(fts_data) == TVM_IRQ_LENT_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_LENT_NOTIFIED); +} + +static int fts_ts_vm_mem_release(struct fts_ts_data *fts_data) +{ + int rc = 0; + + if (!fts_data->vm_info->vm_mem_handle) { + pr_err("Invalid memory handle\n"); + return -EINVAL; + } + + rc = gh_rm_mem_release(fts_data->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("VM mem release failed: rc=%d\n", rc); + + rc = gh_rm_mem_notify(fts_data->vm_info->vm_mem_handle, + GH_RM_MEM_NOTIFY_OWNER_RELEASED, + fts_data->vm_info->mem_tag, 0); + if (rc) + pr_err("Failed to notify mem release to PVM: rc=%d\n"); + pr_debug("vm mem release succeded\n"); + + fts_data->vm_info->vm_mem_handle = 0; + return rc; +} + +static void fts_ts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_data *fts_data) +{ + int rc = 0; + + if (atomic_read(&fts_data->trusted_touch_abort_status)) { + fts_ts_trusted_touch_abort_tvm(fts_data); + return; + } + + fts_irq_disable(); + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_INTERRUPT_DISABLED); + + rc = gh_irq_release(fts_data->vm_info->irq_label); + if (rc) { + pr_err("Failed to release irq rc:%d\n", rc); + goto error; + } else { + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_RELEASED); + } + rc = gh_irq_release_notify(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); + + pr_debug("vm irq release succeded\n"); + + fts_release_all_finger(); + + if (fts_data->bus_type == BUS_TYPE_I2C) + pm_runtime_put_sync(fts_data->client->adapter->dev.parent); + else + pm_runtime_put_sync(fts_data->spi->master->dev.parent); + + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_I2C_SESSION_RELEASED); + rc = fts_ts_vm_mem_release(fts_data); + if (rc) { + pr_err("Failed to release mem rc:%d\n", rc); + goto error; + } else { + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_RELEASED); + } + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT); + atomic_set(&fts_data->trusted_touch_enabled, 0); + pr_info("trusted touch disabled\n"); + return; +error: + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_RELEASE_FAILURE); +} + +int fts_ts_handle_trusted_touch_tvm(struct fts_ts_data *fts_data, int value) +{ + int err = 0; + + switch (value) { + case 0: + if ((atomic_read(&fts_data->trusted_touch_enabled) == 0) && + (atomic_read(&fts_data->trusted_touch_abort_status) == 0)) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_ts_trusted_touch_tvm_vm_mode_disable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&fts_data->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_ts_trusted_touch_tvm_vm_mode_enable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + FTS_ERROR("unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +static void fts_ts_trusted_touch_abort_tvm(struct fts_ts_data *fts_data) +{ + int rc = 0; + int vm_state = fts_ts_trusted_touch_get_vm_state(fts_data); + + if (vm_state >= TRUSTED_TOUCH_TVM_STATE_MAX) { + pr_err("invalid tvm driver state: %d\n", vm_state); + return; + } + + switch (vm_state) { + case TVM_INTERRUPT_ENABLED: + fts_irq_disable(); + case TVM_IRQ_ACCEPTED: + case TVM_INTERRUPT_DISABLED: + rc = gh_irq_release(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to release irq rc:%d\n", rc); + rc = gh_irq_release_notify(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify irq release rc:%d\n", rc); + case TVM_I2C_SESSION_ACQUIRED: + case TVM_IOMEM_ACCEPTED: + case TVM_IRQ_RELEASED: + fts_release_all_finger(); + if (fts_data->bus_type == BUS_TYPE_I2C) + pm_runtime_put_sync(fts_data->client->adapter->dev.parent); + else + pm_runtime_put_sync(fts_data->spi->master->dev.parent); + case TVM_I2C_SESSION_RELEASED: + rc = fts_ts_vm_mem_release(fts_data); + if (rc) + pr_err("Failed to release mem rc:%d\n", rc); + case TVM_IOMEM_RELEASED: + case TVM_ALL_RESOURCES_LENT_NOTIFIED: + case TRUSTED_TOUCH_TVM_INIT: + case TVM_IRQ_LENT_NOTIFIED: + case TVM_IOMEM_LENT_NOTIFIED: + atomic_set(&fts_data->trusted_touch_enabled, 0); + } + + atomic_set(&fts_data->trusted_touch_abort_status, 0); + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT); +} + +#else + +static void fts_ts_bus_put(struct fts_ts_data *fts_data); + +static void fts_ts_trusted_touch_abort_pvm(struct fts_ts_data *fts_data) +{ + int rc = 0; + int vm_state = fts_ts_trusted_touch_get_vm_state(fts_data); + + if (vm_state >= TRUSTED_TOUCH_PVM_STATE_MAX) { + pr_err("Invalid driver state: %d\n", vm_state); + return; + } + + switch (vm_state) { + case PVM_IRQ_RELEASE_NOTIFIED: + case PVM_ALL_RESOURCES_RELEASE_NOTIFIED: + case PVM_IRQ_LENT: + case PVM_IRQ_LENT_NOTIFIED: + rc = gh_irq_reclaim(fts_data->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + case PVM_IRQ_RECLAIMED: + case PVM_IOMEM_LENT: + case PVM_IOMEM_LENT_NOTIFIED: + case PVM_IOMEM_RELEASE_NOTIFIED: + rc = gh_rm_mem_reclaim(fts_data->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("failed to reclaim iomem on pvm rc:%d\n", rc); + fts_data->vm_info->vm_mem_handle = 0; + case PVM_IOMEM_RECLAIMED: + case PVM_INTERRUPT_DISABLED: + fts_irq_enable(); + case PVM_I2C_RESOURCE_ACQUIRED: + case PVM_INTERRUPT_ENABLED: + fts_ts_bus_put(fts_data); + case TRUSTED_TOUCH_PVM_INIT: + case PVM_I2C_RESOURCE_RELEASED: + atomic_set(&fts_data->trusted_touch_enabled, 0); + atomic_set(&fts_data->trusted_touch_transition, 0); + } + + atomic_set(&fts_data->trusted_touch_abort_status, 0); + + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT); +} + +static int fts_ts_clk_prepare_enable(struct fts_ts_data *fts_data) +{ + int ret; + + ret = clk_prepare_enable(fts_data->iface_clk); + if (ret) { + FTS_ERROR("error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(fts_data->core_clk); + if (ret) { + clk_disable_unprepare(fts_data->iface_clk); + FTS_ERROR("error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void fts_ts_clk_disable_unprepare(struct fts_ts_data *fts_data) +{ + clk_disable_unprepare(fts_data->core_clk); + clk_disable_unprepare(fts_data->iface_clk); +} + +static int fts_ts_bus_get(struct fts_ts_data *fts_data) +{ + int rc = 0; + struct device *dev = NULL; + + cancel_work_sync(&fts_data->resume_work); + reinit_completion(&fts_data->trusted_touch_powerdown); + fts_ts_enable_reg(fts_data, true); + + if (fts_data->bus_type == BUS_TYPE_I2C) + dev = fts_data->client->adapter->dev.parent; + else + dev = fts_data->spi->master->dev.parent; + + mutex_lock(&fts_data->fts_clk_io_ctrl_mutex); + rc = pm_runtime_get_sync(dev); + if (rc >= 0 && fts_data->core_clk != NULL && + fts_data->iface_clk != NULL) { + rc = fts_ts_clk_prepare_enable(fts_data); + if (rc) + pm_runtime_put_sync(dev); + } + + mutex_unlock(&fts_data->fts_clk_io_ctrl_mutex); + return rc; +} + +static void fts_ts_bus_put(struct fts_ts_data *fts_data) +{ + struct device *dev = NULL; + + if (fts_data->bus_type == BUS_TYPE_I2C) + dev = fts_data->client->adapter->dev.parent; + else + dev = fts_data->spi->master->dev.parent; + + mutex_lock(&fts_data->fts_clk_io_ctrl_mutex); + if (fts_data->core_clk != NULL && fts_data->iface_clk != NULL) + fts_ts_clk_disable_unprepare(fts_data); + pm_runtime_put_sync(dev); + mutex_unlock(&fts_data->fts_clk_io_ctrl_mutex); + complete(&fts_data->trusted_touch_powerdown); + fts_ts_enable_reg(fts_data, false); +} + +static struct gh_notify_vmid_desc *fts_ts_vm_get_vmid(gh_vmid_t vmid) +{ + struct gh_notify_vmid_desc *vmid_desc; + + vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc, + vmid_entries[1]), GFP_KERNEL); + if (!vmid_desc) + return ERR_PTR(ENOMEM); + + vmid_desc->n_vmid_entries = 1; + vmid_desc->vmid_entries[0].vmid = vmid; + return vmid_desc; +} + +static void fts_trusted_touch_pvm_vm_mode_disable(struct fts_ts_data *fts_data) +{ + int rc = 0; + + atomic_set(&fts_data->trusted_touch_transition, 1); + + if (atomic_read(&fts_data->trusted_touch_abort_status)) { + fts_ts_trusted_touch_abort_pvm(fts_data); + return; + } + + if (fts_ts_trusted_touch_get_vm_state(fts_data) != PVM_ALL_RESOURCES_RELEASE_NOTIFIED) + pr_err("all release notifications are not received yet\n"); + + rc = gh_rm_mem_reclaim(fts_data->vm_info->vm_mem_handle, 0); + if (rc) { + pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc); + goto error; + } + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_RECLAIMED); + fts_data->vm_info->vm_mem_handle = 0; + pr_debug("vm mem reclaim succeded!\n"); + + rc = gh_irq_reclaim(fts_data->vm_info->irq_label); + if (rc) { + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + goto error; + } + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_RECLAIMED); + pr_debug("vm irq reclaim succeded!\n"); + + fts_irq_enable(); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_INTERRUPT_ENABLED); + fts_ts_bus_put(fts_data); + atomic_set(&fts_data->trusted_touch_transition, 0); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_I2C_RESOURCE_RELEASED); + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT); + atomic_set(&fts_data->trusted_touch_enabled, 0); + pr_info("trusted touch disabled\n"); + return; +error: + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE); +} + +static void fts_ts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_data *fts_data = data; + + if (notif_type != GH_RM_NOTIF_VM_IRQ_RELEASED) { + pr_err("invalid notification type\n"); + return; + } + + if (fts_ts_trusted_touch_get_vm_state(fts_data) == PVM_IOMEM_RELEASE_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_RELEASE_NOTIFIED); +} + +static void fts_ts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_released_payload *release_payload; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_data *fts_data; + + fts_data = (struct fts_ts_data *)entry_data; + vm_info = fts_data->vm_info; + if (!vm_info) { + pr_err(" Invalid vm_info\n"); + return; + } + + if (notif_type != GH_RM_NOTIF_MEM_RELEASED) { + pr_err(" Invalid notification type\n"); + return; + } + + if (tag != vm_info->mem_tag) { + pr_err(" Invalid tag\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err(" Invalid data or notification message\n"); + return; + } + + release_payload = (struct gh_rm_notif_mem_released_payload *)notif_msg; + if (release_payload->mem_handle != vm_info->vm_mem_handle) { + pr_err("Invalid mem handle detected\n"); + return; + } + + if (fts_ts_trusted_touch_get_vm_state(fts_data) == PVM_IRQ_RELEASE_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_RELEASE_NOTIFIED); +} + +static int fts_ts_vm_mem_lend(struct fts_ts_data *fts_data) +{ + struct gh_acl_desc *acl_desc; + struct gh_sgl_desc *sgl_desc; + struct gh_notify_vmid_desc *vmid_desc; + gh_memparcel_handle_t mem_handle; + gh_vmid_t trusted_vmid; + int rc = 0; + + acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("Failed to get acl of IO memories for Trusted touch\n"); + PTR_ERR(acl_desc); + return -EINVAL; + } + + sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info); + if (IS_ERR(sgl_desc)) { + pr_err("Failed to get sgl of IO memories for Trusted touch\n"); + PTR_ERR(sgl_desc); + rc = -EINVAL; + goto sgl_error; + } + + rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); + if (rc) { + pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", + rc); + goto error; + } + + pr_info("vm mem lend succeded\n"); + + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_LENT); + + gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); + + vmid_desc = fts_ts_vm_get_vmid(trusted_vmid); + + rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED, + fts_data->vm_info->mem_tag, vmid_desc); + if (rc) { + pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); + goto vmid_error; + } + + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_LENT_NOTIFIED); + + fts_data->vm_info->vm_mem_handle = mem_handle; +vmid_error: + kfree(vmid_desc); +error: + kfree(sgl_desc); +sgl_error: + kfree(acl_desc); + + return rc; +} + +static int fts_ts_trusted_touch_pvm_vm_mode_enable(struct fts_ts_data *fts_data) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info = fts_data->vm_info; + + atomic_set(&fts_data->trusted_touch_transition, 1); + mutex_lock(&fts_data->transition_lock); + + if (fts_data->suspended) { + FTS_ERROR("Invalid power state for operation\n"); + atomic_set(&fts_data->trusted_touch_transition, 0); + rc = -EPERM; + goto error; + } + + /* i2c session start and resource acquire */ + if (fts_ts_bus_get(fts_data) < 0) { + FTS_ERROR("fts_ts_bus_get failed\n"); + rc = -EIO; + goto error; + } + + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_I2C_RESOURCE_ACQUIRED); + /* flush pending interurpts from FIFO */ + fts_irq_disable(); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_INTERRUPT_DISABLED); + fts_release_all_finger(); + + rc = fts_ts_vm_mem_lend(fts_data); + if (rc) { + pr_err("Failed to lend memory\n"); + goto abort_handler; + } + pr_debug("vm mem lend succeded\n"); + rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, + fts_data->irq, &fts_ts_vm_irq_on_release_callback, fts_data); + if (rc) { + pr_err("Failed to lend irq\n"); + goto abort_handler; + } + + pr_debug("vm irq lend succeded for irq:%d\n", fts_data->irq); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_LENT); + + rc = gh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + goto abort_handler; + } + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_LENT_NOTIFIED); + + mutex_unlock(&fts_data->transition_lock); + atomic_set(&fts_data->trusted_touch_transition, 0); + atomic_set(&fts_data->trusted_touch_enabled, 1); + pr_info("trusted touch enabled\n"); + return rc; + +abort_handler: + fts_ts_trusted_touch_abort_handler(fts_data, TRUSTED_TOUCH_EVENT_LEND_FAILURE); + +error: + mutex_unlock(&fts_data->transition_lock); + return rc; +} + +int fts_ts_handle_trusted_touch_pvm(struct fts_ts_data *fts_data, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&fts_data->trusted_touch_enabled) == 0 && + (atomic_read(&fts_data->trusted_touch_abort_status) == 0)) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_trusted_touch_pvm_vm_mode_disable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&fts_data->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + err = fts_ts_trusted_touch_pvm_vm_mode_enable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + FTS_ERROR("unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +#endif + +static void fts_ts_trusted_touch_event_notify(struct fts_ts_data *fts_data, int event) +{ + atomic_set(&fts_data->trusted_touch_event, event); + sysfs_notify(&fts_data->dev->kobj, NULL, "trusted_touch_event"); +} + +static void fts_ts_trusted_touch_abort_handler(struct fts_ts_data *fts_data, int error) +{ + atomic_set(&fts_data->trusted_touch_abort_status, error); + pr_err("TUI session aborted with failure:%d\n", error); + fts_ts_trusted_touch_event_notify(fts_data, error); +#ifdef CONFIG_ARCH_QTI_VM + pr_err("Resetting touch controller\n"); + if (fts_ts_trusted_touch_get_vm_state(fts_data) >= TVM_IOMEM_ACCEPTED && + error == TRUSTED_TOUCH_EVENT_I2C_FAILURE) { + pr_err("Resetting touch controller\n"); + fts_ts_trusted_touch_reset_gpio_toggle(fts_data); + } +#endif +} + +static int fts_ts_vm_init(struct fts_ts_data *fts_data) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + void *mem_cookie; + + rc = fts_ts_populate_vm_info(fts_data); + if (rc) { + pr_err("Cannot setup vm pipeline\n"); + rc = -EINVAL; + goto fail; + } + + vm_info = fts_data->vm_info; +#ifdef CONFIG_ARCH_QTI_VM + mem_cookie = gh_mem_notifier_register(vm_info->mem_tag, + fts_ts_vm_mem_on_lend_handler, fts_data); + if (!mem_cookie) { + pr_err("Failed to register on lend mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM, + &fts_ts_vm_irq_on_lend_callback, fts_data); + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT); +#else + mem_cookie = gh_mem_notifier_register(vm_info->mem_tag, + fts_ts_vm_mem_on_release_handler, fts_data); + if (!mem_cookie) { + pr_err("Failed to register on release mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT); +#endif + return rc; +init_fail: + fts_ts_vm_deinit(fts_data); +fail: + return rc; +} + +static void fts_ts_dt_parse_trusted_touch_info(struct fts_ts_data *fts_data) +{ + struct device_node *np = fts_data->dev->of_node; + int rc = 0; + const char *selection; + const char *environment; + + rc = of_property_read_string(np, "focaltech,trusted-touch-mode", + &selection); + if (rc) { + dev_warn(fts_data->dev, + "%s: No trusted touch mode selection made\n", __func__); + atomic_set(&fts_data->trusted_touch_mode, + TRUSTED_TOUCH_MODE_NONE); + return; + } + + if (!strcmp(selection, "vm_mode")) { + atomic_set(&fts_data->trusted_touch_mode, + TRUSTED_TOUCH_VM_MODE); + pr_err("Selected trusted touch mode to VM mode\n"); + } else { + atomic_set(&fts_data->trusted_touch_mode, + TRUSTED_TOUCH_MODE_NONE); + pr_err("Invalid trusted_touch mode\n"); + } + + rc = of_property_read_string(np, "focaltech,touch-environment", + &environment); + if (rc) { + dev_warn(fts_data->dev, + "%s: No trusted touch mode environment\n", __func__); + } + fts_data->touch_environment = environment; + pr_err("Trusted touch environment:%s\n", + fts_data->touch_environment); +} + +static void fts_ts_trusted_touch_init(struct fts_ts_data *fts_data) +{ + int rc = 0; + + atomic_set(&fts_data->trusted_touch_initialized, 0); + fts_ts_dt_parse_trusted_touch_info(fts_data); + + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_MODE_NONE) + return; + + init_completion(&fts_data->trusted_touch_powerdown); + + /* Get clocks */ + fts_data->core_clk = devm_clk_get(fts_data->dev->parent, + "m-ahb"); + if (IS_ERR(fts_data->core_clk)) { + fts_data->core_clk = NULL; + dev_warn(fts_data->dev, + "%s: core_clk is not defined\n", __func__); + } + + fts_data->iface_clk = devm_clk_get(fts_data->dev->parent, + "se-clk"); + if (IS_ERR(fts_data->iface_clk)) { + fts_data->iface_clk = NULL; + dev_warn(fts_data->dev, + "%s: iface_clk is not defined\n", __func__); + } + + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + rc = fts_ts_vm_init(fts_data); + if (rc) + pr_err("Failed to init VM\n"); + } + atomic_set(&fts_data->trusted_touch_initialized, 1); +} + +#endif + +/***************************************************************************** +* Name: fts_wait_tp_to_valid +* Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG), +* need call when reset/power on/resume... +* Input: +* Output: +* Return: return 0 if tp valid, otherwise return error code +*****************************************************************************/ +int fts_wait_tp_to_valid(void) +{ + int ret = 0; + int cnt = 0; + u8 idh = 0; + u8 idl = 0; + u8 chip_idh = fts_data->ic_info.ids.chip_idh; + u8 chip_idl = fts_data->ic_info.ids.chip_idl; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + ret = fts_read_reg(FTS_REG_CHIP_ID2, &idl); + if ((ret < 0) || (idh != chip_idh) || (idl != chip_idl)) { + FTS_DEBUG("TP Not Ready,ReadData:0x%02x%02x", idh, idl); + } else if ((idh == chip_idh) && (idl == chip_idl)) { + FTS_INFO("TP Ready,Device ID:0x%02x%02x", idh, idl); + return 0; + } + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} + +/***************************************************************************** +* Name: fts_tp_state_recovery +* Brief: Need execute this function when reset +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_tp_state_recovery(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + /* wait tp stable */ + fts_wait_tp_to_valid(); + /* recover TP charger state 0x8B */ + /* recover TP glove state 0xC0 */ + /* recover TP cover state 0xC1 */ + fts_ex_mode_recovery(ts_data); + /* recover TP gesture state 0xD0 */ + fts_gesture_recovery(ts_data); + FTS_FUNC_EXIT(); +} + +int fts_reset_proc(int hdelayms) +{ + FTS_DEBUG("tp reset"); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(1); + gpio_direction_output(fts_data->pdata->reset_gpio, 1); + if (hdelayms) { + msleep(hdelayms); + } + + return 0; +} + +void fts_irq_disable(void) +{ + unsigned long irqflags; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (!fts_data->irq_disabled) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH + if (atomic_read(&fts_data->trusted_touch_transition)) + disable_irq_wake(fts_data->irq); + else + disable_irq_nosync(fts_data->irq); +#else + disable_irq_nosync(fts_data->irq); +#endif + fts_data->irq_disabled = true; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +void fts_irq_enable(void) +{ + unsigned long irqflags = 0; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (fts_data->irq_disabled) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH + if (atomic_read(&fts_data->trusted_touch_transition)) + enable_irq_wake(fts_data->irq); + else + enable_irq(fts_data->irq); +#else + enable_irq(fts_data->irq); +#endif + fts_data->irq_disabled = false; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +void fts_hid2std(void) +{ + int ret = 0; + u8 buf[3] = {0xEB, 0xAA, 0x09}; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + ret = fts_write(buf, 3); + if (ret < 0) { + FTS_ERROR("hid2std cmd write fail"); + return; + } + + msleep(20); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_read(NULL, 0, buf, 3); + if (ret < 0) + FTS_ERROR("hid2std cmd read fail"); + else if ((buf[0] == 0xEB) && (buf[1] == 0xAA) && (buf[2] == 0x08)) + FTS_DEBUG("hidi2c change to stdi2c successful"); + else + FTS_DEBUG("hidi2c change to stdi2c not support or fail"); + +} + +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + int i = 0; + u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t); + + if ((0x0 == id_h) || (0x0 == id_l)) { + FTS_ERROR("id_h/id_l is 0"); + return -EINVAL; + } + + FTS_DEBUG("verify id:0x%02x%02x", id_h, id_l); + for (i = 0; i < ctype_entries; i++) { + if (VALID == fw_valid) { + if ((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl)) + break; + } else { + if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl)) + || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl)) + || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl))) + break; + } + } + + if (i >= ctype_entries) + return -ENODATA; + + ts_data->ic_info.ids = ctype[i]; + return 0; +} + +static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id) +{ + int ret = 0; + u8 chip_id[2] = { 0 }; + u8 id_cmd[4] = { 0 }; + u32 id_cmd_len = 0; + + id_cmd[0] = FTS_CMD_START1; + id_cmd[1] = FTS_CMD_START2; + ret = fts_write(id_cmd, 2); + if (ret < 0) { + FTS_ERROR("start cmd write fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + id_cmd[0] = FTS_CMD_READ_ID; + id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; + if (ts_data->ic_info.is_incell) + id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + id_cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_read(id_cmd, id_cmd_len, chip_id, 2); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]); + return -EIO; + } + + id[0] = chip_id[0]; + id[1] = chip_id[1]; + return 0; +} + +/***************************************************************************** +* Name: fts_get_ic_information +* Brief: read chip id to get ic information, after run the function, driver w- +* ill know which IC is it. +* If cant get the ic information, maybe not focaltech's touch IC, need +* unregister the driver +* Input: +* Output: +* Return: return 0 if get correct ic information, otherwise return error code +*****************************************************************************/ +static int fts_get_ic_information(struct fts_ts_data *ts_data) +{ + int ret = 0; + int cnt = 0; + u8 chip_id[2] = { 0 }; + u32 type = ts_data->pdata->type; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC(type); + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED(type); + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]); + ret = fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_DEBUG("i2c read invalid, read:0x%02x%02x", + chip_id[0], chip_id[1]); + } else { + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID); + if (!ret) + break; + else + FTS_DEBUG("TP not ready, read:0x%02x%02x", + chip_id[0], chip_id[1]); + } + + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) { + FTS_INFO("fw is invalid, need read boot id"); + if (ts_data->ic_info.hid_supported) { + fts_hid2std(); + } + + ret = fts_read_bootid(ts_data, &chip_id[0]); + if (ret < 0) { + FTS_ERROR("read boot id fail"); + return ret; + } + + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_ERROR("can't get ic informaton"); + return ret; + } + } + + FTS_INFO("get ic information, chip id = 0x%02x%02x", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + + return 0; +} + +/***************************************************************************** +* Reprot related +*****************************************************************************/ +static void fts_show_touch_buffer(u8 *data, int datalen) +{ + int i = 0; + int count = 0; + char *tmpbuf = NULL; + + tmpbuf = kzalloc(1024, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("tmpbuf zalloc fail"); + return; + } + + for (i = 0; i < datalen; i++) { + count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]); + if (count >= 1024) + break; + } + FTS_DEBUG("point buffer:%s", tmpbuf); + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +void fts_release_all_finger(void) +{ + struct input_dev *input_dev = fts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = fts_data->pdata->max_touch_number; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&fts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < max_touches; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + + fts_data->touchs = 0; + fts_data->key_state = 0; + mutex_unlock(&fts_data->report_mutex); + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_input_report_key +* Brief: process key events,need report key-event if key enable. +* if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50), +* need report it to key event. +* x_dim: parse from dts, means key x_coordinate, dimension:+-50 +* y_dim: parse from dts, means key y_coordinate, dimension:+-50 +* Input: +* Output: +* Return: return 0 if it's key event, otherwise return error code +*****************************************************************************/ +static int fts_input_report_key(struct fts_ts_data *data, int index) +{ + int i = 0; + int x = data->events[index].x; + int y = data->events[index].y; + int *x_dim = &data->pdata->key_x_coords[0]; + int *y_dim = &data->pdata->key_y_coords[0]; + + if (!data->pdata->have_key) { + return -EINVAL; + } + + for (i = 0; i < data->pdata->key_number; i++) { + if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) && + (y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) { + if (EVENT_DOWN(data->events[index].flag) + && !(data->key_state & (1 << i))) { + input_report_key(data->input_dev, data->pdata->keys[i], 1); + data->key_state |= (1 << i); + FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y); + } else if (EVENT_UP(data->events[index].flag) + && (data->key_state & (1 << i))) { + input_report_key(data->input_dev, data->pdata->keys[i], 0); + data->key_state &= ~(1 << i); + FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y); + } + return 0; + } + } + return -EINVAL; +} + +#if FTS_MT_PROTOCOL_B_EN +static int fts_input_report_b(struct fts_ts_data *data) +{ + int i = 0; + int uppoint = 0; + int touchs = 0; + bool va_reported = false; + u32 max_touch_num = data->pdata->max_touch_number; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (fts_input_report_key(data, i) == 0) + continue; + + va_reported = true; + input_mt_slot(data->input_dev, events[i].id); + + if (EVENT_DOWN(events[i].flag)) { + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); + +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); + + touchs |= BIT(events[i].id); + data->touchs |= BIT(events[i].id); + + if ((data->log_level >= 2) || + ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { + FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!", + events[i].id, + events[i].x, events[i].y, + events[i].p, events[i].area); + } + } else { + uppoint++; + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + data->touchs &= ~BIT(events[i].id); + if (data->log_level >= 1) { + FTS_DEBUG("[B]P%d UP!", events[i].id); + } + } + } + + if (unlikely(data->touchs ^ touchs)) { + for (i = 0; i < max_touch_num; i++) { + if (BIT(i) & (data->touchs ^ touchs)) { + if (data->log_level >= 1) { + FTS_DEBUG("[B]P%d UP!", i); + } + va_reported = true; + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + } + } + } + data->touchs = touchs; + + if (va_reported) { + /* touchs==0, there's no point but key */ + if (EVENT_NO_DOWN(data) || (!touchs)) { + if (data->log_level >= 1) { + FTS_DEBUG("[B]Points All Up!"); + } + input_report_key(data->input_dev, BTN_TOUCH, 0); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} + +#else +static int fts_input_report_a(struct fts_ts_data *data) +{ + int i = 0; + int touchs = 0; + bool va_reported = false; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (fts_input_report_key(data, i) == 0) { + continue; + } + + va_reported = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); + + input_mt_sync(data->input_dev); + + if ((data->log_level >= 2) || + ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { + FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!", + events[i].id, + events[i].x, events[i].y, + events[i].p, events[i].area); + } + touchs++; + } + } + + /* last point down, current no point but key */ + if (data->touchs && !touchs) { + va_reported = true; + } + data->touchs = touchs; + + if (va_reported) { + if (EVENT_NO_DOWN(data)) { + if (data->log_level >= 1) { + FTS_DEBUG("[A]Points All Up!"); + } + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_mt_sync(data->input_dev); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} +#endif + +static int fts_read_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + u8 *buf = data->point_buf; + + memset(buf, 0xFF, data->pnt_buf_size); + buf[0] = 0x01; + + if (data->gesture_mode) { + if (0 == fts_gesture_readdata(data, NULL)) { + FTS_INFO("succuss to get gesture data in irq handler"); + return 1; + } + } + + ret = fts_read(buf, 1, buf + 1, data->pnt_buf_size - 1); + if (ret < 0) { + FTS_ERROR("read touchdata failed, ret:%d", ret); + return ret; + } + + if (data->log_level >= 3) { + fts_show_touch_buffer(buf, data->pnt_buf_size); + } + + return 0; +} + +static int fts_read_parse_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + int i = 0; + u8 pointid = 0; + int base = 0; + struct ts_event *events = data->events; + int max_touch_num = data->pdata->max_touch_number; + u8 *buf = data->point_buf; + + ret = fts_read_touchdata(data); + if (ret) { + return ret; + } + + data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; + data->touch_point = 0; + + if (data->ic_info.is_incell) { + if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF) + && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) { + FTS_DEBUG("touch buff is 0xff, need recovery state"); + fts_release_all_finger(); + fts_tp_state_recovery(data); + return -EIO; + } + } + + if (data->point_num > max_touch_num) { + FTS_INFO("invalid point_num(%d)", data->point_num); + return -EIO; + } + + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i; + pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4; + if (pointid >= FTS_MAX_ID) + break; + else if (pointid >= max_touch_num) { + FTS_ERROR("ID(%d) beyond max_touch_number", pointid); + return -EINVAL; + } + + data->touch_point++; + events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_X_L_POS + base] & 0xFF); + events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF); + events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6; + events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4; + events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4; + events[i].p = buf[FTS_TOUCH_PRE_POS + base]; + + if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } + + if (data->touch_point == 0) { + FTS_INFO("no touch point information"); + return -EIO; + } + + return 0; +} + +static void fts_irq_read_report(void) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(1); +#endif + +#if FTS_POINT_REPORT_CHECK_EN + fts_prc_queue_work(ts_data); +#endif + + ret = fts_read_parse_touchdata(ts_data); + if (ret == 0) { + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data); +#else + fts_input_report_a(ts_data); +#endif + mutex_unlock(&ts_data->report_mutex); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(0); +#endif +} + +static irqreturn_t fts_irq_handler(int irq, void *data) +{ + struct fts_ts_data *fts_data = data; + + if (!fts_data) { + pr_err("%s: Invalid fts_data\n", __func__); + return IRQ_HANDLED; + } + + if (!mutex_trylock(&fts_data->transition_lock)) + return IRQ_HANDLED; + + fts_irq_read_report(); + mutex_unlock(&fts_data->transition_lock); + + return IRQ_HANDLED; +} + +static int fts_irq_registration(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + +#ifdef CONFIG_ARCH_QTI_VM + pdata->irq_gpio_flags = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler, + pdata->irq_gpio_flags, + FTS_DRIVER_NAME, ts_data); +#else + ts_data->irq = gpio_to_irq(pdata->irq_gpio); + pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler, + pdata->irq_gpio_flags, + FTS_DRIVER_NAME, ts_data); +#endif + return ret; +} + +static int fts_input_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + int key_num = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev; + + FTS_FUNC_ENTER(); + input_dev = input_allocate_device(); + if (!input_dev) { + FTS_ERROR("Failed to allocate memory for input device"); + return -ENOMEM; + } + + /* Init and register Input device */ + input_dev->name = FTS_DRIVER_NAME; + if (ts_data->bus_type == BUS_TYPE_I2C) + input_dev->id.bustype = BUS_I2C; + else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = ts_data->dev; + + input_set_drvdata(input_dev, ts_data); + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + if (pdata->have_key) { + FTS_INFO("set key capabilities"); + for (key_num = 0; key_num < pdata->key_number; key_num++) + input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]); + } + +#if FTS_MT_PROTOCOL_B_EN + input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT); +#else + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0); +#endif + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0); +#if FTS_REPORT_PRESSURE_EN + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); +#endif + + ret = input_register_device(input_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } + + ts_data->input_dev = input_dev; + + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_report_buffer_init(struct fts_ts_data *ts_data) +{ + int point_num = 0; + int events_num = 0; + + point_num = FTS_MAX_POINTS_SUPPORT; + ts_data->pnt_buf_size = FTS_TOUCH_DATA_LEN + FTS_GESTURE_DATA_LEN; + ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size + 1, GFP_KERNEL); + if (!ts_data->point_buf) { + FTS_ERROR("failed to alloc memory for point buf"); + return -ENOMEM; + } + + events_num = point_num * sizeof(struct ts_event); + ts_data->events = (struct ts_event *)kzalloc(events_num, GFP_KERNEL); + if (!ts_data->events) { + FTS_ERROR("failed to alloc memory for point events"); + kfree_safe(ts_data->point_buf); + return -ENOMEM; + } + + return 0; +} + +#if FTS_POWER_SOURCE_CUST_EN +/***************************************************************************** +* Power Control +*****************************************************************************/ +#if FTS_PINCTRL_EN +static int fts_pinctrl_init(struct fts_ts_data *ts) +{ + int ret = 0; + + ts->pinctrl = devm_pinctrl_get(ts->dev); + if (IS_ERR_OR_NULL(ts->pinctrl)) { + FTS_ERROR("Failed to get pinctrl, please check dts"); + ret = PTR_ERR(ts->pinctrl); + goto err_pinctrl_get; + } + + ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(ts->pins_active)) { + FTS_ERROR("Pin state[active] not found"); + ret = PTR_ERR(ts->pins_active); + goto err_pinctrl_lookup; + } + + ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(ts->pins_suspend)) { + FTS_ERROR("Pin state[suspend] not found"); + ret = PTR_ERR(ts->pins_suspend); + goto err_pinctrl_lookup; + } + + ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(ts->pins_release)) { + FTS_ERROR("Pin state[release] not found"); + ret = PTR_ERR(ts->pins_release); + } + + return 0; +err_pinctrl_lookup: + if (ts->pinctrl) { + devm_pinctrl_put(ts->pinctrl); + } +err_pinctrl_get: + ts->pinctrl = NULL; + ts->pins_release = NULL; + ts->pins_suspend = NULL; + ts->pins_active = NULL; + return ret; +} + +static int fts_pinctrl_select_normal(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_active) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_active); + if (ret < 0) { + FTS_ERROR("Set normal pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_suspend(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_suspend) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend); + if (ret < 0) { + FTS_ERROR("Set suspend pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_release(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl) { + if (IS_ERR_OR_NULL(ts->pins_release)) { + devm_pinctrl_put(ts->pinctrl); + ts->pinctrl = NULL; + } else { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_release); + if (ret < 0) + FTS_ERROR("Set gesture pin state error:%d", ret); + } + } + + return ret; +} +#endif /* FTS_PINCTRL_EN */ + +static int fts_power_configure(struct fts_ts_data *ts_data, bool enable) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + if (enable) { + if (regulator_count_voltages(ts_data->vdd) > 0) { + ret = regulator_set_load(ts_data->vdd, FTS_LOAD_MAX_UA); + if (ret) { + FTS_ERROR("vdd regulator set_load failed ret=%d", ret); + return ret; + } + + ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV, + FTS_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret); + goto err_vdd_load; + } + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + ret = regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_AVDD_UA); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_load failed ret=%d", ret); + goto err_vdd_load; + } + + ret = regulator_set_voltage(ts_data->vcc_i2c, + FTS_I2C_VTG_MIN_UV, + FTS_I2C_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_vtg failed,ret=%d", ret); + goto err_vcc_load; + } + } + } + } else { + if (regulator_count_voltages(ts_data->vdd) > 0) { + ret = regulator_set_load(ts_data->vdd, FTS_LOAD_DISABLE_UA); + if (ret) { + FTS_ERROR("vdd regulator set_load failed ret=%d", ret); + return ret; + } + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + ret = regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_DISABLE_UA); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_load failed ret=%d", ret); + return ret; + } + } + } + } + + FTS_FUNC_EXIT(); + return ret; + +err_vcc_load: + regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_DISABLE_UA); +err_vdd_load: + regulator_set_load(ts_data->vdd, FTS_LOAD_DISABLE_UA); + return ret; +} + +static int fts_ts_enable_reg(struct fts_ts_data *ts_data, bool enable) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(ts_data->vdd)) { + FTS_ERROR("vdd is invalid"); + return -EINVAL; + } + + if (enable) { + fts_power_configure(ts_data, true); + ret = regulator_enable(ts_data->vdd); + if (ret) + FTS_ERROR("enable vdd regulator failed,ret=%d", ret); + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + ret = regulator_enable(ts_data->vcc_i2c); + if (ret) + FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret); + } + } else { + ret = regulator_disable(ts_data->vdd); + if (ret) + FTS_ERROR("disable vdd regulator failed,ret=%d", ret); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + ret = regulator_disable(ts_data->vcc_i2c); + if (ret) + FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret); + } + fts_power_configure(ts_data, false); + } + + return ret; +} + +static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(ts_data->vdd)) { + FTS_ERROR("vdd is invalid"); + return -EINVAL; + } + + FTS_FUNC_ENTER(); + if (enable) { + if (ts_data->power_disabled) { + FTS_DEBUG("regulator enable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + msleep(1); + ret = fts_ts_enable_reg(ts_data, true); + if (ret) + FTS_ERROR("Touch reg enable failed\n"); + ts_data->power_disabled = false; + } + } else { + if (!ts_data->power_disabled) { + FTS_DEBUG("regulator disable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + msleep(1); + ret = fts_ts_enable_reg(ts_data, false); + if (ret) + FTS_ERROR("Touch reg disable failed"); + ts_data->power_disabled = true; + } + } + + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_power_source_init +* Brief: Init regulator power:vdd/vcc_io(if have), generally, no vcc_io +* vdd---->vdd-supply in dts, kernel will auto add "-supply" to parse +* Must be call after fts_gpio_configure() execute,because this function +* will operate reset-gpio which request gpio in fts_gpio_configure() +* Input: +* Output: +* Return: return 0 if init power successfully, otherwise return error code +*****************************************************************************/ +static int fts_power_source_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ts_data->vdd = regulator_get(ts_data->dev, "vdd"); + if (IS_ERR_OR_NULL(ts_data->vdd)) { + ret = PTR_ERR(ts_data->vdd); + FTS_ERROR("get vdd regulator failed,ret=%d", ret); + return ret; + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (IS_ERR_OR_NULL(ts_data->vcc_i2c)) + FTS_INFO("get vcc_i2c regulator failed"); + +#if FTS_PINCTRL_EN + fts_pinctrl_init(ts_data); + fts_pinctrl_select_normal(ts_data); +#endif + + ts_data->power_disabled = true; + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret) { + FTS_ERROR("fail to enable power(regulator)"); + } + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_power_source_exit(struct fts_ts_data *ts_data) +{ +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + + fts_power_source_ctrl(ts_data, DISABLE); + + if (!IS_ERR_OR_NULL(ts_data->vdd)) { + if (regulator_count_voltages(ts_data->vdd) > 0) + regulator_set_voltage(ts_data->vdd, 0, FTS_VTG_MAX_UV); + regulator_put(ts_data->vdd); + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) + regulator_set_voltage(ts_data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV); + regulator_put(ts_data->vcc_i2c); + } + + return 0; +} + +static int fts_power_source_suspend(struct fts_ts_data *ts_data) +{ + int ret = 0; + +#if FTS_PINCTRL_EN + fts_pinctrl_select_suspend(ts_data); +#endif + + ret = fts_power_source_ctrl(ts_data, DISABLE); + if (ret < 0) { + FTS_ERROR("power off fail, ret=%d", ret); + } + + return ret; +} + +static int fts_power_source_resume(struct fts_ts_data *ts_data) +{ + int ret = 0; + +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif + + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret < 0) { + FTS_ERROR("power on fail, ret=%d", ret); + } + + return ret; +} +#endif /* FTS_POWER_SOURCE_CUST_EN */ + +static int fts_gpio_configure(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + /* request irq gpio */ + if (gpio_is_valid(data->pdata->irq_gpio)) { + ret = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio"); + if (ret) { + FTS_ERROR("[GPIO]irq gpio request failed"); + goto err_irq_gpio_req; + } + + ret = gpio_direction_input(data->pdata->irq_gpio); + if (ret) { + FTS_ERROR("[GPIO]set_direction for irq gpio failed"); + goto err_irq_gpio_dir; + } + } + + /* request reset gpio */ + if (gpio_is_valid(data->pdata->reset_gpio)) { + ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio"); + if (ret) { + FTS_ERROR("[GPIO]reset gpio request failed"); + goto err_irq_gpio_dir; + } + + ret = gpio_direction_output(data->pdata->reset_gpio, 1); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_reset_gpio_dir; + } + } + + FTS_FUNC_EXIT(); + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_get_dt_coords(struct device *dev, char *name, + struct fts_ts_platform_data *pdata) +{ + int ret = 0; + u32 coords[FTS_COORDS_ARR_SIZE] = { 0 }; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != FTS_COORDS_ARR_SIZE) { + FTS_ERROR("invalid:%s, size:%d", name, coords_size); + return -EINVAL; + } + + ret = of_property_read_u32_array(np, name, coords, coords_size); + if (ret < 0) { + FTS_ERROR("Unable to read %s, please check dts", name); + pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT; + pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT; + pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT; + pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT; + return -ENODATA; + } else { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } + + FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, + pdata->y_min, pdata->y_max); + return 0; +} + +static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) +{ + int ret = 0; + struct device_node *np = dev->of_node; + u32 temp_val = 0; + + FTS_FUNC_ENTER(); + + ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata); + if (ret < 0) + FTS_ERROR("Unable to get display-coords"); + + /* key */ + pdata->have_key = of_property_read_bool(np, "focaltech,have-key"); + if (pdata->have_key) { + ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number); + if (ret < 0) + FTS_ERROR("Key number undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,keys", + pdata->keys, pdata->key_number); + if (ret < 0) + FTS_ERROR("Keys undefined!"); + else if (pdata->key_number > FTS_MAX_KEYS) + pdata->key_number = FTS_MAX_KEYS; + + ret = of_property_read_u32_array(np, "focaltech,key-x-coords", + pdata->key_x_coords, + pdata->key_number); + if (ret < 0) + FTS_ERROR("Key Y Coords undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,key-y-coords", + pdata->key_y_coords, + pdata->key_number); + if (ret < 0) + FTS_ERROR("Key X Coords undefined!"); + + FTS_INFO("VK Number:%d, key:(%d,%d,%d), " + "coords:(%d,%d),(%d,%d),(%d,%d)", + pdata->key_number, + pdata->keys[0], pdata->keys[1], pdata->keys[2], + pdata->key_x_coords[0], pdata->key_y_coords[0], + pdata->key_x_coords[1], pdata->key_y_coords[1], + pdata->key_x_coords[2], pdata->key_y_coords[2]); + } + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", + 0, &pdata->reset_gpio_flags); + if (pdata->reset_gpio < 0) + FTS_ERROR("Unable to get reset_gpio"); + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", + 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + FTS_ERROR("Unable to get irq_gpio"); + + ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val); + if (ret < 0) { + FTS_ERROR("Unable to get max-touch-number, please check dts"); + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + } else { + if (temp_val < 2) + pdata->max_touch_number = 2; /* max_touch_number must >= 2 */ + else if (temp_val > FTS_MAX_POINTS_SUPPORT) + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + else + pdata->max_touch_number = temp_val; + } + + FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d", + pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); + + ret = of_property_read_u32(np, "focaltech,ic-type", &temp_val); + if (ret < 0) + pdata->type = _FT3518; + else + pdata->type = temp_val; + + FTS_FUNC_EXIT(); + return 0; +} + +#if defined(CONFIG_DRM) +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, + resume_work); + + fts_ts_resume(ts_data->dev); +} + +static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct fts_ts_data *ts_data = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + FTS_DEBUG("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + FTS_DEBUG("resume notification pre commit\n"); + else + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + break; + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else { + FTS_DEBUG("suspend notification post commit\n"); + } + break; + case DRM_PANEL_EVENT_BLANK_LP: + FTS_DEBUG("received lp event\n"); + break; + case DRM_PANEL_EVENT_FPS_CHANGE: + FTS_DEBUG("shashank:Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + FTS_DEBUG("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +#elif defined(CONFIG_FB) +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, + resume_work); + + fts_ts_resume(ts_data->dev); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("FB event:%lu,blank:%d", event, *blank); + switch (*blank) { + case FB_BLANK_UNBLANK: + if (FB_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (FB_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + + case FB_BLANK_POWERDOWN: + if (FB_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (FB_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + + default: + FTS_INFO("FB BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void fts_ts_early_suspend(struct early_suspend *handler) +{ + struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, + early_suspend); + + fts_ts_suspend(ts_data->dev); +} + +static void fts_ts_late_resume(struct early_suspend *handler) +{ + struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, + early_suspend); + + fts_ts_resume(ts_data->dev); +} +#endif + +static int fts_ts_probe_delayed(struct fts_ts_data *fts_data) +{ + int ret = 0; + +/* Avoid setting up hardware for TVM during probe */ +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (!atomic_read(&fts_data->delayed_vm_probe_pending)) { + atomic_set(&fts_data->delayed_vm_probe_pending, 1); + return 0; + } + goto tvm_setup; +#endif +#endif + ret = fts_gpio_configure(fts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(fts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + + if (!FTS_CHIP_IDC(fts_data->pdata->type)) + fts_reset_proc(200); + + ret = fts_get_ic_information(fts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + +#ifdef CONFIG_ARCH_QTI_VM +tvm_setup: +#endif + ret = fts_irq_registration(fts_data); + if (ret) { + FTS_ERROR("request irq failed"); +#ifdef CONFIG_ARCH_QTI_VM + return ret; +#endif + goto err_irq_req; + } + +#ifdef CONFIG_ARCH_QTI_VM + return ret; +#endif + + ret = fts_fwupg_init(fts_data); + if (ret) + FTS_ERROR("init fw upgrade fail"); + + return 0; + +err_irq_req: + if (gpio_is_valid(fts_data->pdata->reset_gpio)) + gpio_free(fts_data->pdata->reset_gpio); + if (gpio_is_valid(fts_data->pdata->irq_gpio)) + gpio_free(fts_data->pdata->irq_gpio); +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(fts_data); +#endif +err_gpio_config: + return ret; +} + +static int fts_ts_probe_entry(struct fts_ts_data *ts_data) +{ + int ret = 0; + int pdata_size = sizeof(struct fts_ts_platform_data); + + FTS_FUNC_ENTER(); + FTS_INFO("%s", FTS_DRIVER_VERSION); + ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL); + if (!ts_data->pdata) { + FTS_ERROR("allocate memory for platform_data fail"); + return -ENOMEM; + } + + if (ts_data->dev->of_node) { + ret = fts_parse_dt(ts_data->dev, ts_data->pdata); + if (ret) + FTS_ERROR("device-tree parse fail"); + } else { + if (ts_data->dev->platform_data) { + memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size); + } else { + FTS_ERROR("platform_data is null"); + return -ENODEV; + } + } + + ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); + if (!ts_data->ts_workqueue) { + FTS_ERROR("create fts workqueue fail"); + } + + spin_lock_init(&ts_data->irq_lock); + mutex_init(&ts_data->report_mutex); + mutex_init(&ts_data->bus_lock); + mutex_init(&ts_data->transition_lock); + + /* Init communication interface */ + ret = fts_bus_init(ts_data); + if (ret) { + FTS_ERROR("bus initialize fail"); + goto err_bus_init; + } + + ret = fts_input_init(ts_data); + if (ret) { + FTS_ERROR("input initialize fail"); + goto err_input_init; + } + + ret = fts_report_buffer_init(ts_data); + if (ret) { + FTS_ERROR("report buffer init fail"); + goto err_report_buffer; + } + + ret = fts_create_apk_debug_channel(ts_data); + if (ret) { + FTS_ERROR("create apk debug node fail"); + } + + ret = fts_create_sysfs(ts_data); + if (ret) { + FTS_ERROR("create sysfs node fail"); + } + +#if FTS_POINT_REPORT_CHECK_EN + ret = fts_point_report_check_init(ts_data); + if (ret) { + FTS_ERROR("init point report check fail"); + } +#endif + + ret = fts_ex_mode_init(ts_data); + if (ret) { + FTS_ERROR("init glove/cover/charger fail"); + } + + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } + + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + fts_ts_trusted_touch_init(ts_data); + mutex_init(&(ts_data->fts_clk_io_ctrl_mutex)); +#endif + ret = fts_ts_probe_delayed(ts_data); + if (ret) { + FTS_ERROR("Failed to enable resources\n"); + goto err_probe_delayed; + } + +#if defined(CONFIG_DRM) + if (ts_data->ts_workqueue) + INIT_WORK(&ts_data->resume_work, fts_resume_work); + + if (!strcmp(fts_data->touch_environment, "pvm")) + fts_ts_register_for_panel_events(ts_data->dev->of_node, ts_data); +#elif defined(CONFIG_FB) + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } + ts_data->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret); + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL; + ts_data->early_suspend.suspend = fts_ts_early_suspend; + ts_data->early_suspend.resume = fts_ts_late_resume; + register_early_suspend(&ts_data->early_suspend); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_probe_delayed: + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); +err_report_buffer: + input_unregister_device(ts_data->input_dev); +err_input_init: + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); +err_bus_init: + kfree_safe(ts_data->bus_tx_buf); + kfree_safe(ts_data->bus_rx_buf); + kfree_safe(ts_data->pdata); + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_ts_remove_entry(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + +#if FTS_POINT_REPORT_CHECK_EN + fts_point_report_check_exit(ts_data); +#endif + + fts_release_apk_debug_channel(ts_data); + fts_remove_sysfs(ts_data); + fts_ex_mode_exit(ts_data); + + fts_fwupg_exit(ts_data); + + +#if FTS_ESDCHECK_EN + fts_esdcheck_exit(ts_data); +#endif + + fts_gesture_exit(ts_data); + fts_bus_exit(ts_data); + + free_irq(ts_data->irq, ts_data); + input_unregister_device(ts_data->input_dev); + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#if defined(CONFIG_DRM) + if (active_panel && ts_data->notifier_cookie) + panel_event_notifier_unregister(ts_data->notifier_cookie); + +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif + + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); + + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_exit(ts_data); +#endif + + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); + + kfree_safe(ts_data->pdata); + kfree_safe(ts_data); + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_ts_suspend(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (ts_data->suspended) { + FTS_INFO("Already in suspend state"); + return 0; + } + + if (ts_data->fw_loading) { + FTS_INFO("fw upgrade in process, can't suspend"); + return 0; + } + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + if (atomic_read(&fts_data->trusted_touch_transition) + || atomic_read(&fts_data->trusted_touch_enabled)) + wait_for_completion_interruptible( + &fts_data->trusted_touch_powerdown); +#endif + + mutex_lock(&ts_data->transition_lock); + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(); +#endif + + if (ts_data->gesture_mode) { + fts_gesture_suspend(ts_data); + } else { + fts_irq_disable(); + + FTS_INFO("make TP enter into sleep mode"); + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_suspend(ts_data); + if (ret < 0) { + FTS_ERROR("power enter suspend fail"); + } +#endif + } + } + + fts_release_all_finger(); + ts_data->suspended = true; + mutex_unlock(&ts_data->transition_lock); + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + + if (atomic_read(&ts_data->trusted_touch_transition)) + wait_for_completion_interruptible( + &ts_data->trusted_touch_powerdown); +#endif + + mutex_lock(&ts_data->transition_lock); + + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(ts_data); +#endif + fts_reset_proc(200); + } + + fts_wait_tp_to_valid(); + fts_ex_mode_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + + if (ts_data->gesture_mode) { + fts_gesture_resume(ts_data); + } else { + fts_irq_enable(); + } + + ts_data->suspended = false; + mutex_unlock(&ts_data->transition_lock); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* TP Driver +*****************************************************************************/ +static int fts_ts_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 0; + + 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 0; + } + } + + return PTR_ERR(panel); +} + +static int fts_ts_check_default_tp(struct device_node *dt, const char *prop) +{ + const char **active_tp = NULL; + int count, tmp, score = 0; + const char *active; + int ret, i; + + count = of_property_count_strings(dt->parent, prop); + if (count <= 0 || count > 3) + return -ENODEV; + + active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!active_tp) { + FTS_ERROR("FTS alloc failed\n"); + return -ENOMEM; + } + + ret = of_property_read_string_array(dt->parent, prop, + active_tp, count); + if (ret < 0) { + FTS_ERROR("fail to read %s %d\n", prop, ret); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < count; i++) { + active = active_tp[i]; + if (active != NULL) { + tmp = of_device_is_compatible(dt, active); + if (tmp > 0) + score++; + } + } + + if (score <= 0) { + FTS_INFO("not match this driver\n"); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} + +static int fts_ts_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + struct device_node *dp = client->dev.of_node; + + FTS_INFO("Touch Screen(I2C BUS) driver prboe..."); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FTS_ERROR("I2C not supported"); + return -ENODEV; + } + + ret = fts_ts_check_dt(dp); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + if (!fts_ts_check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } + + /* malloc memory for global struct variable */ + ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("allocate memory for fts_data fail"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->client = client; + ts_data->dev = &client->dev; + ts_data->log_level = 1; + ts_data->fw_is_running = 0; + ts_data->bus_type = BUS_TYPE_I2C; + i2c_set_clientdata(client, ts_data); + + ret = fts_ts_probe_entry(ts_data); + if (ret) { + FTS_ERROR("Touch Screen(I2C BUS) driver probe fail"); + kfree_safe(ts_data); + return ret; + } + + FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully"); + return 0; +} + +static int fts_ts_i2c_remove(struct i2c_client *client) +{ + return fts_ts_remove_entry(i2c_get_clientdata(client)); +} + +static const struct i2c_device_id fts_ts_i2c_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts_ts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct i2c_driver fts_ts_i2c_driver = { + .probe = fts_ts_i2c_probe, + .remove = fts_ts_i2c_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fts_dt_match), + }, + .id_table = fts_ts_i2c_id, +}; + +static int __init fts_ts_i2c_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = i2c_add_driver(&fts_ts_i2c_driver); + if (ret != 0) + FTS_ERROR("Focaltech touch screen driver init failed!"); + + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_i2c_exit(void) +{ + i2c_del_driver(&fts_ts_i2c_driver); +} + +static int fts_ts_spi_probe(struct spi_device *spi) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + struct device_node *dp = spi->dev.of_node; + + FTS_INFO("Touch Screen(SPI BUS) driver prboe..."); + + ret = fts_ts_check_dt(dp); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + if (!fts_ts_check_default_tp(dp, "qcom,spi-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) { + FTS_ERROR("spi setup fail"); + return ret; + } + + /* malloc memory for global struct variable */ + ts_data = kzalloc(sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("allocate memory for fts_data fail"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->spi = spi; + ts_data->dev = &spi->dev; + ts_data->log_level = 1; + + ts_data->bus_type = BUS_TYPE_SPI_V2; + spi_set_drvdata(spi, ts_data); + + ret = fts_ts_probe_entry(ts_data); + if (ret) { + FTS_ERROR("Touch Screen(SPI BUS) driver probe fail"); + kfree_safe(ts_data); + return ret; + } + + FTS_INFO("Touch Screen(SPI BUS) driver prboe successfully"); + return 0; +} + +static int fts_ts_spi_remove(struct spi_device *spi) +{ + return fts_ts_remove_entry(spi_get_drvdata(spi)); +} + +static const struct spi_device_id fts_ts_spi_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; + +static struct spi_driver fts_ts_spi_driver = { + .probe = fts_ts_spi_probe, + .remove = fts_ts_spi_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + .pm = &fts_dev_pm_ops, +#endif + .of_match_table = of_match_ptr(fts_dt_match), + }, + .id_table = fts_ts_spi_id, +}; + +static int __init fts_ts_spi_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = spi_register_driver(&fts_ts_spi_driver); + if (ret != 0) + FTS_ERROR("Focaltech touch screen driver init failed!"); + + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_spi_exit(void) +{ + spi_unregister_driver(&fts_ts_spi_driver); +} + +static int __init fts_ts_init(void) +{ + int ret = 0; + + ret = fts_ts_i2c_init(); + if (ret) + FTS_ERROR("Focaltech I2C driver init failed!"); + + ret = fts_ts_spi_init(); + if (ret) + FTS_ERROR("Focaltech SPI driver init failed!"); + + return ret; +} + +static void __exit fts_ts_exit(void) +{ + fts_ts_i2c_exit(); + fts_ts_spi_exit(); +} + +#ifdef CONFIG_ARCH_QTI_VM +module_init(fts_ts_init); +#else +late_initcall(fts_ts_init); +#endif +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/focaltech_touch/focaltech_core.h b/focaltech_touch/focaltech_core.h new file mode 100644 index 0000000000..9be61d55fd --- /dev/null +++ b/focaltech_touch/focaltech_core.h @@ -0,0 +1,389 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.h + +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_CORE_H__ +#define __LINUX_FOCALTECH_CORE_H__ +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_MAX_POINTS_SUPPORT 10 /* constant value, can't be changed */ +#define FTS_MAX_KEYS 4 +#define FTS_KEY_DIM 10 +#define FTS_ONE_TCH_LEN 6 +#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 3) + +#define FTS_GESTURE_POINTS_MAX 6 +#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4) + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_X_H_POS 3 +#define FTS_TOUCH_X_L_POS 4 +#define FTS_TOUCH_Y_H_POS 5 +#define FTS_TOUCH_Y_L_POS 6 +#define FTS_TOUCH_PRE_POS 7 +#define FTS_TOUCH_AREA_POS 8 +#define FTS_TOUCH_POINT_NUM 2 +#define FTS_TOUCH_EVENT_POS 3 +#define FTS_TOUCH_ID_POS 5 +#define FTS_COORDS_ARR_SIZE 4 +#define FTS_X_MIN_DISPLAY_DEFAULT 0 +#define FTS_Y_MIN_DISPLAY_DEFAULT 0 +#define FTS_X_MAX_DISPLAY_DEFAULT 720 +#define FTS_Y_MAX_DISPLAY_DEFAULT 1280 + +#define FTS_TOUCH_DOWN 0 +#define FTS_TOUCH_UP 1 +#define FTS_TOUCH_CONTACT 2 +#define EVENT_DOWN(flag) ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag)) +#define EVENT_UP(flag) (FTS_TOUCH_UP == flag) +#define EVENT_NO_DOWN(data) (!data->point_num) + +#define FTX_MAX_COMPATIBLE_TYPE 4 +#define FTX_MAX_COMMMAND_LENGTH 16 + + +/***************************************************************************** +* Alternative mode (When something goes wrong, the modules may be able to solve the problem.) +*****************************************************************************/ +/* + * For commnication error in PM(deep sleep) state + */ +#define FTS_PATCH_COMERR_PM 0 +#define FTS_TIMEOUT_COMERR_PM 700 + + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct ftxxxx_proc { + struct proc_dir_entry *proc_entry; + u8 opmode; + u8 cmd_len; + u8 cmd[FTX_MAX_COMMMAND_LENGTH]; +}; + +struct fts_ts_platform_data { + u32 type; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool have_key; + u32 key_number; + u32 keys[FTS_MAX_KEYS]; + u32 key_y_coords[FTS_MAX_KEYS]; + u32 key_x_coords[FTS_MAX_KEYS]; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 max_touch_number; +}; + +struct ts_event { + int x; /*x coordinate */ + int y; /*y coordinate */ + int p; /* pressure */ + int flag; /* touch event flag: 0 -- down; 1-- up; 2 -- contact */ + int id; /*touch ID */ + int area; +}; + +enum trusted_touch_mode_config { + TRUSTED_TOUCH_VM_MODE, + TRUSTED_TOUCH_MODE_NONE +}; + +enum trusted_touch_pvm_states { + TRUSTED_TOUCH_PVM_INIT, + PVM_I2C_RESOURCE_ACQUIRED, + PVM_INTERRUPT_DISABLED, + PVM_IOMEM_LENT, + PVM_IOMEM_LENT_NOTIFIED, + PVM_IRQ_LENT, + PVM_IRQ_LENT_NOTIFIED, + PVM_IOMEM_RELEASE_NOTIFIED, + PVM_IRQ_RELEASE_NOTIFIED, + PVM_ALL_RESOURCES_RELEASE_NOTIFIED, + PVM_IRQ_RECLAIMED, + PVM_IOMEM_RECLAIMED, + PVM_INTERRUPT_ENABLED, + PVM_I2C_RESOURCE_RELEASED, + TRUSTED_TOUCH_PVM_STATE_MAX +}; + +enum trusted_touch_tvm_states { + TRUSTED_TOUCH_TVM_INIT, + TVM_IOMEM_LENT_NOTIFIED, + TVM_IRQ_LENT_NOTIFIED, + TVM_ALL_RESOURCES_LENT_NOTIFIED, + TVM_IOMEM_ACCEPTED, + TVM_I2C_SESSION_ACQUIRED, + TVM_IRQ_ACCEPTED, + TVM_INTERRUPT_ENABLED, + TVM_INTERRUPT_DISABLED, + TVM_IRQ_RELEASED, + TVM_I2C_SESSION_RELEASED, + TVM_IOMEM_RELEASED, + TRUSTED_TOUCH_TVM_STATE_MAX +}; + +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#define TRUSTED_TOUCH_MEM_LABEL 0x7 + +#define TOUCH_RESET_GPIO_BASE 0xF114000 +#define TOUCH_RESET_GPIO_SIZE 0x1000 +#define TOUCH_RESET_GPIO_OFFSET 0x4 +#define TOUCH_INTR_GPIO_BASE 0xF115000 +#define TOUCH_INTR_GPIO_SIZE 0x1000 +#define TOUCH_INTR_GPIO_OFFSET 0x8 + +#define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1 +#define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2 +#define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3 +#define TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4 +#define TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5 +#define TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6 +#define TRUSTED_TOUCH_EVENT_I2C_FAILURE -7 +#define TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5 + +struct trusted_touch_vm_info { + enum gh_irq_label irq_label; + enum gh_mem_notifier_tag mem_tag; + enum gh_vm_names vm_name; + const char *trusted_touch_type; + u32 hw_irq; + gh_memparcel_handle_t vm_mem_handle; + u32 *iomem_bases; + u32 *iomem_sizes; + u32 iomem_list_size; + void *mem_cookie; + atomic_t vm_state; +}; +#endif + +struct fts_ts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct input_dev *input_dev; + struct fts_ts_platform_data *pdata; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct work_struct fwupg_work; + struct delayed_work esdcheck_work; + struct delayed_work prc_work; + struct work_struct resume_work; + struct work_struct suspend_work; + struct ftxxxx_proc proc; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + struct mutex transition_lock; + int irq; + int log_level; + int fw_is_running; /* confirm fw is running when using spi:default 0 */ + int dummy_byte; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + struct completion pm_completion; + bool pm_suspend; +#endif + bool suspended; + bool fw_loading; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool gesture_mode; /* gesture enable or disable, default: disable */ + int report_rate; + /* multi-touch */ + struct ts_event *events; + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + u8 *point_buf; + void *notifier_cookie; + int pnt_buf_size; + int touchs; + int key_state; + int touch_point; + int point_num; + struct regulator *vdd; + struct regulator *vcc_i2c; +#if FTS_PINCTRL_EN + struct pinctrl *pinctrl; + struct pinctrl_state *pins_active; + struct pinctrl_state *pins_suspend; + struct pinctrl_state *pins_release; +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + const char *touch_environment; + struct completion trusted_touch_powerdown; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t trusted_touch_transition; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif +}; + +enum _FTS_BUS_TYPE { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_SPI_V2, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_ts_data *fts_data; + +/* communication interface */ +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen); +int fts_read_reg(u8 addr, u8 *value); +int fts_write(u8 *writebuf, u32 writelen); +int fts_write_reg(u8 addr, u8 value); +void fts_hid2std(void); +int fts_bus_init(struct fts_ts_data *ts_data); +int fts_bus_exit(struct fts_ts_data *ts_data); + +/* Gesture functions */ +int fts_gesture_init(struct fts_ts_data *ts_data); +int fts_gesture_exit(struct fts_ts_data *ts_data); +void fts_gesture_recovery(struct fts_ts_data *ts_data); +int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data); +int fts_gesture_suspend(struct fts_ts_data *ts_data); +int fts_gesture_resume(struct fts_ts_data *ts_data); + +/* Apk and functions */ +int fts_create_apk_debug_channel(struct fts_ts_data *); +void fts_release_apk_debug_channel(struct fts_ts_data *); + +/* ADB functions */ +int fts_create_sysfs(struct fts_ts_data *ts_data); +int fts_remove_sysfs(struct fts_ts_data *ts_data); + +/* ESD */ +#if FTS_ESDCHECK_EN +int fts_esdcheck_init(struct fts_ts_data *ts_data); +int fts_esdcheck_exit(struct fts_ts_data *ts_data); +int fts_esdcheck_switch(bool enable); +int fts_esdcheck_proc_busy(bool proc_debug); +int fts_esdcheck_set_intr(bool intr); +int fts_esdcheck_suspend(void); +int fts_esdcheck_resume(void); +#endif + +/* Production test */ +#if FTS_TEST_EN +int fts_test_init(struct fts_ts_data *ts_data); +int fts_test_exit(struct fts_ts_data *ts_data); +#endif + +/* Point Report Check*/ +#if FTS_POINT_REPORT_CHECK_EN +int fts_point_report_check_init(struct fts_ts_data *ts_data); +int fts_point_report_check_exit(struct fts_ts_data *ts_data); +void fts_prc_queue_work(struct fts_ts_data *ts_data); +#endif + +/* FW upgrade */ +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); +int fts_enter_test_environment(bool test_state); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_wait_tp_to_valid(void); +void fts_release_all_finger(void); +void fts_tp_state_recovery(struct fts_ts_data *ts_data); +int fts_ex_mode_init(struct fts_ts_data *ts_data); +int fts_ex_mode_exit(struct fts_ts_data *ts_data); +int fts_ex_mode_recovery(struct fts_ts_data *ts_data); + +void fts_irq_disable(void); +void fts_irq_enable(void); +int fts_ts_handle_trusted_touch_pvm(struct fts_ts_data *ts_data, int value); +int fts_ts_handle_trusted_touch_tvm(struct fts_ts_data *ts_data, int value); +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM +void fts_ts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_data *fts_data); +#endif +#endif +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/focaltech_touch/focaltech_esdcheck.c b/focaltech_touch/focaltech_esdcheck.c new file mode 100644 index 0000000000..295f91d2f8 --- /dev/null +++ b/focaltech_touch/focaltech_esdcheck.c @@ -0,0 +1,465 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_esdcheck.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-03 +* +* Abstract: ESD check function +* +* Version: v1.0 +* +* Revision History: +* v1.0: +* First release. By luougojin 2016-08-03 +* v1.1: By luougojin 2017-02-15 +* 1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_ESDCHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define ESDCHECK_WAIT_TIME 1000 /* ms */ +#define LCD_ESD_PATCH 0 +#define ESDCHECK_INTRCNT_MAX 2 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_esdcheck_st { + u8 mode : 1; /* 1- need check esd 0- no esd check */ + u8 suspend : 1; + u8 proc_debug : 1; /* apk or adb use */ + u8 intr : 1; /* 1- Interrupt trigger */ + u8 unused : 4; + u8 intr_cnt; + u8 flow_work_hold_cnt; /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */ + u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */ + u32 hardware_reset_cnt; + u32 nack_cnt; + u32 dataerror_cnt; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_esdcheck_st fts_esdcheck_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +#if LCD_ESD_PATCH +int lcd_need_reset; +static int tp_need_recovery; /* LCD reset cause Tp reset */ +int idc_esdcheck_lcderror(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 val = 0; + + FTS_DEBUG("check LCD ESD"); + if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) { + tp_need_recovery = 0; + /* LCD reset, need recover TP state */ + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + } + + ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val); + if ( ret < 0) { + FTS_ERROR("read reg0xED fail,ret:%d", ret); + return -EIO; + } + + if (val == 0xAA) { + /* + * 1. Set flag lcd_need_reset = 1; + * 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0 + * 3. recover TP state + */ + FTS_INFO("LCD ESD, need execute LCD reset"); + lcd_need_reset = 1; + tp_need_recovery = 1; + } + + return 0; +} +#endif + +static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.hardware_reset_cnt++; + + fts_reset_proc(200); + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + + FTS_FUNC_EXIT(); + return 0; +} + +static bool get_chip_id(struct fts_ts_data *ts_data) +{ + int ret = 0; + int i = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + reg_addr = FTS_REG_CHIP_ID; + ret = fts_read(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("read chip id fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if (reg_value == chip_id) { + break; + } else { + FTS_DEBUG("read chip_id:%x,retry:%d", reg_value, i); + fts_esdcheck_data.dataerror_cnt++; + } + } + msleep(10); + } + + /* if can't get correct data in 3 times, then need hardware reset */ + if (i >= 3) { + FTS_ERROR("read chip id 3 times fail, need execute TP reset"); + return true; + } + + return false; +} + +/***************************************************************************** +* Name: get_flow_cnt +* Brief: Read flow cnt(0x91) +* Input: +* Output: +* Return: 1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times +* 0(false) - Reg 0x91(flow cnt) normal +*****************************************************************************/ +static bool get_flow_cnt(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + + reg_addr = FTS_REG_FLOW_WORK_CNT; + ret = fts_read(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("read reg0x91 fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) { + FTS_DEBUG("reg0x91,val:%x,last:%x", reg_value, + fts_esdcheck_data.flow_work_cnt_last); + fts_esdcheck_data.flow_work_hold_cnt++; + } else { + fts_esdcheck_data.flow_work_hold_cnt = 0; + } + + fts_esdcheck_data.flow_work_cnt_last = reg_value; + } + + /* Flow Work Cnt keep a value for 5 times, need execute TP reset */ + if (fts_esdcheck_data.flow_work_hold_cnt >= 5) { + FTS_DEBUG("reg0x91 keep a value for 5 times, need execute TP reset"); + return true; + } + + return false; +} + +static int esdcheck_algorithm(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + bool hardware_reset = 0; + + /* 1. esdcheck is interrupt, then return */ + if (fts_esdcheck_data.intr == 1) { + fts_esdcheck_data.intr_cnt++; + if (fts_esdcheck_data.intr_cnt > ESDCHECK_INTRCNT_MAX) + fts_esdcheck_data.intr = 0; + else + return 0; + } + + /* 2. check power state, if suspend, no need check esd */ + if (fts_esdcheck_data.suspend == 1) { + FTS_DEBUG("In suspend, not check esd"); + /* because in suspend state, adb can be used, when upgrade FW, will + * active ESD check(active = 1); But in suspend, then will don't + * queue_delayed_work, when resume, don't check ESD again + */ + return 0; + } + + /* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/ + if (fts_esdcheck_data.proc_debug == 1) { + FTS_INFO("In apk/adb command mode, not check esd"); + return 0; + } + + /* 4. In factory mode, can't check esd */ + reg_addr = FTS_REG_WORKMODE; + ret = fts_read_reg(reg_addr, ®_value); + if ( ret < 0 ) { + fts_esdcheck_data.nack_cnt++; + } else if ( (reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) { + FTS_DEBUG("not in work mode(%x), no check esd", reg_value); + return 0; + } + + /* 5. IDC esd check lcd default:close */ +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + + /* 6. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */ + if (!hardware_reset) { + hardware_reset = get_flow_cnt(ts_data); + } + + /* 8. If need hardware reset, then handle it here */ + if (hardware_reset == 1) { + FTS_DEBUG("NoACK=%d, Error Data=%d, Hardware Reset=%d", + fts_esdcheck_data.nack_cnt, + fts_esdcheck_data.dataerror_cnt, + fts_esdcheck_data.hardware_reset_cnt); + fts_esdcheck_tp_reset(ts_data); + } + + return 0; +} + +static void esdcheck_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, esdcheck_work.work); + + if (ENABLE == fts_esdcheck_data.mode) { + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, + &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } +} + +int fts_esdcheck_set_intr(bool intr) +{ + /* interrupt don't add debug message */ + fts_esdcheck_data.intr = intr; + fts_esdcheck_data.intr_cnt = (u8)intr; + return 0; +} + +static int fts_esdcheck_get_status(void) +{ + /* interrupt don't add debug message */ + return fts_esdcheck_data.mode; +} + +/***************************************************************************** +* Name: fts_esdcheck_proc_busy +* Brief: When APK or ADB command access TP via driver, then need set proc_debug, +* then will not check ESD. +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_proc_busy(bool proc_debug) +{ + fts_esdcheck_data.proc_debug = proc_debug; + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_switch(bool enable) +{ + struct fts_ts_data *ts_data = fts_data; + FTS_FUNC_ENTER(); + if (fts_esdcheck_data.mode == ENABLE) { + if (enable) { + FTS_DEBUG("ESD check start"); + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.flow_work_cnt_last = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + queue_delayed_work(ts_data->ts_workqueue, + &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } else { + FTS_DEBUG("ESD check stop"); + cancel_delayed_work_sync(&ts_data->esdcheck_work); + } + } + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_suspend(void) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(DISABLE); + fts_esdcheck_data.suspend = 1; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_resume( void ) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ENABLE); + fts_esdcheck_data.suspend = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + return 0; +} + +static ssize_t fts_esdcheck_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_switch(ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_switch(DISABLE); + fts_esdcheck_data.mode = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_esdcheck_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \ + fts_esdcheck_get_status() ? "On" : "Off"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* sysfs esd node + * read example: cat fts_esd_mode ---read esd mode + * write example:echo 01 > fts_esd_mode ---make esdcheck enable + * + */ +static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store); + +static struct attribute *fts_esd_mode_attrs[] = { + + &dev_attr_fts_esd_mode.attr, + NULL, +}; + +static struct attribute_group fts_esd_group = { + .attrs = fts_esd_mode_attrs, +}; + +int fts_create_esd_sysfs(struct device *dev) +{ + int ret = 0; + + ret = sysfs_create_group(&dev->kobj, &fts_esd_group); + if ( ret != 0) { + FTS_ERROR("fts_create_esd_sysfs(sysfs) create fail"); + sysfs_remove_group(&dev->kobj, &fts_esd_group); + return ret; + } + return 0; +} + +int fts_esdcheck_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run esd check func!"); + return -EINVAL; + } + + memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st)); + + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + fts_esdcheck_switch(ENABLE); + fts_create_esd_sysfs(ts_data->dev); + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group); + return 0; +} + +#endif /* FTS_ESDCHECK_EN */ + diff --git a/focaltech_touch/focaltech_ex_fun.c b/focaltech_touch/focaltech_ex_fun.c new file mode 100644 index 0000000000..518a7682cd --- /dev/null +++ b/focaltech_touch/focaltech_ex_fun.c @@ -0,0 +1,1229 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: Focaltech_ex_fun.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define PROC_UPGRADE 0 +#define PROC_READ_REGISTER 1 +#define PROC_WRITE_REGISTER 2 +#define PROC_AUTOCLB 4 +#define PROC_UPGRADE_INFO 5 +#define PROC_WRITE_DATA 6 +#define PROC_READ_DATA 7 +#define PROC_SET_TEST_FLAG 8 +#define PROC_SET_SLAVE_ADDR 10 +#define PROC_HW_RESET 11 +#define PROC_READ_STATUS 12 +#define PROC_SET_BOOT_MODE 13 +#define PROC_ENTER_TEST_ENVIRONMENT 14 +#define PROC_NAME "ftxxxx-debug" +#define PROC_BUF_SIZE 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +enum { + RWREG_OP_READ = 0, + RWREG_OP_WRITE = 1, +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct rwreg_operation_t { + int type; /* 0: read, 1: write */ + int reg; /* register */ + int len; /* read/write length */ + int val; /* length = 1; read: return value, write: op return */ + int res; /* 0: success, otherwise: fail */ + char *opbuf; /* length >= 1, read return value, write: op return */ +} rw_op; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +static ssize_t fts_debug_write( + struct file *filp, const char __user *buff, size_t count, loff_t *ppos) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[PROC_BUF_SIZE]; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 1) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); + if (writebuf[1] == 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + } else { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + } + break; + + case PROC_READ_REGISTER: + proc->cmd[0] = writebuf[1]; + break; + + case PROC_WRITE_REGISTER: + ret = fts_write_reg(writebuf[1], writebuf[2]); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_REGISTER write error"); + goto proc_write_err; + } + break; + + case PROC_READ_DATA: + writelen = buflen - 1; + if (writelen >= FTX_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA) len(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_WRITE_DATA: + writelen = buflen - 1; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_SET_SLAVE_ADDR: + break; + + case PROC_HW_RESET: + snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + break; + + case PROC_SET_BOOT_MODE: + FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); + if (0 == writebuf[1]) { + ts_data->fw_is_running = true; + } else { + ts_data->fw_is_running = false; + } + break; + + case PROC_ENTER_TEST_ENVIRONMENT: + FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); + if (0 == writebuf[1]) { + fts_enter_test_environment(0); + } else { + fts_enter_test_environment(1); + } + break; + + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +static ssize_t fts_debug_read( + struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *readbuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + switch (proc->opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_read_reg(proc->cmd[0], &readbuf[0]); + if (ret < 0) { + FTS_ERROR("PROC_READ_REGISTER read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_REGISTER: + break; + + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_read(NULL, 0, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_DATA: + break; + + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + goto proc_read_err; + } + + ret = num_read_chars; +proc_read_err: + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +static const struct proc_ops fts_proc_fops = { + .proc_read = fts_debug_read, + .proc_write = fts_debug_write, +}; +#else +static int fts_debug_write(struct file *filp, + const char __user *buff, unsigned long len, void *data) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[PROC_BUF_SIZE]; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 1) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); + if (writebuf[1] == 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + } else { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + } + break; + + case PROC_READ_REGISTER: + proc->cmd[0] = writebuf[1]; + break; + + case PROC_WRITE_REGISTER: + ret = fts_write_reg(writebuf[1], writebuf[2]); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_REGISTER write error"); + goto proc_write_err; + } + break; + + case PROC_READ_DATA: + writelen = buflen - 1; + if (writelen >= FTX_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_WRITE_DATA: + writelen = buflen - 1; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_SET_SLAVE_ADDR: + break; + + case PROC_HW_RESET: + snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + break; + + case PROC_SET_BOOT_MODE: + FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); + if (0 == writebuf[1]) { + ts_data->fw_is_running = true; + } else { + ts_data->fw_is_running = false; + } + break; + + case PROC_ENTER_TEST_ENVIRONMENT: + FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); + if (0 == writebuf[1]) { + fts_enter_test_environment(0); + } else { + fts_enter_test_environment(1); + } + break; + + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +static int fts_debug_read( + char *page, char **start, off_t off, int count, int *eof, void *data ) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *readbuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + switch (proc->opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_read_reg(proc->cmd[0], &readbuf[0]); + if (ret < 0) { + FTS_ERROR("PROC_READ_REGISTER read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_REGISTER: + break; + + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_read(NULL, 0, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_DATA: + break; + + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + goto proc_read_err; + } + + ret = num_read_chars; +proc_read_err: + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} +#endif + +int fts_create_apk_debug_channel(struct fts_ts_data *ts_data) +{ + struct ftxxxx_proc *proc = &ts_data->proc; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc->proc_entry = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops); + if (NULL == proc->proc_entry) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } +#else + proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL); + if (NULL == proc->proc_entry) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } + proc->proc_entry->write_proc = fts_debug_write; + proc->proc_entry->read_proc = fts_debug_read; +#endif + + FTS_INFO("Create proc entry success!"); + return 0; +} + +void fts_release_apk_debug_channel(struct fts_ts_data *ts_data) +{ + struct ftxxxx_proc *proc = &ts_data->proc; + + if (proc->proc_entry) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc_remove(proc->proc_entry); +#else + remove_proc_entry(PROC_NAME, NULL); +#endif + } +} + +/************************************************************************ + * sysfs interface + ***********************************************************************/ +/* fts_hw_reset interface */ +static ssize_t fts_hw_reset_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_dev = fts_data->input_dev; + ssize_t count = 0; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + count = snprintf(buf, PAGE_SIZE, "hw reset executed\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_hw_reset_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_irq interface */ +static ssize_t fts_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct irq_desc *desc = irq_to_desc(fts_data->irq); + + count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth); + + return count; +} + +static ssize_t fts_irq_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_boot_mode interface */ +static ssize_t fts_bootmode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[EX-FUN]set to boot mode"); + fts_data->fw_is_running = false; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]set to fw mode"); + fts_data->fw_is_running = true; + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +static ssize_t fts_bootmode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct input_dev *input_dev = fts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (true == fts_data->fw_is_running) { + count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n"); + } else { + count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n"); + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_tpfwver interface */ +static ssize_t fts_fw_version_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + mutex_lock(&input_dev->mutex); + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + fts_read_reg(FTS_REG_FW_VER, &fwver); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + if ((fwver == 0xFF) || (fwver == 0x00)) + num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n"); + else + num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +static ssize_t fts_fw_version_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_rw_reg */ +static ssize_t fts_tprwreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (rw_op.len < 0) { + count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n"); + } else if (rw_op.len == 1) { + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } else { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } + } else { + if (RWREG_OP_READ == rw_op.type) { + count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len); + count += snprintf(buf + count, PAGE_SIZE, "Result: "); + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res); + } else { + if (rw_op.opbuf) { + for (i = 0; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + } + } else { + count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1); + count += snprintf(buf + count, PAGE_SIZE, "Write Data: "); + if (rw_op.opbuf) { + for (i = 1; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res); + } else { + count += snprintf(buf + count, PAGE_SIZE, "Result: success\n"); + } + } + /*if (rw_op.opbuf) { + * kfree(rw_op.opbuf); + * rw_op.opbuf = NULL; + *} + */ + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static int shex_to_int(const char *hex_buf, int size) +{ + int i; + int base = 1; + int value = 0; + char single; + + for (i = size - 1; i >= 0; i--) { + single = hex_buf[i]; + + if ((single >= '0') && (single <= '9')) { + value += (single - '0') * base; + } else if ((single >= 'a') && (single <= 'z')) { + value += (single - 'a' + 10) * base; + } else if ((single >= 'A') && (single <= 'Z')) { + value += (single - 'A' + 10) * base; + } else { + return -EINVAL; + } + + base *= 16; + } + + return value; +} + + +static u8 shex_to_u8(const char *hex_buf, int size) +{ + return (u8)shex_to_int(hex_buf, size); +} +/* + * Format buf: + * [0]: '0' write, '1' read(reserved) + * [1-2]: addr, hex + * [3-4]: length, hex + * [5-6]...[n-(n+1)]: data, hex + */ +static int fts_parse_buf(const char *buf, size_t cmd_len) +{ + int length; + int i; + char *tmpbuf; + + rw_op.reg = shex_to_u8(buf + 1, 2); + length = shex_to_int(buf + 3, 2); + + if (buf[0] == '1') { + rw_op.len = length; + rw_op.type = RWREG_OP_READ; + FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len); + } else { + if (cmd_len < (length * 2 + 5)) { + pr_err("data invalided!\n"); + return -EINVAL; + } + FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length); + + /* first byte is the register addr */ + rw_op.type = RWREG_OP_WRITE; + rw_op.len = length + 1; + } + + if (rw_op.len > 0) { + tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("allocate memory failed!\n"); + return -ENOMEM; + } + + if (RWREG_OP_WRITE == rw_op.type) { + tmpbuf[0] = rw_op.reg & 0xFF; + FTS_DEBUG("write buffer: "); + for (i = 1; i < rw_op.len; i++) { + tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2); + FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF); + } + } + rw_op.opbuf = tmpbuf; + } + + return rw_op.len; +} + +static ssize_t fts_tprwreg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + ssize_t cmd_length = 0; + + mutex_lock(&input_dev->mutex); + cmd_length = count - 1; + + if (rw_op.opbuf) { + kfree(rw_op.opbuf); + rw_op.opbuf = NULL; + } + + FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf); + /* compatible old ops */ + if (2 == cmd_length) { + rw_op.type = RWREG_OP_READ; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + } else if (4 == cmd_length) { + rw_op.type = RWREG_OP_WRITE; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + rw_op.val = shex_to_int(buf + 2, 2); + } else if (cmd_length < 5) { + FTS_ERROR("Invalid cmd buffer"); + mutex_unlock(&input_dev->mutex); + return -EINVAL; + } else { + rw_op.len = fts_parse_buf(buf, cmd_length); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + if (rw_op.len < 0) { + FTS_ERROR("cmd buffer error!"); + goto exit; + } + + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + rw_op.res = fts_read_reg(reg, &val); + rw_op.val = val; + } else { + char reg; + reg = rw_op.reg & 0xFF; + + rw_op.res = fts_read(®, 1, rw_op.opbuf, rw_op.len); + } + + if (rw_op.res < 0) { + FTS_ERROR("Could not read 0x%02x", rw_op.reg); + } else { + FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len); + rw_op.res = 0; + } + + } else { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + val = rw_op.val & 0xFF; + rw_op.res = fts_write_reg(reg, val); + } else { + rw_op.res = fts_write(rw_op.opbuf, rw_op.len); + } + if (rw_op.res < 0) { + FTS_ERROR("Could not write 0x%02x", rw_op.reg); + + } else { + FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len); + rw_op.res = 0; + } + } + +exit: +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_driver_info interface */ +static ssize_t fts_driverinfo_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = fts_data; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += scnprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", FTS_DRIVER_VERSION); + + count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n", + pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max); + + count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", pdata->max_touch_number); + + count += scnprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += scnprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + if (ts_data->bus_type == BUS_TYPE_I2C) + count += scnprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + else + count += scnprintf(buf + count, PAGE_SIZE, + "BUS:%s,mode:%d,max_freq:%d\n", "SPI", + ts_data->spi->mode, ts_data->spi->max_speed_hz); + + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_driverinfo_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_dump_reg interface */ +static ssize_t fts_dumpreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + fts_read_reg(FTS_REG_POWER_MODE, &val); + count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val); + + fts_read_reg(FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_LIC_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val); + + fts_read_reg(FTS_REG_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val); + + fts_read_reg(FTS_REG_LCD_BUSY_NUM, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val); + + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val); + + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val); + + fts_read_reg(FTS_REG_INT_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val); + + fts_read_reg(FTS_REG_FLOW_WORK_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_dumpreg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_dump_reg interface */ +static ssize_t fts_tpbuf_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + int i = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n"); + for (i = 0; i < fts_data->pnt_buf_size; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02x ", + fts_data->point_buf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_tpbuf_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_log_level interface */ +static ssize_t fts_log_level_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n", + fts_data->log_level); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_log_level_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct input_dev *input_dev = fts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("log level:%d->%d", fts_data->log_level, value); + fts_data->log_level = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + +static ssize_t trusted_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *info = fts_data; + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_enabled)); +} + +static ssize_t trusted_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *info = fts_data; + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + +#ifdef CONFIG_ARCH_QTI_VM + err = fts_ts_handle_trusted_touch_tvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in tvm\n"); + return -EINVAL; + } +#else + err = fts_ts_handle_trusted_touch_pvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in pvm\n"); + return -EINVAL; + } +#endif + err = count; + return err; +} + +static ssize_t trusted_touch_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *info = fts_data; + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_event)); +} + +static ssize_t trusted_touch_event_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *info = fts_data; + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + + if (value) + return -EIO; + + atomic_set(&info->trusted_touch_event, value); + + return count; +} + +static ssize_t trusted_touch_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *info = fts_data; + + return scnprintf(buf, PAGE_SIZE, "%s", info->vm_info->trusted_touch_type); +} + +#endif + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR_RW(fts_fw_version); + +/* read and write register(s) +* All data type is **HEX** +* Single Byte: +* read: echo 88 > rw_reg ---read register 0x88 +* write: echo 8807 > rw_reg ---write 0x07 into register 0x88 +* Multi-bytes: +* [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex] +* rw-flag: 0, write; 1, read +* read: echo 10005 > rw_reg ---read reg 0x00-0x05 +* write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05 +* Get result: +* cat rw_reg +*/ +static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store); +static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store); +static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store); +static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store); +static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store); +static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store); +static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store); +static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store); +#ifdef CONFIG_FTS_TRUSTED_TOUCH +static DEVICE_ATTR_RW(trusted_touch_enable); +static DEVICE_ATTR_RW(trusted_touch_event); +static DEVICE_ATTR_RO(trusted_touch_type); +#endif + +/* add your attr in here*/ +static struct attribute *fts_attributes[] = { + &dev_attr_fts_fw_version.attr, + &dev_attr_fts_rw_reg.attr, + &dev_attr_fts_dump_reg.attr, + &dev_attr_fts_driver_info.attr, + &dev_attr_fts_hw_reset.attr, + &dev_attr_fts_irq.attr, + &dev_attr_fts_boot_mode.attr, + &dev_attr_fts_touch_point.attr, + &dev_attr_fts_log_level.attr, +#ifdef CONFIG_FTS_TRUSTED_TOUCH + &dev_attr_trusted_touch_enable.attr, + &dev_attr_trusted_touch_event.attr, + &dev_attr_trusted_touch_type.attr, +#endif + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +int fts_create_sysfs(struct fts_ts_data *ts_data) +{ + int ret = 0; + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group); + if (ret) { + FTS_ERROR("[EX]: sysfs_create_group() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return -ENOMEM; + } else { + FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); + } + + return ret; +} + +int fts_remove_sysfs(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return 0; +} diff --git a/focaltech_touch/focaltech_ex_mode.c b/focaltech_touch/focaltech_ex_mode.c new file mode 100644 index 0000000000..03cec68d1a --- /dev/null +++ b/focaltech_touch/focaltech_ex_mode.c @@ -0,0 +1,359 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_ex_mode.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-31 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* 2.Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* 3.Private enumerations, structures and unions using typedef +*****************************************************************************/ +enum _ex_mode { + MODE_GLOVE = 0, + MODE_COVER, + MODE_CHARGER, + REPORT_RATE, +}; + +/***************************************************************************** +* 4.Static variables +*****************************************************************************/ + +/***************************************************************************** +* 5.Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* 6.Static function prototypes +*******************************************************************************/ +static int fts_ex_mode_switch(enum _ex_mode mode, u8 value) +{ + int ret = 0; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, value > 0 ? 1 : 0); + if (ret < 0) + FTS_ERROR("MODE_GLOVE switch to %d fail", value); + break; + + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, value > 0 ? 1 : 0); + if (ret < 0) + FTS_ERROR("MODE_COVER switch to %d fail", value); + break; + + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, value > 0 ? 1 : 0); + if (ret < 0) + FTS_ERROR("MODE_CHARGER switch to %d fail", value); + break; + + case REPORT_RATE: + ret = fts_write_reg(FTS_REG_REPORT_RATE, value); + if (ret < 0) + FTS_ERROR("REPORT_RATE switch to %d fail", value); + break; + + default: + FTS_ERROR("mode(%d) unsupport", mode); + ret = -EINVAL; + break; + } + + return ret; +} + +static ssize_t fts_glove_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val); + count = scnprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Glove Reg(0xC0):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_glove_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->glove_mode) { + FTS_DEBUG("enter glove mode"); + ret = fts_ex_mode_switch(MODE_GLOVE, ENABLE); + if (ret >= 0) { + ts_data->glove_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->glove_mode) { + FTS_DEBUG("exit glove mode"); + ret = fts_ex_mode_switch(MODE_GLOVE, DISABLE); + if (ret >= 0) { + ts_data->glove_mode = DISABLE; + } + } + } + + FTS_DEBUG("glove mode:%d", ts_data->glove_mode); + return count; +} + + +static ssize_t fts_cover_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_COVER_MODE_EN, &val); + count = scnprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Cover Reg(0xC1):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_cover_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->cover_mode) { + FTS_DEBUG("enter cover mode"); + ret = fts_ex_mode_switch(MODE_COVER, ENABLE); + if (ret >= 0) { + ts_data->cover_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->cover_mode) { + FTS_DEBUG("exit cover mode"); + ret = fts_ex_mode_switch(MODE_COVER, DISABLE); + if (ret >= 0) { + ts_data->cover_mode = DISABLE; + } + } + } + + FTS_DEBUG("cover mode:%d", ts_data->cover_mode); + return count; +} + +static ssize_t fts_charger_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count = scnprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Charger Reg(0x8B):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_charger_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->charger_mode) { + FTS_DEBUG("enter charger mode"); + ret = fts_ex_mode_switch(MODE_CHARGER, ENABLE); + if (ret >= 0) { + ts_data->charger_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->charger_mode) { + FTS_DEBUG("exit charger mode"); + ret = fts_ex_mode_switch(MODE_CHARGER, DISABLE); + if (ret >= 0) { + ts_data->charger_mode = DISABLE; + } + } + } + + FTS_DEBUG("charger mode:%d", ts_data->glove_mode); + return count; +} + +static ssize_t fts_report_rate_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_REPORT_RATE, &val); + count = scnprintf(buf + count, PAGE_SIZE, + "Report Rate:%d\n", ts_data->report_rate); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Report Rate Reg(0x88):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_report_rate_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + int rate; + + ret = kstrtoint(buf, 16, &rate); + if (ret) + return ret; + + if (rate != ts_data->report_rate) { + ret = fts_ex_mode_switch(REPORT_RATE, (u8)rate); + if (ret >= 0) + ts_data->report_rate = rate; + } + + FTS_DEBUG("report rate:%d", ts_data->report_rate); + return count; +} + + +/* read and write charger mode + * read example: cat fts_glove_mode ---read glove mode + * write example:echo 1 > fts_glove_mode ---write glove mode to 01 + */ +static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR, + fts_glove_mode_show, fts_glove_mode_store); + +static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR, + fts_cover_mode_show, fts_cover_mode_store); + +static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR, + fts_charger_mode_show, fts_charger_mode_store); + +static DEVICE_ATTR_RW(fts_report_rate); + +static struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.attr, + &dev_attr_fts_report_rate.attr, + NULL, +}; + +static struct attribute_group fts_touch_mode_group = { + .attrs = fts_touch_mode_attrs, +}; + +int fts_ex_mode_recovery(struct fts_ts_data *ts_data) +{ + if (ts_data->glove_mode) { + fts_ex_mode_switch(MODE_GLOVE, ENABLE); + } + + if (ts_data->cover_mode) { + fts_ex_mode_switch(MODE_COVER, ENABLE); + } + + if (ts_data->charger_mode) { + fts_ex_mode_switch(MODE_CHARGER, ENABLE); + } + + if (ts_data->report_rate > 0) + fts_ex_mode_switch(REPORT_RATE, ts_data->report_rate); + + return 0; +} + +int fts_ex_mode_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + ts_data->glove_mode = DISABLE; + ts_data->cover_mode = DISABLE; + ts_data->charger_mode = DISABLE; + ts_data->report_rate = 0; + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group); + if (ret < 0) { + FTS_ERROR("create sysfs(ex_mode) fail"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group); + return ret; + } else { + FTS_DEBUG("create sysfs(ex_mode) succeedfully"); + } + + return 0; +} + +int fts_ex_mode_exit(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group); + return 0; +} diff --git a/focaltech_touch/focaltech_flash.c b/focaltech_touch/focaltech_flash.c new file mode 100644 index 0000000000..c1e54b10ff --- /dev/null +++ b/focaltech_touch/focaltech_flash.c @@ -0,0 +1,2079 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_flash.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_FW_REQUEST_SUPPORT 1 +/* Example: focaltech_ts_fw_tianma.bin */ +#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[1] = { +0, +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, +}; + +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5452, + &upgrade_func_ft5652, +}; + +struct fts_upgrade *fwupgrade; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static bool fts_fwupg_check_state( + struct fts_upgrade *upg, enum FW_STATUS rstate); + +/************************************************************************ +* Name: fts_fwupg_get_boot_state +* Brief: read boot id(rom/pram/bootloader), confirm boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_fwupg_get_boot_state( + struct fts_upgrade *upg, + enum FW_STATUS *fw_sts) +{ + int ret = 0; + u8 cmd[4] = { 0 }; + u32 cmd_len = 0; + u8 val[2] = { 0 }; + struct ft_chip_t *ids = NULL; + + FTS_INFO("**********read boot id**********"); + if ((!upg) || (!upg->func) || (!upg->ts_data) || (!fw_sts)) { + FTS_ERROR("upg/func/ts_data/fw_sts is null"); + return -EINVAL; + } + + if (upg->func->hid_supported) + fts_hid2std(); + + cmd[0] = FTS_CMD_START1; + cmd[1] = FTS_CMD_START2; + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) + cmd_len = 1; + else + cmd_len = 2; + ret = fts_write(cmd, cmd_len); + if (ret < 0) { + FTS_ERROR("write 55 cmd fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + cmd[0] = FTS_CMD_READ_ID; + cmd[1] = cmd[2] = cmd[3] = 0x00; + if (fts_data->ic_info.is_incell || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_read(cmd, cmd_len, val, 2); + if (ret < 0) { + FTS_ERROR("write 90 cmd fail"); + return ret; + } + FTS_INFO("read boot id:0x%02x%02x", val[0], val[1]); + + ids = &upg->ts_data->ic_info.ids; + if ((val[0] == ids->rom_idh) && (val[1] == ids->rom_idl)) { + FTS_INFO("tp run in romboot"); + *fw_sts = FTS_RUN_IN_ROM; + } else if ((val[0] == ids->pb_idh) && (val[1] == ids->pb_idl)) { + FTS_INFO("tp run in pramboot"); + *fw_sts = FTS_RUN_IN_PRAM; + } else if ((val[0] == ids->bl_idh) && (val[1] == ids->bl_idl)) { + FTS_INFO("tp run in bootloader"); + *fw_sts = FTS_RUN_IN_BOOTLOADER; + } + + return 0; +} + +static int fts_fwupg_reset_to_boot(struct fts_upgrade *upg) +{ + int ret = 0; + u8 reg = FTS_REG_UPGRADE; + + FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment"); + if (upg && upg->func && upg->func->is_reset_register_BC) { + reg = FTS_REG_UPGRADE2; + } + + ret = fts_write_reg(reg, FTS_UPGRADE_AA); + if (ret < 0) { + FTS_ERROR("write FC=0xAA fail"); + return ret; + } + msleep(FTS_DELAY_UPGRADE_AA); + + ret = fts_write_reg(reg, FTS_UPGRADE_55); + if (ret < 0) { + FTS_ERROR("write FC=0x55 fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_romboot +* Brief: reset to romboot, to load pramboot +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_fwupg_reset_to_romboot(struct fts_upgrade *upg) +{ + int ret = 0; + int i = 0; + u8 cmd = FTS_CMD_RESET; + enum FW_STATUS state = FTS_RUN_IN_ERROR; + + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + mdelay(10); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(upg, &state); + if (FTS_RUN_IN_ROM == state) + break; + mdelay(5); + } + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("reset to romboot fail"); + return -EIO; + } + + return 0; +} + +static u16 fts_crc16_calc_host(u8 *pbuf, u32 length) +{ + u16 ecc = 0; + u32 i = 0; + u32 j = 0; + + for ( i = 0; i < length; i += 2 ) { + ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF); + else + ecc >>= 1; + } + } + + return ecc; +} + +static u16 fts_pram_ecc_calc_host(u8 *pbuf, u32 length) +{ + return fts_crc16_calc_host(pbuf, length); +} + +static int fts_pram_ecc_cal_algo( + struct fts_upgrade *upg, + u32 start_addr, + u32 ecc_length) +{ + int ret = 0; + int i = 0; + int ecc = 0; + u8 val[2] = { 0 }; + u8 tmp = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_INFO("read out pramboot checksum"); + if ((!upg) || (!upg->func)) { + FTS_ERROR("upg/func is null"); + return -EINVAL; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(start_addr); + cmd[2] = BYTE_OFF_8(start_addr); + cmd[3] = BYTE_OFF_0(start_addr); + cmd[4] = BYTE_OFF_16(ecc_length); + cmd[5] = BYTE_OFF_8(ecc_length); + cmd[6] = BYTE_OFF_0(ecc_length); + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("write pramboot ecc cal cmd fail"); + return ret; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + msleep(1); + ret = fts_read(cmd, 1, val, 1); + if (ret < 0) { + FTS_ERROR("ecc_finish read cmd fail"); + return ret; + } + if (upg->func->new_return_value_from_ic || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) { + tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5; + } else { + tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_00; + } + if (tmp == val[0]) + break; + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish fail"); + return -EIO; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, val, 2); + if (ret < 0) { + FTS_ERROR("read pramboot ecc fail"); + return ret; + } + + ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF; + return ecc; +} + +static int fts_pram_ecc_cal_xor(void) +{ + int ret = 0; + u8 reg_val = 0; + + FTS_INFO("read out pramboot checksum"); + + ret = fts_read_reg(FTS_ROMBOOT_CMD_ECC, ®_val); + if (ret < 0) { + FTS_ERROR("read pramboot ecc fail"); + return ret; + } + + return (int)reg_val; +} + +static int fts_pram_ecc_cal(struct fts_upgrade *upg, u32 saddr, u32 len) +{ + if ((!upg) || (!upg->func)) { + FTS_ERROR("upg/func is null"); + return -EINVAL; + } + + if ((upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + return fts_pram_ecc_cal_algo(upg, saddr, len); + + return fts_pram_ecc_cal_xor(); +} + +static int fts_pram_write_buf(struct fts_upgrade *upg, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number; + u32 packet_len = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_tmp = 0; + int ecc_in_host = 0; + u32 cmdlen = 0; + + FTS_INFO("write pramboot to pram"); + if ((!upg) || (!upg->func) || !buf) { + FTS_ERROR("upg/func/buf is null"); + return -EINVAL; + } + + FTS_INFO("pramboot len=%d", len); + if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) { + FTS_ERROR("pramboot length(%d) fail", len); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) { + packet_buf[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + ret = fts_write(packet_buf, FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN); + if (ret < 0) { + FTS_ERROR("pramboot set write address(%d) fail", i); + return ret; + } + + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + cmdlen = 1; + } else { + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + cmdlen = 6; + } + + for (j = 0; j < packet_len; j++) { + packet_buf[cmdlen + j] = buf[offset + j]; + if (upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_XOR) + ecc_tmp ^= packet_buf[cmdlen + j]; + } + + ret = fts_write(packet_buf, packet_len + cmdlen); + if (ret < 0) { + FTS_ERROR("pramboot write data(%d) fail", i); + return ret; + } + } + + if ((upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len); + else + ecc_in_host = (int)ecc_tmp; + + return ecc_in_host; +} + +static int fts_pram_start(void) +{ + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + int ret = 0; + + FTS_INFO("remap to start pramboot"); + + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + msleep(FTS_DELAY_PRAMBOOT_START); + + return 0; +} + +static int fts_pram_write_remap(struct fts_upgrade *upg) +{ + int ret = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + u8 *pb_buf = NULL; + u32 pb_len = 0; + + FTS_INFO("write pram and remap"); + if (!upg || !upg->func || !upg->func->pramboot) { + FTS_ERROR("upg/func/pramboot is null"); + return -EINVAL; + } + + if (upg->func->pb_length < FTS_MIN_LEN) { + FTS_ERROR("pramboot length(%d) fail", upg->func->pb_length); + return -EINVAL; + } + + pb_buf = upg->func->pramboot; + pb_len = upg->func->pb_length; + + /* write pramboot to pram */ + ecc_in_host = fts_pram_write_buf(upg, pb_buf, pb_len); + if (ecc_in_host < 0) { + FTS_ERROR( "write pramboot fail"); + return ecc_in_host; + } + + /* read out checksum */ + ecc_in_tp = fts_pram_ecc_cal(upg, 0, pb_len); + if (ecc_in_tp < 0) { + FTS_ERROR( "read pramboot ecc fail"); + return ecc_in_tp; + } + + FTS_INFO("pram ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + /* pramboot checksum != fw checksum, upgrade fail */ + if (ecc_in_host != ecc_in_tp) { + FTS_ERROR("pramboot ecc check fail"); + return -EIO; + } + + /*start pram*/ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + return 0; +} + +static int fts_pram_init(void) +{ + int ret = 0; + u8 reg_val = 0; + u8 wbuf[3] = { 0 }; + + FTS_INFO("pramboot initialization"); + + /* read flash ID */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + ret = fts_read(wbuf, 1, ®_val, 1); + if (ret < 0) { + FTS_ERROR("read flash type fail"); + return ret; + } + + /* set flash clk */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + wbuf[1] = reg_val; + wbuf[2] = 0x00; + ret = fts_write(wbuf, 3); + if (ret < 0) { + FTS_ERROR("write flash type fail"); + return ret; + } + + return 0; +} + +static int fts_pram_write_init(struct fts_upgrade *upg) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + + FTS_INFO("**********pram write and init**********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + if (!upg->func->pramboot_supported) { + FTS_ERROR("ic not support pram"); + return -EINVAL; + } + + FTS_DEBUG("check whether tp is in romboot or not "); + /* need reset to romboot when non-romboot state */ + ret = fts_fwupg_get_boot_state(upg, &status); + if (status != FTS_RUN_IN_ROM) { + if (FTS_RUN_IN_PRAM == status) { + FTS_INFO("tp is in pramboot, need send reset cmd before upgrade"); + ret = fts_pram_init(); + if (ret < 0) { + FTS_ERROR("pramboot(before) init fail"); + return ret; + } + } + + FTS_INFO("tp isn't in romboot, need send reset to romboot"); + ret = fts_fwupg_reset_to_romboot(upg); + if (ret < 0) { + FTS_ERROR("reset to romboot fail"); + return ret; + } + } + + /* check the length of the pramboot */ + ret = fts_pram_write_remap(upg); + if (ret < 0) { + FTS_ERROR("pram write fail, ret=%d", ret); + return ret; + } + + FTS_DEBUG("after write pramboot, confirm run in pramboot"); + state = fts_fwupg_check_state(upg, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("not in pramboot"); + return -EIO; + } + + ret = fts_pram_init(); + if (ret < 0) { + FTS_ERROR("pramboot init fail"); + return ret; + } + + return 0; +} + +static bool fts_fwupg_check_fw_valid(void) +{ + int ret = 0; + + ret = fts_wait_tp_to_valid(); + if (ret < 0) { + FTS_INFO("tp fw invaild"); + return false; + } + + FTS_INFO("tp fw vaild"); + return true; +} + +/************************************************************************ +* Name: fts_fwupg_check_state +* Brief: confirm tp run in which mode: romboot/pramboot/bootloader +* Input: +* Output: +* Return: return true if state is match, otherwise return false +***********************************************************************/ +static bool fts_fwupg_check_state( + struct fts_upgrade *upg, enum FW_STATUS rstate) +{ + int ret = 0; + int i = 0; + enum FW_STATUS cstate = FTS_RUN_IN_ERROR; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(upg, &cstate); + /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */ + if (cstate == rstate) + return true; + msleep(FTS_DELAY_READ_ID); + } + + return false; +} + +/************************************************************************ +* Name: fts_fwupg_reset_in_boot +* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_in_boot(void) +{ + int ret = 0; + u8 cmd = FTS_CMD_RESET; + + FTS_INFO("reset in boot environment"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +static int fts_fwupg_enter_into_boot_old(struct fts_upgrade *upg) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + + FTS_INFO("***********enter into pramboot/bootloader***********"); + if ((!upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(); + if (fwvalid) { + ret = fts_fwupg_reset_to_boot(upg); + if (ret < 0) { + FTS_ERROR("enter into romboot/bootloader fail"); + return ret; + } + } else if (upg->func->read_boot_id_need_reset) { + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) { + FTS_ERROR("reset before read boot id when fw invalid fail"); + return ret; + } + } + + if (upg->func->pramboot_supported) { + FTS_INFO("pram supported, write pramboot and init"); + /* pramboot */ + ret = fts_pram_write_init(upg); + if (ret < 0) { + FTS_ERROR("pram write_init fail"); + return ret; + } + } else { + FTS_DEBUG("pram not supported, confirm in bootloader"); + /* bootloader */ + state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("fw not in bootloader, fail"); + return -EIO; + } + } + + return 0; +} + +static int fts_hardware_reset_to_boot(void) +{ + u8 cmd[2] = {0}; + int i = 0; + u8 chip_id[2] = { 0 }; + u8 id_cmd[4] = { 0 }; + int ret = 0; + + fts_reset_proc(0); + FTS_INFO("reset finish!!"); + usleep_range(10000, 11000); + + for (i = 0; i < 20; i++) { + FTS_INFO("start send 55 AA"); + cmd[0] = 0x55; + cmd[1] = 0xAA; + ret = fts_write(cmd, 2); + if (ret < 0) + FTS_ERROR("send 55 AA cmd fail"); + + usleep_range(8000, 9000); + FTS_INFO("start send 90 00 00 00"); + id_cmd[0] = 0x90; + id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; + ret = fts_read(id_cmd, 1, chip_id, 2); + if (ret < 0) + FTS_ERROR("send 90 cmd fail"); + + FTS_INFO("read boot id = %x--%x", chip_id[0], chip_id[1]); + if (chip_id[1] == 0xB2) + break; + + usleep_range(1000, 2000); + + if (i == 10) { + fts_reset_proc(0); + FTS_INFO("reset again finish!!"); + usleep_range(10000, 11000); + } + + } + + if (i >= 20) { + FTS_INFO("enter into romboot fail"); + return -EIO; + } + return 0; + +} + +static int fts_fwupg_enter_into_boot_new(struct fts_upgrade *upg) +{ + int ret = 0; + bool state = false; + + FTS_INFO("***********enter into pramboot/bootloader***********"); + if ((!upg) || (upg->func == NULL)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + ret = fts_hardware_reset_to_boot(); + if (!ret) { + FTS_INFO("tp run in bootloader success"); + return ret; + } + + if (upg->func->pramboot_supported) { + FTS_INFO("pram supported, write pramboot and init"); + /* pramboot */ + if (upg->func->write_pramboot_private) + ret = upg->func->write_pramboot_private(); + else + ret = fts_pram_write_init(upg); + + if (ret < 0) { + FTS_ERROR("pram write_init fail"); + return ret; + } + } else { + FTS_DEBUG("pram not supported, confirm in bootloader"); + /* bootloader */ + state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("fw not in bootloader, fail"); + return -EIO; + } + } + + return 0; +} + +int fts_fwupg_enter_into_boot(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) + return fts_fwupg_enter_into_boot_new(upg); + + return fts_fwupg_enter_into_boot_old(upg); +} + +/************************************************************************ + * Name: fts_fwupg_check_flash_status + * Brief: read status from tp + * Input: flash_status: correct value from tp + * retries: read retry times + * retries_delay: retry delay + * Output: + * Return: return true if flash status check pass, otherwise return false + ***********************************************************************/ +static bool fts_fwupg_check_flash_status( + u16 flash_status, + int retries, + int retries_delay) +{ + int ret = 0; + int i = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + + for (i = 0; i < retries; i++) { + cmd = FTS_CMD_FLASH_STATUS; + ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (flash_status == read_status) { + /* FTS_DEBUG("[UPGRADE]flash status ok"); */ + return true; + } + /* FTS_DEBUG("flash status fail,ok:%04x read:%04x, retries:%d", flash_status, read_status, i); */ + msleep(retries_delay); + } + + return false; +} + +/************************************************************************ + * Name: fts_fwupg_erase + * Brief: erase flash area + * Input: delay - delay after erase + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_erase(u32 delay) +{ + int ret = 0; + u8 cmd = 0; + bool flag = false; + + FTS_INFO("**********erase now**********"); + + /*send to erase flash*/ + cmd = FTS_CMD_ERASE_APP; + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("erase cmd fail"); + return ret; + } + msleep(delay); + + /* read status 0xF0AA: success */ + flag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ERASE_OK, + FTS_RETRIES_REASE, + FTS_RETRIES_DELAY_REASE); + if (!flag) { + FTS_ERROR("ecc flash status check fail"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * Name: fts_fwupg_ecc_cal + * Brief: calculate and get ecc from tp + * Input: saddr - start address need calculate ecc + * len - length need calculate ecc + * Output: + * Return: return data ecc of tp if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_ecc_cal(u32 saddr, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 cmdlen = FTS_CMD_ECC_CAL_LEN; + u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 }; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + int ecc = 0; + int ecc_len = 0; + u32 packet_num = 0; + u32 packet_len = 0; + u32 remainder = 0; + u32 addr = 0; + u32 offset = 0; + bool bflag = false; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO( "**********read out checksum**********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_write(wbuf, 1); + if (ret < 0) { + FTS_ERROR("ecc init cmd write fail"); + return ret; + } + + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) { + packet_num = 1; + remainder = 0; + packet_len = len; + } else { + packet_num = len / FTS_MAX_LEN_ECC_CALC; + remainder = len % FTS_MAX_LEN_ECC_CALC; + if (remainder) + packet_num++; + packet_len = FTS_MAX_LEN_ECC_CALC; + } + FTS_INFO("ecc calc num:%d, remainder:%d", packet_num, remainder); + + /* send commond to start checksum */ + wbuf[0] = FTS_CMD_ECC_CAL; + for (i = 0; i < packet_num; i++) { + offset = FTS_MAX_LEN_ECC_CALC * i; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) { + wbuf[4] = BYTE_OFF_16(packet_len); + wbuf[5] = BYTE_OFF_8(packet_len); + wbuf[6] = BYTE_OFF_0(packet_len); + cmdlen = FTS_CMD_ECC_CAL_LEN; + } else { + if ((i == (packet_num - 1)) && remainder) + packet_len = remainder; + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + cmdlen = FTS_CMD_ECC_CAL_LEN - 1; + } + + FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len); + ret = fts_write(wbuf, cmdlen); + if (ret < 0) { + FTS_ERROR("ecc calc cmd write fail"); + return ret; + } + + msleep(packet_len / 256); + + /* read status if check sum is finished */ + bflag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ECC_OK, + FTS_RETRIES_ECC_CAL, + FTS_RETRIES_DELAY_ECC_CAL); + if (!bflag) { + FTS_ERROR("ecc flash status read fail"); + return -EIO; + } + } + + ecc_len = 1; + if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc_len = 2; + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_read(wbuf, 1, val, ecc_len); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + + if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc = (int)((u16)(val[0] << 8) + val[1]); + else + ecc = (int)val[0]; + + return ecc; +} + +/************************************************************************ + * Name: fts_flash_write_buf + * Brief: write buf data to flash address + * Input: saddr - start address data write to flash + * buf - data buffer + * len - data length + * delay - delay after write + * Output: + * Return: return data ecc of host if success, otherwise return error code + ***********************************************************************/ +int fts_flash_write_buf( + u32 saddr, + u8 *buf, + u32 len, + u32 delay) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u32 cmdlen = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_tmp = 0; + int ecc_in_host = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + u16 wr_ok = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO( "**********write data to flash**********"); + if ((!upg) || (!upg->func || !buf || !len)) { + FTS_ERROR("upgrade/func/buf/len is invalid"); + return -EINVAL; + } + + FTS_INFO("data buf start addr=0x%x, len=0x%x", saddr, len); + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) { + packet_buf[0] = FTS_CMD_SET_WFLASH_ADDR; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + ret = fts_write(packet_buf, FTS_LEN_SET_ADDR); + if (ret < 0) { + FTS_ERROR("set flash address fail"); + return ret; + } + + packet_buf[0] = FTS_CMD_WRITE; + cmdlen = 1; + } else { + packet_buf[0] = FTS_CMD_WRITE; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + cmdlen = 6; + } + + for (j = 0; j < packet_len; j++) { + packet_buf[cmdlen + j] = buf[offset + j]; + ecc_tmp ^= packet_buf[cmdlen + j]; + } + + ret = fts_write(packet_buf, packet_len + cmdlen); + if (ret < 0) { + FTS_ERROR("app write fail"); + return ret; + } + mdelay(delay); + + /* read status */ + wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len; + for (j = 0; j < FTS_RETRIES_WRITE; j++) { + cmd = FTS_CMD_FLASH_STATUS; + ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + /* FTS_INFO("%x %x", wr_ok, read_status); */ + if (read_status == wr_ok) + break; + + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + ecc_in_host = (int)ecc_tmp; + if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc_in_host = (int)fts_crc16_calc_host(buf, len); + + return ecc_in_host; +} + +/************************************************************************ + * Name: fts_flash_read_buf + * Brief: read data from flash + * Input: saddr - start address data write to flash + * buf - buffer to store data read from flash + * len - read length + * Output: + * Return: return 0 if success, otherwise return error code + * + * Warning: can't call this function directly, need call in boot environment + ***********************************************************************/ +static int fts_flash_read_buf(u32 saddr, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u8 wbuf[FTS_CMD_READ_LEN_SPI] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !buf || !len) { + FTS_ERROR("upgrade/buf is NULL or len is 0"); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) { + packet_number++; + } + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_INFO("read packet_number:%d, remainder:%d", packet_number, remainder); + + + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + if (upg->ts_data->bus_type == BUS_TYPE_I2C) { + wbuf[0] = FTS_CMD_READ; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + ret = fts_write(wbuf, FTS_CMD_READ_LEN); + if (ret < 0) { + FTS_ERROR("pram/bootloader write 03 command fail"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */ + ret = fts_read(NULL, 0, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03 command fail"); + return ret; + } + } else if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) { + wbuf[0] = FTS_CMD_SET_RFLASH_ADDR; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + ret = fts_write(wbuf, FTS_LEN_SET_ADDR); + if (ret < 0) { + FTS_ERROR("set flash address fail"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); + wbuf[0] = FTS_CMD_READ; + ret = fts_read(wbuf, 1, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03(SPI_V2) command fail"); + return ret; + } + } else if (upg->ts_data->bus_type == BUS_TYPE_SPI) { + wbuf[0] = FTS_CMD_READ; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + ret = fts_read(wbuf, FTS_CMD_READ_LEN_SPI, + buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03(SPI) command fail"); + return ret; + } + } + } + + return 0; +} + +/************************************************************************ + * Name: fts_flash_read + * Brief: + * Input: addr - address of flash + * len - length of read + * Output: buf - data read from flash + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_flash_read(u32 addr, u8 *buf, u32 len) +{ + int ret = 0; + + FTS_INFO("***********read flash***********"); + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("buf is NULL or len is 0"); + return -EINVAL; + } + + ret = fts_fwupg_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail"); + goto read_flash_err; + } + + ret = fts_flash_read_buf(addr, buf, len); + if (ret < 0) { + FTS_ERROR("read flash fail"); + goto read_flash_err; + } + +read_flash_err: + /* reset to normal boot */ + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + return ret; +} + +int fts_enter_test_environment(bool test_state) +{ + return 0; +} +#if FTS_AUTO_LIC_UPGRADE_EN +static int fts_lic_get_vid_in_tp(u16 *vid) +{ + int ret = 0; + u8 val[2] = { 0 }; + + if (NULL == vid) { + FTS_ERROR("vid is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_VENDOR_ID, &val[0]); + if (fts_data->ic_info.is_incell) + ret = fts_read_reg(FTS_REG_MODULE_ID, &val[1]); + if (ret < 0) { + FTS_ERROR("read vid from tp fail"); + return ret; + } + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_vid_in_host(struct fts_upgrade *upg, u16 *vid) +{ + u8 val[2] = { 0 }; + u8 *licbuf = NULL; + u32 conf_saddr = 0; + + if (!upg || !upg->func || !upg->lic || !vid) { + FTS_ERROR("upgrade/func/get_hlic_ver/lic/vid is null"); + return -EINVAL; + } + + if (upg->lic_length < FTS_MAX_LEN_SECTOR) { + FTS_ERROR("lic length(%x) fail", upg->lic_length); + return -EINVAL; + } + + licbuf = upg->lic; + conf_saddr = upg->func->fwcfgoff; + val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF]; + if (fts_data->ic_info.is_incell) + val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF]; + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_ver_in_tp(u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_LIC_VER, ver); + if (ret < 0) { + FTS_ERROR("read lcd initcode ver from tp fail"); + return ret; + } + + return 0; +} + +static int fts_lic_get_ver_in_host(struct fts_upgrade *upg, u8 *ver) +{ + int ret = 0; + + if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) { + FTS_ERROR("upgrade/func/get_hlic_ver/lic is null"); + return -EINVAL; + } + + ret = upg->func->get_hlic_ver(upg->lic); + if (ret < 0) { + FTS_ERROR("get host lcd initial code version fail"); + return ret; + } + + *ver = (u8)ret; + return ret; +} + +static bool fts_lic_need_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + u8 initcode_ver_in_tp = 0; + u8 initcode_ver_in_host = 0; + u16 vid_in_tp = 0; + u16 vid_in_host = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(); + if ( !fwvalid) { + FTS_INFO("fw is invalid, no upgrade lcd init code"); + return false; + } + + ret = fts_lic_get_vid_in_host(upg, &vid_in_host); + if (ret < 0) { + FTS_ERROR("vendor id in host invalid"); + return false; + } + + ret = fts_lic_get_vid_in_tp(&vid_in_tp); + if (ret < 0) { + FTS_ERROR("vendor id in tp invalid"); + return false; + } + + FTS_DEBUG("vid in tp:0x%04x, host:0x%04x", vid_in_tp, vid_in_host); + if (vid_in_tp != vid_in_host) { + FTS_INFO("vendor id in tp&host are different, no upgrade lic"); + return false; + } + + ret = fts_lic_get_ver_in_host(upg, &initcode_ver_in_host); + if (ret < 0) { + FTS_ERROR("init code in host invalid"); + return false; + } + + ret = fts_lic_get_ver_in_tp(&initcode_ver_in_tp); + if (ret < 0) { + FTS_ERROR("read reg0xE4 fail"); + return false; + } + + FTS_DEBUG("lcd initial code version in tp:%x, host:%x", + initcode_ver_in_tp, initcode_ver_in_host); + if (0xA5 == initcode_ver_in_tp) { + FTS_INFO("lcd init code ver is 0xA5, don't upgade init code"); + return false; + } else if (0xFF == initcode_ver_in_tp) { + FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code"); + return true; + } else if (initcode_ver_in_tp < initcode_ver_in_host) + return true; + else + return false; +} + +static int fts_lic_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + bool hlic_upgrade = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_INFO("lcd initial code auto upgrade function"); + if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) { + FTS_ERROR("lcd upgrade function is null"); + return -EINVAL; + } + + hlic_upgrade = fts_lic_need_upgrade(upg); + FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade); + if (hlic_upgrade) { + FTS_INFO("lcd initial code need upgrade, upgrade begin..."); + do { + FTS_INFO("lcd initial code upgrade times:%d", upgrade_count); + upgrade_count++; + + ret = upg->func->lic_upgrade(upg->lic, upg->lic_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + fts_lic_get_ver_in_tp(&ver); + FTS_INFO("success upgrade to lcd initcode ver:%02x", ver); + break; + } + } while (upgrade_count < 2); + } else { + FTS_INFO("lcd initial code don't need upgrade"); + } + + return ret; +} +#endif /* FTS_AUTO_LIC_UPGRADE_EN */ + + +static int fts_param_get_ver_in_tp(u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_IDE_PARA_VER_ID, ver); + if (ret < 0) { + FTS_ERROR("read fw param ver from tp fail"); + return ret; + } + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("param version in tp invalid"); + return -EIO; + } + + return 0; +} + +static int fts_param_get_ver_in_host(struct fts_upgrade *upg, u8 *ver) +{ + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgveroff) { + FTS_ERROR("fw len(%x) < paramcfg ver offset(%x)", + upg->fw_length, upg->func->paramcfgveroff); + return -EINVAL; + } + + FTS_INFO("fw paramcfg version offset:%x", upg->func->paramcfgveroff); + *ver = upg->fw[upg->func->paramcfgveroff]; + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("param version in host invalid"); + return -EIO; + } + + return 0; +} + +/* + * return: < 0 : error + * == 0: no ide + * == 1: ide + */ +static int fts_param_ide_in_host(struct fts_upgrade *upg) +{ + u32 off = 0; + + if ((!upg) || (!upg->func) || (!upg->fw)) { + FTS_ERROR("fts_data/upgrade/func/fw is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN) { + FTS_INFO("fw len(%x) < paramcfg offset(%x), no IDE", + upg->fw_length, upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN); + return 0; + } + + off = upg->func->paramcfgoff; + if (0 == memcmp(&upg->fw[off], FTS_FW_IDE_SIG, FTS_FW_IDE_SIG_LEN)) { + FTS_INFO("fw in host is IDE version"); + return 1; + } + + FTS_INFO("fw in host isn't IDE version"); + return 0; +} + +/* + * return: < 0 : error + * 0 : no ide + * 1 : ide + */ +static int fts_param_ide_in_tp(u8 *val) +{ + int ret = 0; + + ret = fts_read_reg(FTS_REG_IDE_PARA_STATUS, val); + if (ret < 0) { + FTS_ERROR("read IDE PARAM STATUS in tp fail"); + return ret; + } + + if ((*val != 0xFF) && ((*val & 0x80) == 0x80)) { + FTS_INFO("fw in tp is IDE version"); + return 1; + } + + FTS_INFO("fw in tp isn't IDE version"); + return 0; +} + +/************************************************************************ + * fts_param_need_upgrade - check fw paramcfg need upgrade or not + * + * Return: < 0 : error if paramcfg need upgrade + * 0 : no need upgrade + * 1 : need upgrade app + param + * 2 : need upgrade param + ***********************************************************************/ +static int fts_param_need_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + u8 val = 0; + int ide_in_host = 0; + int ide_in_tp = 0; + u8 ver_in_host = 0; + u8 ver_in_tp = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(); + if ( !fwvalid) { + FTS_INFO("fw is invalid, upgrade app+param"); + return 1; + } + + ide_in_host = fts_param_ide_in_host(upg); + if (ide_in_host < 0) { + FTS_INFO("fts_param_ide_in_host fail"); + return ide_in_host; + } + + ide_in_tp = fts_param_ide_in_tp(&val); + if (ide_in_tp < 0) { + FTS_INFO("fts_param_ide_in_tp fail"); + return ide_in_tp; + } + + if ((0 == ide_in_host) && (0 == ide_in_tp)) { + FTS_INFO("fw in host&tp are both no ide"); + return 0; + } else if (ide_in_host != ide_in_tp) { + FTS_INFO("fw in host&tp not equal, need upgrade app+param"); + return 1; + } else if ((1 == ide_in_host) && (1 == ide_in_tp)) { + FTS_INFO("fw in host&tp are both ide"); + if ((val & 0x7F) != 0x00) { + FTS_INFO("param invalid, need upgrade param"); + return 2; + } + + ret = fts_param_get_ver_in_host(upg, &ver_in_host); + if (ret < 0) { + FTS_ERROR("param version in host invalid"); + return ret; + } + + ret = fts_param_get_ver_in_tp(&ver_in_tp); + if (ret < 0) { + FTS_ERROR("get IDE param ver in tp fail"); + return ret; + } + + FTS_INFO("fw paramcfg version in tp:%x, host:%x", + ver_in_tp, ver_in_host); + if (ver_in_tp != ver_in_host) { + return 2; + } + } + + return 0; +} + +static int fts_fwupg_get_ver_in_tp(u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_FW_VER, ver); + if (ret < 0) { + FTS_ERROR("read fw ver from tp fail"); + return ret; + } + + return 0; +} + +static int fts_fwupg_get_ver_in_host(struct fts_upgrade *upg, u8 *ver) +{ + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->fwveroff) { + FTS_ERROR("fw len(0x%0x) < fw ver offset(0x%x)", + upg->fw_length, upg->func->fwveroff); + return -EINVAL; + } + + FTS_INFO("fw version offset:0x%x", upg->func->fwveroff); + *ver = upg->fw[upg->func->fwveroff]; + return 0; +} + +static bool fts_fwupg_need_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + bool fwvalid = false; + u8 fw_ver_in_host = 0; + u8 fw_ver_in_tp = 0; + + fwvalid = fts_fwupg_check_fw_valid(); + if (fwvalid) { + ret = fts_fwupg_get_ver_in_host(upg, &fw_ver_in_host); + if (ret < 0) { + FTS_ERROR("get fw ver in host fail"); + return false; + } + + ret = fts_fwupg_get_ver_in_tp(&fw_ver_in_tp); + if (ret < 0) { + FTS_ERROR("get fw ver in tp fail"); + return false; + } + + FTS_INFO("fw version in tp:%x, host:%x", fw_ver_in_tp, fw_ver_in_host); + if (fw_ver_in_tp != fw_ver_in_host) { + return true; + } + } else { + FTS_INFO("fw invalid, need upgrade fw"); + return true; + } + + return false; +} + +/************************************************************************ + * Name: fts_fw_upgrade + * Brief: fw upgrade main entry, run in following steps + * 1. check fw version(A6), not equal, will upgrade app(+param) + * 2. if fw version equal, will check ide, will upgrade app(+param) + * in the follow situation + * a. host&tp IDE's type are not equal, will upgrade app+param + * b. host&tp are both IDE's type, and param's version are not + * equal, will upgrade param + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + bool upgrade_flag = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_INFO("fw auto upgrade function"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upg/upg->func is null"); + return -EINVAL; + } + + upgrade_flag = fts_fwupg_need_upgrade(upg); + FTS_INFO("fw upgrade flag:%d", upgrade_flag); + do { + upgrade_count++; + if (upgrade_flag) { + FTS_INFO("upgrade fw app(times:%d)", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + fts_fwupg_get_ver_in_tp(&ver); + FTS_INFO("success upgrade to fw version %02x", ver); + break; + } + } else { + FTS_ERROR("upgrade func/upgrade is null, return immediately"); + ret = -ENODATA; + break; + } + } else { + if (upg->func->param_upgrade) { + ret = fts_param_need_upgrade(upg); + if (ret <= 0) { + FTS_INFO("param don't need upgrade"); + break; + } else if (1 == ret) { + FTS_INFO("force upgrade fw app(times:%d)", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + break; + } + } + } else if (2 == ret) { + FTS_INFO("upgrade param area(times:%d)", upgrade_count); + ret = upg->func->param_upgrade(upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + fts_param_get_ver_in_tp(&ver); + FTS_INFO("success upgrade to fw param version %02x", ver); + break; + } + } else + break; + } else { + break; + } + } + } while (upgrade_count < 2); + + return ret; +} + +/************************************************************************ + * fts_fwupg_auto_upgrade - upgrade main entry + ***********************************************************************/ +static void fts_fwupg_auto_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + + FTS_INFO("********************FTS enter upgrade********************"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + ret = fts_fwupg_upgrade(upg); + if (ret < 0) + FTS_ERROR("**********tp fw(app/param) upgrade failed**********"); + else + FTS_INFO("**********tp fw(app/param) no upgrade/upgrade success**********"); + +#if FTS_AUTO_LIC_UPGRADE_EN + ret = fts_lic_upgrade(upg); + if (ret < 0) + FTS_ERROR("**********lcd init code upgrade failed**********"); + else + FTS_INFO("**********lcd init code no upgrade/upgrade success**********"); +#endif + + FTS_INFO("********************FTS exit upgrade********************"); +} + +static int fts_fwupg_get_vendorid(struct fts_upgrade *upg, int *vid) +{ + int ret = 0; + bool fwvalid = false; + u8 vendor_id = 0; + u8 module_id = 0; + u32 fwcfg_addr = 0; + u8 cmd = 0; + u8 cfgbuf[FTS_HEADER_LEN] = { 0 }; + + FTS_INFO("read vendor id from tp"); + if ((!upg) || (!upg->func) || (!upg->ts_data) || (!vid)) { + FTS_ERROR("upgrade/func/ts_data/vid is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(); + if (fwvalid) { + ret = fts_read_reg(FTS_REG_VENDOR_ID, &vendor_id); + if (upg->ts_data->ic_info.is_incell) + ret = fts_read_reg(FTS_REG_MODULE_ID, &module_id); + } else { + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) { + cmd = FTS_CMD_READ_FW_CONF; + ret = fts_read(&cmd, 1, cfgbuf, FTS_HEADER_LEN); + } else { + fwcfg_addr = upg->func->fwcfgoff; + ret = fts_flash_read(fwcfg_addr, cfgbuf, FTS_HEADER_LEN); + } + + if ((cfgbuf[FTS_CONIFG_VENDORID_OFF] + + cfgbuf[FTS_CONIFG_VENDORID_OFF + 1]) == 0xFF) + vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF]; + if (upg->ts_data->ic_info.is_incell) { + if ((cfgbuf[FTS_CONIFG_MODULEID_OFF] + + cfgbuf[FTS_CONIFG_MODULEID_OFF + 1]) == 0xFF) + module_id = cfgbuf[FTS_CONIFG_MODULEID_OFF]; + } + } + + if (ret < 0) { + FTS_ERROR("fail to get vendor id from tp"); + return ret; + } + + *vid = (int)((module_id << 8) + vendor_id); + return 0; +} + +static int fts_fwupg_get_module_info(struct fts_upgrade *upg) +{ + int ret = 0; + int i = 0; + struct upgrade_module *info = &module_list[0]; + + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + if (FTS_GET_MODULE_NUM > 1) { + /* support multi modules, must read correct module id(vendor id) */ + ret = fts_fwupg_get_vendorid(upg, &upg->module_id); + if (ret < 0) { + FTS_ERROR("get vendor id failed"); + return ret; + } + FTS_INFO("module id:%04x", upg->module_id); + for (i = 0; i < FTS_GET_MODULE_NUM; i++) { + info = &module_list[i]; + if (upg->module_id == info->id) { + FTS_INFO("module id match, get module info pass"); + break; + } + } + if (i >= FTS_GET_MODULE_NUM) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg) +{ + int ret = 0; + const struct firmware *fw = NULL; + u8 *tmpbuf = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + + if (!upg || !upg->ts_data || !upg->ts_data->dev) { + FTS_ERROR("upg/ts_data/dev is null"); + return -EINVAL; + } + + if (upg->ts_data->pdata->type == _FT3658U) + snprintf(fwname, FILE_NAME_LENGTH, "%s%s_ft3658.bin", + FTS_FW_NAME_PREX_WITH_REQUEST, + upg->module_info->vendor_name); + else + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", + FTS_FW_NAME_PREX_WITH_REQUEST, + upg->module_info->vendor_name); + + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (ret == 0) { + FTS_INFO("firmware(%s) request successfully", fwname); + tmpbuf = vmalloc(fw->size); + if (NULL == tmpbuf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(tmpbuf, fw->data, fw->size); + upg->fw = tmpbuf; + upg->fw_length = fw->size; + upg->fw_from_request = 1; + } + } else { + FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret); + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int fts_get_fw_file_via_i(struct fts_upgrade *upg) +{ + upg->fw = upg->module_info->fw_file; + upg->fw_length = upg->module_info->fw_len; + upg->fw_from_request = 0; + + return 0; +} + +/***************************************************************************** + * Name: fts_fwupg_get_fw_file + * Brief: get fw image/file, + * If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_- + * MODULE_ID/FTS_MODULE_NAME; + * If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F- + * TS_MODULE_ID; will use module id to distingwish different modules; + * If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT + * =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt- + * ech_ts_fw_" & FTS_VENDOR_NAME; + * + * If have flash, module_id=vendor_id, If non-flash,module_id need + * transfer from LCD driver(gpio or lcm_id or ...); + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + *****************************************************************************/ +static int fts_fwupg_get_fw_file(struct fts_upgrade *upg) +{ + int ret = 0; + bool get_fw_i_flag = false; + + FTS_DEBUG("get upgrade fw file"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + ret = fts_fwupg_get_module_info(upg); + if ((ret < 0) || (!upg->module_info)) { + FTS_ERROR("get module info fail"); + return ret; + } + + if (FTS_FW_REQUEST_SUPPORT) { + ret = fts_get_fw_file_via_request_firmware(upg); + if (ret != 0) { + get_fw_i_flag = true; + } + } else { + get_fw_i_flag = true; + } + + if (get_fw_i_flag) { + ret = fts_get_fw_file_via_i(upg); + } + + upg->lic = upg->fw; + upg->lic_length = upg->fw_length; + + FTS_INFO("upgrade fw file len:%d", upg->fw_length); + if ((upg->fw_length < FTS_MIN_LEN) + || (upg->fw_length > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file len(%d) fail", upg->fw_length); + return -ENODATA; + } + + return ret; +} + +static void fts_fwupg_init_ic_detail(struct fts_upgrade *upg) +{ + if (upg && upg->func && upg->func->init) { + upg->func->init(upg->fw, upg->fw_length); + } +} + +/***************************************************************************** + * Name: fts_fwupg_work + * Brief: 1. get fw image/file + * 2. ic init if have + * 3. call upgrade main function(fts_fwupg_auto_upgrade) + * Input: + * Output: + * Return: + *****************************************************************************/ +static void fts_fwupg_work(struct work_struct *work) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + +#if !FTS_AUTO_UPGRADE_EN + FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on"); + return ; +#endif + + FTS_INFO("fw upgrade work function"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + /* get fw */ + ret = fts_fwupg_get_fw_file(upg); + if (ret < 0) { + FTS_ERROR("get file fail, can't upgrade"); + } else { + /* ic init if have */ + fts_fwupg_init_ic_detail(upg); + /* run auto upgrade */ + fts_fwupg_auto_upgrade(upg); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + upg->ts_data->fw_loading = 0; +} + +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + int ic_stype = 0; + struct upgrade_func *func = upgrade_func_list[0]; + int func_count = sizeof(upgrade_func_list) / sizeof(upgrade_func_list[0]); + + FTS_INFO("fw upgrade init function"); + + if (!ts_data || !ts_data->ts_workqueue) { + FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function"); + return -EINVAL; + } + + if (0 == func_count) { + FTS_ERROR("no upgrade function in tp driver"); + return -ENODATA; + } + + fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL); + if (NULL == fwupgrade) { + FTS_ERROR("malloc memory for upgrade fail"); + return -ENOMEM; + } + + ic_stype = ts_data->ic_info.ids.type; + if (1 == func_count) { + fwupgrade->func = func; + } else { + for (i = 0; i < func_count; i++) { + func = upgrade_func_list[i]; + for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) { + if (0 == func->ctype[j]) + break; + else if (func->ctype[j] == ic_stype) { + FTS_INFO("match upgrade function,type:%x", (int)func->ctype[j]); + fwupgrade->func = func; + } + } + } + } + + if (NULL == fwupgrade->func) { + FTS_ERROR("no upgrade function match, can't upgrade"); + kfree(fwupgrade); + fwupgrade = NULL; + return -ENODATA; + } + + fwupgrade->ts_data = ts_data; + INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work); + queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work); + + return 0; +} + +int fts_fwupg_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (fwupgrade) { + if (fwupgrade->fw_from_request) { + vfree(fwupgrade->fw); + fwupgrade->fw = NULL; + } + + kfree(fwupgrade); + fwupgrade = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} diff --git a/focaltech_touch/focaltech_flash.h b/focaltech_touch/focaltech_flash.h new file mode 100644 index 0000000000..8518ef460a --- /dev/null +++ b/focaltech_touch/focaltech_flash.h @@ -0,0 +1,216 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_flash.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-07 +* +* Abstract: +* +************************************************************************/ +#ifndef __LINUX_FOCALTECH_FLASH_H__ +#define __LINUX_FOCALTECH_FLASH_H__ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_CMD_RESET 0x07 +#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR 0xAD +#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN 4 +#define FTS_ROMBOOT_CMD_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 100 +#define FTS_ROMBOOT_CMD_ECC 0xCC +#define FTS_PRAM_SADDR 0x000000 +#define FTS_DRAM_SADDR 0xD00000 + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#define FTS_CMD_READ_LEN_SPI 6 +#define FTS_CMD_FLASH_TYPE 0x05 +#define FTS_CMD_FLASH_MODE 0x09 +#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A +#define FLASH_MODE_UPGRADE_VALUE 0x0B +#define FLASH_MODE_LIC_VALUE 0x0C +#define FLASH_MODE_PARAM_VALUE 0x0D +#define FTS_CMD_ERASE_APP 0x61 +#define FTS_REASE_APP_DELAY 1350 +#define FTS_ERASE_SECTOR_DELAY 60 +#define FTS_RETRIES_REASE 50 +#define FTS_RETRIES_DELAY_REASE 400 +#define FTS_CMD_FLASH_STATUS 0x6A +#define FTS_CMD_FLASH_STATUS_LEN 2 +#define FTS_CMD_FLASH_STATUS_NOP 0x0000 +#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055 +#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA +#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000 +#define FTS_CMD_ECC_INIT 0x64 +#define FTS_CMD_ECC_CAL 0x65 +#define FTS_CMD_ECC_CAL_LEN 7 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 4 +#define FTS_CMD_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_WRITE 0xBF +#define FTS_RETRIES_WRITE 100 +#define FTS_RETRIES_DELAY_WRITE 1 +#define FTS_CMD_WRITE_LEN 6 +#define FTS_DELAY_READ_ID 20 +#define FTS_DELAY_UPGRADE_RESET 80 +#define PRAMBOOT_MIN_SIZE 0x120 +#define PRAMBOOT_MAX_SIZE (64*1024) +#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */ +#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */ +#define FTS_MIN_LEN 0x120 +#define FTS_MAX_LEN_FILE (128 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_SECTOR (4 * 1024) +#define FTS_CONIFG_VENDORID_OFF 0x04 +#define FTS_CONIFG_MODULEID_OFF 0x1E +#define FTS_CONIFG_PROJECTID_OFF 0x20 +#define FTS_APPINFO_OFF 0x100 +#define FTS_APPINFO_APPLEN_OFF 0x00 +#define FTS_APPINFO_APPLEN2_OFF 0x12 +#define FTS_REG_UPGRADE 0xFC +#define FTS_REG_UPGRADE2 0xBC +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_UPGRADE_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/sdcard/" +#define FTS_FW_IDE_SIG "IDE_" +#define FTS_FW_IDE_SIG_LEN 4 +#define MAX_MODULE_VENDOR_NAME_LEN 16 + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ECC_FINISH_TIMEOUT 100 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5 0xA5 +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00 0x00 +#define FTS_ROMBOOT_CMD_ECC_READ 0xCD +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +#define FTS_APP_INFO_OFFSET 0x100 + +enum FW_STATUS { + FTS_RUN_IN_ERROR, + FTS_RUN_IN_APP, + FTS_RUN_IN_ROM, + FTS_RUN_IN_PRAM, + FTS_RUN_IN_BOOTLOADER, +}; + +enum FW_FLASH_MODE { + FLASH_MODE_APP, + FLASH_MODE_LIC, + FLASH_MODE_PARAM, + FLASH_MODE_ALL, +}; + +enum ECC_CHECK_MODE { + ECC_CHECK_MODE_XOR, + ECC_CHECK_MODE_CRC16, +}; + +enum UPGRADE_SPEC { + UPGRADE_SPEC_V_1_0 = 0x0100, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + int pram_ecc_check_mode; + int fw_ecc_check_mode; + int upgspec_version; + bool new_return_value_from_ic; + bool appoff_handle_in_ic; + bool is_reset_register_BC; + bool read_boot_id_need_reset; + bool hid_supported; + bool pramboot_supported; + u8 *pramboot; + u32 pb_length; + int (*init)(u8 *, u32); + int (*write_pramboot_private)(void); + int (*upgrade)(u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(u8 *, u32); + int (*param_upgrade)(u8 *, u32); + int (*force_upgrade)(u8 *, u32); +}; + +struct upgrade_setting_nf { + u8 rom_idh; + u8 rom_idl; + u16 reserved; + u32 app2_offset; + u32 ecclen_max; + u8 eccok_val; + u8 upgsts_boot; + u8 delay_init; + bool spi_pe; + bool half_length; + bool fd_check; + bool drwr_support; +}; + +struct upgrade_module { + int id; + char vendor_name[MAX_MODULE_VENDOR_NAME_LEN]; + u8 *fw_file; + u32 fw_len; +}; + +struct fts_upgrade { + struct fts_ts_data *ts_data; + struct upgrade_module *module_info; + struct upgrade_func *func; + struct upgrade_setting_nf *setting_nf; + int module_id; + bool fw_from_request; + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct upgrade_func upgrade_func_ft5452; +extern struct upgrade_func upgrade_func_ft5652; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +int fts_fwupg_reset_in_boot(void); +int fts_fwupg_enter_into_boot(void); +int fts_fwupg_erase(u32 delay); +int fts_fwupg_ecc_cal(u32 saddr, u32 len); +int fts_flash_write_buf(u32 saddr, u8 *buf, u32 len, u32 delay); +int fts_fwupg_upgrade(struct fts_upgrade *upg); +#endif diff --git a/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c b/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c new file mode 100644 index 0000000000..c42e7b5ad9 --- /dev/null +++ b/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c @@ -0,0 +1,292 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_upgrade_ft5452.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-15 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "../focaltech_flash.h" + +/************************************************************************ +* Name: fts_ft5452_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5452_upgrade(u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + int i = 0; + u8 wbuf[7] = { 0 }; + u8 reg_val[4] = {0}; + + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto fw_reset; + } + + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_UPGRADE_VALUE; + ret = fts_write(cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + cmd[0] = FTS_CMD_DATA_LEN; + cmd[1] = BYTE_OFF_16(len); + cmd[2] = BYTE_OFF_8(len); + cmd[3] = BYTE_OFF_0(len); + ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + ret = fts_fwupg_erase(FTS_REASE_APP_DELAY); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5452.appoff; + ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1); + if (ecc_in_host < 0 ) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + FTS_INFO( "**********read out checksum**********"); + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_write(wbuf, 1); + if (ret < 0) { + FTS_ERROR("ecc init cmd write fail"); + return ret; + } + + /* send commond to start checksum */ + wbuf[0] = FTS_CMD_ECC_CAL; + wbuf[1] = BYTE_OFF_16(start_addr); + wbuf[2] = BYTE_OFF_8(start_addr); + wbuf[3] = BYTE_OFF_0(start_addr); + + wbuf[4] = BYTE_OFF_16(len); + wbuf[5] = BYTE_OFF_8(len); + wbuf[6] = BYTE_OFF_0(len); + + FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", start_addr, len); + ret = fts_write(wbuf, 7); + if (ret < 0) { + FTS_ERROR("ecc calc cmd write fail"); + return ret; + } + + msleep(len / 256); + + /* read status if check sum is finished */ + for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) { + wbuf[0] = FTS_CMD_FLASH_STATUS; + reg_val[0] = reg_val[1] = 0x00; + fts_read(wbuf, 1, reg_val, 2); + FTS_DEBUG("[UPGRADE]: reg_val[0]=%02x reg_val[0]=%02x!!", reg_val[0], reg_val[1]); + if ((0xF0 == reg_val[0]) && (0x55 == reg_val[1])) { + break; + } + msleep(FTS_RETRIES_DELAY_ECC_CAL); + } + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_read(wbuf, 1, reg_val, 1); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + ecc_in_tp = reg_val[0]; + + FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + goto fw_reset; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + msleep(200); + return 0; + +fw_reset: + FTS_INFO("upgrade fail, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return -EIO; +} + +struct upgrade_func upgrade_func_ft5452 = { + .ctype = {0x81}, + .fwveroff = 0x010E, + .fwcfgoff = 0x1FFB0, + .appoff = 0x0000, + .pramboot_supported = false, + .hid_supported = true, + .upgrade = fts_ft5452_upgrade, +}; + + +#define FTS_DELAY_ERASE_PAGE_2K 80 +#define FTS_SIZE_PAGE_2K 2048 + +/************************************************************************ + * Name: fts_ft5652_upgrade + * Brief: + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + **********************************************************************/ +static int fts_ft5652_upgrade(u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + u32 delay = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + if ((buf == NULL) || (len < FTS_MIN_LEN)) { + FTS_ERROR("buffer/len(%x) is invalid", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto fw_reset; + } + + cmd[0] = FTS_CMD_APP_DATA_LEN_INCELL; + cmd[1] = BYTE_OFF_16(len); + cmd[2] = BYTE_OFF_8(len); + cmd[3] = BYTE_OFF_0(len); + ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_UPGRADE_VALUE; + ret = fts_write(cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + delay = FTS_DELAY_ERASE_PAGE_2K * (len / FTS_SIZE_PAGE_2K); + ret = fts_fwupg_erase(delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5652.appoff; + ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1); + if (ecc_in_host < 0) { + FTS_ERROR("flash write fail"); + goto fw_reset; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(start_addr, len); + if (ecc_in_tp < 0) { + FTS_ERROR("ecc read fail"); + goto fw_reset; + } + + FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + goto fw_reset; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + msleep(200); + return 0; + +fw_reset: + FTS_INFO("upgrade fail, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + return -EIO; +} + +struct upgrade_func upgrade_func_ft5652 = { + .ctype = {0x88}, + .fwveroff = 0x010E, + .fwcfgoff = 0x1F80, + .appoff = 0x0000, + .upgspec_version = UPGRADE_SPEC_V_1_0, + .pramboot_supported = false, + .hid_supported = true, + .upgrade = fts_ft5652_upgrade, +}; diff --git a/focaltech_touch/focaltech_gesture.c b/focaltech_touch/focaltech_gesture.c new file mode 100644 index 0000000000..92fe990967 --- /dev/null +++ b/focaltech_touch/focaltech_gesture.c @@ -0,0 +1,477 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_gestrue.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/****************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define KEY_GESTURE_U KEY_U +#define KEY_GESTURE_UP KEY_UP +#define KEY_GESTURE_DOWN KEY_DOWN +#define KEY_GESTURE_LEFT KEY_LEFT +#define KEY_GESTURE_RIGHT KEY_RIGHT +#define KEY_GESTURE_O KEY_O +#define KEY_GESTURE_E KEY_E +#define KEY_GESTURE_M KEY_M +#define KEY_GESTURE_L KEY_L +#define KEY_GESTURE_W KEY_W +#define KEY_GESTURE_S KEY_S +#define KEY_GESTURE_V KEY_V +#define KEY_GESTURE_C KEY_C +#define KEY_GESTURE_Z KEY_Z + +#define GESTURE_LEFT 0x20 +#define GESTURE_RIGHT 0x21 +#define GESTURE_UP 0x22 +#define GESTURE_DOWN 0x23 +#define GESTURE_DOUBLECLICK 0x24 +#define GESTURE_O 0x30 +#define GESTURE_W 0x31 +#define GESTURE_M 0x32 +#define GESTURE_E 0x33 +#define GESTURE_L 0x44 +#define GESTURE_S 0x46 +#define GESTURE_V 0x54 +#define GESTURE_Z 0x41 +#define GESTURE_C 0x34 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* +* gesture_id - mean which gesture is recognised +* point_num - points number of this gesture +* coordinate_x - All gesture point x coordinate +* coordinate_y - All gesture point y coordinate +* mode - gesture enable/disable, need enable by host +* - 1:enable gesture function(default) 0:disable +* active - gesture work flag, +* always set 1 when suspend, set 0 when resume +*/ +struct fts_gesture_st { + u8 gesture_id; + u8 point_num; + u16 coordinate_x[FTS_GESTURE_POINTS_MAX]; + u16 coordinate_y[FTS_GESTURE_POINTS_MAX]; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_gesture_st fts_gesture_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static ssize_t fts_gesture_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + + mutex_lock(&ts_data->input_dev->mutex); + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n", + ts_data->gesture_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val); + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = fts_data; + + mutex_lock(&ts_data->input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable gesture"); + ts_data->gesture_mode = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable gesture"); + ts_data->gesture_mode = DISABLE; + } + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_buf_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + int i = 0; + struct input_dev *input_dev = fts_data->input_dev; + struct fts_gesture_st *gesture = &fts_gesture_data; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n", gesture->gesture_id); + count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n", + gesture->point_num); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n"); + + /* save point data,max:6 */ + for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i, + gesture->coordinate_x[i], gesture->coordinate_y[i]); + + if ((i + 1) % 4 == 0) + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_buf_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + + +/* sysfs gesture node + * read example: cat fts_gesture_mode ---read gesture mode + * write example:echo 1 > fts_gesture_mode --- write gesture mode to 1 + * + */ +static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show, + fts_gesture_store); +/* + * read example: cat fts_gesture_buf --- read gesture buf + */ +static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR, + fts_gesture_buf_show, fts_gesture_buf_store); + +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + NULL, +}; + +static struct attribute_group fts_gesture_group = { + .attrs = fts_gesture_mode_attrs, +}; + +static int fts_create_gesture_sysfs(struct device *dev) +{ + int ret = 0; + + ret = sysfs_create_group(&dev->kobj, &fts_gesture_group); + if (ret) { + FTS_ERROR("gesture sys node create fail"); + sysfs_remove_group(&dev->kobj, &fts_gesture_group); + return ret; + } + + return 0; +} + +static void fts_gesture_report(struct input_dev *input_dev, int gesture_id) +{ + int gesture; + + FTS_DEBUG("gesture_id:0x%x", gesture_id); + switch (gesture_id) { + case GESTURE_LEFT: + gesture = KEY_GESTURE_LEFT; + break; + + case GESTURE_RIGHT: + gesture = KEY_GESTURE_RIGHT; + break; + + case GESTURE_UP: + gesture = KEY_GESTURE_UP; + break; + + case GESTURE_DOWN: + gesture = KEY_GESTURE_DOWN; + break; + + case GESTURE_DOUBLECLICK: + gesture = KEY_POWER; + + break; + + case GESTURE_O: + gesture = KEY_GESTURE_O; + break; + + case GESTURE_W: + gesture = KEY_GESTURE_W; + break; + + case GESTURE_M: + gesture = KEY_GESTURE_M; + break; + + case GESTURE_E: + gesture = KEY_GESTURE_E; + break; + + case GESTURE_L: + gesture = KEY_GESTURE_L; + break; + + case GESTURE_S: + gesture = KEY_GESTURE_S; + break; + + case GESTURE_V: + gesture = KEY_GESTURE_V; + break; + + case GESTURE_Z: + gesture = KEY_GESTURE_Z; + break; + + case GESTURE_C: + gesture = KEY_GESTURE_C; + break; + + default: + gesture = -1; + break; + } + + /* report event key */ + if (gesture != -1) { + FTS_DEBUG("Gesture Code=%d", gesture); + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); + } +} + +/***************************************************************************** +* Name: fts_gesture_readdata +* Brief: Read information about gesture: enable flag/gesture points..., if ges- +* ture enable, save gesture points' information, and report to OS. +* It will be called this function every intrrupt when FTS_GESTURE_EN = 1 +* +* gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4 +* Input: ts_data - global struct data +* data - gesture data buffer if non-flash, else NULL +* Output: +* Return: 0 - read gesture data successfully, the report data is gesture data +* 1 - tp not in suspend/gesture not enable in TP FW +* -Exx - error +*****************************************************************************/ +int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data) +{ + int ret = 0; + int i = 0; + int index = 0; + u8 buf[FTS_GESTURE_DATA_LEN] = { 0 }; + struct input_dev *input_dev = ts_data->input_dev; + struct fts_gesture_st *gesture = &fts_gesture_data; + + if (!ts_data->suspended || !ts_data->gesture_mode) { + return 1; + } + + + ret = fts_read_reg(FTS_REG_GESTURE_EN, &buf[0]); + if ((ret < 0) || (buf[0] != ENABLE)) { + FTS_DEBUG("gesture not enable in fw, don't process gesture"); + return 1; + } + + buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2); + if (ret < 0) { + FTS_ERROR("read gesture header data fail"); + return ret; + } + + /* init variable before read gesture point */ + memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16)); + memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16)); + gesture->gesture_id = buf[2]; + gesture->point_num = buf[3]; + FTS_DEBUG("gesture_id=%d, point_num=%d", + gesture->gesture_id, gesture->point_num); + + /* save point data,max:6 */ + for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) { + index = 4 * i + 4; + gesture->coordinate_x[i] = (u16)(((buf[0 + index] & 0x0F) << 8) + + buf[1 + index]); + gesture->coordinate_y[i] = (u16)(((buf[2 + index] & 0x0F) << 8) + + buf[3 + index]); + } + + /* report gesture to OS */ + fts_gesture_report(input_dev, gesture->gesture_id); + return 0; +} + +void fts_gesture_recovery(struct fts_ts_data *ts_data) +{ + if (ts_data->gesture_mode && ts_data->suspended) { + FTS_DEBUG("gesture recovery..."); + fts_write_reg(0xD1, 0xFF); + fts_write_reg(0xD2, 0xFF); + fts_write_reg(0xD5, 0xFF); + fts_write_reg(0xD6, 0xFF); + fts_write_reg(0xD7, 0xFF); + fts_write_reg(0xD8, 0xFF); + fts_write_reg(FTS_REG_GESTURE_EN, ENABLE); + } +} + +int fts_gesture_suspend(struct fts_ts_data *ts_data) +{ + int i = 0; + u8 state = 0xFF; + + FTS_FUNC_ENTER(); + if (enable_irq_wake(ts_data->irq)) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + + for (i = 0; i < 5; i++) { + fts_write_reg(0xD1, 0xFF); + fts_write_reg(0xD2, 0xFF); + fts_write_reg(0xD5, 0xFF); + fts_write_reg(0xD6, 0xFF); + fts_write_reg(0xD7, 0xFF); + fts_write_reg(0xD8, 0xFF); + fts_write_reg(FTS_REG_GESTURE_EN, ENABLE); + msleep(1); + fts_read_reg(FTS_REG_GESTURE_EN, &state); + if (state == ENABLE) + break; + } + + if (i >= 5) + FTS_ERROR("make IC enter into gesture(suspend) fail,state:%x", state); + else + FTS_INFO("Enter into gesture(suspend) successfully"); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_resume(struct fts_ts_data *ts_data) +{ + int i = 0; + u8 state = 0xFF; + + FTS_FUNC_ENTER(); + if (disable_irq_wake(ts_data->irq)) { + FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq); + } + + for (i = 0; i < 5; i++) { + fts_write_reg(FTS_REG_GESTURE_EN, DISABLE); + msleep(1); + fts_read_reg(FTS_REG_GESTURE_EN, &state); + if (state == DISABLE) + break; + } + + if (i >= 5) + FTS_ERROR("make IC exit gesture(resume) fail,state:%x", state); + else + FTS_INFO("resume from gesture successfully"); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_init(struct fts_ts_data *ts_data) +{ + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C); + + __set_bit(KEY_GESTURE_RIGHT, input_dev->keybit); + __set_bit(KEY_GESTURE_LEFT, input_dev->keybit); + __set_bit(KEY_GESTURE_UP, input_dev->keybit); + __set_bit(KEY_GESTURE_DOWN, input_dev->keybit); + __set_bit(KEY_GESTURE_U, input_dev->keybit); + __set_bit(KEY_GESTURE_O, input_dev->keybit); + __set_bit(KEY_GESTURE_E, input_dev->keybit); + __set_bit(KEY_GESTURE_M, input_dev->keybit); + __set_bit(KEY_GESTURE_W, input_dev->keybit); + __set_bit(KEY_GESTURE_L, input_dev->keybit); + __set_bit(KEY_GESTURE_S, input_dev->keybit); + __set_bit(KEY_GESTURE_V, input_dev->keybit); + __set_bit(KEY_GESTURE_C, input_dev->keybit); + __set_bit(KEY_GESTURE_Z, input_dev->keybit); + + fts_create_gesture_sysfs(ts_data->dev); + + memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st)); + ts_data->gesture_mode = FTS_GESTURE_EN; + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group); + FTS_FUNC_EXIT(); + return 0; +} diff --git a/focaltech_touch/focaltech_i2c.c b/focaltech_touch/focaltech_i2c.c new file mode 100644 index 0000000000..8d439d3ac7 --- /dev/null +++ b/focaltech_touch/focaltech_i2c.c @@ -0,0 +1,548 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/************************************************************************ +* +* File Name: focaltech_i2c.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-04 +* +* Abstract: i2c communication with TP +* +* Version: v1.0 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define I2C_RETRY_NUMBER 3 +#define I2C_BUF_LENGTH 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_ts_data *ts_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +static int fts_i2c_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + struct i2c_msg msg_list[2]; + struct i2c_msg *msg = NULL; + int msg_num = 0; + + /* must have data when read */ + if (!ts_data || !ts_data->client || !data || !datalen + || (datalen >= I2C_BUF_LENGTH) || (cmdlen >= I2C_BUF_LENGTH)) { + FTS_ERROR("fts_data/client/cmdlen(%d)/data/datalen(%d) is invalid", + cmdlen, datalen); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + memset(&msg_list[0], 0, sizeof(struct i2c_msg)); + memset(&msg_list[1], 0, sizeof(struct i2c_msg)); + memcpy(ts_data->bus_tx_buf, cmd, cmdlen); + msg_list[0].addr = ts_data->client->addr; + msg_list[0].flags = 0; + msg_list[0].len = cmdlen; + msg_list[0].buf = ts_data->bus_tx_buf; + msg_list[1].addr = ts_data->client->addr; + msg_list[1].flags = I2C_M_RD; + msg_list[1].len = datalen; + msg_list[1].buf = ts_data->bus_rx_buf; + if (cmd && cmdlen) { + msg = &msg_list[0]; + msg_num = 2; + } else { + msg = &msg_list[1]; + msg_num = 1; + } + + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(ts_data->client->adapter, msg, msg_num); + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (atomic_read(&ts_data->trusted_touch_enabled) && + ret == -ECONNRESET) { + pr_err("failed i2c read reacquiring session\n"); + pm_runtime_put_sync( + ts_data->client->adapter->dev.parent); + pm_runtime_get_sync( + ts_data->client->adapter->dev.parent); + } +#endif +#endif + FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret); + } else { + memcpy(data, ts_data->bus_rx_buf, datalen); + break; + } + } + + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + pr_err("initiating abort due to i2c xfer failure\n"); + fts_ts_trusted_touch_tvm_i2c_failure_report(ts_data); +#endif +#endif + } + + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_i2c_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct i2c_msg msgs; + + if (!ts_data || !ts_data->client || !writebuf || !writelen + || (writelen >= I2C_BUF_LENGTH)) { + FTS_ERROR("fts_data/client/data/datalen(%d) is invalid", writelen); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + memset(&msgs, 0, sizeof(struct i2c_msg)); + memcpy(ts_data->bus_tx_buf, writebuf, writelen); + msgs.addr = ts_data->client->addr; + msgs.flags = 0; + msgs.len = writelen; + msgs.buf = ts_data->bus_tx_buf; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(ts_data->client->adapter, &msgs, 1); + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (atomic_read(&ts_data->trusted_touch_enabled) && + ret == -ECONNRESET){ + pr_err("failed i2c write reacquiring session\n"); + pm_runtime_put_sync( + ts_data->client->adapter->dev.parent); + pm_runtime_get_sync( + ts_data->client->adapter->dev.parent); + } +#endif +#endif + FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret); + } else { + break; + } + } + + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + pr_err("initiating abort due to i2c xfer failure\n"); + fts_ts_trusted_touch_tvm_i2c_failure_report(ts_data); +#endif +#endif + } + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_i2c_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_tx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_rx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_i2c_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (ts_data && ts_data->bus_tx_buf) { + kfree(ts_data->bus_tx_buf); + ts_data->bus_tx_buf = NULL; + } + + if (ts_data && ts_data->bus_rx_buf) { + kfree(ts_data->bus_rx_buf); + ts_data->bus_rx_buf = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** + * Private constant and macro definitions using #define + ****************************************************************************/ +#define SPI_RETRY_NUMBER 3 +#define CS_HIGH_DELAY 150 /* unit: us */ +#define SPI_BUF_LENGTH 256 + +#define DATA_CRC_EN 0x20 +#define WRITE_CMD 0x00 +#define READ_CMD (0x80 | DATA_CRC_EN) + +#define SPI_DUMMY_BYTE 3 +#define SPI_HEADER_LENGTH 6 /*CRC*/ + +/***************************************************************************** + * functions body + ****************************************************************************/ +/* spi interface */ +static int fts_spi_transfer(u8 *tx_buf, u8 *rx_buf, u32 len) +{ + int ret = 0; + struct spi_device *spi = fts_data->spi; + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = tx_buf, + .rx_buf = rx_buf, + .len = len, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + if (ret) { + FTS_ERROR("spi_sync fail,ret:%d", ret); + return ret; + } + + return ret; +} + +static void crckermit(u8 *data, u32 len, u16 *crc_out) +{ + u32 i = 0; + u32 j = 0; + u16 crc = 0xFFFF; + + for (i = 0; i < len; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8408; + else + crc = (crc >> 1); + } + } + + *crc_out = crc; +} + +static int rdata_check(u8 *rdata, u32 rlen) +{ + u16 crc_calc = 0; + u16 crc_read = 0; + + crckermit(rdata, rlen - 2, &crc_calc); + crc_read = (u16)(rdata[rlen - 1] << 8) + rdata[rlen - 2]; + if (crc_calc != crc_read) + return -EIO; + + return 0; +} + +static int fts_spi_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u32 txlen_need = writelen + SPI_HEADER_LENGTH + ts_data->dummy_byte; + u32 datalen = writelen - 1; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen_need > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen_need, GFP_KERNEL); + if (txbuf == NULL) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (rxbuf == NULL) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + txbuf[txlen++] = writebuf[0]; + txbuf[txlen++] = WRITE_CMD; + txbuf[txlen++] = (datalen >> 8) & 0xFF; + txbuf[txlen++] = datalen & 0xFF; + if (datalen > 0) { + txlen = txlen + SPI_DUMMY_BYTE; + memcpy(&txbuf[txlen], &writebuf[1], datalen); + txlen = txlen + datalen; + } + + for (i = 0; i < SPI_RETRY_NUMBER; i++) { + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if ((ret == 0) && ((rxbuf[3] & 0xA0) == 0)) + break; + + FTS_DEBUG("data write(addr:%x),status:%x,retry:%d,ret:%d", + writebuf[0], rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + + if (ret < 0) { + FTS_ERROR("data write(addr:%x) fail,status:%x,ret:%d", + writebuf[0], rxbuf[3], ret); + } + +err_write: + if (txlen_need > SPI_BUF_LENGTH) { + kfree(txbuf); + kfree(rxbuf); + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_spi_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u32 txlen_need = datalen + SPI_HEADER_LENGTH + ts_data->dummy_byte; + u8 ctrl = READ_CMD; + u32 dp = 0; + + if (!cmd || !cmdlen || !data || !datalen) { + FTS_ERROR("cmd/cmdlen/data/datalen is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen_need > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen_need, GFP_KERNEL); + if (txbuf == NULL) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (rxbuf == NULL) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + txbuf[txlen++] = cmd[0]; + txbuf[txlen++] = ctrl; + txbuf[txlen++] = (datalen >> 8) & 0xFF; + txbuf[txlen++] = datalen & 0xFF; + dp = txlen + SPI_DUMMY_BYTE; + txlen = dp + datalen; + if (ctrl & DATA_CRC_EN) + txlen = txlen + 2; + + for (i = 0; i < SPI_RETRY_NUMBER; i++) { + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if ((ret == 0) && ((rxbuf[3] & 0xA0) == 0)) { + memcpy(data, &rxbuf[dp], datalen); + /* crc check */ + if (ctrl & DATA_CRC_EN) { + ret = rdata_check(&rxbuf[dp], txlen - dp); + if (ret < 0) { + FTS_DEBUG("data read(addr:%x) crc abnormal,retry:%d", + cmd[0], i); + udelay(CS_HIGH_DELAY); + continue; + } + } + break; + } + + FTS_DEBUG("data read(addr:%x) status:%x,retry:%d,ret:%d", + cmd[0], rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + + if (ret < 0) { + FTS_ERROR("data read(addr:%x) %s,status:%x,ret:%d", cmd[0], + (i >= SPI_RETRY_NUMBER) ? "crc abnormal" : "fail", + rxbuf[3], ret); + } + +err_read: + if (txlen_need > SPI_BUF_LENGTH) { + kfree(txbuf); + kfree(rxbuf); + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_spi_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_tx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_rx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + + ts_data->dummy_byte = SPI_DUMMY_BYTE; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_spi_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (ts_data && ts_data->bus_tx_buf) { + kfree(ts_data->bus_tx_buf); + ts_data->bus_tx_buf = NULL; + } + + if (ts_data && ts_data->bus_rx_buf) { + kfree(ts_data->bus_rx_buf); + ts_data->bus_rx_buf = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + + if (ts_data->bus_type == BUS_TYPE_I2C) + ret = fts_i2c_read(cmd, cmdlen, data, datalen); + else + ret = fts_spi_read(cmd, cmdlen, data, datalen); + + return ret; +} + +int fts_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + + if (ts_data->bus_type == BUS_TYPE_I2C) + ret = fts_i2c_write(writebuf, writelen); + else + ret = fts_spi_write(writebuf, writelen); + + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + +int fts_write_reg(u8 addr, u8 value) +{ + u8 buf[2] = { 0 }; + + buf[0] = addr; + buf[1] = value; + return fts_write(buf, sizeof(buf)); +} + +int fts_bus_init(struct fts_ts_data *_ts_data) +{ + ts_data = _ts_data; + + if (ts_data->bus_type == BUS_TYPE_I2C) + return fts_i2c_init(ts_data); + + return fts_spi_init(ts_data); +} + +int fts_bus_exit(struct fts_ts_data *ts_data) +{ + if (ts_data->bus_type == BUS_TYPE_I2C) + return fts_i2c_exit(ts_data); + + return fts_spi_exit(ts_data); +} \ No newline at end of file diff --git a/focaltech_touch/focaltech_point_report_check.c b/focaltech_touch/focaltech_point_report_check.c new file mode 100644 index 0000000000..0f626408a9 --- /dev/null +++ b/focaltech_touch/focaltech_point_report_check.c @@ -0,0 +1,135 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_point_report_check.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-11-16 +* +* Abstract: point report check function +* +* Version: v1.0 +* +* Revision History: +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_POINT_REPORT_CHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */ + +/***************************************************************************** +* functions body +*****************************************************************************/ +/***************************************************************************** +* Name: fts_prc_func +* Brief: fts point report check work func, report whole up of points +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_prc_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, prc_work.work); + struct input_dev *input_dev = ts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = fts_data->pdata->max_touch_number; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&ts_data->report_mutex); + +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < max_touches; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + + mutex_unlock(&ts_data->report_mutex); + + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_prc_queue_work +* Brief: fts point report check queue work, call it when interrupt comes +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_prc_queue_work(struct fts_ts_data *ts_data) +{ + cancel_delayed_work_sync(&ts_data->prc_work); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); +} + +/***************************************************************************** +* Name: fts_point_report_check_init +* Brief: +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_point_report_check_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run point report check function"); + return -EINVAL; + } + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_point_report_check_exit +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_point_report_check_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_POINT_REPORT_CHECK_EN */ + diff --git a/nt36xxx/nt36xxx.c b/nt36xxx/nt36xxx.c new file mode 100644 index 0000000000..fa3e6a3fd1 --- /dev/null +++ b/nt36xxx/nt36xxx.c @@ -0,0 +1,4601 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DRM) +#include +#endif + +#if defined(CONFIG_DRM_PANEL) +#include +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#include "nt36xxx.h" +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_ESD_PROTECT +static struct delayed_work nvt_esd_check_work; +static struct workqueue_struct *nvt_esd_check_wq; +static unsigned long irq_timer = 0; +uint8_t esd_check = false; +uint8_t esd_retry = 0; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_EXT_PROC +extern int32_t nvt_extra_proc_init(void); +extern void nvt_extra_proc_deinit(void); +#endif + +#if NVT_TOUCH_MP +extern int32_t nvt_mp_proc_init(void); +extern void nvt_mp_proc_deinit(void); +#endif + +struct nvt_ts_data *ts; + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +static void nvt_i2c_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data); + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); + +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#if TOUCH_KEY_NUM > 0 +const uint16_t touch_key_array[TOUCH_KEY_NUM] = { + KEY_BACK, + KEY_HOME, + KEY_MENU +}; +#endif + +#if WAKEUP_GESTURE +const uint16_t gesture_key_array[] = { + KEY_POWER, //GESTURE_WORD_C + KEY_POWER, //GESTURE_WORD_W + KEY_POWER, //GESTURE_WORD_V + KEY_POWER, //GESTURE_DOUBLE_CLICK + KEY_POWER, //GESTURE_WORD_Z + KEY_POWER, //GESTURE_WORD_M + KEY_POWER, //GESTURE_WORD_O + KEY_POWER, //GESTURE_WORD_e + KEY_POWER, //GESTURE_WORD_S + KEY_POWER, //GESTURE_SLIDE_UP + KEY_POWER, //GESTURE_SLIDE_DOWN + KEY_POWER, //GESTURE_SLIDE_LEFT + KEY_POWER, //GESTURE_SLIDE_RIGHT +}; +#endif + +static uint8_t bTouchIsAwake = 0; + +#if defined(CONFIG_DRM) +static void nvt_i2c_register_for_panel_events(struct device_node *dp, + struct nvt_ts_data *ts) +{ + void *cookie = NULL; + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &nvt_i2c_panel_notifier_callback, ts); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + ts->notifier_cookie = cookie; +} +#endif +/******************************************************* + * Description: + * Novatek touchscreen irq enable/disable function. + * + * return: + * n.a. + *******************************************************/ +static void nvt_irq_enable(bool enable) +{ + struct irq_desc *desc; + + if (enable) { + if (!ts->irq_enabled) { + enable_irq(ts->client->irq); + ts->irq_enabled = true; + } + } else { + if (ts->irq_enabled) { + disable_irq(ts->client->irq); + ts->irq_enabled = false; + } + } + + desc = irq_to_desc(ts->client->irq); + NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth); +} + +/******************************************************* + * Description: + * Novatek touchscreen i2c read function. + * + * return: + * Executive outcomes. 2---succeed. -5---I/O error + *******************************************************/ +int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf, + uint16_t len) +{ + struct i2c_msg msgs[2]; + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + msgs[0].flags = !I2C_M_RD; + msgs[0].addr = address; + msgs[0].len = 1; + msgs[0].buf = &buf[0]; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = address; + msgs[1].len = len - 1; + msgs[1].buf = ts->xbuf; + + while (retries < 5) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("error, ret=%d\n", ret); + ret = -EIO; + } + + memcpy(buf + 1, ts->xbuf, len - 1); + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen i2c write function. + * + * return: + * Executive outcomes. 1---succeed. -5---I/O error + *******************************************************/ +int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf, + uint16_t len) +{ + struct i2c_msg msg; + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + msg.flags = !I2C_M_RD; + msg.addr = address; + msg.len = len; + memcpy(ts->xbuf, buf, len); + msg.buf = ts->xbuf; + + while (retries < 5) { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("error, ret=%d\n", ret); + ret = -EIO; + } + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen set index/page/addr address. + * + * return: + * Executive outcomes. 0---succeed. -5---access fail. + ********************************************************/ +int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr) +{ + uint8_t buf[4] = {0}; + + buf[0] = 0xFF; //set index/page/addr command + buf[1] = (addr >> 16) & 0xFF; + buf[2] = (addr >> 8) & 0xFF; + + return CTP_I2C_WRITE(ts->client, i2c_addr, buf, 3); +} + +/******************************************************* + * Description: + * Novatek touchscreen reset MCU then into idle mode + * function. + * + * return: + * n.a. + *******************************************************/ +void nvt_sw_reset_idle(void) +{ + uint8_t buf[4]={0}; + + //---write i2c cmds to reset idle--- + buf[0]=0x00; + buf[1]=0xA5; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + + msleep(15); +} + +/******************************************************* + * Description: + * Novatek touchscreen reset MCU (boot) function. + * + * return: + * n.a. + *******************************************************/ +void nvt_bootloader_reset(void) +{ + uint8_t buf[8] = {0}; + + NVT_LOG("start\n"); + + //---write i2c cmds to reset--- + buf[0] = 0x00; + buf[1] = 0x69; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + + // need 35ms delay after bootloader reset + msleep(35); + + NVT_LOG("end\n"); +} + +/******************************************************* + * Description: + * Novatek touchscreen clear FW status function. + * + * return: + * Executive outcomes. 0---succeed. -1---fail. + *******************************************************/ +int32_t nvt_clear_fw_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 20; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---clear fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0xFF; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 10000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -1; + } else { + return 0; + } +} + +/******************************************************* + * Description: + * Novatek touchscreen check FW status function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + *******************************************************/ +int32_t nvt_check_fw_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 50; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if ((buf[1] & 0xF0) == 0xA0) + break; + + usleep_range(10000, 10000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -1; + } else { + return 0; + } +} + +/******************************************************* + * Description: + * Novatek touchscreen check FW reset state function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + ******************************************************/ +int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + + while (1) { + usleep_range(10000, 10000); + + //---read reset state--- + buf[0] = EVENT_MAP_RESET_COMPLETE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6); + + if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX)) { + ret = 0; + break; + } + + retry++; + if(unlikely(retry > 100)) { + NVT_ERR("error, retry=%d, buf[1]=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + retry, buf[1], buf[2], buf[3], buf[4], buf[5]); + ret = -1; + break; + } + } + + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen get novatek project id information + * function. + * + * return: + * Executive outcomes. 0---success. -1---fail. + *******************************************************/ +int32_t nvt_read_pid(void) +{ + uint8_t buf[3] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID); + + //---read project id--- + buf[0] = EVENT_MAP_PROJECTID; + buf[1] = 0x00; + buf[2] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 3); + + ts->nvt_pid = (buf[2] << 8) + buf[1]; + + NVT_LOG("PID=%04X\n", ts->nvt_pid); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen get firmware related information + function. + +return: + Executive outcomes. 0---success. -1---fail. +*******************************************************/ +int32_t nvt_get_fw_info(void) +{ + uint8_t buf[64] = {0}; + uint32_t retry_count = 0; + int32_t ret = 0; + +info_retry: + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + + //---read fw info--- + buf[0] = EVENT_MAP_FWINFO; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 17); + ts->fw_ver = buf[1]; + ts->x_num = buf[3]; + ts->y_num = buf[4]; + ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]); + ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]); + ts->max_button_num = buf[11]; + + //---clear x_num, y_num if fw info is broken--- + if ((buf[1] + buf[2]) != 0xFF) { + NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]); + ts->fw_ver = 0; + ts->x_num = 18; + ts->y_num = 32; + ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; + ts->max_button_num = TOUCH_KEY_NUM; + + if(retry_count < 3) { + retry_count++; + NVT_ERR("retry_count=%d\n", retry_count); + goto info_retry; + } else { + NVT_ERR("Set default fw_ver=%d, x_num=%d, y_num=%d, " + "abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n", + ts->fw_ver, ts->x_num, ts->y_num, + ts->abs_x_max, ts->abs_y_max, ts->max_button_num); + ret = -1; + } + } else { + ret = 0; + } + + //---Get Novatek PID--- + nvt_read_pid(); + + return ret; +} + +/******************************************************* + * Create Device Node (Proc Entry) + *******************************************************/ +#if NVT_TOUCH_PROC +static struct proc_dir_entry *NVT_proc_entry; +#define DEVICE_NAME "NVTflash" + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash read function. + * + * return: + * Executive outcomes. 2---succeed. -5,-14---failed. + *******************************************************/ +static ssize_t nvt_flash_read(struct file *file, char __user *buff, + size_t count, loff_t *offp) +{ + uint8_t str[68] = {0}; + int32_t ret = -1; + int32_t retries = 0; + int8_t i2c_wr = 0; + + if (count > sizeof(str)) { + NVT_ERR("error count=%zu\n", count); + return -EFAULT; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + return -EFAULT; + } + +#if NVT_TOUCH_ESD_PROTECT + /* + * stop esd check work to avoid case that 0x77 report righ after here to enable esd check again + * finally lead to trigger esd recovery bootloader reset + */ + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + i2c_wr = str[0] >> 7; + + if (i2c_wr == 0) { //I2C write + while (retries < 20) { + ret = CTP_I2C_WRITE(ts->client, (str[0] & 0x7F), &str[2], str[1]); + if (ret == 1) + break; + else + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + return -EIO; + } + + return ret; + } else if (i2c_wr == 1) { //I2C read + while (retries < 20) { + ret = CTP_I2C_READ(ts->client, (str[0] & 0x7F), &str[2], str[1]); + if (ret == 2) + break; + else + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + // copy buff to user if i2c transfer + if (retries < 20) { + if (copy_to_user(buff, str, count)) + return -EFAULT; + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + return -EIO; + } + + return ret; + } else { + NVT_ERR("Call error, str[0]=%d\n", str[0]); + return -EFAULT; + } +} + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash open function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + *******************************************************/ +static int32_t nvt_flash_open(struct inode *inode, struct file *file) +{ + struct nvt_flash_data *dev; + + dev = kmalloc(sizeof(struct nvt_flash_data), GFP_KERNEL); + if (dev == NULL) { + NVT_ERR("Failed to allocate memory for nvt flash data\n"); + return -ENOMEM; + } + + rwlock_init(&dev->lock); + file->private_data = dev; + + return 0; +} + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash close function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_flash_close(struct inode *inode, struct file *file) +{ + struct nvt_flash_data *dev = file->private_data; + + kfree(dev); + + return 0; +} + +static const struct proc_ops nvt_flash_fops = { + .proc_open = nvt_flash_open, + .proc_release = nvt_flash_close, + .proc_read = nvt_flash_read, +}; + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash initial function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + *******************************************************/ +static int32_t nvt_flash_proc_init(void) +{ + NVT_proc_entry = proc_create(DEVICE_NAME, 0444, NULL,&nvt_flash_fops); + if (NVT_proc_entry == NULL) { + NVT_ERR("Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("Succeeded!\n"); + } + + NVT_LOG("==========================================================\n"); + NVT_LOG("Create /proc/%s\n", DEVICE_NAME); + NVT_LOG("==========================================================\n"); + + return 0; +} + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash deinitial function. + * + * return: + * n.a. + *******************************************************/ +static void nvt_flash_proc_deinit(void) +{ + if (NVT_proc_entry != NULL) { + remove_proc_entry(DEVICE_NAME, NULL); + NVT_proc_entry = NULL; + NVT_LOG("Removed /proc/%s\n", DEVICE_NAME); + } +} +#endif + +#if WAKEUP_GESTURE +#define GESTURE_WORD_C 12 +#define GESTURE_WORD_W 13 +#define GESTURE_WORD_V 14 +#define GESTURE_DOUBLE_CLICK 15 +#define GESTURE_WORD_Z 16 +#define GESTURE_WORD_M 17 +#define GESTURE_WORD_O 18 +#define GESTURE_WORD_e 19 +#define GESTURE_WORD_S 20 +#define GESTURE_SLIDE_UP 21 +#define GESTURE_SLIDE_DOWN 22 +#define GESTURE_SLIDE_LEFT 23 +#define GESTURE_SLIDE_RIGHT 24 +/* customized gesture id */ +#define DATA_PROTOCOL 30 + +/* function page definition */ +#define FUNCPAGE_GESTURE 1 + +/******************************************************* + * Description: + * Novatek touchscreen wake up gesture key report function. + * + * return: + * n.a. + *******************************************************/ +void nvt_ts_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data) +{ + uint32_t keycode = 0; + uint8_t func_type = data[2]; + uint8_t func_id = data[3]; + + /* support fw specifal data protocol */ + if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) { + gesture_id = func_id; + } else if (gesture_id > DATA_PROTOCOL) { + NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n", gesture_id, func_type, func_id); + return; + } + + NVT_LOG("gesture_id = %d\n", gesture_id); + + switch (gesture_id) { + case GESTURE_WORD_C: + NVT_LOG("Gesture : Word-C.\n"); + keycode = gesture_key_array[0]; + break; + case GESTURE_WORD_W: + NVT_LOG("Gesture : Word-W.\n"); + keycode = gesture_key_array[1]; + break; + case GESTURE_WORD_V: + NVT_LOG("Gesture : Word-V.\n"); + keycode = gesture_key_array[2]; + break; + case GESTURE_DOUBLE_CLICK: + NVT_LOG("Gesture : Double Click.\n"); + keycode = gesture_key_array[3]; + break; + case GESTURE_WORD_Z: + NVT_LOG("Gesture : Word-Z.\n"); + keycode = gesture_key_array[4]; + break; + case GESTURE_WORD_M: + NVT_LOG("Gesture : Word-M.\n"); + keycode = gesture_key_array[5]; + break; + case GESTURE_WORD_O: + NVT_LOG("Gesture : Word-O.\n"); + keycode = gesture_key_array[6]; + break; + case GESTURE_WORD_e: + NVT_LOG("Gesture : Word-e.\n"); + keycode = gesture_key_array[7]; + break; + case GESTURE_WORD_S: + NVT_LOG("Gesture : Word-S.\n"); + keycode = gesture_key_array[8]; + break; + case GESTURE_SLIDE_UP: + NVT_LOG("Gesture : Slide UP.\n"); + keycode = gesture_key_array[9]; + break; + case GESTURE_SLIDE_DOWN: + NVT_LOG("Gesture : Slide DOWN.\n"); + keycode = gesture_key_array[10]; + break; + case GESTURE_SLIDE_LEFT: + NVT_LOG("Gesture : Slide LEFT.\n"); + keycode = gesture_key_array[11]; + break; + case GESTURE_SLIDE_RIGHT: + NVT_LOG("Gesture : Slide RIGHT.\n"); + keycode = gesture_key_array[12]; + break; + default: + break; + } + + if (keycode > 0) { + input_report_key(ts->input_dev, keycode, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, keycode, 0); + input_sync(ts->input_dev); + } +} +#endif + +/******************************************************* + * Description: + * Novatek touchscreen parse device tree function. + * + * return: + * n.a. + *******************************************************/ +#ifdef CONFIG_OF +static void nvt_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + +#if NVT_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags); + NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio); +#endif + ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags); + NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio); + +} +#else +static void nvt_parse_dt(struct device *dev) +{ +#if NVT_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = NVTTOUCH_RST_PIN; +#endif + ts->irq_gpio = NVTTOUCH_INT_PIN; +} +#endif + +/******************************************************* + * Description: + * Novatek touchscreen config and request gpio + * + * return: + * Executive outcomes. 0---succeed. not 0---failed. + *******************************************************/ +static int nvt_gpio_config(struct nvt_ts_data *ts) +{ + int32_t ret = 0; + +#if NVT_TOUCH_SUPPORT_HW_RST + /* request RST-pin (Output/High) */ + if (gpio_is_valid(ts->reset_gpio)) { + ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_HIGH, "NVT-tp-rst"); + if (ret) { + NVT_ERR("Failed to request NVT-tp-rst GPIO\n"); + goto err_request_reset_gpio; + } + } +#endif + + /* request INT-pin (Input) */ + if (gpio_is_valid(ts->irq_gpio)) { + ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int"); + if (ret) { + NVT_ERR("Failed to request NVT-int GPIO\n"); + goto err_request_irq_gpio; + } + } + + return ret; + +err_request_irq_gpio: +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_free(ts->reset_gpio); +err_request_reset_gpio: +#endif + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen deconfig gpio + * + * return: + * n.a. + *******************************************************/ +static void nvt_gpio_deconfig(struct nvt_ts_data *ts) +{ + if (gpio_is_valid(ts->irq_gpio)) + gpio_free(ts->irq_gpio); +#if NVT_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_free(ts->reset_gpio); +#endif +} + +static uint8_t nvt_fw_recovery(uint8_t *point_data) +{ + uint8_t i = 0; + uint8_t detected = true; + + /* check pattern */ + for (i=1 ; i<7 ; i++) { + if (point_data[i] != 0x77) { + detected = false; + break; + } + } + + return detected; +} + +#if NVT_TOUCH_ESD_PROTECT +void nvt_esd_check_enable(uint8_t enable) +{ + /* update interrupt timer */ + irq_timer = jiffies; + /* clear esd_retry counter, if protect function is enabled */ + esd_retry = enable ? 0 : esd_retry; + /* enable/disable esd check flag */ + esd_check = enable; +} + +static void nvt_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - irq_timer); + + //NVT_ERR("esd_check = %d (retry %d)\n", esd_check, esd_retry); //DEBUG + + if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) { + mutex_lock(&ts->lock); + NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry); + /* do esd recovery, bootloader reset */ + nvt_bootloader_reset(); + mutex_unlock(&ts->lock); + /* update interrupt timer */ + irq_timer = jiffies; + /* update esd_retry counter */ + esd_retry++; + } + + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +} +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#define POINT_DATA_LEN 65 +/******************************************************* + * Description: + * Novatek touchscreen work function. + * + * return: + * n.a. + *******************************************************/ +static irqreturn_t nvt_ts_work_func(int irq, void *data) +{ + int32_t ret = -1; + uint8_t point_data[POINT_DATA_LEN + 1] = {0}; + uint32_t position = 0; + uint32_t input_x = 0; + uint32_t input_y = 0; + uint32_t input_w = 0; + uint32_t input_p = 0; + uint8_t input_id = 0; +#if MT_PROTOCOL_B + uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0}; +#endif /* MT_PROTOCOL_B */ + int32_t i = 0; + int32_t finger_cnt = 0; + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + pm_wakeup_event(&ts->input_dev->dev, 5000); + } +#endif + + mutex_lock(&ts->lock); + + ret = CTP_I2C_READ(ts->client, I2C_FW_Address, point_data, POINT_DATA_LEN + 1); + if (ret < 0) { + NVT_ERR("CTP_I2C_READ failed.(%d)\n", ret); + goto XFER_ERROR; + } + + if (nvt_fw_recovery(point_data)) { +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(true); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + goto XFER_ERROR; + } + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_ts_wakeup_gesture_report(input_id, point_data); + mutex_unlock(&ts->lock); + return IRQ_HANDLED; + } +#endif + + finger_cnt = 0; + + for (i = 0; i < ts->max_touch_num; i++) { + position = 1 + 6 * i; + input_id = (uint8_t)(point_data[position + 0] >> 3); + if ((input_id == 0) || (input_id > ts->max_touch_num)) + continue; + + if (((point_data[position] & 0x07) == 0x01) || ((point_data[position] & 0x07) == 0x02)) { //finger down (enter & moving) +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + input_x = (uint32_t)(point_data[position + 1] << 4) + (uint32_t) (point_data[position + 3] >> 4); + input_y = (uint32_t)(point_data[position + 2] << 4) + (uint32_t) (point_data[position + 3] & 0x0F); + if ((input_x < 0) || (input_y < 0)) + continue; + if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) + continue; + input_w = (uint32_t)(point_data[position + 4]); + if (input_w == 0) + input_w = 1; + if (i < 2) { + input_p = (uint32_t)(point_data[position + 5]) + (uint32_t)(point_data[i + 63] << 8); + if (input_p > TOUCH_FORCE_NUM) + input_p = TOUCH_FORCE_NUM; + } else { + input_p = (uint32_t)(point_data[position + 5]); + } + if (input_p == 0) + input_p = 1; + +#if MT_PROTOCOL_B + press_id[input_id - 1] = 1; + input_mt_slot(ts->input_dev, input_id - 1); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); +#else /* MT_PROTOCOL_B */ + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif /* MT_PROTOCOL_B */ + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); + +#if MT_PROTOCOL_B +#else /* MT_PROTOCOL_B */ + input_mt_sync(ts->input_dev); +#endif /* MT_PROTOCOL_B */ + + finger_cnt++; + } + } + +#if MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + if (press_id[i] != 1) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); +#else /* MT_PROTOCOL_B */ + if (finger_cnt == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(ts->input_dev); + } +#endif /* MT_PROTOCOL_B */ + +#if TOUCH_KEY_NUM > 0 + if (point_data[61] == 0xF8) { +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + for (i = 0; i < ts->max_button_num; i++) { + input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01)); + } + } else { + for (i = 0; i < ts->max_button_num; i++) { + input_report_key(ts->input_dev, touch_key_array[i], 0); + } + } +#endif + + input_sync(ts->input_dev); + +XFER_ERROR: + + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + +/******************************************************* + * Description: + * Novatek touchscreen check and stop crc reboot loop. + * + * return: + * n.a. +*******************************************************/ +void nvt_stop_crc_reboot(void) +{ + uint8_t buf[8] = {0}; + int32_t retry = 0; + + //read dummy buffer to check CRC fail reboot is happening or not + + //---change I2C index to prevent geting 0xFF, but not 0xFC--- + nvt_set_page(I2C_BLDR_Address, 0x1F64E); + + //---read to check if buf is 0xFC which means IC is in CRC reboot --- + buf[0] = 0x4E; + CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 4); + + if ((buf[1] == 0xFC) || + ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) { + + //IC is in CRC fail reboot loop, needs to be stopped! + for (retry = 5; retry > 0; retry--) { + + //---write i2c cmds to reset idle : 1st--- + buf[0]=0x00; + buf[1]=0xA5; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + + //---write i2c cmds to reset idle : 2rd--- + buf[0]=0x00; + buf[1]=0xA5; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + msleep(1); + + //---clear CRC_ERR_FLAG--- + nvt_set_page(I2C_BLDR_Address, 0x3F135); + + buf[0] = 0x35; + buf[1] = 0xA5; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 2); + + //---check CRC_ERR_FLAG--- + nvt_set_page(I2C_BLDR_Address, 0x3F135); + + buf[0] = 0x35; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 2); + + if (buf[1] == 0xA5) + break; + } + if (retry == 0) + NVT_ERR("CRC auto reboot is not able to be stopped!\n"); + } + + return; +} + +/******************************************************* + * Description: + * Novatek touchscreen check chip version trim function. + * + * return: + * Executive outcomes. 0---NVT IC. -1---not NVT IC. +*******************************************************/ +static int8_t nvt_ts_check_chip_ver_trim(uint32_t chip_ver_trim_addr) +{ + uint8_t buf[8] = {0}; + int32_t retry = 0; + int32_t list = 0; + int32_t i = 0; + int32_t found_nvt_chip = 0; + int32_t ret = -1; + + nvt_bootloader_reset(); // NOT in retry loop + + //---Check for 5 times--- + for (retry = 5; retry > 0; retry--) { + nvt_sw_reset_idle(); + + buf[0] = 0x00; + buf[1] = 0x35; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + msleep(10); + + nvt_set_page(I2C_BLDR_Address, chip_ver_trim_addr); + + buf[0] = chip_ver_trim_addr & 0xFF; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 7); + NVT_LOG("buf[1]=0x%02X, buf[2]=0x%02X, buf[3]=0x%02X, buf[4]=0x%02X, buf[5]=0x%02X, buf[6]=0x%02X\n", + buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + //---Stop CRC check to prevent IC auto reboot--- + if ((buf[1] == 0xFC) || + ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) { + nvt_stop_crc_reboot(); + continue; + } + + // compare read chip id on supported list + for (list = 0; list < (sizeof(trim_id_table) / sizeof(struct nvt_ts_trim_id_table)); list++) { + found_nvt_chip = 0; + + // compare each byte + for (i = 0; i < NVT_ID_BYTE_MAX; i++) { + if (trim_id_table[list].mask[i]) { + if (buf[i + 1] != trim_id_table[list].id[i]) + break; + } + } + + if (i == NVT_ID_BYTE_MAX) { + found_nvt_chip = 1; + } + + if (found_nvt_chip) { + NVT_LOG("This is NVT touch IC\n"); + ts->mmap = trim_id_table[list].mmap; + ts->carrier_system = trim_id_table[list].hwinfo->carrier_system; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(10); + } + +out: + return ret; +} + +#if defined(CONFIG_DRM) +static int nvt_ts_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 0; + + 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; + NVT_LOG(" %s:find\n", __func__); + return 0; + } + } + + NVT_ERR(" %s: not find\n", __func__); + return -ENODEV; +} + +static int nvt_ts_check_default_tp(struct device_node *dt, const char *prop) +{ + const char **active_tp = NULL; + int count, tmp, score = 0; + const char *active; + int ret, i; + + count = of_property_count_strings(dt->parent, prop); + if (count <= 0 || count > 3) + return -ENODEV; + + active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!active_tp) { + NVT_ERR("FTS alloc failed\n"); + return -ENOMEM; + } + + ret = of_property_read_string_array(dt->parent, prop, + active_tp, count); + if (ret < 0) { + NVT_ERR("fail to read %s %d\n", prop, ret); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < count; i++) { + active = active_tp[i]; + if (active != NULL) { + tmp = of_device_is_compatible(dt, active); + if (tmp > 0) + score++; + } + } + + if (score <= 0) { + NVT_ERR("not match this driver\n"); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} +#endif + +/******************************************************* + * Description: + * Novatek touchscreen driver probe function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed + *******************************************************/ +static int32_t nvt_ts_late_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t ret = 0; +#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE) + int32_t retry = 0; +#endif + //---request and config GPIOs--- + ret = nvt_gpio_config(ts); + if (ret) { + NVT_ERR("gpio config error!\n"); + goto err_gpio_config_failed; + } + + //---check chip version trim--- + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR); + if (ret) { + NVT_LOG("try to check from old chip ver trim address\n"); + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_OLD_ADDR); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + goto err_chipvertrim_failed; + } + } + + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + + //---allocate input device--- + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + NVT_ERR("allocate input device failed\n"); + ret = -ENOMEM; + goto err_input_dev_alloc_failed; + } + + ts->max_touch_num = TOUCH_MAX_FINGER_NUM; + +#if TOUCH_KEY_NUM > 0 + ts->max_button_num = TOUCH_KEY_NUM; +#endif + + ts->int_trigger_type = INT_TRIGGER_TYPE; + + + //---set input device info.--- + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + +#if MT_PROTOCOL_B + input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0); +#endif + + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, TOUCH_FORCE_NUM, 0, 0); //pressure = TOUCH_FORCE_NUM + +#if TOUCH_MAX_FINGER_NUM > 1 + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //area = 255 + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); +#if MT_PROTOCOL_B + // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it +#else + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); +#endif //MT_PROTOCOL_B +#endif //TOUCH_MAX_FINGER_NUM > 1 + +#if TOUCH_KEY_NUM > 0 + for (retry = 0; retry < ts->max_button_num; retry++) { + input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]); + } +#endif + +#if WAKEUP_GESTURE + for (retry = 0; retry < ARRAY_SIZE(gesture_key_array); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } +#endif + + snprintf(ts->phys, sizeof(ts->phys), "input/ts"); + ts->input_dev->name = NVT_TS_NAME; + ts->input_dev->phys = ts->phys; + ts->input_dev->id.bustype = BUS_I2C; + + //---register input device--- + ret = input_register_device(ts->input_dev); + if (ret) { + NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret); + goto err_input_register_device_failed; + } + + //---set int-pin & request irq--- + client->irq = gpio_to_irq(ts->irq_gpio); + if (client->irq) { + NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type); + ts->irq_enabled = true; + ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func, + ts->int_trigger_type | IRQF_ONESHOT, NVT_I2C_NAME, ts); + if (ret != 0) { + NVT_ERR("request irq failed. ret=%d\n", ret); + goto err_int_request_failed; + } else { + nvt_irq_enable(false); + NVT_LOG("request irq %d succeed\n", client->irq); + } + } + +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 1); +#endif + +#if BOOT_UPDATE_FIRMWARE + nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!nvt_fwu_wq) { + NVT_ERR("nvt_fwu_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_fwu_wq_failed; + } + INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware); + // please make sure boot update start after display reset(RESX) sequence + queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000)); +#endif + + NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT); +#if NVT_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func); + nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1); + if (!nvt_esd_check_wq) { + NVT_ERR("nvt_esd_check_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_esd_check_wq_failed; + } + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + //---set device node--- +#if NVT_TOUCH_PROC + ret = nvt_flash_proc_init(); + if (ret != 0) { + NVT_ERR("nvt flash proc init failed. ret=%d\n", ret); + goto err_flash_proc_init_failed; + } +#endif + +#if NVT_TOUCH_EXT_PROC + ret = nvt_extra_proc_init(); + if (ret != 0) { + NVT_ERR("nvt extra proc init failed. ret=%d\n", ret); + goto err_extra_proc_init_failed; + } +#endif + +#if NVT_TOUCH_MP + ret = nvt_mp_proc_init(); + if (ret != 0) { + NVT_ERR("nvt mp proc init failed. ret=%d\n", ret); + goto err_mp_proc_init_failed; + } +#endif + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_irq_enable(true); + + return 0; + +#if NVT_TOUCH_MP +nvt_mp_proc_deinit(); +err_mp_proc_init_failed: +#endif +#if NVT_TOUCH_EXT_PROC +nvt_extra_proc_deinit(); +err_extra_proc_init_failed: +#endif +#if NVT_TOUCH_PROC +nvt_flash_proc_deinit(); +err_flash_proc_init_failed: +#endif +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +err_create_nvt_esd_check_wq_failed: +#endif +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +err_create_nvt_fwu_wq_failed: +#endif +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + free_irq(client->irq, ts); +err_int_request_failed: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; +err_input_register_device_failed: + if (ts->input_dev) { + input_free_device(ts->input_dev); + ts->input_dev = NULL; + } +err_input_dev_alloc_failed: +err_chipvertrim_failed: + nvt_gpio_deconfig(ts); +err_gpio_config_failed: + NVT_ERR("ret = %d\n", ret); + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen driver probe function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed + *******************************************************/ +static int32_t nvt_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t ret = 0; +#if defined(CONFIG_DRM) + struct device_node *dp = client->dev.of_node; +#endif + + NVT_LOG("start\n"); + +#if defined(CONFIG_DRM) + if (nvt_ts_check_dt(dp)) { + if (!nvt_ts_check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } +#endif + + ts = devm_kzalloc(&client->dev, sizeof(struct nvt_ts_data), GFP_KERNEL); + if (ts == NULL) { + NVT_ERR("failed to allocated memory for nvt ts data\n"); + return -ENOMEM; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + //---parse dts--- + nvt_parse_dt(&client->dev); + + //---check i2c func.--- + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + NVT_ERR("i2c_check_functionality failed. (no I2C_FUNC_I2C)\n"); + return -ENODEV; + } + + mutex_init(&ts->lock); + mutex_init(&ts->xbuf_lock); + + ts->id = id; + +#if defined(CONFIG_DRM) + nvt_i2c_register_for_panel_events(client->dev.of_node, ts); +#elif defined(_MSM_DRM_NOTIFY_H_) + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + ret = msm_drm_register_client(&ts->drm_notif); + if (ret) { + NVT_ERR("register drm_notifier failed. ret=%d\n", ret); + goto err_register_drm_notif_failed; + } +#else + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + if (ret) { + NVT_ERR("register fb_notifier failed. ret=%d\n", ret); + goto err_register_fb_notif_failed; + } +#endif + NVT_LOG("end\n"); + return 0; +#if defined(CONFIG_DRM) + +#elif defined(_MSM_DRM_NOTIFY_H_) +err_register_drm_notif_failed: +#else +err_register_fb_notif_failed: +#endif + return -ENODEV; +} + +/******************************************************* + * Description: + * Novatek touchscreen driver release function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_ts_remove(struct i2c_client *client) +{ + NVT_LOG("Removing driver...\n"); + +#if defined(CONFIG_DRM) + if (active_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_TOUCH_MP + nvt_mp_proc_deinit(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_deinit(); +#endif +#if NVT_TOUCH_PROC + nvt_flash_proc_deinit(); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +#endif + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +#endif + +#if WAKEUP_GESTURE + if (ts->input_dev) + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + + nvt_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_gpio_deconfig(ts); + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + i2c_set_clientdata(client, NULL); + + if (ts) { + kfree(ts); + ts = NULL; + } + + return 0; +} + +static void nvt_ts_shutdown(struct i2c_client *client) +{ + NVT_LOG("Shutdown driver...\n"); + + nvt_irq_enable(false); + +#if defined(CONFIG_DRM) + if (active_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_TOUCH_MP + nvt_mp_proc_deinit(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_deinit(); +#endif +#if NVT_TOUCH_PROC + nvt_flash_proc_deinit(); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +#endif + +#if WAKEUP_GESTURE + if (ts->input_dev) + device_init_wakeup(&ts->input_dev->dev, 0); +#endif +} + +/******************************************************* + * Description: + * Novatek touchscreen driver suspend function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_ts_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; +#if MT_PROTOCOL_B + uint32_t i = 0; +#endif + + if (!bTouchIsAwake) { + NVT_LOG("Touch is already suspend\n"); + return 0; + } + +#if !WAKEUP_GESTURE + nvt_irq_enable(false); +#endif + +#if NVT_TOUCH_ESD_PROTECT + NVT_LOG("cancel delayed work sync\n"); + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + bTouchIsAwake = 0; + +#if WAKEUP_GESTURE + //---write command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else // WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); +#endif // WAKEUP_GESTURE + + mutex_unlock(&ts->lock); + + /* release all touches */ +#if MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } +#endif + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#if !MT_PROTOCOL_B + input_mt_sync(ts->input_dev); +#endif + input_sync(ts->input_dev); + + msleep(50); + + NVT_LOG("end\n"); + + return 0; +} + +/******************************************************* + * Description: + * Novatek touchscreen driver resume function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_ts_resume(struct device *dev) +{ + NVT_LOG("start\n"); + + if (bTouchIsAwake || ts->fw_ver == 0) { + nvt_ts_late_probe(ts->client, ts->id); + NVT_LOG("nvt_ts_late_probe\n"); + return 0; + } + + mutex_lock(&ts->lock); + + // make sure display reset(RESX) sequence and dsi cmds sent before this +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + // NT36772 IC due to no boot-load when RESX/TP_RESX + // nvt_bootloader_reset(); + if (nvt_check_fw_reset_state(RESET_STATE_REK)) { + NVT_ERR("FW is not ready! Try to bootloader reset...\n"); + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_REK); + } + +#if !WAKEUP_GESTURE + nvt_irq_enable(true); +#endif + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + bTouchIsAwake = 1; + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + +#if defined(CONFIG_DRM) + +static void nvt_i2c_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct nvt_ts_data *ts = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + NVT_LOG("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + NVT_LOG("resume notification pre commit\n"); + else + nvt_ts_resume(&ts->client->dev); + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) + nvt_ts_suspend(&ts->client->dev); + else + NVT_LOG("suspend notification post commit\n"); + break; + + case DRM_PANEL_EVENT_BLANK_LP: + NVT_LOG("received lp event\n"); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + NVT_LOG("Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + NVT_LOG("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct nvt_ts_data *ts = + container_of(self, struct nvt_ts_data, drm_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + if (evdata->data && ts) { + blank = evdata->data; + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_suspend(&ts->client->dev); + } + } else if (event == MSM_DRM_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + } + + return 0; +} + +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct nvt_ts_data *ts = + container_of(self, struct nvt_ts_data, fb_notif); + + if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#endif + +static const struct i2c_device_id nvt_ts_id[] = { + { NVT_I2C_NAME, 0 }, + { } +}; + +#ifdef CONFIG_OF +static struct of_device_id nvt_match_table[] = { + { .compatible = "novatek,NVT-ts",}, + { }, +}; +#endif + +static struct i2c_driver nvt_i2c_driver = { + .probe = nvt_ts_probe, + .remove = nvt_ts_remove, + .shutdown = nvt_ts_shutdown, + .id_table = nvt_ts_id, + .driver = { + .name = NVT_I2C_NAME, +#ifdef CONFIG_OF + .of_match_table = nvt_match_table, +#endif + }, +}; + +/******************************************************* + * Description: + * Driver Install function. + * + * return: + * Executive Outcomes. 0---succeed. not 0---failed. + ********************************************************/ +static int32_t __init nvt_driver_init(void) +{ + int32_t ret = 0; + + NVT_LOG("start\n"); + //---add i2c driver--- + ret = i2c_add_driver(&nvt_i2c_driver); + if (ret) { + NVT_ERR("failed to add i2c driver"); + goto err_driver; + } + + NVT_LOG("finished\n"); + +err_driver: + return ret; +} + +/******************************************************* + * Description: + * Driver uninstall function. + * + * return: + * n.a. + ********************************************************/ +static void __exit nvt_driver_exit(void) +{ + i2c_del_driver(&nvt_i2c_driver); +} + +//late_initcall(nvt_driver_init); +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); + +#else /* NT36XXX_SPI */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DRM) +#include +#endif + +#include "nt36xxx.h" +#if NVT_SPI_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +#if NVT_SPI_TOUCH_ESD_PROTECT +static struct delayed_work nvt_spi_esd_check_work; +static struct workqueue_struct *nvt_spi_esd_check_wq; +static unsigned long nvt_spi_irq_timer; +uint8_t nvt_spi_esd_check; +uint8_t nvt_spi_esd_retry; +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +struct nvt_spi_data_t *nvt_spi_data; + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_spi_fwu_wq; +#endif + +#if defined(CONFIG_DRM) +static struct drm_panel *active_spi_panel; +static void nvt_spi_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *event, void *client_data); + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback( + struct notifier_block *self, unsigned long event, void *data); +#else +static int nvt_fb_notifier_callback( + struct notifier_block *self, unsigned long event, void *data); +#endif + +static uint32_t NVT_SPI_ENG_RST_ADDR = 0x7FFF80; +uint32_t NVT_SPI_SWRST_N8_ADDR; //read from dtsi +uint32_t NVT_SPI_RD_FAST_ADDR; //read from dtsi + +#if NVT_SPI_TOUCH_KEY_NUM > 0 +const uint16_t nvt_spi_touch_key_array[NVT_SPI_TOUCH_KEY_NUM] = { + KEY_BACK, + KEY_HOME, + KEY_MENU +}; +#endif + +#if NVT_SPI_WAKEUP_GESTURE +const uint16_t nvt_spi_gesture_key_array[] = { + KEY_POWER, //GESTURE_WORD_C + KEY_POWER, //GESTURE_WORD_W + KEY_POWER, //GESTURE_WORD_V + KEY_POWER, //GESTURE_DOUBLE_CLICK + KEY_POWER, //GESTURE_WORD_Z + KEY_POWER, //GESTURE_WORD_M + KEY_POWER, //GESTURE_WORD_O + KEY_POWER, //GESTURE_WORD_e + KEY_POWER, //GESTURE_WORD_S + KEY_POWER, //GESTURE_SLIDE_UP + KEY_POWER, //GESTURE_SLIDE_DOWN + KEY_POWER, //GESTURE_SLIDE_LEFT + KEY_POWER, //GESTURE_SLIDE_RIGHT +}; +#endif + +static uint8_t bTouchIsAwake; + +#if defined(CONFIG_DRM) +static void nvt_spi_register_for_panel_events(struct device_node *dp, + struct nvt_spi_data_t *ts) +{ + void *cookie = NULL; + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_spi_panel, + &nvt_spi_panel_notifier_callback, ts); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + ts->notifier_cookie = cookie; +} +#endif + +/* + ******************************************************* + * Description: + * Novatek touchscreen irq enable/disable function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_irq_enable(bool enable) +{ + struct irq_desc *desc; + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (enable) { + if (!ts->irq_enabled) { + enable_irq(ts->client->irq); + ts->irq_enabled = true; + } + } else { + if (ts->irq_enabled) { + disable_irq(ts->client->irq); + ts->irq_enabled = false; + } + } + + desc = irq_to_desc(ts->client->irq); + NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen spi read/write core function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static inline int32_t nvt_spi_read_write( + struct spi_device *client, uint8_t *buf, size_t len, enum NVT_SPI_RW rw) +{ + struct spi_message m; + struct spi_transfer t = { + .len = len, + }; + struct nvt_spi_data_t *ts = nvt_spi_data; + + memset(ts->xbuf, 0, len + NVT_SPI_DUMMY_BYTES); + memcpy(ts->xbuf, buf, len); + + switch (rw) { + case NVT_SPI_READ: + t.tx_buf = ts->xbuf; + t.rx_buf = ts->rbuf; + t.len = (len + NVT_SPI_DUMMY_BYTES); + break; + + case NVT_SPI_WRITE: + t.tx_buf = ts->xbuf; + break; + } + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spi_sync(client, &m); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen spi read function. + * + * return: + * Executive outcomes. 2---succeed. -5---I/O error + ****************************************************** + */ +int32_t nvt_spi_read(uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = NVT_SPI_READ_MASK(buf[0]); + + while (retries < 5) { + ret = nvt_spi_read_write(ts->client, buf, len, NVT_SPI_READ); + if (ret == 0) + break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("read error, ret=%d\n", ret); + ret = -EIO; + } else + memcpy((buf+1), (ts->rbuf+2), (len-1)); + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen spi write function. + * + * return: + * Executive outcomes. 1---succeed. -5---I/O error + ****************************************************** + */ +int32_t nvt_spi_write(uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = NVT_SPI_WRITE_MASK(buf[0]); + + while (retries < 5) { + ret = nvt_spi_read_write(ts->client, buf, len, NVT_SPI_WRITE); + if (ret == 0) + break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("error, ret=%d\n", ret); + ret = -EIO; + } + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen set index/page/addr address. + * + * return: + * Executive outcomes. 0---succeed. -5---access fail. + ****************************************************** + */ +int32_t nvt_spi_set_page(uint32_t addr) +{ + uint8_t buf[4] = {0}; + + buf[0] = 0xFF; //set index/page/addr command + buf[1] = (addr >> 15) & 0xFF; + buf[2] = (addr >> 7) & 0xFF; + + return nvt_spi_write(buf, 3); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen write data to specify address. + * + * return: + * Executive outcomes. 0---succeed. -5---access fail. + ******************************************************* + */ +int32_t nvt_spi_write_addr(uint32_t addr, uint8_t data) +{ + int32_t ret = 0; + uint8_t buf[4] = {0}; + + //---set xdata index--- + buf[0] = 0xFF; //set index/page/addr command + buf[1] = (addr >> 15) & 0xFF; + buf[2] = (addr >> 7) & 0xFF; + ret = nvt_spi_write(buf, 3); + if (ret) { + NVT_ERR("set page 0x%06X failed, ret = %d\n", addr, ret); + return ret; + } + + //---write data to index--- + buf[0] = addr & (0x7F); + buf[1] = data; + ret = nvt_spi_write(buf, 2); + if (ret) { + NVT_ERR("write data to 0x%06X failed, ret = %d\n", addr, ret); + return ret; + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen enable hw bld crc function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_bld_crc_enable(void) +{ + uint8_t buf[4] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to BLD_CRC_EN_ADDR--- + nvt_spi_set_page(ts->mmap->BLD_CRC_EN_ADDR); + + //---read data from index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + //---write data to index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = buf[1] | (0x01 << 7); + nvt_spi_write(buf, 2); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen clear status & enable fw crc function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_fw_crc_enable(void) +{ + uint8_t buf[4] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR); + + //---clear fw reset status--- + buf[0] = NVT_SPI_EVENT_MAP_RESET_COMPLETE & (0x7F); + buf[1] = 0x00; + nvt_spi_write(buf, 2); + + //---enable fw crc--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD & (0x7F); + buf[1] = 0xAE; //enable fw crc command + nvt_spi_write(buf, 2); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen set boot ready function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_boot_ready(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---write BOOT_RDY status cmds--- + nvt_spi_write_addr(ts->mmap->BOOT_RDY_ADDR, 1); + + mdelay(5); + + if (!ts->hw_crc) { + //---write BOOT_RDY status cmds--- + nvt_spi_write_addr(ts->mmap->BOOT_RDY_ADDR, 0); + + //---write POR_CD cmds--- + nvt_spi_write_addr(ts->mmap->POR_CD_ADDR, 0xA0); + } +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen enable auto copy mode function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_tx_auto_copy_mode(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---write TX_AUTO_COPY_EN cmds--- + nvt_spi_write_addr(ts->mmap->TX_AUTO_COPY_EN, 0x69); + + NVT_ERR("tx auto copy mode enable\n"); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen check spi dma tx info function. + * + * return: + * N/A. + ****************************************************** + */ +int32_t nvt_spi_check_spi_dma_tx_info(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 200; + struct nvt_spi_data_t *ts = nvt_spi_data; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->SPI_DMA_TX_INFO); + + //---read fw status--- + buf[0] = ts->mmap->SPI_DMA_TX_INFO & 0x7F; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(1000, 1100); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen eng reset cmd function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_eng_reset(void) +{ + //---eng reset cmds to ENG_RST_ADDR--- + nvt_spi_write_addr(NVT_SPI_ENG_RST_ADDR, 0x5A); + + mdelay(1); //wait tMCU_Idle2TP_REX_Hi after TP_RST +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen reset MCU function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_sw_reset(void) +{ + //---software reset cmds to SWRST_N8_ADDR--- + nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0x55); + + msleep(20); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen reset MCU then into idle mode + * function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_sw_reset_idle(void) +{ + //---MCU idle cmds to SWRST_N8_ADDR--- + nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0xAA); + + msleep(20); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen reset MCU (boot) function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_bootloader_reset(void) +{ + //---reset cmds to SWRST_N8_ADDR--- + nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0x69); + + mdelay(5); //wait tBRST2FR after Bootload RST + + if (NVT_SPI_RD_FAST_ADDR) { + /* disable SPI_RD_FAST */ + nvt_spi_write_addr(NVT_SPI_RD_FAST_ADDR, 0x00); + } + + NVT_LOG("end\n"); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen clear FW status function. + * + * return: + * Executive outcomes. 0---succeed. -1---fail. + ****************************************************** + */ +int32_t nvt_spi_clear_fw_status(void) +{ + uint32_t addr; + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 20; + struct nvt_spi_data_t *ts = nvt_spi_data; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + addr = ts->mmap->EVENT_BUF_ADDR; + nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---clear fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + nvt_spi_write(buf, 2); + + //---read fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen check FW status function. + + * return: + * Executive outcomes. 0---succeed. -1---failed. + ****************************************************** + */ +int32_t nvt_spi_check_fw_status(void) +{ + uint32_t addr; + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 50; + struct nvt_spi_data_t *ts = nvt_spi_data; + + usleep_range(20000, 21000); + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + addr = ts->mmap->EVENT_BUF_ADDR; + nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + nvt_spi_read(buf, 2); + + if ((buf[1] & 0xF0) == 0xA0) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen check FW reset state function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + ****************************************************** + */ +int32_t nvt_spi_check_fw_reset_state(enum NVT_SPI_RST_COMPLETE_STATE check_reset_state) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + int32_t retry_max = (check_reset_state == NVT_SPI_RESET_STATE_INIT) ? 10 : 50; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_RESET_COMPLETE); + + while (1) { + //---read reset state--- + buf[0] = NVT_SPI_EVENT_MAP_RESET_COMPLETE; + buf[1] = 0x00; + nvt_spi_read(buf, 6); + + if ((buf[1] >= check_reset_state) && (buf[1] <= NVT_SPI_RESET_STATE_MAX)) { + ret = 0; + break; + } + + retry++; + if (unlikely(retry > retry_max)) { + NVT_ERR("error, retry=%d, buf[1]=0x%02X,0x%02X,0x%02X,0x%02X,0x%02X\n", + retry, buf[1], buf[2], buf[3], buf[4], buf[5]); + ret = -1; + break; + } + + usleep_range(10000, 11000); + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen get firmware related information + * function. + * + * return: + * Executive outcomes. 0---success. -1---fail. + ****************************************************** + */ +int32_t nvt_spi_get_fw_info(void) +{ + uint8_t buf[64] = {0}; + uint32_t retry_count = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + +info_retry: + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_FWINFO); + + //---read fw info--- + buf[0] = NVT_SPI_EVENT_MAP_FWINFO; + nvt_spi_read(buf, 39); + if ((buf[1] + buf[2]) != 0xFF) { + NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]); + if (retry_count < 3) { + retry_count++; + NVT_ERR("retry_count=%d\n", retry_count); + goto info_retry; + } else { + ts->fw_ver = 0; + ts->abs_x_max = NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT; + ts->max_button_num = NVT_SPI_TOUCH_KEY_NUM; + NVT_ERR("Set default fw_ver=%d, abs_x_max=%d, ", + ts->fw_ver, ts->abs_x_max); + NVT_ERR("abs_y_max=%d, max_button_num=%d!\n", + ts->abs_y_max, ts->max_button_num); + ret = -1; + goto out; + } + } + ts->fw_ver = buf[1]; + ts->x_num = buf[3]; + ts->y_num = buf[4]; + ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]); + ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]); + ts->max_button_num = buf[11]; + ts->nvt_pid = (uint16_t)((buf[36] << 8) | buf[35]); + if (ts->pen_support) { + ts->x_gang_num = buf[37]; + ts->y_gang_num = buf[38]; + } + + NVT_ERR("fw_ver=0x%02X, fw_type=0x%02X, PID=0x%04X\n", ts->fw_ver, buf[14], ts->nvt_pid); + + ret = 0; +out: + + return ret; +} + +/* + ****************************************************** + * Create Device Node (Proc Entry) + ****************************************************** + */ +#if NVT_SPI_TOUCH_PROC +static struct proc_dir_entry *nvt_spi_proc_entry; +#define DEVICE_NAME "NVTSPI" + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI read function. + * + * return: + * Executive outcomes. 2---succeed. -5,-14---failed. + ****************************************************** + */ +static ssize_t nvt_spi_flash_read( + struct file *file, char __user *buff, size_t count, loff_t *offp) +{ + uint8_t *str = NULL; + int32_t ret = 0; + int32_t retries = 0; + int8_t spi_wr = 0; + uint8_t *buf; + + if ((count > NVT_SPI_TRANSFER_LEN + 3) || (count < 3)) { + NVT_ERR("invalid transfer len!\n"); + return -EFAULT; + } + + /* allocate buffer for spi transfer */ + str = kzalloc((count), GFP_KERNEL); + if (str == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + goto kzalloc_failed; + } + + buf = kzalloc((count), GFP_KERNEL | GFP_DMA); + if (buf == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + kfree(str); + str = NULL; + goto kzalloc_failed; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + ret = -EFAULT; + goto out; + } + +#if NVT_SPI_TOUCH_ESD_PROTECT + /* + * stop esd check work to avoid case that 0x77 report righ after here to enable esd + * check again finally lead to trigger esd recovery bootloader reset + */ + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + spi_wr = str[0] >> 7; + memcpy(buf, str+2, ((str[0] & 0x7F) << 8) | str[1]); + + if (spi_wr == NVT_SPI_WRITE) { //SPI write + while (retries < 20) { + ret = nvt_spi_write(buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + ret = -EIO; + goto out; + } + } else if (spi_wr == NVT_SPI_READ) { //SPI read + while (retries < 20) { + ret = nvt_spi_read(buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + memcpy(str+2, buf, ((str[0] & 0x7F) << 8) | str[1]); + // copy buff to user if spi transfer + if (retries < 20) { + if (copy_to_user(buff, str, count)) { + ret = -EFAULT; + goto out; + } + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + ret = -EIO; + goto out; + } + } else { + NVT_ERR("Call error, str[0]=%d\n", str[0]); + ret = -EFAULT; + goto out; + } + +out: + kfree(str); + kfree(buf); +kzalloc_failed: + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI open function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + ****************************************************** + */ +static int32_t nvt_spi_flash_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_flash_data *dev; + + dev = kmalloc(sizeof(struct nvt_spi_flash_data), GFP_KERNEL); + if (dev == NULL) { + NVT_ERR("Failed to allocate memory for nvt flash data\n"); + return -ENOMEM; + } + + rwlock_init(&dev->lock); + file->private_data = dev; + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI close function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_flash_close(struct inode *inode, struct file *file) +{ + struct nvt_spi_flash_data *dev = file->private_data; + + kfree(dev); + + return 0; +} + +static const struct proc_ops nvt_flash_fops_spi = { + .proc_open = nvt_spi_flash_open, + .proc_release = nvt_spi_flash_close, + .proc_read = nvt_spi_flash_read, +}; + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI initial function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + ****************************************************** + */ +static int32_t nvt_spi_flash_proc_init(void) +{ + nvt_spi_proc_entry = proc_create(DEVICE_NAME, 0444, NULL, &nvt_flash_fops_spi); + if (nvt_spi_proc_entry == NULL) { + NVT_ERR("Failed!\n"); + return -ENOMEM; + } + + NVT_LOG("Succeeded!\n"); + + NVT_LOG("============================================================\n"); + NVT_LOG("Create /proc/%s\n", DEVICE_NAME); + NVT_LOG("============================================================\n"); + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI deinitial function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_flash_proc_deinit(void) +{ + if (nvt_spi_proc_entry != NULL) { + remove_proc_entry(DEVICE_NAME, NULL); + nvt_spi_proc_entry = NULL; + NVT_LOG("Removed /proc/%s\n", DEVICE_NAME); + } +} +#endif + +#if NVT_SPI_WAKEUP_GESTURE +#define GESTURE_WORD_C 12 +#define GESTURE_WORD_W 13 +#define GESTURE_WORD_V 14 +#define GESTURE_DOUBLE_CLICK 15 +#define GESTURE_WORD_Z 16 +#define GESTURE_WORD_M 17 +#define GESTURE_WORD_O 18 +#define GESTURE_WORD_e 19 +#define GESTURE_WORD_S 20 +#define GESTURE_SLIDE_UP 21 +#define GESTURE_SLIDE_DOWN 22 +#define GESTURE_SLIDE_LEFT 23 +#define GESTURE_SLIDE_RIGHT 24 +/* customized gesture id */ +#define DATA_PROTOCOL 30 + +/* function page definition */ +#define FUNCPAGE_GESTURE 1 + +/* + ******************************************************* + * Description: + * Novatek touchscreen wake up gesture key report function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data) +{ + uint32_t keycode = 0; + uint8_t func_type = data[2]; + uint8_t func_id = data[3]; + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* support fw specifal data protocol */ + if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) + gesture_id = func_id; + else if (gesture_id > DATA_PROTOCOL) { + NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n", + gesture_id, func_type, func_id); + return; + } + + NVT_LOG("gesture_id = %d\n", gesture_id); + + switch (gesture_id) { + case GESTURE_WORD_C: + NVT_LOG("Gesture : Word-C.\n"); + keycode = nvt_spi_gesture_key_array[0]; + break; + case GESTURE_WORD_W: + NVT_LOG("Gesture : Word-W.\n"); + keycode = nvt_spi_gesture_key_array[1]; + break; + case GESTURE_WORD_V: + NVT_LOG("Gesture : Word-V.\n"); + keycode = nvt_spi_gesture_key_array[2]; + break; + case GESTURE_DOUBLE_CLICK: + NVT_LOG("Gesture : Double Click.\n"); + keycode = nvt_spi_gesture_key_array[3]; + break; + case GESTURE_WORD_Z: + NVT_LOG("Gesture : Word-Z.\n"); + keycode = nvt_spi_gesture_key_array[4]; + break; + case GESTURE_WORD_M: + NVT_LOG("Gesture : Word-M.\n"); + keycode = nvt_spi_gesture_key_array[5]; + break; + case GESTURE_WORD_O: + NVT_LOG("Gesture : Word-O.\n"); + keycode = nvt_spi_gesture_key_array[6]; + break; + case GESTURE_WORD_e: + NVT_LOG("Gesture : Word-e.\n"); + keycode = nvt_spi_gesture_key_array[7]; + break; + case GESTURE_WORD_S: + NVT_LOG("Gesture : Word-S.\n"); + keycode = nvt_spi_gesture_key_array[8]; + break; + case GESTURE_SLIDE_UP: + NVT_LOG("Gesture : Slide UP.\n"); + keycode = nvt_spi_gesture_key_array[9]; + break; + case GESTURE_SLIDE_DOWN: + NVT_LOG("Gesture : Slide DOWN.\n"); + keycode = nvt_spi_gesture_key_array[10]; + break; + case GESTURE_SLIDE_LEFT: + NVT_LOG("Gesture : Slide LEFT.\n"); + keycode = nvt_spi_gesture_key_array[11]; + break; + case GESTURE_SLIDE_RIGHT: + NVT_LOG("Gesture : Slide RIGHT.\n"); + keycode = nvt_spi_gesture_key_array[12]; + break; + default: + break; + } + + if (keycode > 0) { + input_report_key(ts->input_dev, keycode, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, keycode, 0); + input_sync(ts->input_dev); + } +} +#endif + +/* + ****************************************************** + * Description: + * Novatek touchscreen parse device tree function. + * + * return: + * n.a. + ****************************************************** + */ +#ifdef CONFIG_OF +static int32_t nvt_spi_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags); + NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio); +#endif + ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags); + NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio); + + ts->pen_support = of_property_read_bool(np, "novatek,pen-support"); + NVT_LOG("novatek,pen-support=%d\n", ts->pen_support); + + ts->wgp_stylus = of_property_read_bool(np, "novatek,wgp-stylus"); + NVT_LOG("novatek,wgp-stylus=%d\n", ts->wgp_stylus); + + ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &NVT_SPI_SWRST_N8_ADDR); + if (ret) { + NVT_ERR("error reading novatek,swrst-n8-addr. ret=%d\n", ret); + return ret; + } + NVT_LOG("SWRST_N8_ADDR=0x%06X\n", NVT_SPI_SWRST_N8_ADDR); + + ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &NVT_SPI_RD_FAST_ADDR); + if (ret) { + NVT_LOG("not support novatek,spi-rd-fast-addr\n"); + NVT_SPI_RD_FAST_ADDR = 0; + ret = 0; + } else + NVT_LOG("SPI_RD_FAST_ADDR=0x%06X\n", NVT_SPI_RD_FAST_ADDR); + + return ret; +} +#else +static int32_t nvt_spi_parse_dt(struct device *dev) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = NVTTOUCH_RST_PIN; +#endif + ts->irq_gpio = NVTTOUCH_INT_PIN; + return 0; +} +#endif + +/* + ****************************************************** + * Description: + * Novatek touchscreen config and request gpio + * + * return: + * Executive outcomes. 0---succeed. not 0---failed. + ****************************************************** + */ +static int nvt_spi_gpio_config(struct nvt_spi_data_t *ts) +{ + int32_t ret = 0; + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + /* request RST-pin (Output/High) */ + if (gpio_is_valid(ts->reset_gpio)) { + ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_LOW, "NVT-tp-rst"); + if (ret) { + NVT_ERR("Failed to request NVT-tp-rst GPIO\n"); + goto err_request_reset_gpio; + } + } +#endif + + /* request INT-pin (Input) */ + if (gpio_is_valid(ts->irq_gpio)) { + ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int"); + if (ret) { + NVT_ERR("Failed to request NVT-int GPIO\n"); + goto err_request_irq_gpio; + } + } + + return ret; + +err_request_irq_gpio: +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_free(ts->reset_gpio); +err_request_reset_gpio: +#endif + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen deconfig gpio + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_gpio_deconfig(struct nvt_spi_data_t *ts) +{ + if (gpio_is_valid(ts->irq_gpio)) + gpio_free(ts->irq_gpio); +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_free(ts->reset_gpio); +#endif +} + +static uint8_t nvt_spi_fw_recovery(uint8_t *point_data) +{ + uint8_t i = 0; + uint8_t detected = true; + + /* check pattern */ + for (i = 1; i < 7; i++) { + if (point_data[i] != 0x77) { + detected = false; + break; + } + } + + return detected; +} + +#if NVT_SPI_TOUCH_ESD_PROTECT +void nvt_spi_esd_check_enable(uint8_t enable) +{ + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; + /* clear esd_retry counter, if protect function is enabled */ + nvt_spi_esd_retry = enable ? 0 : nvt_spi_esd_retry; + /* enable/disable esd check flag */ + nvt_spi_esd_check = enable; +} + +static void nvt_spi_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - nvt_spi_irq_timer); + struct nvt_spi_data_t *ts = nvt_spi_data; + + //NVT_LOG("esd_check = %d (retry %d)\n", esd_check, esd_retry); + + if ((timer > NVT_SPI_TOUCH_ESD_CHECK_PERIOD) && esd_check) { + mutex_lock(&ts->lock); + NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, nvt_spi_esd_retry); + /* do esd recovery, reload fw */ + nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME); + mutex_unlock(&ts->lock); + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; + /* update esd_retry counter */ + nvt_spi_esd_retry++; + } + + queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work, + msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD)); +} +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +#define NVT_SPI_PEN_DATA_LEN 14 +#if NVT_SPI_CHECK_PEN_DATA_CHECKSUM +static int32_t nvt_spi_pen_data_checksum(uint8_t *buf, uint8_t length) +{ + uint8_t checksum = 0; + int32_t i = 0; + + // Calculate checksum + for (i = 0; i < length - 1; i++) + checksum += buf[i]; + checksum = (~checksum + 1); + + // Compare ckecksum and dump fail data + if (checksum != buf[length - 1]) { + NVT_ERR("pen packet checksum not match. (buf[%d]=0x%02X, checksum=0x%02X)\n", + length - 1, buf[length - 1], checksum); + //--- dump pen buf --- + for (i = 0; i < length; i++) + NVT_ERR("%02X ", buf[i]); + + NVT_ERR("\n"); + + return -EIO; + } + + return 0; +} +#endif // #if NVT_SPI_CHECK_PEN_DATA_CHECKSUM + +#if NVT_SPI_TOUCH_WDT_RECOVERY +static uint8_t nvt_spi_recovery_cnt; +static uint8_t nvt_spi_wdt_fw_recovery(uint8_t *point_data) +{ + uint32_t recovery_cnt_max = 10; + uint8_t recovery_enable = false; + uint8_t i = 0; + + nvt_spi_recovery_cnt++; + + /* check pattern */ + for (i = 1 ; i < 7 ; i++) { + if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) { + nvt_spi_recovery_cnt = 0; + break; + } + } + + if (nvt_spi_recovery_cnt > recovery_cnt_max) { + recovery_enable = true; + nvt_spi_recovery_cnt = 0; + } + + return recovery_enable; +} +#endif /* #if NVT_SPI_TOUCH_WDT_RECOVERY */ + +#if NVT_SPI_POINT_DATA_CHECKSUM +static int32_t nvt_spi_point_data_checksum(uint8_t *buf, uint8_t length) +{ + uint8_t checksum = 0; + int32_t i = 0; + + // Generate checksum + for (i = 0; i < length - 1; i++) + checksum += buf[i + 1]; + + checksum = (~checksum + 1); + + // Compare ckecksum and dump fail data + if (checksum != buf[length]) { + NVT_ERR("packet checksum not match. (point_data[%d]=0x%02X, checksum=0x%02X)\n", + length, buf[length], checksum); + + for (i = 0; i < 10; i++) + NVT_LOG("%02X %02X %02X %02X %02X %02X\n", + buf[1 + i * 6], buf[2 + i * 6], buf[3 + i * 6], + buf[4 + i * 6], buf[5 + i * 6], buf[6 + i * 6]); + + NVT_LOG("%02X %02X %02X %02X %02X\n", + buf[61], buf[62], buf[63], buf[64], buf[65]); + return -EIO; + } + + return 0; +} +#endif /* NVT_SPI_POINT_DATA_CHECKSUM */ + +/* + ****************************************************** + * Description: + * Novatek touchscreen work function. + * + * return: + * n.a. + ****************************************************** + */ +#define NVT_SPI_POINT_DATA_LEN 65 +#define NVT_SPI_POINT (NVT_SPI_POINT_DATA_LEN + NVT_SPI_PEN_DATA_LEN + 1 + NVT_SPI_DUMMY_BYTES) +static irqreturn_t nvt_spi_work_func(int irq, void *data) +{ + int32_t ret = -1; + uint8_t point_data[NVT_SPI_POINT] = {0}; + uint32_t position = 0; + uint32_t input_x = 0; + uint32_t input_y = 0; + uint32_t input_w = 0; + uint32_t input_p = 0; + uint8_t input_id = 0; +#if NVT_SPI_MT_PROTOCOL_B + uint8_t press_id[NVT_SPI_TOUCH_MAX_FINGER_NUM] = {0}; +#endif /* NVT_SPI_MT_PROTOCOL_B */ + int32_t i = 0; + int32_t finger_cnt = 0; + uint8_t pen_format_id = 0; + uint32_t pen_x = 0; + uint32_t pen_y = 0; + uint32_t pen_pressure = 0; + uint32_t pen_distance = 0; + int8_t pen_tilt_x = 0; + int8_t pen_tilt_y = 0; + uint32_t pen_btn1 = 0; + uint32_t pen_btn2 = 0; + uint32_t pen_battery = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + +#if NVT_SPI_WAKEUP_GESTURE + if (bTouchIsAwake == 0) + pm_wakeup_event(&ts->input_dev->dev, 5000); + +#endif + + mutex_lock(&ts->lock); + + if (ts->pen_support) + ret = nvt_spi_read(point_data, NVT_SPI_POINT_DATA_LEN + NVT_SPI_PEN_DATA_LEN + 1); + else + ret = nvt_spi_read(point_data, NVT_SPI_POINT_DATA_LEN + 1); + + if (ret < 0) { + NVT_ERR("nvt_spi_read failed.(%d)\n", ret); + goto XFER_ERROR; + } + + //--- dump SPI buf --- +/* + * for (i = 0; i < 10; i++) { + * printk("%02X %02X %02X %02X %02X %02X ", + * point_data[1+i*6], point_data[2+i*6], point_data[3+i*6], + * point_data[4+i*6], point_data[5+i*6], point_data[6+i*6]); + * } + * printk("\n"); + */ + +#if NVT_SPI_TOUCH_WDT_RECOVERY + /* ESD protect by WDT */ + if (nvt_spi_wdt_fw_recovery(point_data)) { + NVT_ERR("Recover for fw reset, %02X\n", point_data[1]); + nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME); + goto XFER_ERROR; + } +#endif /* #if NVT_SPI_TOUCH_WDT_RECOVERY */ + + /* ESD protect by FW handshake */ + if (nvt_spi_fw_recovery(point_data)) { +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(true); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + goto XFER_ERROR; + } + +#if NVT_SPI_POINT_DATA_CHECKSUM + if (NVT_SPI_POINT_DATA_LEN >= NVT_SPI_POINT_DATA_CHECKSUM_LEN) { + ret = nvt_spi_point_data_checksum(point_data, NVT_SPI_POINT_DATA_CHECKSUM_LEN); + if (ret) + goto XFER_ERROR; + } +#endif /* NVT_SPI_POINT_DATA_CHECKSUM */ + +#if NVT_SPI_WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_spi_wakeup_gesture_report(input_id, point_data); + mutex_unlock(&ts->lock); + return IRQ_HANDLED; + } +#endif + + finger_cnt = 0; + + for (i = 0; i < ts->max_touch_num; i++) { + position = 1 + 6 * i; + input_id = (uint8_t)(point_data[position + 0] >> 3); + if ((input_id == 0) || (input_id > ts->max_touch_num)) + continue; + + if (((point_data[position] & 0x07) == 0x01) + || ((point_data[position] & 0x07) == 0x02)) { + //finger down (enter & moving) +#if NVT_SPI_TOUCH_ESD_PROTECT + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + input_x = (uint32_t)(point_data[position + 1] << 4); + input_x += (uint32_t) (point_data[position + 3] >> 4); + + input_y = (uint32_t)(point_data[position + 2] << 4); + input_y += (uint32_t) (point_data[position + 3] & 0x0F); + if ((input_x < 0) || (input_y < 0)) + continue; + if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) + continue; + input_w = (uint32_t)(point_data[position + 4]); + if (input_w == 0) + input_w = 1; + if (i < 2) { + input_p = (uint32_t)(point_data[position + 5]); + input_p += (uint32_t)(point_data[i + 63] << 8); + if (input_p > NVT_SPI_TOUCH_FORCE_NUM) + input_p = NVT_SPI_TOUCH_FORCE_NUM; + } else + input_p = (uint32_t)(point_data[position + 5]); + + if (input_p == 0) + input_p = 1; + +#if NVT_SPI_MT_PROTOCOL_B + press_id[input_id - 1] = 1; + input_mt_slot(ts->input_dev, input_id - 1); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); +#else /* NVT_SPI_MT_PROTOCOL_B */ + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif /* NVT_SPI_MT_PROTOCOL_B */ + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); + +#if NVT_SPI_MT_PROTOCOL_B +#else /* NVT_SPI_MT_PROTOCOL_B */ + input_mt_sync(ts->input_dev); +#endif /* NVT_SPI_MT_PROTOCOL_B */ + + finger_cnt++; + } + } + +#if NVT_SPI_MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + if (press_id[i] != 1) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); +#else /* NVT_SPI_MT_PROTOCOL_B */ + if (finger_cnt == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(ts->input_dev); + } +#endif /* NVT_SPI_MT_PROTOCOL_B */ + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + if (point_data[61] == 0xF8) { +#if NVT_SPI_TOUCH_ESD_PROTECT + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + for (i = 0; i < ts->max_button_num; i++) + input_report_key(ts->input_dev, nvt_spi_touch_key_array[i], + ((point_data[62] >> i) & 0x01)); + + } else { + for (i = 0; i < ts->max_button_num; i++) + input_report_key(ts->input_dev, nvt_spi_touch_key_array[i], 0); + } +#endif + + input_sync(ts->input_dev); + + if (ts->pen_support) { + + //--- dump pen buf --- +/* + * printk("%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + * point_data[66], point_data[67], point_data[68], point_data[69], + * point_data[70], point_data[71], point_data[72], point_data[73], + * point_data[74], point_data[75], point_data[76], point_data[77], + * point_data[78], point_data[79]); + */ +#if NVT_SPI_CHECK_PEN_DATA_CHECKSUM + if (nvt_spi_pen_data_checksum(&point_data[66], NVT_SPI_PEN_DATA_LEN)) { + // pen data packet checksum not match, skip it + goto XFER_ERROR; + } +#endif // #if NVT_SPI_CHECK_PEN_DATA_CHECKSUM + + // parse and handle pen report + pen_format_id = point_data[66]; + if (pen_format_id != 0xFF) { + if (pen_format_id == 0x01) { + // report pen data + pen_x = (uint32_t)(point_data[67] << 8); + pen_x += (uint32_t)(point_data[68]); + + pen_y = (uint32_t)(point_data[69] << 8); + pen_y += (uint32_t)(point_data[70]); + + pen_pressure = (uint32_t)(point_data[71] << 8); + pen_pressure += (uint32_t)(point_data[72]); + + pen_tilt_x = (int32_t)point_data[73]; + pen_tilt_y = (int32_t)point_data[74]; + + pen_distance = (uint32_t)(point_data[75] << 8); + pen_distance += (uint32_t)(point_data[76]); + + pen_btn1 = (uint32_t)(point_data[77] & 0x01); + pen_btn2 = (uint32_t)((point_data[77] >> 1) & 0x01); + pen_battery = (uint32_t)point_data[78]; +// printk("x=%d,y=%d,p=%d,tx=%d,ty=%d,d=%d,b1=%d,b2=%d,bat=%d\n", +// pen_x, pen_y, pen_pressure, pen_tilt_x, pen_tilt_y, +// pen_distance, pen_btn1, pen_btn2, pen_battery); + + input_report_abs(ts->pen_input_dev, ABS_X, pen_x); + input_report_abs(ts->pen_input_dev, ABS_Y, pen_y); + input_report_abs(ts->pen_input_dev, ABS_PRESSURE, pen_pressure); + input_report_key(ts->pen_input_dev, BTN_TOUCH, !!pen_pressure); + input_report_abs(ts->pen_input_dev, ABS_TILT_X, pen_tilt_x); + input_report_abs(ts->pen_input_dev, ABS_TILT_Y, pen_tilt_y); + input_report_abs(ts->pen_input_dev, ABS_DISTANCE, pen_distance); + input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, + !!pen_distance || !!pen_pressure); + input_report_key(ts->pen_input_dev, BTN_STYLUS, pen_btn1); + input_report_key(ts->pen_input_dev, BTN_STYLUS2, pen_btn2); + // TBD: pen battery event report + // NVT_LOG("pen_battery=%d\n", pen_battery); + } else if (pen_format_id == 0xF0) { + // report Pen ID + } else { + NVT_ERR("Unknown pen format id!\n"); + goto XFER_ERROR; + } + } else { // pen_format_id = 0xFF, i.e. no pen present + input_report_abs(ts->pen_input_dev, ABS_X, 0); + input_report_abs(ts->pen_input_dev, ABS_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0); + input_report_key(ts->pen_input_dev, BTN_TOUCH, 0); + input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); + } + + input_sync(ts->pen_input_dev); + } /* if (ts->pen_support) */ + +XFER_ERROR: + + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + + +/* + ******************************************************* + * Description: + * Novatek touchscreen check chip version trim function. + * + * return: + * Executive outcomes. 0---NVT IC. -1---not NVT IC. + ****************************************************** + */ +static int8_t nvt_spi_check_chip_ver_trim(uint32_t chip_ver_trim_addr) +{ + uint8_t buf[8] = {0}; + int32_t retry = 0; + int32_t list = 0; + int32_t i = 0; + int32_t found_nvt_chip = 0; + int32_t ret = -1; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---Check for 5 times--- + for (retry = 5; retry > 0; retry--) { + + nvt_spi_bootloader_reset(); + + nvt_spi_set_page(chip_ver_trim_addr); + + buf[0] = chip_ver_trim_addr & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + nvt_spi_read(buf, 7); + NVT_LOG("buf[1]=0x%02X,[2]=0x%02X,[3]=0x%02X,[4]=0x%02X,[5]=0x%02X,[6]=0x%02X\n", + buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + // compare read chip id on supported list + for (list = 0; list < ARRAY_SIZE(nvt_spi_trim_id_table); list++) { + found_nvt_chip = 0; + + // compare each byte + for (i = 0; i < NVT_SPI_ID_BYTE_MAX; i++) { + if (nvt_spi_trim_id_table[list].mask[i]) { + if (buf[i + 1] != nvt_spi_trim_id_table[list].id[i]) + break; + } + } + + if (i == NVT_SPI_ID_BYTE_MAX) + found_nvt_chip = 1; + + if (found_nvt_chip) { + NVT_LOG("This is NVT touch IC\n"); + ts->mmap = nvt_spi_trim_id_table[list].mmap; + ts->hw_crc = nvt_spi_trim_id_table[list].hwinfo->hw_crc; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(20); + } + +out: + return ret; +} + +#if defined(CONFIG_DRM) +static int nvt_spi_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 0; + + 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_spi_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} +#endif + +static int32_t nvt_spi_late_probe(struct spi_device *client) +{ + int32_t ret = 0; + + //---check chip version trim--- + ret = nvt_spi_check_chip_ver_trim(NVT_SPI_CHIP_VER_TRIM_ADDR); + if (ret) { + NVT_LOG("try to check from old chip ver trim address\n"); + ret = nvt_spi_check_chip_ver_trim(NVT_SPI_CHIP_VER_TRIM_OLD_ADDR); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + return ret; + } + } + + if (nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME)) + NVT_ERR("download firmware failed, ignore check fw state\n"); + else + nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_REK); + + //---set device node--- +#if NVT_SPI_TOUCH_PROC + ret = nvt_spi_flash_proc_init(); + if (ret != 0) { + NVT_ERR("nvt flash proc init failed. ret=%d\n", ret); + goto err_flash_proc_init_failed; + } +#endif + +#if NVT_SPI_TOUCH_EXT_PROC + ret = nvt_spi_extra_proc_init(); + if (ret != 0) { + NVT_ERR("nvt extra proc init failed. ret=%d\n", ret); + goto err_extra_proc_init_failed; + } +#endif + +#if NVT_SPI_TOUCH_MP + ret = nvt_spi_mp_proc_init(); + if (ret != 0) { + NVT_ERR("nvt mp proc init failed. ret=%d\n", ret); + goto err_mp_proc_init_failed; + } +#endif + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_spi_irq_enable(true); + return 0; + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +err_mp_proc_init_failed: +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); +err_extra_proc_init_failed: +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); +err_flash_proc_init_failed: +#endif + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen driver probe function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed + ****************************************************** + */ +static int32_t nvt_spi_probe(struct spi_device *client) +{ + int32_t ret = 0; +#if defined(CONFIG_DRM) + struct device_node *dp = NULL; +#endif +#if ((NVT_SPI_TOUCH_KEY_NUM > 0) || NVT_SPI_WAKEUP_GESTURE) + int32_t retry = 0; +#endif + struct nvt_spi_data_t *ts; + +#if defined(CONFIG_DRM) + dp = client->dev.of_node; + + ret = nvt_spi_check_dt(dp); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + ret = -ENODEV; + return ret; + } +#endif + + NVT_LOG("start\n"); + + ts = kzalloc(sizeof(struct nvt_spi_data_t), GFP_KERNEL); + if (ts == NULL) { + NVT_ERR("failed to allocated memory for nvt ts data\n"); + return -ENOMEM; + } + nvt_spi_data = ts; + + ts->xbuf = kzalloc(NVT_SPI_XBUF_LEN, GFP_KERNEL); + if (ts->xbuf == NULL) { + NVT_ERR("kzalloc for xbuf failed!\n"); + ret = -ENOMEM; + goto err_malloc_xbuf; + } + + ts->rbuf = kzalloc(NVT_SPI_READ_LEN, GFP_KERNEL); + if (ts->rbuf == NULL) { + NVT_ERR("kzalloc for rbuf failed!\n"); + ret = -ENOMEM; + goto err_malloc_rbuf; + } + + ts->client = client; + spi_set_drvdata(client, ts); + + //---prepare for spi parameter--- + if (ts->client->master->flags & SPI_MASTER_HALF_DUPLEX) { + NVT_ERR("Full duplex not supported by master\n"); + ret = -EIO; + goto err_ckeck_full_duplex; + } + ts->client->bits_per_word = 8; + ts->client->mode = SPI_MODE_0; + + ret = spi_setup(ts->client); + if (ret < 0) { + NVT_ERR("Failed to perform SPI setup\n"); + goto err_spi_setup; + } + + NVT_LOG("mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz); + + //---parse dts--- + ret = nvt_spi_parse_dt(&client->dev); + if (ret) { + NVT_ERR("parse dt error\n"); + goto err_spi_setup; + } + + //---request and config GPIOs--- + ret = nvt_spi_gpio_config(ts); + if (ret) { + NVT_ERR("gpio config error!\n"); + goto err_gpio_config_failed; + } + + mutex_init(&ts->lock); + mutex_init(&ts->xbuf_lock); + + //---eng reset before TP_RESX high + nvt_spi_eng_reset(); + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + // need 10ms delay after POR(power on reset) + msleep(20); + + ts->abs_x_max = NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT; + + //---allocate input device--- + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + NVT_ERR("allocate input device failed\n"); + ret = -ENOMEM; + goto err_input_dev_alloc_failed; + } + + ts->max_touch_num = NVT_SPI_TOUCH_MAX_FINGER_NUM; + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + ts->max_button_num = NVT_SPI_TOUCH_KEY_NUM; +#endif + + ts->int_trigger_type = NVT_SPI_INT_TRIGGER_TYPE; + + + //---set input device info.--- + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + +#if NVT_SPI_MT_PROTOCOL_B + input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0); +#endif + + // pressure = NVT_SPI_TOUCH_FORCE_NUM + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, NVT_SPI_TOUCH_FORCE_NUM, 0, 0); + +#if NVT_SPI_TOUCH_MAX_FINGER_NUM > 1 + // area = 255 + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); +#if NVT_SPI_MT_PROTOCOL_B + // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it +#else + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); +#endif //NVT_SPI_MT_PROTOCOL_B +#endif //NVT_SPI_TOUCH_MAX_FINGER_NUM > 1 + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + for (retry = 0; retry < ts->max_button_num; retry++) + input_set_capability(ts->input_dev, EV_KEY, nvt_spi_touch_key_array[retry]); +#endif + +#if NVT_SPI_WAKEUP_GESTURE + for (retry = 0; retry < ARRAY_SIZE(nvt_spi_gesture_key_array); retry++) + input_set_capability(ts->input_dev, EV_KEY, nvt_spi_gesture_key_array[retry]); +#endif + + snprintf(ts->phys, sizeof(ts->phys), "input/ts"); + ts->input_dev->name = NVT_SPI_TS_NAME; + ts->input_dev->phys = ts->phys; + ts->input_dev->id.bustype = BUS_SPI; + + //---register input device--- + ret = input_register_device(ts->input_dev); + if (ret) { + NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret); + goto err_input_register_device_failed; + } + + if (ts->pen_support) { + //---allocate pen input device--- + ts->pen_input_dev = input_allocate_device(); + if (ts->pen_input_dev == NULL) { + NVT_ERR("allocate pen input device failed\n"); + ret = -ENOMEM; + goto err_pen_input_dev_alloc_failed; + } + + //---set pen input device info.--- + ts->pen_input_dev->evbit[0] = + BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts->pen_input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); + //ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] + // |= BIT_MASK(BTN_TOOL_RUBBER); + ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); + ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); + ts->pen_input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + + if (ts->wgp_stylus) { + input_set_abs_params(ts->pen_input_dev, ABS_X, 0, + ts->abs_x_max * 2, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, + ts->abs_y_max * 2, 0, 0); + } else { + input_set_abs_params(ts->pen_input_dev, ABS_X, 0, + ts->abs_x_max, 0, 0); + + input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, + ts->abs_y_max, 0, 0); + } + input_set_abs_params(ts->pen_input_dev, ABS_PRESSURE, 0, + NVT_SPI_PEN_PRESSURE_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_DISTANCE, 0, + NVT_SPI_PEN_DISTANCE_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_TILT_X, + NVT_SPI_PEN_TILT_MIN, NVT_SPI_PEN_TILT_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_TILT_Y, + NVT_SPI_PEN_TILT_MIN, NVT_SPI_PEN_TILT_MAX, 0, 0); + + snprintf(ts->pen_phys, sizeof(ts->pen_phys), "input/pen"); + ts->pen_input_dev->name = NVT_SPI_PEN_NAME; + ts->pen_input_dev->phys = ts->pen_phys; + ts->pen_input_dev->id.bustype = BUS_SPI; + + //---register pen input device--- + ret = input_register_device(ts->pen_input_dev); + if (ret) { + NVT_ERR("register pen input device (%s) failed. ret=%d\n", + ts->pen_input_dev->name, ret); + goto err_pen_input_register_device_failed; + } + } /* if (ts->pen_support) */ + + //---set int-pin & request irq--- + client->irq = gpio_to_irq(ts->irq_gpio); + if (client->irq) { + NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type); + ts->irq_enabled = true; + ret = request_threaded_irq(client->irq, NULL, nvt_spi_work_func, + ts->int_trigger_type | IRQF_ONESHOT, NVT_SPI_NAME, ts); + if (ret != 0) { + NVT_ERR("request irq failed. ret=%d\n", ret); + goto err_int_request_failed; + } else { + nvt_spi_irq_enable(false); + NVT_LOG("request irq %d succeed\n", client->irq); + } + } + +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 1); +#endif + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + nvt_spi_fwu_wq = alloc_workqueue("nvt_spi_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!nvt_spi_fwu_wq) { + NVT_ERR("nvt_spi_fwu_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_spi_fwu_wq_failed; + } + INIT_DELAYED_WORK(&ts->nvt_fwu_work, nvt_spi_update_firmware_work); + + // please make sure boot update start after display reset(RESX) sequence + //queue_delayed_work(nvt_spi_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000)); +#endif + + NVT_LOG("NVT_SPI_TOUCH_ESD_PROTECT is %d\n", NVT_SPI_TOUCH_ESD_PROTECT); +#if NVT_SPI_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_spi_esd_check_work, nvt_spi_esd_check_func); + nvt_spi_esd_check_wq = alloc_workqueue("nvt_spi_esd_check_wq", WQ_MEM_RECLAIM, 1); + if (!nvt_spi_esd_check_wq) { + NVT_ERR("nvt_spi_esd_check_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_spi_esd_check_wq_failed; + } + queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work, + msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + +#if defined(CONFIG_DRM) + //if (!strcmp(ts->touch_environment, "pvm")) + nvt_spi_register_for_panel_events(client->dev.of_node, ts); +#elif defined(_MSM_DRM_NOTIFY_H_) + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + ret = msm_drm_register_client(&ts->drm_notif); + if (ret) { + NVT_ERR("register drm_notifier failed. ret=%d\n", ret); + goto err_register_drm_notif_failed; + } +#else + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + if (ret) { + NVT_ERR("register fb_notifier failed. ret=%d\n", ret); + goto err_register_fb_notif_failed; + } +#endif + + NVT_LOG("end\n"); + return 0; + +#if defined(CONFIG_DRM) + +#elif defined(_MSM_DRM_NOTIFY_H_) +err_register_drm_notif_failed: +#else +err_register_fb_notif_failed: +#endif + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); + +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); + +#endif +#if NVT_SPI_TOUCH_ESD_PROTECT + if (nvt_spi_esd_check_wq) { + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + destroy_workqueue(nvt_spi_esd_check_wq); + nvt_spi_esd_check_wq = NULL; + } +err_create_nvt_spi_esd_check_wq_failed: +#endif +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + if (nvt_spi_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_spi_fwu_wq); + nvt_spi_fwu_wq = NULL; + } +err_create_nvt_spi_fwu_wq_failed: +#endif +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + free_irq(client->irq, ts); +err_int_request_failed: + if (ts->pen_support) { + input_unregister_device(ts->pen_input_dev); + ts->pen_input_dev = NULL; + } +err_pen_input_register_device_failed: + if (ts->pen_support) { + if (ts->pen_input_dev) { + input_free_device(ts->pen_input_dev); + ts->pen_input_dev = NULL; + } + } +err_pen_input_dev_alloc_failed: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; +err_input_register_device_failed: + if (ts->input_dev) { + input_free_device(ts->input_dev); + ts->input_dev = NULL; + } +err_input_dev_alloc_failed: + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + nvt_spi_gpio_deconfig(ts); +err_gpio_config_failed: +err_spi_setup: +err_ckeck_full_duplex: + spi_set_drvdata(client, NULL); + kfree(ts->rbuf); + ts->rbuf = NULL; +err_malloc_rbuf: + kfree(ts->xbuf); + ts->xbuf = NULL; +err_malloc_xbuf: + kfree(ts); + ts = NULL; + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen driver release function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_remove(struct spi_device *client) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + NVT_LOG("Removing driver...\n"); + +#if defined(CONFIG_DRM) + if (active_spi_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + if (nvt_spi_esd_check_wq) { + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); + destroy_workqueue(nvt_spi_esd_check_wq); + nvt_spi_esd_check_wq = NULL; + } +#endif + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + if (nvt_spi_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_spi_fwu_wq); + nvt_spi_fwu_wq = NULL; + } +#endif + +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + + nvt_spi_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_spi_gpio_deconfig(ts); + + if (ts->pen_support) { + if (ts->pen_input_dev) { + input_unregister_device(ts->pen_input_dev); + ts->pen_input_dev = NULL; + } + } + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + spi_set_drvdata(client, NULL); + + kfree(ts->xbuf); + ts->xbuf = NULL; + + kfree(ts); + ts = NULL; + + return 0; +} + +static void nvt_spi_shutdown(struct spi_device *client) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + NVT_LOG("Shutdown driver...\n"); + + nvt_spi_irq_enable(false); + +#if defined(CONFIG_DRM) + if (active_spi_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + if (nvt_spi_esd_check_wq) { + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); + destroy_workqueue(nvt_spi_esd_check_wq); + nvt_spi_esd_check_wq = NULL; + } +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + if (nvt_spi_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_spi_fwu_wq); + nvt_spi_fwu_wq = NULL; + } +#endif + +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen driver suspend function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; +#if NVT_SPI_MT_PROTOCOL_B + uint32_t i = 0; +#endif + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (!bTouchIsAwake) { + NVT_LOG("Touch is already suspend\n"); + return 0; + } + +#if !NVT_SPI_WAKEUP_GESTURE + nvt_spi_irq_enable(false); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + NVT_LOG("cancel delayed work sync\n"); + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + bTouchIsAwake = 0; + +#if NVT_SPI_WAKEUP_GESTURE + //---write command to enter "wakeup gesture mode"--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + nvt_spi_write(buf, 2); + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else // NVT_SPI_WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + nvt_spi_write(buf, 2); +#endif // NVT_SPI_WAKEUP_GESTURE + + mutex_unlock(&ts->lock); + + /* release all touches */ +#if NVT_SPI_MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } +#endif + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#if !NVT_SPI_MT_PROTOCOL_B + input_mt_sync(ts->input_dev); +#endif + input_sync(ts->input_dev); + + /* release pen event */ + if (ts->pen_support) { + input_report_abs(ts->pen_input_dev, ABS_X, 0); + input_report_abs(ts->pen_input_dev, ABS_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0); + input_report_key(ts->pen_input_dev, BTN_TOUCH, 0); + input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); + input_sync(ts->pen_input_dev); + } + + msleep(50); + + NVT_LOG("end\n"); + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen driver resume function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_resume(struct device *dev) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (bTouchIsAwake || ts->fw_ver == 0) { + nvt_spi_late_probe(ts->client); + NVT_LOG("nvt_spi_late_probe\n"); + return 0; + } + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + // please make sure display reset(RESX) sequence and mipi dsi cmds sent before this +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + if (nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME)) + NVT_ERR("download firmware failed, ignore check fw state\n"); + else + nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_REK); + +#if !NVT_SPI_WAKEUP_GESTURE + nvt_spi_irq_enable(true); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); + queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work, + msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + bTouchIsAwake = 1; + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + +#if defined(CONFIG_DRM) + +static void nvt_spi_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct nvt_spi_data_t *ts = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + NVT_LOG("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + NVT_LOG("resume notification pre commit\n"); + else + nvt_spi_resume(&ts->client->dev); + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) + nvt_spi_suspend(&ts->client->dev); + else + NVT_LOG("suspend notification post commit\n"); + break; + + case DRM_PANEL_EVENT_BLANK_LP: + NVT_LOG("received lp event\n"); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + NVT_LOG("shashank:Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + NVT_LOG("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct nvt_spi_data_t *ts = + container_of(self, struct nvt_spi_data_t, drm_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + if (evdata->data && ts) { + blank = evdata->data; + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_spi_suspend(&ts->client->dev); + } + } else if (event == MSM_DRM_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + } + + return 0; +} +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct nvt_spi_data_t *ts = + container_of(self, struct nvt_spi_data_t, fb_notif); + + if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_spi_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#endif + +static const struct spi_device_id nvt_spi_id[] = { + { NVT_SPI_NAME, 0 }, + { } +}; + +#ifdef CONFIG_OF +static const struct of_device_id nvt_spi_match_table[] = { + { .compatible = "novatek,NVT-ts",}, + { }, +}; +#endif + +static struct spi_driver nvt_spi_driver = { + .probe = nvt_spi_probe, + .remove = nvt_spi_remove, + .shutdown = nvt_spi_shutdown, + .id_table = nvt_spi_id, + .driver = { + .name = NVT_SPI_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = nvt_spi_match_table, +#endif + }, +}; + +/* + ****************************************************** + * Description: + * Driver Install function. + * + * return: + * Executive Outcomes. 0---succeed. not 0---failed. + ******************************************************* + */ +static int32_t __init nvt_spi_driver_init(void) +{ + int32_t ret = 0; + + NVT_LOG("start\n"); + + //---add spi driver--- + ret = spi_register_driver(&nvt_spi_driver); + if (ret) { + NVT_ERR("failed to add spi driver"); + goto err_driver; + } + + NVT_LOG("finished\n"); + +err_driver: + return ret; +} + +/* + ****************************************************** + * Description: + * Driver uninstall function. + * + * return: + * n.a. + ******************************************************* + */ +static void __exit nvt_spi_driver_exit(void) +{ + spi_unregister_driver(&nvt_spi_driver); +} + +module_init(nvt_spi_driver_init); +module_exit(nvt_spi_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); + +#endif diff --git a/nt36xxx/nt36xxx.h b/nt36xxx/nt36xxx.h new file mode 100644 index 0000000000..1a298efb6a --- /dev/null +++ b/nt36xxx/nt36xxx.h @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#ifndef _LINUX_NVT_TOUCH_H +#define _LINUX_NVT_TOUCH_H + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "nt36xxx_mem_map.h" + +#define NVT_DEBUG 1 + +//---GPIO number--- +#define NVTTOUCH_RST_PIN 980 +#define NVTTOUCH_INT_PIN 943 + + +//---INT trigger mode--- +//#define IRQ_TYPE_EDGE_RISING 1 +//#define IRQ_TYPE_EDGE_FALLING 2 +#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + + +//---I2C driver info.--- +#define NVT_I2C_NAME "NVT-ts" +#define I2C_BLDR_Address 0x01 +#define I2C_FW_Address 0x01 +#define I2C_HW_Address 0x62 + +#if NVT_DEBUG +#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) pr_info("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args) + +//---Input device info.--- +#define NVT_TS_NAME "NVTCapacitiveTouchScreen" + + +//---Touch info.--- +#define TOUCH_DEFAULT_MAX_WIDTH 1080 +#define TOUCH_DEFAULT_MAX_HEIGHT 2408 +#define TOUCH_MAX_FINGER_NUM 10 +#define TOUCH_KEY_NUM 0 +#if TOUCH_KEY_NUM > 0 +extern const uint16_t touch_key_array[TOUCH_KEY_NUM]; +#endif +#define TOUCH_FORCE_NUM 1000 + +/* Enable only when module have tp reset pin and connected to host */ +#define NVT_TOUCH_SUPPORT_HW_RST 1 + +//---Customerized func.--- +#define NVT_TOUCH_PROC 1 +#define NVT_TOUCH_EXT_PROC 1 +#define NVT_TOUCH_MP 1 +#define MT_PROTOCOL_B 1 +#define WAKEUP_GESTURE 0 +#if WAKEUP_GESTURE +extern const uint16_t gesture_key_array[]; +#endif +#define BOOT_UPDATE_FIRMWARE 1 +#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin" + +//---ESD Protect.--- +#define NVT_TOUCH_ESD_PROTECT 0 +#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ + +struct nvt_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; + const struct i2c_device_id *id; +#if defined(CONFIG_DRM) + struct notifier_block drm_panel_notif; +#elif defined(_MSM_DRM_NOTIFY_H_) + struct notifier_block drm_notif; +#else + struct notifier_block fb_notif; + +#endif + uint8_t fw_ver; + uint8_t x_num; + uint8_t y_num; + uint16_t abs_x_max; + uint16_t abs_y_max; + uint8_t max_touch_num; + uint8_t max_button_num; + uint32_t int_trigger_type; + int32_t irq_gpio; + uint32_t irq_flags; + int32_t reset_gpio; + uint32_t reset_flags; + struct mutex lock; + const struct nvt_ts_mem_map *mmap; + uint8_t carrier_system; + uint16_t nvt_pid; + uint8_t xbuf[1025]; + struct mutex xbuf_lock; + bool irq_enabled; + void *notifier_cookie; +}; + +#if NVT_TOUCH_PROC +struct nvt_flash_data{ + rwlock_t lock; + struct i2c_client *client; +}; +#endif + +typedef enum { + RESET_STATE_INIT = 0xA0,// IC reset + RESET_STATE_REK, // ReK baseline + RESET_STATE_REK_FINISH, // baseline is ready + RESET_STATE_NORMAL_RUN, // normal run + RESET_STATE_MAX = 0xAF +} RST_COMPLETE_STATE; + +typedef enum { + EVENT_MAP_HOST_CMD = 0x50, + EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51, + EVENT_MAP_RESET_COMPLETE = 0x60, + EVENT_MAP_FWINFO = 0x78, + EVENT_MAP_PROJECTID = 0x9A, +} I2C_EVENT_MAP; + +//---extern structures--- +extern struct nvt_ts_data *ts; + +//---extern functions--- +extern int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len); +extern int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len); +extern void nvt_bootloader_reset(void); +extern void nvt_sw_reset_idle(void); +extern int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state); +extern int32_t nvt_get_fw_info(void); +extern int32_t nvt_clear_fw_status(void); +extern int32_t nvt_check_fw_status(void); +extern int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr); +#if NVT_TOUCH_ESD_PROTECT +extern void nvt_esd_check_enable(uint8_t enable); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +extern void nvt_stop_crc_reboot(void); + +#else /* NT36XXX_SPI */ + +#include +#include +#include +#include +#include + +#include "nt36xxx_mem_map.h" + +#define NVT_SPI_DEBUG 0 + +//---GPIO number--- +#define NVTTOUCH_RST_PIN 980 +#define NVTTOUCH_INT_PIN 943 + +//---INT trigger mode--- +//#define NVT_SPI_IRQ_TYPE_EDGE_RISING 1 +//#define NVT_SPI_IRQ_TYPE_EDGE_FALLING 2 +#define NVT_SPI_INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + +//---SPI driver info.--- +#define NVT_SPI_NAME "NVT-SPI" + +#if NVT_SPI_DEBUG +#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) pr_debug("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args) + +//---Input device info.--- +#define NVT_SPI_TS_NAME "NVTCapacitiveTouchScreen" +#define NVT_SPI_PEN_NAME "NVTCapacitivePen" + +//---Touch info.--- +#define NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH 1080 +#define NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT 2400 +#define NVT_SPI_TOUCH_MAX_FINGER_NUM 10 +#define NVT_SPI_TOUCH_KEY_NUM 0 +#if NVT_SPI_TOUCH_KEY_NUM > 0 +extern const uint16_t nvt_spi_touch_key_array[NVT_SPI_TOUCH_KEY_NUM]; +#endif +#define NVT_SPI_TOUCH_FORCE_NUM 1000 +//---for Pen--- +#define NVT_SPI_PEN_PRESSURE_MAX (4095) +#define NVT_SPI_PEN_DISTANCE_MAX (1) +#define NVT_SPI_PEN_TILT_MIN (-60) +#define NVT_SPI_PEN_TILT_MAX (60) + +/* Enable only when module have tp reset pin and connected to host */ +#define NVT_SPI_TOUCH_SUPPORT_HW_RST 0 + +//---Customerized func.--- +#define NVT_SPI_TOUCH_PROC 1 +#define NVT_SPI_TOUCH_EXT_PROC 1 +#define NVT_SPI_TOUCH_MP 0 +#define NVT_SPI_MT_PROTOCOL_B 1 +#define NVT_SPI_WAKEUP_GESTURE 0 + +#if NVT_SPI_WAKEUP_GESTURE +extern const uint16_t nvt_spi_gesture_key_array[]; +#endif +#define NVT_SPI_BOOT_UPDATE_FIRMWARE 1 +#define NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME "novatek_spi_fw.bin" +#define NVT_SPI_MP_UPDATE_FIRMWARE_NAME "novatek_ts_mp.bin" +#define NVT_SPI_POINT_DATA_CHECKSUM 1 +#define NVT_SPI_POINT_DATA_CHECKSUM_LEN 65 + +//---ESD Protect.--- +#define NVT_SPI_TOUCH_ESD_PROTECT 0 +#define NVT_SPI_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ +#define NVT_SPI_TOUCH_WDT_RECOVERY 1 + +#define NVT_SPI_CHECK_PEN_DATA_CHECKSUM 0 + +struct nvt_spi_data_t { + struct spi_device *client; + struct input_dev *input_dev; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; + +#if defined(CONFIG_DRM) + struct notifier_block drm_panel_notif; +#elif defined(_MSM_DRM_NOTIFY_H_) + struct notifier_block drm_notif; +#else + struct notifier_block fb_notif; +#endif + + uint8_t fw_ver; + uint8_t x_num; + uint8_t y_num; + uint16_t abs_x_max; + uint16_t abs_y_max; + uint8_t max_touch_num; + uint8_t max_button_num; + uint32_t int_trigger_type; + int32_t irq_gpio; + uint32_t irq_flags; + int32_t reset_gpio; + uint32_t reset_flags; + struct mutex lock; + const struct nvt_spi_mem_map *mmap; + uint8_t hw_crc; + uint16_t nvt_pid; + uint8_t *rbuf; + uint8_t *xbuf; + struct mutex xbuf_lock; + bool irq_enabled; + bool pen_support; + bool wgp_stylus; + uint8_t x_gang_num; + uint8_t y_gang_num; + struct input_dev *pen_input_dev; + int8_t pen_phys[32]; + + void *notifier_cookie; + const char *touch_environment; + +#ifdef CONFIG_NOVATEK_SPI_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + struct completion trusted_touch_powerdown; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t trusted_touch_underway; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif +}; + +#if NVT_SPI_TOUCH_PROC +struct nvt_spi_flash_data { + rwlock_t lock; +}; +#endif + +enum NVT_SPI_RST_COMPLETE_STATE { + NVT_SPI_RESET_STATE_INIT = 0xA0,// IC reset + NVT_SPI_RESET_STATE_REK, // ReK baseline + NVT_SPI_RESET_STATE_REK_FINISH, // baseline is ready + NVT_SPI_RESET_STATE_NORMAL_RUN, // normal run + NVT_SPI_RESET_STATE_MAX = 0xAF +}; + +enum NVT_SPI_EVENT_MAP { + NVT_SPI_EVENT_MAP_HOST_CMD = 0x50, + NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51, + NVT_SPI_EVENT_MAP_RESET_COMPLETE = 0x60, + NVT_SPI_EVENT_MAP_FWINFO = 0x78, + NVT_SPI_EVENT_MAP_PROJECTID = 0x9A, +}; + +//---SPI READ/WRITE--- +#define NVT_SPI_WRITE_MASK(a) (a | 0x80) +#define NVT_SPI_READ_MASK(a) (a & 0x7F) + +#define NVT_SPI_DUMMY_BYTES (1) +#define NVT_SPI_TRANSFER_LEN (63*1024) +#define NVT_SPI_READ_LEN (2*1024) +#define NVT_SPI_XBUF_LEN (NVT_SPI_TRANSFER_LEN+1+NVT_SPI_DUMMY_BYTES) + +enum NVT_SPI_RW { + NVT_SPI_WRITE = 0, + NVT_SPI_READ = 1 +}; + +//---extern structures--- +extern struct nvt_spi_data_t *nvt_spi_data; + +//---extern functions--- +int32_t nvt_spi_read(uint8_t *buf, uint16_t len); +int32_t nvt_spi_write(uint8_t *buf, uint16_t len); +void nvt_spi_bootloader_reset(void); +void nvt_spi_eng_reset(void); +void nvt_spi_sw_reset(void); +void nvt_spi_sw_reset_idle(void); +void nvt_spi_boot_ready(void); +void nvt_spi_bld_crc_enable(void); +void nvt_spi_fw_crc_enable(void); +void nvt_spi_tx_auto_copy_mode(void); +int32_t nvt_spi_update_firmware(char *firmware_name); +void nvt_spi_update_firmware_work(struct work_struct *work); +int32_t nvt_spi_check_fw_reset_state(enum NVT_SPI_RST_COMPLETE_STATE reset_state); +int32_t nvt_spi_get_fw_info(void); +int32_t nvt_spi_clear_fw_status(void); +int32_t nvt_spi_check_fw_status(void); +int32_t nvt_spi_check_spi_dma_tx_info(void); +int32_t nvt_spi_set_page(uint32_t addr); +int32_t nvt_spi_write_addr(uint32_t addr, uint8_t data); + +#if NVT_SPI_TOUCH_EXT_PROC +int32_t nvt_spi_extra_proc_init(void); +void nvt_spi_extra_proc_deinit(void); +#endif + +#if NVT_SPI_TOUCH_MP +extern int32_t nvt_spi_mp_proc_init(void); +extern void nvt_spi_mp_proc_deinit(void); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT +extern void nvt_spi_esd_check_enable(uint8_t enable); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + +#endif + +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/nt36xxx/nt36xxx_ext_proc.c b/nt36xxx/nt36xxx_ext_proc.c new file mode 100644 index 0000000000..d08d8b3162 --- /dev/null +++ b/nt36xxx/nt36xxx_ext_proc.c @@ -0,0 +1,1537 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#if !defined(NVT_NT36XXX_SPI) /* TOUCHSCREEN_NT36XXX I2C */ + +#include +#include + +#include "nt36xxx.h" + +#if NVT_TOUCH_EXT_PROC +#define NVT_FW_VERSION "nvt_fw_version" +#define NVT_BASELINE "nvt_baseline" +#define NVT_RAW "nvt_raw" +#define NVT_DIFF "nvt_diff" + +#define BUS_TRANSFER_LENGTH 64 + +#define NORMAL_MODE 0x00 +#define TEST_MODE_1 0x21 +#define TEST_MODE_2 0x22 +#define HANDSHAKING_HOST_READY 0xBB + +#define XDATA_SECTOR_SIZE 256 + +static uint8_t xdata_tmp[2048] = {0}; +static int32_t xdata[2048] = {0}; + +static struct proc_dir_entry *NVT_proc_fw_version_entry; +static struct proc_dir_entry *NVT_proc_baseline_entry; +static struct proc_dir_entry *NVT_proc_raw_entry; +static struct proc_dir_entry *NVT_proc_diff_entry; + +/******************************************************* +Description: + Novatek touchscreen change mode function. + +return: + n.a. +*******************************************************/ +void nvt_change_mode(uint8_t mode) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = mode; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + if (mode == NORMAL_MODE) { + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = HANDSHAKING_HOST_READY; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + msleep(20); + } +} + +/******************************************************* +Description: + Novatek touchscreen get firmware pipe function. + +return: + Executive outcomes. 0---pipe 0. 1---pipe 1. +*******************************************************/ +uint8_t nvt_get_fw_pipe(void) +{ + uint8_t buf[8]= {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + //NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]); + + return (buf[1] & 0x01); +} + +/******************************************************* +Description: + Novatek touchscreen read meta data function. + +return: + n.a. +*******************************************************/ +void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t buf[BUS_TRANSFER_LENGTH + 1] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = ts->x_num * ts->y_num * 2; + residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, head_addr + XDATA_SECTOR_SIZE * i); + + //---read xdata by BUS_TRANSFER_LENGTH + for (j = 0; j < (XDATA_SECTOR_SIZE / BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = BUS_TRANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < BUS_TRANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + BUS_TRANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, xdata_addr + data_len - residual_len); + + //---read xdata by BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = BUS_TRANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < BUS_TRANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + BUS_TRANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + xdata[i] = (int16_t)(xdata_tmp[dummy_len + i * 2] + 256 * xdata_tmp[dummy_len + i * 2 + 1]); + } + +#if TOUCH_KEY_NUM > 0 + //read button xdata : step3 + //---change xdata index--- + nvt_set_page(I2C_FW_Address, xdata_btn_addr); + + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, (TOUCH_KEY_NUM * 2 + 1)); + + //---2bytes-to-1data--- + for (i = 0; i < TOUCH_KEY_NUM; i++) { + xdata[ts->x_num * ts->y_num + i] = (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]); + } +#endif + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR); +} + +/******************************************************* +Description: + Novatek touchscreen get meta data function. + +return: + n.a. +*******************************************************/ +void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num) +{ + *m_x_num = ts->x_num; + *m_y_num = ts->y_num; + memcpy(buf, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t))); +} + +/******************************************************* +Description: + Novatek touchscreen firmware version show function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_fw_version_show(struct seq_file *m, void *v) +{ + seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n", ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print show + function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + int32_t j = 0; + + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) { + seq_printf(m, "%5d, ", xdata[i * ts->x_num + j]); + } + seq_puts(m, "\n"); + } + +#if TOUCH_KEY_NUM > 0 + for (i = 0; i < TOUCH_KEY_NUM; i++) { + seq_printf(m, "%5d, ", xdata[ts->x_num * ts->y_num + i]); + } + seq_puts(m, "\n"); +#endif + + seq_printf(m, "\n\n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print start + function. + +return: + Executive outcomes. 1---call next function. + NULL---not call next function and sequence loop + stop. +*******************************************************/ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print next + function. + +return: + Executive outcomes. NULL---no next and call sequence + stop function. +*******************************************************/ +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print stop + function. + +return: + n.a. +*******************************************************/ +static void c_stop(struct seq_file *m, void *v) +{ + return; +} + +const struct seq_operations nvt_fw_version_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_fw_version_show +}; + +const struct seq_operations nvt_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_fw_version open + function. + +return: + n.a. +*******************************************************/ +static int32_t nvt_fw_version_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_fw_version_seq_ops); +} + +static const struct proc_ops nvt_fw_version_fops = { + .proc_open = nvt_fw_version_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_baseline open function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_baseline_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_change_mode(TEST_MODE_2); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR); + + nvt_change_mode(NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_seq_ops); +} + +static const struct proc_ops nvt_baseline_fops = { + .proc_open = nvt_baseline_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_raw open function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_raw_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_change_mode(TEST_MODE_2); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR); + else + nvt_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR); + + nvt_change_mode(NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_seq_ops); +} + +static const struct proc_ops nvt_raw_fops = { + .proc_open = nvt_raw_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_diff open function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +static int32_t nvt_diff_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_change_mode(TEST_MODE_2); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + + nvt_change_mode(NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_seq_ops); +} + +static const struct proc_ops nvt_diff_fops = { + .proc_open = nvt_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen extra function proc. file node + initial function. + +return: + Executive outcomes. 0---succeed. -12---failed. +*******************************************************/ +int32_t nvt_extra_proc_init(void) +{ + NVT_proc_fw_version_entry = proc_create(NVT_FW_VERSION, 0444, NULL,&nvt_fw_version_fops); + if (NVT_proc_fw_version_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_FW_VERSION); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_FW_VERSION); + } + + NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL,&nvt_baseline_fops); + if (NVT_proc_baseline_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_BASELINE); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_BASELINE); + } + + NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL,&nvt_raw_fops); + if (NVT_proc_raw_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_RAW); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_RAW); + } + + NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL,&nvt_diff_fops); + if (NVT_proc_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_DIFF); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_DIFF); + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen extra function proc. file node + deinitial function. + +return: + n.a. +*******************************************************/ +void nvt_extra_proc_deinit(void) +{ + if (NVT_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_FW_VERSION, NULL); + NVT_proc_fw_version_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_FW_VERSION); + } + + if (NVT_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_BASELINE, NULL); + NVT_proc_baseline_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_BASELINE); + } + + if (NVT_proc_raw_entry != NULL) { + remove_proc_entry(NVT_RAW, NULL); + NVT_proc_raw_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_RAW); + } + + if (NVT_proc_diff_entry != NULL) { + remove_proc_entry(NVT_DIFF, NULL); + NVT_proc_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_DIFF); + } +} +#endif + +#else /* NT36XXX_SPI */ + +#include +#include + +#include "nt36xxx.h" + +#if NVT_SPI_TOUCH_EXT_PROC +#define NVT_SPI_FW_VERSION "nvt_fw_version" +#define NVT_SPI_BASELINE "nvt_baseline" +#define NVT_SPI_RAW "nvt_raw" +#define NVT_SPI_DIFF "nvt_diff" +#define NVT_SPI_PEN_DIFF "nvt_pen_diff" + +#define NVT_SPI_BUS_TRANSFER_LENGTH 256 + +#define NVT_SPI_NORMAL_MODE 0x00 +#define NVT_SPI_TEST_MODE_2 0x22 +#define NVT_SPI_HANDSHAKING_HOST_READY 0xBB + +#define NVT_SPI_XDATA_SECTOR_SIZE 256 + +static uint8_t nvt_spi_xdata_tmp[5000] = {0}; +static int32_t nvt_spi_xdata[2500] = {0}; +static int32_t nvt_spi_xdata_pen_tip_x[256] = {0}; +static int32_t nvt_spi_xdata_pen_tip_y[256] = {0}; +static int32_t nvt_spi_xdata_pen_ring_x[256] = {0}; +static int32_t nvt_spi_xdata_pen_ring_y[256] = {0}; + +static struct proc_dir_entry *nvt_spi_proc_fw_version_entry; +static struct proc_dir_entry *nvt_spi_proc_baseline_entry; +static struct proc_dir_entry *nvt_spi_proc_raw_entry; +static struct proc_dir_entry *nvt_spi_proc_diff_entry; +static struct proc_dir_entry *nvt_spi_proc_pen_diff_entry; + +/* + ******************************************************* + * Description: + * Novatek touchscreen change mode function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_change_mode(uint8_t mode) +{ + uint8_t buf[8] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = mode; + nvt_spi_write(buf, 2); + + if (mode == NVT_SPI_NORMAL_MODE) { + usleep_range(20000, 21000); + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = NVT_SPI_HANDSHAKING_HOST_READY; + nvt_spi_write(buf, 2); + usleep_range(20000, 21000); + } +} + +static int32_t nvt_set_pen_inband_mode_1_spi(uint8_t freq_idx, uint8_t x_term) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 5; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xC1; + buf[2] = 0x02; + buf[3] = freq_idx; + buf[4] = x_term; + nvt_spi_write(buf, 5); + + for (i = 0; i < retry; i++) { + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +static int32_t nvt_spi_set_pen_normal_mode(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 5; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xC1; + buf[2] = 0x04; + nvt_spi_write(buf, 3); + + for (i = 0; i < retry; i++) { + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen get firmware pipe function. + * + * return: + * Executive outcomes. 0---pipe 0. 1---pipe 1. + ****************************************************** + */ +uint8_t nvt_spi_get_fw_pipe(void) +{ + uint32_t addr; + uint8_t buf[8] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + addr = ts->mmap->EVENT_BUF_ADDR; + nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + nvt_spi_read(buf, 2); + + //NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]); + + return (buf[1] & 0x01); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen read meta data function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t buf[NVT_SPI_BUS_TRANSFER_LENGTH + 2] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + int32_t index, data; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % NVT_SPI_XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = ts->x_num * ts->y_num * 2; + residual_len = (head_addr + dummy_len + data_len) % NVT_SPI_XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", + // head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / NVT_SPI_XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_spi_set_page(head_addr + NVT_SPI_XDATA_SECTOR_SIZE * i); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (NVT_SPI_XDATA_SECTOR_SIZE / NVT_SPI_BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = NVT_SPI_XDATA_SECTOR_SIZE * i; + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], index); + } + } + //printk("addr=0x%05X\n", (head_addr+NVT_SPI_XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_spi_set_page(xdata_addr + data_len - residual_len); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / NVT_SPI_BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = (dummy_len + data_len - residual_len); + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], index); + + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + data = nvt_spi_xdata_tmp[dummy_len + i * 2]; + data += 256 * nvt_spi_xdata_tmp[dummy_len + i * 2 + 1]; + nvt_spi_xdata[i] = (int16_t)data; + } + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + //read button xdata : step3 + //---change xdata index--- + nvt_spi_set_page(xdata_btn_addr); + + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + nvt_spi_read(buf, (NVT_SPI_TOUCH_KEY_NUM * 2 + 1)); + + //---2bytes-to-1data--- + for (i = 0; i < NVT_SPI_TOUCH_KEY_NUM; i++) + nvt_spi_xdata[ts->x_num * ts->y_num + i] = + (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]); +#endif + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen get meta data function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + *m_x_num = ts->x_num; + *m_y_num = ts->y_num; + memcpy(buf, nvt_spi_xdata, + ((ts->x_num * ts->y_num + NVT_SPI_TOUCH_KEY_NUM) * sizeof(int32_t))); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen read and get number of meta data function. + * + * return: + * n.a. + ******************************************************* + */ +void nvt_spi_read_get_num_mdata(uint32_t xdata_addr, int32_t *buffer, uint32_t num) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t buf[NVT_SPI_BUS_TRANSFER_LENGTH + 2] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + int32_t index, data; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % NVT_SPI_XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = num * 2; + residual_len = (head_addr + dummy_len + data_len) % NVT_SPI_XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", + // head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / NVT_SPI_XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_spi_set_page(head_addr + NVT_SPI_XDATA_SECTOR_SIZE * i); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (NVT_SPI_XDATA_SECTOR_SIZE / NVT_SPI_BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = NVT_SPI_XDATA_SECTOR_SIZE * i; + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + } + } + //printk("addr=0x%05X\n", (head_addr+NVT_SPI_XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_spi_set_page(xdata_addr + data_len - residual_len); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / NVT_SPI_BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = (dummy_len + data_len - residual_len); + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], index)); + + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + data = nvt_spi_xdata_tmp[dummy_len + i * 2]; + data += 256 * nvt_spi_xdata_tmp[dummy_len + i * 2 + 1]; + buffer[i] = (int16_t)data; + } + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen firmware version show function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t c_fw_version_show(struct seq_file *m, void *v) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n", + ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num); + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print show + * function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t c_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + int32_t j = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) + seq_printf(m, "%5d, ", nvt_spi_xdata[i * ts->x_num + j]); + + seq_puts(m, "\n"); + } + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + for (i = 0; i < NVT_SPI_TOUCH_KEY_NUM; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata[ts->x_num * ts->y_num + i]); + + seq_puts(m, "\n"); +#endif + + seq_puts(m, "\n\n"); + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek pen 1D diff xdata sequence print show + * function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t c_pen_1d_diff_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + seq_puts(m, "Tip X:\n"); + for (i = 0; i < ts->x_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_tip_x[i]); + + seq_puts(m, "\n"); + seq_puts(m, "Tip Y:\n"); + for (i = 0; i < ts->y_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_tip_y[i]); + + seq_puts(m, "\n"); + seq_puts(m, "Ring X:\n"); + for (i = 0; i < ts->x_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_ring_x[i]); + + seq_puts(m, "\n"); + seq_puts(m, "Ring Y:\n"); + for (i = 0; i < ts->y_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_ring_y[i]); + + seq_puts(m, "\n"); + + seq_puts(m, "\n\n"); + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print start + * function. + * + * return: + * Executive outcomes. 1---call next function. + * NULL---not call next function and sequence loop + * stop. + ******************************************************* + */ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print next + * function. + * + * return: + * Executive outcomes. NULL---no next and call sequence + * stop function. + ****************************************************** + */ +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print stop + * function. + * + * return: + * n.a. + ****************************************************** + */ +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations nvt_spi_fw_version_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_fw_version_show +}; + +const struct seq_operations nvt_spi_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show +}; + +const struct seq_operations nvt_spi_pen_diff_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_pen_1d_diff_show +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_fw_version open + * function. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_fw_version_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_fw_version_seq_ops); +} + +static const struct proc_ops nvt_spi_fw_version_fops = { + .proc_open = nvt_spi_fw_version_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_baseline open function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_baseline_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_seq_ops); +} + +static const struct proc_ops nvt_spi_baseline_fops = { + .proc_open = nvt_spi_baseline_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_raw open function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_raw_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_pipe() == 0) + nvt_spi_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR); + else + nvt_spi_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_seq_ops); +} + +static const struct proc_ops nvt_spi_raw_fops = { + .proc_open = nvt_spi_raw_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/nvt_diff open function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed. + ****************************************************** + */ +static int32_t nvt_spi_diff_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_pipe() == 0) + nvt_spi_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_spi_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_seq_ops); +} + +static const struct proc_ops nvt_spi_diff_fops = { + .proc_open = nvt_spi_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_pen_diff open function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed. + ****************************************************** + */ +static int32_t nvt_spi_pen_diff_open(struct inode *inode, struct file *file) +{ + uint32_t addr; + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_set_pen_inband_mode_1_spi(0xFF, 0x00)) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_NORMAL_RUN)) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + addr = ts->mmap->PEN_1D_DIFF_TIP_X_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_tip_x, ts->x_num); + + addr = ts->mmap->PEN_1D_DIFF_TIP_Y_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_tip_y, ts->y_num); + + addr = ts->mmap->PEN_1D_DIFF_RING_X_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_ring_x, ts->x_num); + + addr = ts->mmap->PEN_1D_DIFF_RING_Y_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_ring_y, ts->y_num); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + nvt_spi_set_pen_normal_mode(); + + nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_NORMAL_RUN); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_pen_diff_seq_ops); +} + +static const struct proc_ops nvt_spi_pen_diff_fops = { + .proc_open = nvt_spi_pen_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen extra function proc. file node + * initial function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + ****************************************************** + */ +int32_t nvt_spi_extra_proc_init(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + nvt_spi_proc_fw_version_entry = proc_create(NVT_SPI_FW_VERSION, 0444, NULL, + &nvt_spi_fw_version_fops); + if (nvt_spi_proc_fw_version_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_FW_VERSION); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_FW_VERSION); + + nvt_spi_proc_baseline_entry = proc_create(NVT_SPI_BASELINE, 0444, NULL, + &nvt_spi_baseline_fops); + if (nvt_spi_proc_baseline_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_BASELINE); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_BASELINE); + + nvt_spi_proc_raw_entry = proc_create(NVT_SPI_RAW, 0444, NULL, &nvt_spi_raw_fops); + if (nvt_spi_proc_raw_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_RAW); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_RAW); + + nvt_spi_proc_diff_entry = proc_create(NVT_SPI_DIFF, 0444, NULL, &nvt_spi_diff_fops); + if (nvt_spi_proc_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_DIFF); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_DIFF); + + if (ts->pen_support) { + nvt_spi_proc_pen_diff_entry = proc_create(NVT_SPI_PEN_DIFF, 0444, NULL, + &nvt_spi_pen_diff_fops); + if (nvt_spi_proc_pen_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_PEN_DIFF); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_PEN_DIFF); + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen extra function proc. file node + * deinitial function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_extra_proc_deinit(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (nvt_spi_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_SPI_FW_VERSION, NULL); + nvt_spi_proc_fw_version_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_FW_VERSION); + } + + if (nvt_spi_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_SPI_BASELINE, NULL); + nvt_spi_proc_baseline_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_BASELINE); + } + + if (nvt_spi_proc_raw_entry != NULL) { + remove_proc_entry(NVT_SPI_RAW, NULL); + nvt_spi_proc_raw_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_RAW); + } + + if (nvt_spi_proc_diff_entry != NULL) { + remove_proc_entry(NVT_SPI_DIFF, NULL); + nvt_spi_proc_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_DIFF); + } + + if (ts->pen_support) { + if (nvt_spi_proc_pen_diff_entry != NULL) { + remove_proc_entry(NVT_SPI_PEN_DIFF, NULL); + nvt_spi_proc_pen_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_PEN_DIFF); + } + } +} +#endif + + + +#endif diff --git a/nt36xxx/nt36xxx_fw_update.c b/nt36xxx/nt36xxx_fw_update.c new file mode 100644 index 0000000000..cbce144794 --- /dev/null +++ b/nt36xxx/nt36xxx_fw_update.c @@ -0,0 +1,2002 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +#include + +#include "nt36xxx.h" + +#if BOOT_UPDATE_FIRMWARE + +#define SIZE_4KB 4096 +#define FLASH_SECTOR_SIZE SIZE_4KB +#define SIZE_64KB 65536 +#define BLOCK_64KB_NUM 4 +#define FW_BIN_VER_OFFSET (fw_need_write_size - SIZE_4KB) +#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1) + +#define NVT_FLASH_END_FLAG_LEN 3 +#define NVT_FLASH_END_FLAG_ADDR (fw_need_write_size - NVT_FLASH_END_FLAG_LEN) + +const struct firmware *fw_entry = NULL; +static size_t fw_need_write_size = 0; + +static int32_t nvt_get_fw_need_write_size(const struct firmware *fw_entry) +{ + int32_t i = 0; + int32_t total_sectors_to_check = 0; + + total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE; + /* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */ + + for (i = total_sectors_to_check; i > 0; i--) { + /* printk("current end flag address checked = 0x%X\n", i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); */ + /* check if there is end flag "NVT" at the end of this sector */ + if ((memcmp((const char *)&fw_entry->data[i * FLASH_SECTOR_SIZE - + NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0) || + (memcmp((const char *)&fw_entry->data[i * FLASH_SECTOR_SIZE - + NVT_FLASH_END_FLAG_LEN], "MOD", NVT_FLASH_END_FLAG_LEN) == 0)) { + fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx)\n", fw_need_write_size, fw_need_write_size); + return 0; + } + } + + NVT_ERR("end flag \"NVT\" not found!\n"); + return -1; +} + +/******************************************************* +Description: + Novatek touchscreen request update firmware function. + +return: + Executive outcomes. 0---succeed. -1,-22---failed. +*******************************************************/ +int32_t update_firmware_request(char *filename) +{ + int32_t ret = 0; + + if (NULL == filename) { + return -1; + } + + NVT_LOG("filename is %s\n", filename); + + ret = request_firmware(&fw_entry, filename, &ts->client->dev); + if (ret) { + NVT_ERR("firmware load failed, ret=%d\n", ret); + return ret; + } + + // check FW need to write size + if (nvt_get_fw_need_write_size(fw_entry)) { + NVT_ERR("get fw need to write size fail!\n"); + return -EINVAL; + } + + // check if FW version add FW version bar equals 0xFF + if (*(fw_entry->data + FW_BIN_VER_OFFSET) + *(fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) { + NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n"); + NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", *(fw_entry->data+FW_BIN_VER_OFFSET), *(fw_entry->data+FW_BIN_VER_BAR_OFFSET)); + return -EINVAL; + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen release update firmware function. + +return: + n.a. +*******************************************************/ +void update_firmware_release(void) +{ + if (fw_entry) { + release_firmware(fw_entry); + } + fw_entry=NULL; +} + +/******************************************************* +Description: + Novatek touchscreen check firmware version function. + +return: + Executive outcomes. 0---need update. 1---need not + update. +*******************************************************/ +int32_t Check_FW_Ver(void) +{ + uint8_t buf[16] = {0}; + int32_t ret = 0; + + //write i2c index to EVENT BUF ADDR + ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + if (ret < 0) { + NVT_ERR("i2c write error!(%d)\n", ret); + return ret; + } + + //read Firmware Version + buf[0] = EVENT_MAP_FWINFO; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("i2c read error!(%d)\n", ret); + return ret; + } + + NVT_LOG("IC FW Ver = 0x%02X, FW Ver Bar = 0x%02X\n", buf[1], buf[2]); + NVT_LOG("Bin FW Ver = 0x%02X, FW ver Bar = 0x%02X\n", + fw_entry->data[FW_BIN_VER_OFFSET], fw_entry->data[FW_BIN_VER_BAR_OFFSET]); + + // check IC FW_VER + FW_VER_BAR equals 0xFF or not, need to update if not + if ((buf[1] + buf[2]) != 0xFF) { + NVT_ERR("IC FW_VER + FW_VER_BAR not equals to 0xFF!\n"); + return 0; + } + + // compare IC and binary FW version + if (buf[1] > fw_entry->data[FW_BIN_VER_OFFSET]) + return 1; + else + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen resume from deep power down function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Resume_PD(void) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + + // Resume Command + buf[0] = 0x00; + buf[1] = 0xAB; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Resume Command) + retry = 0; + while(1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Resume Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Resume Command) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + msleep(10); + + NVT_LOG("Resume PD OK\n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen check firmware checksum function. + +return: + Executive outcomes. 0---checksum not match. + 1---checksum match. -1--- checksum read failed. +*******************************************************/ +int32_t Check_CheckSum(void) +{ + uint8_t buf[64] = {0}; + uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR; + int32_t ret = 0; + int32_t i = 0; + int32_t k = 0; + uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0}; + uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0}; + size_t len_in_blk = 0; + int32_t retry = 0; + + if (Resume_PD()) { + NVT_ERR("Resume PD error!!\n"); + return -1; + } + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_need_write_size > (i * SIZE_64KB)) { + // Calculate WR_Filechksum of each 64KB block + len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB); + WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF); + for (k = 0; k < len_in_blk; k++) { + WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB]; + } + WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1; + + // Fast Read Command + buf[0] = 0x00; + buf[1] = 0x07; + buf[2] = i; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = ((len_in_blk - 1) >> 8) & 0xFF; + buf[6] = (len_in_blk - 1) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7); + if (ret < 0) { + NVT_ERR("Fast Read Command error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Fast Read Command) + retry = 0; + while (1) { + msleep(80); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 5)) { + NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + // Read Checksum (write addr high byte & middle byte) + ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr); + if (ret < 0) { + NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret); + return ret; + } + // Read Checksum + buf[0] = (XDATA_Addr) & 0xFF; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Read Checksum error!!(%d)\n", ret); + return ret; + } + + RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]); + if (WR_Filechksum[i] != RD_Filechksum[i]) { + NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]); + NVT_ERR("firmware checksum not match!!\n"); + return 0; + } + } + } + + NVT_LOG("firmware checksum match\n"); + return 1; +} + +/******************************************************* +Description: + Novatek touchscreen initial bootloader and flash + block function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Init_BootLoader(void) +{ + uint8_t buf[64] = {0}; + int32_t ret = 0; + int32_t retry = 0; + + // SW Reset & Idle + nvt_sw_reset_idle(); + + // Initiate Flash Block + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = I2C_FW_Address; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Inittial Flash Block error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Initiate Flash Block) + retry = 0; + while(1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Inittial Flash Block) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Inittial Flash Block) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + NVT_LOG("Init OK \n"); + msleep(20); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen erase flash sectors function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Erase_Flash(void) +{ + uint8_t buf[64] = {0}; + int32_t ret = 0; + int32_t count = 0; + int32_t i = 0; + int32_t Flash_Address = 0; + int32_t retry = 0; + + // Write Enable + buf[0] = 0x00; + buf[1] = 0x06; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable (for Write Status Register) error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Write Enable) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + // Write Status Register + buf[0] = 0x00; + buf[1] = 0x01; + buf[2] = 0x00; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Write Status Register error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Write Status Register) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Status Register) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Status Register) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + // Read Status + retry = 0; + while (1) { + msleep(5); + buf[0] = 0x00; + buf[1] = 0x05; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Read Status (for Write Status Register) error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Read Status) + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Status for Write Status Register) error!!(%d)\n", ret); + return ret; + } + if ((buf[1] == 0xAA) && (buf[2] == 0x00)) { + break; + } + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("Check 0xAA (Read Status for Write Status Register) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry); + return -1; + } + } + + if (fw_need_write_size % FLASH_SECTOR_SIZE) + count = fw_need_write_size / FLASH_SECTOR_SIZE + 1; + else + count = fw_need_write_size / FLASH_SECTOR_SIZE; + + for(i = 0; i < count; i++) { + // Write Enable + buf[0] = 0x00; + buf[1] = 0x06; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable error!!(%d,%d)\n", ret, i); + return ret; + } + // Check 0xAA (Write Enable) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + Flash_Address = i * FLASH_SECTOR_SIZE; + + // Sector Erase + buf[0] = 0x00; + buf[1] = 0x20; // Command : Sector Erase + buf[2] = ((Flash_Address >> 16) & 0xFF); + buf[3] = ((Flash_Address >> 8) & 0xFF); + buf[4] = (Flash_Address & 0xFF); + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 5); + if (ret < 0) { + NVT_ERR("Sector Erase error!!(%d,%d)\n", ret, i); + return ret; + } + // Check 0xAA (Sector Erase) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Sector Erase) error!!(%d,%d)\n", ret, i); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Sector Erase) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + + // Read Status + retry = 0; + while (1) { + msleep(5); + buf[0] = 0x00; + buf[1] = 0x05; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Read Status error!!(%d,%d)\n", ret, i); + return ret; + } + + // Check 0xAA (Read Status) + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Status) error!!(%d,%d)\n", ret, i); + return ret; + } + if ((buf[1] == 0xAA) && (buf[2] == 0x00)) { + break; + } + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry); + return -1; + } + } + } + + NVT_LOG("Erase OK \n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen write flash sectors function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Write_Flash(void) +{ + uint8_t buf[64] = {0}; + uint32_t XDATA_Addr = ts->mmap->RW_FLASH_DATA_ADDR; + uint32_t Flash_Address = 0; + int32_t i = 0, j = 0, k = 0; + uint8_t tmpvalue = 0; + int32_t count = 0; + int32_t ret = 0; + int32_t retry = 0; + int32_t percent = 0; + int32_t previous_percent = -1; + + // change I2C buffer index + ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr); + if (ret < 0) { + NVT_ERR("change I2C buffer index error!!(%d)\n", ret); + return ret; + } + + if (fw_need_write_size % 256) + count = fw_need_write_size / 256 + 1; + else + count = fw_need_write_size / 256; + + for (i = 0; i < count; i++) { + Flash_Address = i * 256; + + // Write Enable + buf[0] = 0x00; + buf[1] = 0x06; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Write Enable) + retry = 0; + while (1) { + udelay(100); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + // Write Page : 256 bytes + for (j = 0; j < min(fw_need_write_size - i * 256, (size_t)256); j += 32) { + buf[0] = (XDATA_Addr + j) & 0xFF; + for (k = 0; k < 32; k++) { + buf[1 + k] = fw_entry->data[Flash_Address + j + k]; + } + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 33); + if (ret < 0) { + NVT_ERR("Write Page error!!(%d), j=%d\n", ret, j); + return ret; + } + } + if (fw_need_write_size - Flash_Address >= 256) + tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (255); + else + tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (fw_need_write_size - Flash_Address - 1); + + for (k = 0; k < min(fw_need_write_size - Flash_Address, (size_t)256); k++) + tmpvalue += fw_entry->data[Flash_Address + k]; + + tmpvalue = 255 - tmpvalue + 1; + + // Page Program + buf[0] = 0x00; + buf[1] = 0x02; + buf[2] = ((Flash_Address >> 16) & 0xFF); + buf[3] = ((Flash_Address >> 8) & 0xFF); + buf[4] = (Flash_Address & 0xFF); + buf[5] = 0x00; + buf[6] = min(fw_need_write_size - Flash_Address, (size_t)256) - 1; + buf[7] = tmpvalue; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 8); + if (ret < 0) { + NVT_ERR("Page Program error!!(%d), i=%d\n", ret, i); + return ret; + } + // Check 0xAA (Page Program) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Page Program error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA || buf[1] == 0xEA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Page Program) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + if (buf[1] == 0xEA) { + NVT_ERR("Page Program error!! i=%d\n", i); + return -3; + } + + // Read Status + retry = 0; + while (1) { + msleep(5); + buf[0] = 0x00; + buf[1] = 0x05; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Read Status error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Read Status) + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Status) error!!(%d)\n", ret); + return ret; + } + if (((buf[1] == 0xAA) && (buf[2] == 0x00)) || (buf[1] == 0xEA)) { + break; + } + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry); + return -1; + } + } + if (buf[1] == 0xEA) { + NVT_ERR("Page Program error!! i=%d\n", i); + return -4; + } + + percent = ((i + 1) * 100) / count; + if (((percent % 10) == 0) && (percent != previous_percent)) { + NVT_LOG("Programming...%2d%%\n", percent); + previous_percent = percent; + } + } + + NVT_LOG("Program OK \n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen verify checksum of written + flash function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Verify_Flash(void) +{ + uint8_t buf[64] = {0}; + uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR; + int32_t ret = 0; + int32_t i = 0; + int32_t k = 0; + uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0}; + uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0}; + size_t len_in_blk = 0; + int32_t retry = 0; + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_need_write_size > (i * SIZE_64KB)) { + // Calculate WR_Filechksum of each 64KB block + len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB); + WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF); + for (k = 0; k < len_in_blk; k++) { + WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB]; + } + WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1; + + // Fast Read Command + buf[0] = 0x00; + buf[1] = 0x07; + buf[2] = i; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = ((len_in_blk - 1) >> 8) & 0xFF; + buf[6] = (len_in_blk - 1) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7); + if (ret < 0) { + NVT_ERR("Fast Read Command error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Fast Read Command) + retry = 0; + while (1) { + msleep(80); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 5)) { + NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + // Read Checksum (write addr high byte & middle byte) + ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr); + if (ret < 0) { + NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret); + return ret; + } + // Read Checksum + buf[0] = (XDATA_Addr) & 0xFF; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Read Checksum error!!(%d)\n", ret); + return ret; + } + + RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]); + if (WR_Filechksum[i] != RD_Filechksum[i]) { + NVT_ERR("Verify Fail%d!!\n", i); + NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]); + return -1; + } + } + } + + NVT_LOG("Verify OK \n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen update firmware function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Update_Firmware(void) +{ + int32_t ret = 0; + + //---Stop CRC check to prevent IC auto reboot--- + nvt_stop_crc_reboot(); + + // Step 1 : initial bootloader + ret = Init_BootLoader(); + if (ret) { + return ret; + } + + // Step 2 : Resume PD + ret = Resume_PD(); + if (ret) { + return ret; + } + + // Step 3 : Erase + ret = Erase_Flash(); + if (ret) { + return ret; + } + + // Step 4 : Program + ret = Write_Flash(); + if (ret) { + return ret; + } + + // Step 5 : Verify + ret = Verify_Flash(); + if (ret) { + return ret; + } + + //Step 6 : Bootloader Reset + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen check flash end flag function. + +return: + Executive outcomes. 0---succeed. 1,negative---failed. +*******************************************************/ +int32_t nvt_check_flash_end_flag(void) +{ + uint8_t buf[8] = {0}; + uint8_t nvt_end_flag[NVT_FLASH_END_FLAG_LEN + 1] = {0}; + int32_t ret = 0; + + // Step 1 : initial bootloader + ret = Init_BootLoader(); + if (ret) { + return ret; + } + + // Step 2 : Resume PD + ret = Resume_PD(); + if (ret) { + return ret; + } + + // Step 3 : unlock + buf[0] = 0x00; + buf[1] = 0x35; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("write unlock error!!(%d)\n", ret); + return ret; + } + msleep(10); + + //Step 4 : Flash Read Command + buf[0] = 0x00; + buf[1] = 0x03; + buf[2] = (NVT_FLASH_END_FLAG_ADDR >> 16) & 0xFF; //Addr_H + buf[3] = (NVT_FLASH_END_FLAG_ADDR >> 8) & 0xFF; //Addr_M + buf[4] = NVT_FLASH_END_FLAG_ADDR & 0xFF; //Addr_L + buf[5] = (NVT_FLASH_END_FLAG_LEN >> 8) & 0xFF; //Len_H + buf[6] = NVT_FLASH_END_FLAG_LEN & 0xFF; //Len_L + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7); + if (ret < 0) { + NVT_ERR("write Read Command error!!(%d)\n", ret); + return ret; + } + msleep(10); + + // Check 0xAA (Read Command) + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] != 0xAA) { + NVT_ERR("Check 0xAA (Read Command) error!! status=0x%02X\n", buf[1]); + return -1; + } + + msleep(10); + + //Step 5 : Read Flash Data + ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->READ_FLASH_CHECKSUM_ADDR); + if (ret < 0) { + NVT_ERR("change index error!! (%d)\n", ret); + return ret; + } + msleep(10); + + // Read Back + buf[0] = ts->mmap->READ_FLASH_CHECKSUM_ADDR & 0xFF; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 6); + if (ret < 0) { + NVT_ERR("Read Back error!! (%d)\n", ret); + return ret; + } + + //buf[3:5] => NVT End Flag + strlcpy(nvt_end_flag, &buf[3], sizeof(nvt_end_flag)); + NVT_LOG("nvt_end_flag=%s (%02X %02X %02X)\n", nvt_end_flag, buf[3], buf[4], buf[5]); + + if ((memcmp(nvt_end_flag, "NVT", NVT_FLASH_END_FLAG_LEN) == 0) || + (memcmp(nvt_end_flag, "MOD", NVT_FLASH_END_FLAG_LEN) == 0)) { + return 0; + } else { + NVT_ERR("\"NVT\" end flag not found!\n"); + return 1; + } +} + +/******************************************************* +Description: + Novatek touchscreen update firmware when booting + function. + +return: + n.a. +*******************************************************/ +void Boot_Update_Firmware(struct work_struct *work) +{ + int32_t ret = 0; + + char firmware_name[256] = ""; + + snprintf(firmware_name, sizeof(firmware_name), + BOOT_UPDATE_FIRMWARE_NAME); + + // request bin file in "/etc/firmware" + ret = update_firmware_request(firmware_name); + if (ret) { + NVT_ERR("update_firmware_request failed. (%d)\n", ret); + return; + } + + mutex_lock(&ts->lock); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_sw_reset_idle(); + + ret = Check_CheckSum(); + + if (ret < 0) { // read firmware checksum failed + NVT_ERR("read firmware checksum failed\n"); + Update_Firmware(); + } else if ((ret == 0) && (Check_FW_Ver() == 0)) { // (fw checksum not match) && (bin fw version >= ic fw version) + NVT_LOG("firmware version not match\n"); + Update_Firmware(); + } else if (nvt_check_flash_end_flag()) { + NVT_LOG("check flash end flag failed\n"); + Update_Firmware(); + } else { + // Bootloader Reset + nvt_bootloader_reset(); + ret = nvt_check_fw_reset_state(RESET_STATE_INIT); + if (ret) { + NVT_LOG("check fw reset state failed\n"); + Update_Firmware(); + } + } + + mutex_unlock(&ts->lock); + + update_firmware_release(); +} +#endif /* BOOT_UPDATE_FIRMWARE */ + +#else /* NT36XXX_SPI */ + +#include +#include + +#include "nt36xxx.h" + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + +#define SIZE_4KB 4096 +#define FLASH_SECTOR_SIZE SIZE_4KB +#define FW_BIN_VER_OFFSET (nvt_spi_fw_need_write_size - SIZE_4KB) +#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1) +#define NVT_FLASH_END_FLAG_LEN 3 +#define NVT_FLASH_END_FLAG_ADDR (nvt_spi_fw_need_write_size - NVT_FLASH_END_FLAG_LEN) + +#define NVT_DUMP_PARTITION (0) +#define NVT_DUMP_PARTITION_LEN (1024) +#define NVT_DUMP_PARTITION_PATH "/data/local/tmp" + +static ktime_t nvt_spi_start, nvt_spi_end; +static const struct firmware *nvt_spi_fw_entry; +static size_t nvt_spi_fw_need_write_size; +static uint8_t *nvt_spi_fwbuf; + +struct nvt_spi_bin_map_t { + char name[12]; + uint32_t BIN_addr; + uint32_t SRAM_addr; + uint32_t size; + uint32_t crc; +}; + +static struct nvt_spi_bin_map_t *nvt_spi_bin_map; + +static int32_t nvt_spi_get_fw_need_write_size(const struct firmware *fw_entry) +{ + int32_t i = 0; + int32_t total_sectors_to_check = 0; + + total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE; + /* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */ + + for (i = total_sectors_to_check; i > 0; i--) { + // printk("current end flag address checked = 0x%X\n", + // i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); + /* check if there is end flag "NVT" at the end of this sector */ + if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], + "NVT", NVT_FLASH_END_FLAG_LEN) == 0) { + nvt_spi_fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx), NVT end flag\n", + nvt_spi_fw_need_write_size, nvt_spi_fw_need_write_size); + return 0; + } + + /* check if there is end flag "MOD" at the end of this sector */ + if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], + "MOD", NVT_FLASH_END_FLAG_LEN) == 0) { + nvt_spi_fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx), MOD end flag\n", + nvt_spi_fw_need_write_size, nvt_spi_fw_need_write_size); + return 0; + } + } + + NVT_ERR("end flag \"NVT\" \"MOD\" not found!\n"); + return -EINVAL; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen init variable and allocate buffer + * for download firmware function. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_download_init(void) +{ + uint8_t *buf; + /* allocate buffer for transfer firmware */ + //NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_SPI_TRANSFER_LEN); + + if (nvt_spi_fwbuf == NULL) { + buf = kzalloc((NVT_SPI_TRANSFER_LEN + 1 + NVT_SPI_DUMMY_BYTES), GFP_KERNEL); + if (buf == NULL) { + NVT_ERR("kzalloc for fwbuf failed!\n"); + return -ENOMEM; + } + nvt_spi_fwbuf = buf; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen checksum function. Calculate bin + * file checksum for comparison. + * + * return: + * n.a. + ****************************************************** + */ +static uint32_t CheckSum(const u8 *data, size_t len) +{ + uint32_t i = 0; + uint32_t checksum = 0; + + for (i = 0 ; i < len + 1; i++) + checksum += data[i]; + + checksum += len; + checksum = ~checksum + 1; + + return checksum; +} + +static uint32_t byte_to_word(const uint8_t *data) +{ + return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen parsing bin header function. + * + * return: + * n.a. + ****************************************************** + */ +static uint32_t nvt_spi_partition; +static uint8_t nvt_spi_ilm_dlm_num = 2; +static uint8_t nvt_spi_cascade_2nd_header_info; +static int32_t nvt_spi_bin_header_parser(const u8 *fwdata, size_t fwsize) +{ + uint32_t list = 0; + uint32_t pos = 0x00; + uint32_t end = 0x00; + uint8_t info_sec_num = 0; + uint8_t ovly_sec_num = 0; + uint8_t ovly_info = 0; + uint8_t find_bin_header = 0; + struct nvt_spi_bin_map_t *bin_map = NULL; + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* Find the header size */ + end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24); + + /* check cascade next header */ + nvt_spi_cascade_2nd_header_info = (fwdata[0x20] & 0x02) >> 1; + NVT_LOG("cascade_2nd_header_info = %d\n", nvt_spi_cascade_2nd_header_info); + + if (nvt_spi_cascade_2nd_header_info) { + pos = 0x30; // info section start at 0x30 offset + while (pos < (end / 2)) { + info_sec_num++; + pos += 0x10; /* each header info is 16 bytes */ + } + + info_sec_num = info_sec_num + 1; //next header section + } else { + pos = 0x30; // info section start at 0x30 offset + while (pos < end) { + info_sec_num++; + pos += 0x10; /* each header info is 16 bytes */ + } + } + + /* + * Find the DLM OVLY section + * [0:3] Overlay Section Number + * [4] Overlay Info + */ + ovly_info = (fwdata[0x28] & 0x10) >> 4; + ovly_sec_num = (ovly_info) ? (fwdata[0x28] & 0x0F) : 0; + + /* + * calculate all partition number + * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + nvt_spi_partition = nvt_spi_ilm_dlm_num + ovly_sec_num + info_sec_num; + NVT_LOG("ovly_info=%d, ilm_dlm_num=%d, ovly_sec_num=%d, info_sec_num=%d, partition=%d\n", + ovly_info, nvt_spi_ilm_dlm_num, ovly_sec_num, info_sec_num, nvt_spi_partition); + + + /* allocated memory for header info */ + bin_map = kzalloc((nvt_spi_partition+1) * sizeof(struct nvt_spi_bin_map_t), GFP_KERNEL); + if (bin_map == NULL) { + NVT_ERR("kzalloc for bin_map failed!\n"); + return -ENOMEM; + } + nvt_spi_bin_map = bin_map; + + for (list = 0; list < nvt_spi_partition; list++) { + /* + * [1] parsing ILM & DLM header info + * BIN_addr : SRAM_addr : size (12-bytes) + * crc located at 0x18 & 0x1C + */ + if (list < nvt_spi_ilm_dlm_num) { + bin_map[list].BIN_addr = byte_to_word(&fwdata[0 + list * 12]); + bin_map[list].SRAM_addr = byte_to_word(&fwdata[4 + list * 12]); + bin_map[list].size = byte_to_word(&fwdata[8 + list * 12]); + if (ts->hw_crc) + bin_map[list].crc = byte_to_word(&fwdata[0x18 + list * 4]); + else { //ts->hw_crc + if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) + bin_map[list].crc = CheckSum( + &fwdata[bin_map[list].BIN_addr], + bin_map[list].size); + else { + NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + if (list == 0) + snprintf(bin_map[list].name, sizeof(bin_map[list].name), "ILM"); + else if (list == 1) + snprintf(bin_map[list].name, sizeof(bin_map[list].name), "DLM"); + } + + /* + * [2] parsing others header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if ((list >= nvt_spi_ilm_dlm_num) + && (list < (nvt_spi_ilm_dlm_num + info_sec_num))) { + + if (find_bin_header == 0) { + /* others partition located at 0x30 offset */ + pos = 0x30 + (0x10 * (list - nvt_spi_ilm_dlm_num)); + } else if (find_bin_header && nvt_spi_cascade_2nd_header_info) { + /* cascade 2nd header info */ + pos = end - 0x10; + } + + bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]); + bin_map[list].size = byte_to_word(&fwdata[pos + 4]); + bin_map[list].BIN_addr = byte_to_word(&fwdata[pos + 8]); + if (ts->hw_crc) + bin_map[list].crc = byte_to_word(&fwdata[pos + 12]); + else { //ts->hw_crc + if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) + bin_map[list].crc = CheckSum( + &fwdata[bin_map[list].BIN_addr], + bin_map[list].size); + else { + NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + /* detect header end to protect parser function */ + if ((bin_map[list].BIN_addr < end) && (bin_map[list].size != 0)) { + snprintf(bin_map[list].name, sizeof(bin_map[list].name), + "Header"); + find_bin_header = 1; + } else + snprintf(bin_map[list].name, sizeof(bin_map[list].name), + "Info-%d", (list - nvt_spi_ilm_dlm_num)); + } + + /* + * [3] parsing overlay section header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if (list >= (nvt_spi_ilm_dlm_num + info_sec_num)) { + /* overlay info located at DLM (list = 1) start addr */ + pos = bin_map[1].BIN_addr; + pos += (0x10 * (list - nvt_spi_ilm_dlm_num - info_sec_num)); + + bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]); + bin_map[list].size = byte_to_word(&fwdata[pos + 4]); + bin_map[list].BIN_addr = byte_to_word(&fwdata[pos + 8]); + if (ts->hw_crc) + bin_map[list].crc = byte_to_word(&fwdata[pos + 12]); + else { //ts->hw_crc + if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) + bin_map[list].crc = CheckSum( + &fwdata[bin_map[list].BIN_addr], + bin_map[list].size); + else { + NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + snprintf(bin_map[list].name, sizeof(bin_map[list].name), + "Overlay-%d", + (list - nvt_spi_ilm_dlm_num - info_sec_num)); + } + + /* BIN size error detect */ + if ((bin_map[list].BIN_addr + bin_map[list].size) > fwsize) { + NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + +// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X), CRC (0x%08X)\n", +// list, bin_map[list].name, +// bin_map[list].SRAM_addr, bin_map[list].size, +// bin_map[list].BIN_addr, bin_map[list].crc); + } + + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen release update firmware function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_update_firmware_release(void) +{ + if (nvt_spi_fw_entry) + release_firmware(nvt_spi_fw_entry); + + nvt_spi_fw_entry = NULL; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen request update firmware function. + * + * return: + * Executive outcomes. 0---succeed. -1,-22---failed. + ****************************************************** + */ +static int32_t nvt_spi_update_firmware_request(char *filename) +{ + uint8_t retry = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + uint8_t ver; + + if (filename == NULL) + return -ENOENT; + + while (1) { + NVT_LOG("filename is %s\n", filename); + + ret = request_firmware(&nvt_spi_fw_entry, filename, &ts->client->dev); + if (ret) { + NVT_ERR("firmware load failed, ret=%d\n", ret); + goto request_fail; + } + + // check FW need to write size + if (nvt_spi_get_fw_need_write_size(nvt_spi_fw_entry)) { + NVT_ERR("get fw need to write size fail!\n"); + ret = -EINVAL; + goto invalid; + } + + // check if FW version add FW version bar equals 0xFF + ver = *(nvt_spi_fw_entry->data + FW_BIN_VER_OFFSET); + if (ver + *(nvt_spi_fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) { + NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n"); + NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", + *(nvt_spi_fw_entry->data+FW_BIN_VER_OFFSET), + *(nvt_spi_fw_entry->data+FW_BIN_VER_BAR_OFFSET)); + ret = -ENOEXEC; + goto invalid; + } + + /* BIN Header Parser */ + ret = nvt_spi_bin_header_parser(nvt_spi_fw_entry->data, nvt_spi_fw_entry->size); + if (ret) { + NVT_ERR("bin header parser failed\n"); + goto invalid; + } else + break; + +invalid: + nvt_spi_update_firmware_release(); + if (!IS_ERR_OR_NULL(nvt_spi_bin_map)) { + kfree(nvt_spi_bin_map); + nvt_spi_bin_map = NULL; + } + +request_fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + break; + } + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen write data to sram function. + * + * - fwdata : The buffer is written + * - SRAM_addr: The sram destination address + * - size : Number of data bytes in @fwdata being written + * - BIN_addr : The transferred data offset of @fwdata + * + * return: + * Executive outcomes. 0---succeed. else---fail. + ******************************************************* + */ +static int32_t nvt_spi_write_sram(const u8 *fwdata, + uint32_t SRAM_addr, uint32_t size, uint32_t BIN_addr) +{ + int32_t ret = 0; + uint32_t i = 0; + uint16_t len = 0; + int32_t count = 0; + + if (size % NVT_SPI_TRANSFER_LEN) + count = (size / NVT_SPI_TRANSFER_LEN) + 1; + else + count = (size / NVT_SPI_TRANSFER_LEN); + + for (i = 0 ; i < count ; i++) { + len = (size < NVT_SPI_TRANSFER_LEN) ? size : NVT_SPI_TRANSFER_LEN; + + //---set xdata index to start address of SRAM--- + ret = nvt_spi_set_page(SRAM_addr); + if (ret) { + NVT_ERR("set page failed, ret = %d\n", ret); + return ret; + } + + //---write data into SRAM--- + nvt_spi_fwbuf[0] = SRAM_addr & 0x7F; //offset + memcpy(nvt_spi_fwbuf+1, &fwdata[BIN_addr], len); //payload + ret = nvt_spi_write(nvt_spi_fwbuf, len+1); + if (ret) { + NVT_ERR("write to sram failed, ret = %d\n", ret); + return ret; + } + + SRAM_addr += NVT_SPI_TRANSFER_LEN; + BIN_addr += NVT_SPI_TRANSFER_LEN; + size -= NVT_SPI_TRANSFER_LEN; + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen nvt_spi_write_firmware function to write + * firmware into each partition. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_write_firmware(const u8 *fwdata, size_t fwsize) +{ + uint32_t list = 0; + char *name; + uint32_t BIN_addr, SRAM_addr, size; + int32_t ret = 0; + + memset(nvt_spi_fwbuf, 0, (NVT_SPI_TRANSFER_LEN+1)); + + for (list = 0; list < nvt_spi_partition; list++) { + /* initialize variable */ + SRAM_addr = nvt_spi_bin_map[list].SRAM_addr; + size = nvt_spi_bin_map[list].size; + BIN_addr = nvt_spi_bin_map[list].BIN_addr; + name = nvt_spi_bin_map[list].name; + +// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X)\n", +// list, name, SRAM_addr, size, BIN_addr); + + /* Check data size */ + if ((BIN_addr + size) > fwsize) { + NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", + BIN_addr, BIN_addr + size); + ret = -EINVAL; + goto out; + } + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!size) + continue; + else + size = size + 1; + + /* write data to SRAM */ + ret = nvt_spi_write_sram(fwdata, SRAM_addr, size, BIN_addr); + if (ret) { + NVT_ERR("sram program failed, ret = %d\n", ret); + goto out; + } + } + +out: + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen check checksum function. + * This function will compare file checksum and fw checksum. + * + * return: + * n.a. + ******************************************************* + */ +static int32_t nvt_spi_check_fw_checksum(void) +{ + uint32_t fw_checksum = 0; + uint32_t len = nvt_spi_partition * 4; + uint32_t list = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + memset(nvt_spi_fwbuf, 0, (len+1)); + + //---set xdata index to checksum--- + nvt_spi_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); + + /* read checksum */ + nvt_spi_fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F; + ret = nvt_spi_read(nvt_spi_fwbuf, len+1); + if (ret) { + NVT_ERR("Read fw checksum failed\n"); + return ret; + } + + /* + * Compare each checksum from fw + * ILM + DLM + Overlay + Info + * nvt_spi_ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + for (list = 0; list < nvt_spi_partition; list++) { + fw_checksum = byte_to_word(&nvt_spi_fwbuf[1+list*4]); + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!nvt_spi_bin_map[list].size) + continue; + + if (nvt_spi_bin_map[list].crc != fw_checksum) { + NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n", + list, nvt_spi_bin_map[list].crc, fw_checksum); + ret = -EIO; + } + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen set bootload crc reg bank function. + * This function will set hw crc reg before enable crc function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_set_bld_crc_bank(uint32_t DES_ADDR, uint32_t SRAM_ADDR, + uint32_t LENGTH_ADDR, uint32_t size, + uint32_t G_CHECKSUM_ADDR, uint32_t crc) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* write destination address */ + nvt_spi_set_page(DES_ADDR); + nvt_spi_fwbuf[0] = DES_ADDR & 0x7F; + nvt_spi_fwbuf[1] = (SRAM_ADDR) & 0xFF; + nvt_spi_fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF; + nvt_spi_fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF; + nvt_spi_write(nvt_spi_fwbuf, 4); + + /* write length */ + //nvt_spi_set_page(LENGTH_ADDR); + nvt_spi_fwbuf[0] = LENGTH_ADDR & 0x7F; + nvt_spi_fwbuf[1] = (size) & 0xFF; + nvt_spi_fwbuf[2] = (size >> 8) & 0xFF; + nvt_spi_fwbuf[3] = (size >> 16) & 0x01; + if (ts->hw_crc == 1) + nvt_spi_write(nvt_spi_fwbuf, 3); + else if (ts->hw_crc > 1) + nvt_spi_write(nvt_spi_fwbuf, 4); + + /* write golden dlm checksum */ + //nvt_spi_set_page(G_CHECKSUM_ADDR); + nvt_spi_fwbuf[0] = G_CHECKSUM_ADDR & 0x7F; + nvt_spi_fwbuf[1] = (crc) & 0xFF; + nvt_spi_fwbuf[2] = (crc >> 8) & 0xFF; + nvt_spi_fwbuf[3] = (crc >> 16) & 0xFF; + nvt_spi_fwbuf[4] = (crc >> 24) & 0xFF; + nvt_spi_write(nvt_spi_fwbuf, 5); +} + +/* + ********************************************************* + * Description: + * Novatek touchscreen set BLD hw crc function. + * This function will set ILM and DLM crc information to register. + * + * return: + * n.a. + ********************************************************* + */ +static void nvt_spi_set_bld_hw_crc(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* [0] ILM */ + /* write register bank */ + nvt_spi_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, nvt_spi_bin_map[0].SRAM_addr, + ts->mmap->ILM_LENGTH_ADDR, nvt_spi_bin_map[0].size, + ts->mmap->G_ILM_CHECKSUM_ADDR, nvt_spi_bin_map[0].crc); + + /* [1] DLM */ + /* write register bank */ + nvt_spi_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, nvt_spi_bin_map[1].SRAM_addr, + ts->mmap->DLM_LENGTH_ADDR, nvt_spi_bin_map[1].size, + ts->mmap->G_DLM_CHECKSUM_ADDR, nvt_spi_bin_map[1].crc); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen read BLD hw crc info function. + * This function will check crc results from register. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_read_bld_hw_crc(void) +{ + uint8_t buf[8] = {0}; + uint32_t g_crc = 0, r_crc = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* CRC Flag */ + nvt_spi_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR); + buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F; + buf[1] = 0x00; + nvt_spi_read(buf, 2); + NVT_ERR("crc_done = %d, ilm_crc_flag = %d, dlm_crc_flag = %d\n", + (buf[1] >> 2) & 0x01, (buf[1] >> 0) & 0x01, (buf[1] >> 1) & 0x01); + + /* ILM CRC */ + nvt_spi_set_page(ts->mmap->G_ILM_CHECKSUM_ADDR); + buf[0] = ts->mmap->G_ILM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_spi_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); + buf[0] = ts->mmap->R_ILM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + NVT_ERR("ilm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n", + nvt_spi_bin_map[0].crc, g_crc, r_crc); + + /* DLM CRC */ + nvt_spi_set_page(ts->mmap->G_DLM_CHECKSUM_ADDR); + buf[0] = ts->mmap->G_DLM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_spi_set_page(ts->mmap->R_DLM_CHECKSUM_ADDR); + buf[0] = ts->mmap->R_DLM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + NVT_ERR("dlm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n", + nvt_spi_bin_map[1].crc, g_crc, r_crc); + +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen Download_Firmware with HW CRC + * function. It's complete download firmware flow. + * + * return: + * Executive outcomes. 0---succeed. else---fail. + ****************************************************** + */ +static int32_t nvt_spi_download_firmware_hw_crc(void) +{ + uint8_t retry = 0; + int32_t ret = 0; + const struct firmware *fw = nvt_spi_fw_entry; + + nvt_spi_start = ktime_get(); + + while (1) { + /* bootloader reset to reset MCU */ + nvt_spi_bootloader_reset(); + + /* set ilm & dlm reg bank */ + nvt_spi_set_bld_hw_crc(); + + /* Start to write firmware process */ + if (nvt_spi_cascade_2nd_header_info) { + /* for cascade */ + nvt_spi_tx_auto_copy_mode(); + + ret = nvt_spi_write_firmware(fw->data, fw->size); + if (ret) { + NVT_ERR("Write_Firmware failed. (%d)\n", ret); + goto fail; + } + + ret = nvt_spi_check_spi_dma_tx_info(); + if (ret) { + NVT_ERR("spi dma tx info failed. (%d)\n", ret); + goto fail; + } + } else { + ret = nvt_spi_write_firmware(fw->data, fw->size); + if (ret) { + NVT_ERR("Write_Firmware failed. (%d)\n", ret); + goto fail; + } + } + +#if NVT_DUMP_PARTITION + ret = nvt_dump_partition(); + if (ret) + NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret); +#endif + + /* enable hw bld crc function */ + nvt_spi_bld_crc_enable(); + + /* clear fw reset status & enable fw crc check */ + nvt_spi_fw_crc_enable(); + + /* Set Boot Ready Bit */ + nvt_spi_boot_ready(); + + ret = nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_INIT); + if (ret) { + NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); + goto fail; + } else { + break; + } + +fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + nvt_spi_read_bld_hw_crc(); + break; + } + } + + nvt_spi_end = ktime_get(); + + return ret; +} + +/* + ******************************************************** + * Description: + * Novatek touchscreen Download_Firmware function. It's + * complete download firmware flow. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_download_firmware(void) +{ + uint32_t addr; + uint8_t retry = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + nvt_spi_start = ktime_get(); + + while (1) { + /* + * Send eng reset cmd before download FW + * Keep TP_RESX low when send eng reset cmd + */ +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 0); + mdelay(1); //wait 1ms +#endif + nvt_spi_eng_reset(); +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); + mdelay(10); //wait tRT2BRST after TP_RST +#endif + nvt_spi_bootloader_reset(); + + addr = ts->mmap->EVENT_BUF_ADDR; + /* clear fw reset status */ + nvt_spi_write_addr(addr | NVT_SPI_EVENT_MAP_RESET_COMPLETE, 0x00); + + /* Start to write firmware process */ + ret = nvt_spi_write_firmware(nvt_spi_fw_entry->data, nvt_spi_fw_entry->size); + if (ret) { + NVT_ERR("Write_Firmware failed. (%d)\n", ret); + goto fail; + } + +#if NVT_DUMP_PARTITION + ret = nvt_dump_partition(); + if (ret) + NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret); +#endif + + /* Set Boot Ready Bit */ + nvt_spi_boot_ready(); + + ret = nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_INIT); + if (ret) { + NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); + goto fail; + } + + /* check fw checksum result */ + ret = nvt_spi_check_fw_checksum(); + if (ret) { + NVT_ERR("firmware checksum not match, retry=%d\n", retry); + goto fail; + } else + break; + +fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + break; + } + } + + nvt_spi_end = ktime_get(); + + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen update firmware main function. + * + * return: + * n.a. + ****************************************************** + */ +int32_t nvt_spi_update_firmware(char *firmware_name) +{ + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + // request bin file in "/etc/firmware" + ret = nvt_spi_update_firmware_request(firmware_name); + if (ret) { + NVT_ERR("update_firmware_request failed. (%d)\n", ret); + goto request_firmware_fail; + } + + /* initial buffer and variable */ + ret = nvt_spi_download_init(); + if (ret) { + NVT_ERR("Download Init failed. (%d)\n", ret); + goto download_fail; + } + + /* download firmware process */ + if (ts->hw_crc) + ret = nvt_spi_download_firmware_hw_crc(); + else + ret = nvt_spi_download_firmware(); + if (ret) { + NVT_ERR("Download Firmware failed. (%d)\n", ret); + goto download_fail; + } + + NVT_LOG("Update firmware success! <%ld us>\n", + (long) ktime_us_delta(nvt_spi_end, nvt_spi_start)); + + /* Get FW Info */ + ret = nvt_spi_get_fw_info(); + if (ret) + NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret); + +download_fail: + if (!IS_ERR_OR_NULL(nvt_spi_bin_map)) { + kfree(nvt_spi_bin_map); + nvt_spi_bin_map = NULL; + } + + nvt_spi_update_firmware_release(); +request_firmware_fail: + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen update firmware when booting + * function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_update_firmware_work(struct work_struct *work) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + mutex_lock(&ts->lock); + nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME); + mutex_unlock(&ts->lock); +} +#endif + +#endif diff --git a/nt36xxx/nt36xxx_mem_map.h b/nt36xxx/nt36xxx_mem_map.h new file mode 100644 index 0000000000..c75d2d4cdb --- /dev/null +++ b/nt36xxx/nt36xxx_mem_map.h @@ -0,0 +1,607 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 48764 $ + * $Date: 2019-08-08 14:52:12 +0800 (Thu, 08 Aug 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ +#define CHIP_VER_TRIM_ADDR 0x3F004 +#define CHIP_VER_TRIM_OLD_ADDR 0x1F64E + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +struct nvt_ts_mem_map { + uint32_t EVENT_BUF_ADDR; + uint32_t RAW_PIPE0_ADDR; + uint32_t RAW_PIPE1_ADDR; + uint32_t BASELINE_ADDR; + uint32_t BASELINE_BTN_ADDR; + uint32_t DIFF_PIPE0_ADDR; + uint32_t DIFF_PIPE1_ADDR; + uint32_t RAW_BTN_PIPE0_ADDR; + uint32_t RAW_BTN_PIPE1_ADDR; + uint32_t DIFF_BTN_PIPE0_ADDR; + uint32_t DIFF_BTN_PIPE1_ADDR; + uint32_t READ_FLASH_CHECKSUM_ADDR; + uint32_t RW_FLASH_DATA_ADDR; +}; + +struct nvt_ts_hw_info { + uint8_t carrier_system; + uint8_t hw_crc; +}; + +static const struct nvt_ts_mem_map NT36526_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21758, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20AB0, + .DIFF_PIPE1_ADDR = 0x24AB0, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36675_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21B90, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20C60, + .DIFF_PIPE1_ADDR = 0x24C60, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36672A_memory_map = { + .EVENT_BUF_ADDR = 0x21C00, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE1_ADDR = 0x23000, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_BTN_ADDR = 0x23BFC, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE1_ADDR = 0x236DC, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36772_memory_map = { + .EVENT_BUF_ADDR = 0x11E00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10E70, + .BASELINE_BTN_ADDR = 0x12E70, + .DIFF_PIPE0_ADDR = 0x10830, + .DIFF_PIPE1_ADDR = 0x12830, + .RAW_BTN_PIPE0_ADDR = 0x10E60, + .RAW_BTN_PIPE1_ADDR = 0x12E60, + .DIFF_BTN_PIPE0_ADDR = 0x10E68, + .DIFF_BTN_PIPE1_ADDR = 0x12E68, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static const struct nvt_ts_mem_map NT36525_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static const struct nvt_ts_mem_map NT36676F_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static struct nvt_ts_hw_info NT36526_hw_info = { + .carrier_system = 2, + .hw_crc = 2, +}; + +static struct nvt_ts_hw_info NT36675_hw_info = { + .carrier_system = 2, + .hw_crc = 2, +}; + +static struct nvt_ts_hw_info NT36672A_hw_info = { + .carrier_system = 0, + .hw_crc = 1, +}; + +static struct nvt_ts_hw_info NT36772_hw_info = { + .carrier_system = 0, + .hw_crc = 0, +}; + +static struct nvt_ts_hw_info NT36525_hw_info = { + .carrier_system = 0, + .hw_crc = 0, +}; + +static struct nvt_ts_hw_info NT36676F_hw_info = { + .carrier_system = 0, + .hw_crc = 0, +}; + +#define NVT_ID_BYTE_MAX 6 +struct nvt_ts_trim_id_table { + uint8_t id[NVT_ID_BYTE_MAX]; + uint8_t mask[NVT_ID_BYTE_MAX]; + const struct nvt_ts_mem_map *mmap; + const struct nvt_ts_hw_info *hwinfo; +}; + +static const struct nvt_ts_trim_id_table trim_id_table[] = { + {.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info} +}; + +#else /* NT36XXX_SPI */ + +#define NVT_SPI_CHIP_VER_TRIM_ADDR 0x3F004 +#define NVT_SPI_CHIP_VER_TRIM_OLD_ADDR 0x1F64E + +struct nvt_spi_mem_map { + uint32_t EVENT_BUF_ADDR; + uint32_t RAW_PIPE0_ADDR; + uint32_t RAW_PIPE1_ADDR; + uint32_t BASELINE_ADDR; + uint32_t BASELINE_BTN_ADDR; + uint32_t DIFF_PIPE0_ADDR; + uint32_t DIFF_PIPE1_ADDR; + uint32_t RAW_BTN_PIPE0_ADDR; + uint32_t RAW_BTN_PIPE1_ADDR; + uint32_t DIFF_BTN_PIPE0_ADDR; + uint32_t DIFF_BTN_PIPE1_ADDR; + uint32_t PEN_2D_BL_TIP_X_ADDR; + uint32_t PEN_2D_BL_TIP_Y_ADDR; + uint32_t PEN_2D_BL_RING_X_ADDR; + uint32_t PEN_2D_BL_RING_Y_ADDR; + uint32_t PEN_2D_DIFF_TIP_X_ADDR; + uint32_t PEN_2D_DIFF_TIP_Y_ADDR; + uint32_t PEN_2D_DIFF_RING_X_ADDR; + uint32_t PEN_2D_DIFF_RING_Y_ADDR; + uint32_t PEN_2D_RAW_TIP_X_ADDR; + uint32_t PEN_2D_RAW_TIP_Y_ADDR; + uint32_t PEN_2D_RAW_RING_X_ADDR; + uint32_t PEN_2D_RAW_RING_Y_ADDR; + uint32_t PEN_1D_DIFF_TIP_X_ADDR; + uint32_t PEN_1D_DIFF_TIP_Y_ADDR; + uint32_t PEN_1D_DIFF_RING_X_ADDR; + uint32_t PEN_1D_DIFF_RING_Y_ADDR; + uint32_t READ_FLASH_CHECKSUM_ADDR; + uint32_t RW_FLASH_DATA_ADDR; + /* Phase 2 Host Download */ + uint32_t BOOT_RDY_ADDR; + uint32_t POR_CD_ADDR; + uint32_t TX_AUTO_COPY_EN; + uint32_t SPI_DMA_TX_INFO; + /* BLD CRC */ + uint32_t BLD_LENGTH_ADDR; + uint32_t ILM_LENGTH_ADDR; + uint32_t DLM_LENGTH_ADDR; + uint32_t BLD_DES_ADDR; + uint32_t ILM_DES_ADDR; + uint32_t DLM_DES_ADDR; + uint32_t G_ILM_CHECKSUM_ADDR; + uint32_t G_DLM_CHECKSUM_ADDR; + uint32_t R_ILM_CHECKSUM_ADDR; + uint32_t R_DLM_CHECKSUM_ADDR; + uint32_t BLD_CRC_EN_ADDR; + uint32_t DMA_CRC_EN_ADDR; + uint32_t BLD_ILM_DLM_CRC_ADDR; + uint32_t DMA_CRC_FLAG_ADDR; +}; + +struct nvt_spi_hw_info { + uint8_t hw_crc; +}; + +static const struct nvt_spi_mem_map NT36523_memory_map = { + .EVENT_BUF_ADDR = 0x2FE00, + .RAW_PIPE0_ADDR = 0x30FA0, + .RAW_PIPE1_ADDR = 0x30FA0, + .BASELINE_ADDR = 0x36510, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x373E8, + .DIFF_PIPE1_ADDR = 0x38068, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .PEN_2D_BL_TIP_X_ADDR = 0x2988A, + .PEN_2D_BL_TIP_Y_ADDR = 0x29A1A, + .PEN_2D_BL_RING_X_ADDR = 0x29BAA, + .PEN_2D_BL_RING_Y_ADDR = 0x29D3A, + .PEN_2D_DIFF_TIP_X_ADDR = 0x29ECA, + .PEN_2D_DIFF_TIP_Y_ADDR = 0x2A05A, + .PEN_2D_DIFF_RING_X_ADDR = 0x2A1EA, + .PEN_2D_DIFF_RING_Y_ADDR = 0x2A37A, + .PEN_2D_RAW_TIP_X_ADDR = 0x2A50A, + .PEN_2D_RAW_TIP_Y_ADDR = 0x2A69A, + .PEN_2D_RAW_RING_X_ADDR = 0x2A82A, + .PEN_2D_RAW_RING_Y_ADDR = 0x2A9BA, + .PEN_1D_DIFF_TIP_X_ADDR = 0x2AB4A, + .PEN_1D_DIFF_TIP_Y_ADDR = 0x2ABAE, + .PEN_1D_DIFF_RING_X_ADDR = 0x2AC12, + .PEN_1D_DIFF_RING_Y_ADDR = 0x2AC76, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + .TX_AUTO_COPY_EN = 0x3F7E8, + .SPI_DMA_TX_INFO = 0x3F7F1, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F136, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + +static const struct nvt_spi_mem_map NT36526_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21758, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20AB0, + .DIFF_PIPE1_ADDR = 0x24AB0, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F136, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + + +static const struct nvt_spi_mem_map NT36675_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21B90, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20C60, + .DIFF_PIPE1_ADDR = 0x24C60, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F136, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + +static const struct nvt_spi_mem_map NT36672A_memory_map = { + .EVENT_BUF_ADDR = 0x21C00, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE1_ADDR = 0x23000, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_BTN_ADDR = 0x23BFC, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE1_ADDR = 0x236DC, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F10E, //0x3F10E ~ 0x3F10F (2 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F119 (2 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F131 (2 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F132, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + +static const struct nvt_spi_mem_map NT36772_memory_map = { + .EVENT_BUF_ADDR = 0x11E00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10E70, + .BASELINE_BTN_ADDR = 0x12E70, + .DIFF_PIPE0_ADDR = 0x10830, + .DIFF_PIPE1_ADDR = 0x12830, + .RAW_BTN_PIPE0_ADDR = 0x10E60, + .RAW_BTN_PIPE1_ADDR = 0x12E60, + .DIFF_BTN_PIPE0_ADDR = 0x10E68, + .DIFF_BTN_PIPE1_ADDR = 0x12E68, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +static const struct nvt_spi_mem_map NT36525_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +static const struct nvt_spi_mem_map NT36676F_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static struct nvt_spi_hw_info NT36523_hw_info = { + .hw_crc = 2, +}; + +static struct nvt_spi_hw_info NT36526_hw_info = { + .hw_crc = 2, +}; + +static struct nvt_spi_hw_info NT36675_hw_info = { + .hw_crc = 2, +}; + +static struct nvt_spi_hw_info NT36672A_hw_info = { + .hw_crc = 1, +}; + +static struct nvt_spi_hw_info NT36772_hw_info = { + .hw_crc = 0, +}; + +static struct nvt_spi_hw_info NT36525_hw_info = { + .hw_crc = 0, +}; + +static struct nvt_spi_hw_info NT36676F_hw_info = { + .hw_crc = 0, +}; + +#define NVT_SPI_ID_BYTE_MAX 6 +struct nvt_spi_trim_id_table_t { + uint8_t id[NVT_SPI_ID_BYTE_MAX]; + uint8_t mask[NVT_SPI_ID_BYTE_MAX]; + const struct nvt_spi_mem_map *mmap; + const struct nvt_spi_hw_info *hwinfo; +}; + +static const struct nvt_spi_trim_id_table_t nvt_spi_trim_id_table[] = { + {.id = {0x0D, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x20, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info} +}; + +#endif diff --git a/nt36xxx/nt36xxx_mp_ctrlram.c b/nt36xxx/nt36xxx_mp_ctrlram.c new file mode 100644 index 0000000000..5c07488521 --- /dev/null +++ b/nt36xxx/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1480 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include + +#include "nt36xxx.h" +#include "nt36xxx_mp_ctrlram.h" + +#if NVT_TOUCH_MP + +#define NORMAL_MODE 0x00 +#define TEST_MODE_1 0x21 +#define TEST_MODE_2 0x22 +#define MP_MODE_CC 0x41 +#define FREQ_HOP_DISABLE 0x66 +#define FREQ_HOP_ENABLE 0x65 + +#define SHORT_TEST_CSV_FILE "/data/local/tmp/ShortTest.csv" +#define OPEN_TEST_CSV_FILE "/data/local/tmp/OpenTest.csv" +#define FW_RAWDATA_CSV_FILE "/data/local/tmp/FWMutualTest.csv" +#define FW_CC_CSV_FILE "/data/local/tmp/FWCCTest.csv" +#define NOISE_TEST_CSV_FILE "/data/local/tmp/NoiseTest.csv" + +#define nvt_mp_seq_printf(m, fmt, args...) do { \ + seq_printf(m, fmt, ##args); \ + if (!nvt_mp_test_result_printed) \ + printk(fmt, ##args); \ +} while (0) + +static uint8_t *RecordResult_Short = NULL; +static uint8_t *RecordResult_Open = NULL; +static uint8_t *RecordResult_FWMutual = NULL; +static uint8_t *RecordResult_FW_CC = NULL; +static uint8_t *RecordResult_FW_DiffMax = NULL; +static uint8_t *RecordResult_FW_DiffMin = NULL; + +static int32_t TestResult_Short = 0; +static int32_t TestResult_Open = 0; +static int32_t TestResult_FW_Rawdata = 0; +static int32_t TestResult_FWMutual = 0; +static int32_t TestResult_FW_CC = 0; +static int32_t TestResult_Noise = 0; +static int32_t TestResult_FW_DiffMax = 0; +static int32_t TestResult_FW_DiffMin = 0; + +static int32_t *RawData_Short = NULL; +static int32_t *RawData_Open = NULL; +static int32_t *RawData_Diff = NULL; +static int32_t *RawData_Diff_Min = NULL; +static int32_t *RawData_Diff_Max = NULL; +static int32_t *RawData_FWMutual = NULL; +static int32_t *RawData_FW_CC = NULL; + +static struct proc_dir_entry *NVT_proc_selftest_entry = NULL; +static int8_t nvt_mp_test_result_printed = 0; +static uint8_t fw_ver = 0; + +extern void nvt_change_mode(uint8_t mode); +extern uint8_t nvt_get_fw_pipe(void); +extern void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr); +extern void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num); +int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible); + +/******************************************************* +Description: + Novatek touchscreen allocate buffer for mp selftest. + +return: + Executive outcomes. 0---succeed. -12---Out of memory +*******************************************************/ +static int nvt_mp_buffer_init(void) +{ + size_t RecordResult_BufSize = IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE; + size_t RawData_BufSize = (IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * sizeof(int32_t); + + RecordResult_Short = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short) { + NVT_ERR("kzalloc for RecordResult_Short failed!\n"); + return -ENOMEM; + } + + RecordResult_Open = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Open) { + NVT_ERR("kzalloc for RecordResult_Open failed!\n"); + return -ENOMEM; + } + + RecordResult_FWMutual = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FWMutual) { + NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC) { + NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMax = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMax) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMin = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMin) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n"); + return -ENOMEM; + } + + RawData_Short = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short) { + NVT_ERR("kzalloc for RawData_Short failed!\n"); + return -ENOMEM; + } + + RawData_Open = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Open) { + NVT_ERR("kzalloc for RawData_Open failed!\n"); + return -ENOMEM; + } + + RawData_Diff = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff) { + NVT_ERR("kzalloc for RawData_Diff failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Min = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Min) { + NVT_ERR("kzalloc for RawData_Diff_Min failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Max = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Max) { + NVT_ERR("kzalloc for RawData_Diff_Max failed!\n"); + return -ENOMEM; + } + + RawData_FWMutual = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FWMutual) { + NVT_ERR("kzalloc for RawData_FWMutual failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FW_CC) { + NVT_ERR("kzalloc for RawData_FW_CC failed!\n"); + return -ENOMEM; + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen free buffer for mp selftest. + +return: + n.a. +*******************************************************/ +static void nvt_mp_buffer_deinit(void) +{ + if (RecordResult_Short) { + kfree(RecordResult_Short); + RecordResult_Short = NULL; + } + + if (RecordResult_Open) { + kfree(RecordResult_Open); + RecordResult_Open = NULL; + } + + if (RecordResult_FWMutual) { + kfree(RecordResult_FWMutual); + RecordResult_FWMutual = NULL; + } + + if (RecordResult_FW_CC) { + kfree(RecordResult_FW_CC); + RecordResult_FW_CC = NULL; + } + + if (RecordResult_FW_DiffMax) { + kfree(RecordResult_FW_DiffMax); + RecordResult_FW_DiffMax = NULL; + } + + if (RecordResult_FW_DiffMin) { + kfree(RecordResult_FW_DiffMin); + RecordResult_FW_DiffMin = NULL; + } + + if (RawData_Short) { + kfree(RawData_Short); + RawData_Short = NULL; + } + + if (RawData_Open) { + kfree(RawData_Open); + RawData_Open = NULL; + } + + if (RawData_Diff) { + kfree(RawData_Diff); + RawData_Diff = NULL; + } + + if (RawData_Diff_Min) { + kfree(RawData_Diff_Min); + RawData_Diff_Min = NULL; + } + + if (RawData_Diff_Max) { + kfree(RawData_Diff_Max); + RawData_Diff_Max = NULL; + } + + if (RawData_FWMutual) { + kfree(RawData_FWMutual); + RawData_FWMutual = NULL; + } + + if (RawData_FW_CC) { + kfree(RawData_FW_CC); + RawData_FW_CC = NULL; + } +} + +static void nvt_print_data_log_in_one_line(int32_t *data, int32_t data_num) +{ + char *tmp_log = NULL; + int32_t i = 0; + int32_t count = data_num * 7 + 1; + + tmp_log = kzalloc(count, GFP_KERNEL); + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < data_num; i++) { + snprintf(tmp_log + i * 7, count - i * 7, "%5d, ", data[i]); + } + tmp_log[data_num * 7] = '\0'; + printk("%s", tmp_log); + if (tmp_log) { + kfree(tmp_log); + tmp_log = NULL; + } + + return; +} + +static void nvt_print_result_log_in_one_line(uint8_t *result, int32_t result_num) +{ + char *tmp_log = NULL; + int32_t i = 0; + int32_t count = 0; + + count = result_num * 6 + 1; + tmp_log = kzalloc(count, GFP_KERNEL); + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < result_num; i++) { + snprintf(tmp_log + i * 6, count - i * 6, "0x%02X, ", result[i]); + } + tmp_log[result_num * 6] = '\0'; + printk("%s", tmp_log); + if (tmp_log) { + kfree(tmp_log); + tmp_log = NULL; + } + + return; +} + +/******************************************************* +Description: + Novatek touchscreen self-test criteria print function. + +return: + n.a. +*******************************************************/ +static void nvt_print_lmt_array(int32_t *array, int32_t x_ch, int32_t y_ch) +{ + int32_t j = 0; + + for (j = 0; j < y_ch; j++) { + nvt_print_data_log_in_one_line(array + j * x_ch, x_ch); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(array + y_ch * x_ch, Key_Channel); + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ +} + +static void nvt_print_criteria(void) +{ + NVT_LOG("++\n"); + + //---PS_Config_Lmt_Short_Rawdata--- + printk("PS_Config_Lmt_Short_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_Open_Rawdata--- + printk("PS_Config_Lmt_Open_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Open_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_Rawdata--- + printk("PS_Config_Lmt_FW_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_CC--- + printk("PS_Config_Lmt_FW_CC_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_Diff--- + printk("PS_Config_Lmt_FW_Diff_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_Diff_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_N, X_Channel, Y_Channel); + + NVT_LOG("--\n"); +} + +static int32_t nvt_polling_hand_shake_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 70; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if ((buf[1] == 0xA0) || (buf[1] == 0xA1)) + break; + + usleep_range(10000, 10000); + } + + if (i >= retry) { + NVT_ERR("polling hand shake status failed, buf[1]=0x%02X\n", buf[1]); + + // Read back 5 bytes from offset EVENT_MAP_HOST_CMD for debug check + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6); + NVT_ERR("Read back 5 bytes from offset EVENT_MAP_HOST_CMD: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", buf[1], buf[2], buf[3], buf[4], buf[5]); + + return -1; + } else { + return 0; + } +} + +static int8_t nvt_switch_FreqHopEnDis(uint8_t FreqHopEnDis) +{ + uint8_t buf[8] = {0}; + uint8_t retry = 0; + int8_t ret = 0; + + NVT_LOG("++\n"); + + for (retry = 0; retry < 20; retry++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---switch FreqHopEnDis--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = FreqHopEnDis; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + msleep(35); + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if (buf[1] == 0x00) + break; + } + + if (unlikely(retry == 20)) { + NVT_ERR("switch FreqHopEnDis 0x%02X failed, buf[1]=0x%02X\n", FreqHopEnDis, buf[1]); + ret = -1; + } + + NVT_LOG("--\n"); + + return ret; +} + +static int32_t nvt_read_baseline(int32_t *xdata) +{ + uint8_t x_num = 0; + uint8_t y_num = 0; + uint32_t x = 0; + uint32_t y = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR); + + nvt_get_mdata(xdata, &x_num, &y_num); + + for (y = 0; y < y_num; y++) { + for (x = 0; x < x_num; x++) { + iArrayIndex = y * x_num + x; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("--\n"); + + return 0; +} + +static int32_t nvt_read_CC(int32_t *xdata) +{ + uint8_t x_num = 0; + uint8_t y_num = 0; + uint32_t x = 0; + uint32_t y = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + else + nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + + nvt_get_mdata(xdata, &x_num, &y_num); + + for (y = 0; y < y_num; y++) { + for (x = 0; x < x_num; x++) { + iArrayIndex = y * x_num + x; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("--\n"); + + return 0; +} + +static void nvt_enable_noise_collect(int32_t frame_num) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---enable noise collect--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x47; + buf[2] = 0xAA; + buf[3] = frame_num; + buf[4] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5); +} + +static int32_t nvt_read_fw_noise(int32_t *xdata) +{ + uint8_t x_num = 0; + uint8_t y_num = 0; + uint32_t x = 0; + uint32_t y = 0; + int32_t iArrayIndex = 0; + int32_t frame_num = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + return -EAGAIN; + } + + frame_num = PS_Config_Diff_Test_Frame / 10; + if (frame_num <= 0) + frame_num = 1; + printk("%s: frame_num=%d\n", __func__, frame_num); + nvt_enable_noise_collect(frame_num); + // need wait PS_Config_Diff_Test_Frame * 8.3ms + msleep(frame_num * 83); + + if (nvt_polling_hand_shake_status()) { + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + return -EAGAIN; + } + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + + nvt_get_mdata(xdata, &x_num, &y_num); + + for (y = 0; y < y_num; y++) { + for (x = 0; x < x_num; x++) { + iArrayIndex = y * x_num + x; + RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF); + RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF); + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF); + RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF); + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + NVT_LOG("--\n"); + + return 0; +} + +static void nvt_enable_open_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---enable open test--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x45; + buf[2] = 0xAA; + buf[3] = 0x02; + buf[4] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5); +} + +static void nvt_enable_short_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---enable short test--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x43; + buf[2] = 0xAA; + buf[3] = 0x02; + buf[4] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5); +} + +static int32_t nvt_read_fw_open(int32_t *xdata) +{ + uint32_t raw_pipe_addr = 0; + uint8_t *rawdata_buf = NULL; + uint32_t x = 0; + uint32_t y = 0; + uint8_t buf[128] = {0}; +#if TOUCH_KEY_NUM > 0 + uint32_t raw_btn_pipe_addr = 0; + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + return -EAGAIN; + } + + nvt_enable_open_test(); + + if (nvt_polling_hand_shake_status()) { + return -EAGAIN; + } + +#if TOUCH_KEY_NUM > 0 + rawdata_buf = kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL); +#else + rawdata_buf = kzalloc(IC_X_CFG_SIZE * IC_Y_CFG_SIZE * 2, GFP_KERNEL); +#endif /* #if TOUCH_KEY_NUM > 0 */ + if (!rawdata_buf) { + NVT_ERR("kzalloc for rawdata_buf failed!\n"); + return -ENOMEM; + } + + if (nvt_get_fw_pipe() == 0) + raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR; + else + raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR; + + for (y = 0; y < IC_Y_CFG_SIZE; y++) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * IC_X_CFG_SIZE * 2); + buf[0] = (uint8_t)((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_X_CFG_SIZE * 2 + 1); + memcpy(rawdata_buf + y * IC_X_CFG_SIZE * 2, buf + 1, IC_X_CFG_SIZE * 2); + } +#if TOUCH_KEY_NUM > 0 + if (nvt_get_fw_pipe() == 0) + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR; + else + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR; + + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr); + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_KEY_CFG_SIZE * 2 + 1); + memcpy(rawdata_buf + IC_Y_CFG_SIZE * IC_X_CFG_SIZE * 2, buf + 1, IC_KEY_CFG_SIZE * 2); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + for (y = 0; y < IC_Y_CFG_SIZE; y++) { + for (x = 0; x < IC_X_CFG_SIZE; x++) { + if ((AIN_Y[y] != 0xFF) && (AIN_X[x] != 0xFF)) { + xdata[AIN_Y[y] * X_Channel + AIN_X[x]] = (int16_t)((rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2] + 256 * rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2 + 1])); + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < IC_KEY_CFG_SIZE; k++) { + if (AIN_KEY[k] != 0xFF) + xdata[Y_Channel * X_Channel + AIN_KEY[k]] = (int16_t)(rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2] + 256 * rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2 + 1]); + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + if (rawdata_buf) { + kfree(rawdata_buf); + rawdata_buf = NULL; + } + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + NVT_LOG("--\n"); + + return 0; +} + +static int32_t nvt_read_fw_short(int32_t *xdata) +{ + uint32_t raw_pipe_addr = 0; + uint8_t *rawdata_buf = NULL; + uint32_t x = 0; + uint32_t y = 0; + uint8_t buf[128] = {0}; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + uint32_t raw_btn_pipe_addr = 0; + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + return -EAGAIN; + } + + nvt_enable_short_test(); + + if (nvt_polling_hand_shake_status()) { + return -EAGAIN; + } + +#if TOUCH_KEY_NUM > 0 + rawdata_buf = kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL); +#else + rawdata_buf = kzalloc(X_Channel * Y_Channel * 2, GFP_KERNEL); +#endif /* #if TOUCH_KEY_NUM > 0 */ + if (!rawdata_buf) { + NVT_ERR("kzalloc for rawdata_buf failed!\n"); + return -ENOMEM; + } + + if (nvt_get_fw_pipe() == 0) + raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR; + else + raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR; + + for (y = 0; y < Y_Channel; y++) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * X_Channel * 2); + buf[0] = (uint8_t)((raw_pipe_addr + y * X_Channel * 2) & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, X_Channel * 2 + 1); + memcpy(rawdata_buf + y * X_Channel * 2, buf + 1, X_Channel * 2); + } +#if TOUCH_KEY_NUM > 0 + if (nvt_get_fw_pipe() == 0) + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR; + else + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR; + + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr); + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, Key_Channel * 2 + 1); + memcpy(rawdata_buf + Y_Channel * X_Channel * 2, buf + 1, Key_Channel * 2); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + for (y = 0; y < Y_Channel; y++) { + for (x = 0; x < X_Channel; x++) { + iArrayIndex = y * X_Channel + x; + xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]); + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]); + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + if (rawdata_buf) { + kfree(rawdata_buf); + rawdata_buf = NULL; + } + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + NVT_LOG("--\n"); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen raw data test for each single point function. + +return: + Executive outcomes. 0---passed. negative---failed. +*******************************************************/ +static int32_t RawDataTest_SinglePoint_Sub(int32_t rawdata[], uint8_t RecordResult[], uint8_t x_ch, uint8_t y_ch, int32_t Rawdata_Limit_Postive[], int32_t Rawdata_Limit_Negative[]) +{ + int32_t i = 0; + int32_t j = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + int32_t iArrayIndex = 0; + bool isPass = true; + + for (j = 0; j < y_ch; j++) { + for (i = 0; i < x_ch; i++) { + iArrayIndex = j * x_ch + i; + + RecordResult[iArrayIndex] = 0x00; // default value for PASS + + if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x01; + + if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x02; + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + + RecordResult[iArrayIndex] = 0x00; // default value for PASS + + if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x01; + + if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x02; + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + //---Check RecordResult--- + for (j = 0; j < y_ch; j++) { + for (i = 0; i < x_ch; i++) { + if (RecordResult[j * x_ch + i] != 0) { + isPass = false; + break; + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + if (RecordResult[iArrayIndex] != 0) { + isPass = false; + break; + } + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + if (!isPass) { + return -1; // FAIL + } else { + return 0; // PASS + } +} + +/******************************************************* +Description: + Novatek touchscreen print self-test result function. + +return: + n.a. +*******************************************************/ +void print_selftest_result(struct seq_file *m, int32_t TestResult, uint8_t RecordResult[], int32_t rawdata[], uint8_t x_len, uint8_t y_len) +{ + int32_t i = 0; + int32_t j = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + switch (TestResult) { + case 0: + nvt_mp_seq_printf(m, " PASS!\n"); + break; + + case 1: + nvt_mp_seq_printf(m, " ERROR! Read Data FAIL!\n"); + break; + + case -1: + nvt_mp_seq_printf(m, " FAIL!\n"); + nvt_mp_seq_printf(m, "RecordResult:\n"); + for (i = 0; i < y_len; i++) { + for (j = 0; j < x_len; j++) { + iArrayIndex = i * x_len + j; + seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_result_log_in_one_line(RecordResult + i * x_len, x_len); + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_result_log_in_one_line(RecordResult + y_len * x_len, Key_Channel); + nvt_mp_seq_printf(m, "\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + nvt_mp_seq_printf(m, "ReadData:\n"); + for (i = 0; i < y_len; i++) { + for (j = 0; j < x_len; j++) { + iArrayIndex = i * x_len + j; + seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_data_log_in_one_line(rawdata + i * x_len, x_len); + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_data_log_in_one_line(rawdata + y_len * x_len, Key_Channel); + nvt_mp_seq_printf(m, "\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + break; + } + nvt_mp_seq_printf(m, "\n"); +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print show + function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_show_selftest(struct seq_file *m, void *v) +{ + NVT_LOG("++\n"); + + nvt_mp_seq_printf(m, "FW Version: %d\n\n", fw_ver); + + nvt_mp_seq_printf(m, "Short Test"); + + print_selftest_result(m, TestResult_Short, RecordResult_Short, RawData_Short, X_Channel, Y_Channel); + + nvt_mp_seq_printf(m, "Open Test"); + print_selftest_result(m, TestResult_Open, RecordResult_Open, RawData_Open, X_Channel, Y_Channel); + + nvt_mp_seq_printf(m, "FW Rawdata Test"); + if ((TestResult_FW_Rawdata == 0) || (TestResult_FW_Rawdata == 1)) { + print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel); + } else { // TestResult_FW_Rawdata is -1 + nvt_mp_seq_printf(m, " FAIL!\n"); + if (TestResult_FWMutual == -1) { + nvt_mp_seq_printf(m, "FW Mutual"); + print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel); + } + if (TestResult_FW_CC == -1) { + nvt_mp_seq_printf(m, "FW CC"); + print_selftest_result(m, TestResult_FW_CC, RecordResult_FW_CC, RawData_FW_CC, X_Channel, Y_Channel); + } + } + + nvt_mp_seq_printf(m, "Noise Test"); + if ((TestResult_Noise == 0) || (TestResult_Noise == 1)) { + print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel); + } else { // TestResult_Noise is -1 + nvt_mp_seq_printf(m, " FAIL!\n"); + + if (TestResult_FW_DiffMax == -1) { + nvt_mp_seq_printf(m, "FW Diff Max"); + print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel); + } + if (TestResult_FW_DiffMin == -1) { + nvt_mp_seq_printf(m, "FW Diff Min"); + print_selftest_result(m, TestResult_FW_DiffMin, RecordResult_FW_DiffMin, RawData_Diff_Min, X_Channel, Y_Channel); + } + } + + nvt_mp_test_result_printed = 1; + + NVT_LOG("--\n"); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print start + function. + +return: + Executive outcomes. 1---call next function. + NULL---not call next function and sequence loop + stop. +*******************************************************/ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print next + function. + +return: + Executive outcomes. NULL---no next and call sequence + stop function. +*******************************************************/ +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print stop + function. + +return: + n.a. +*******************************************************/ +static void c_stop(struct seq_file *m, void *v) +{ + return; +} + +const struct seq_operations nvt_selftest_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show_selftest +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_selftest open function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +static int32_t nvt_selftest_open(struct inode *inode, struct file *file) +{ + struct device_node *np = ts->client->dev.of_node; + unsigned char mpcriteria[PAGE_SIZE] = {0}; //novatek-mp-criteria-default + + TestResult_Short = 0; + TestResult_Open = 0; + TestResult_FW_Rawdata = 0; + TestResult_FWMutual = 0; + TestResult_FW_CC = 0; + TestResult_Noise = 0; + TestResult_FW_DiffMax = 0; + TestResult_FW_DiffMin = 0; + + NVT_LOG("++\n"); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + NVT_ERR("get fw info failed!\n"); + return -EAGAIN; + } + + fw_ver = ts->fw_ver; + + /* Parsing criteria from dts */ + if(of_property_read_bool(np, "novatek,mp-support-dt")) { + /* + * Parsing Criteria by Novatek PID + * The string rule is "novatek-mp-criteria-" + * nvt_pid is 2 bytes (show hex). + * + * Ex. nvt_pid = 500A + * mpcriteria = "novatek-mp-criteria-500A" + */ + snprintf(mpcriteria, PAGE_SIZE, "novatek-mp-criteria-%04X", ts->nvt_pid); + + if (nvt_mp_parse_dt(np, mpcriteria)) { + mutex_unlock(&ts->lock); + NVT_ERR("mp parse device tree failed!\n"); + return -EINVAL; + } + } else { + NVT_LOG("Not found novatek,mp-support-dt, use default setting\n"); + //---Print Test Criteria--- + nvt_print_criteria(); + } + + if (nvt_switch_FreqHopEnDis(FREQ_HOP_DISABLE)) { + mutex_unlock(&ts->lock); + NVT_ERR("switch frequency hopping disable failed!\n"); + return -EAGAIN; + } + + if (nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN)) { + mutex_unlock(&ts->lock); + NVT_ERR("check fw reset state failed!\n"); + return -EAGAIN; + } + + msleep(100); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + NVT_ERR("clear fw status failed!\n"); + return -EAGAIN; + } + + nvt_change_mode(MP_MODE_CC); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + NVT_ERR("check fw status failed!\n"); + return -EAGAIN; + } + + //---FW Rawdata Test--- + if (nvt_read_baseline(RawData_FWMutual) != 0) { + TestResult_FWMutual = 1; + } else { + TestResult_FWMutual = RawDataTest_SinglePoint_Sub(RawData_FWMutual, RecordResult_FWMutual, X_Channel, Y_Channel, + PS_Config_Lmt_FW_Rawdata_P, PS_Config_Lmt_FW_Rawdata_N); + } + if (nvt_read_CC(RawData_FW_CC) != 0) { + TestResult_FW_CC = 1; + } else { + TestResult_FW_CC = RawDataTest_SinglePoint_Sub(RawData_FW_CC, RecordResult_FW_CC, X_Channel, Y_Channel, + PS_Config_Lmt_FW_CC_P, PS_Config_Lmt_FW_CC_N); + } + + if ((TestResult_FWMutual == 1) || (TestResult_FW_CC == 1)) { + TestResult_FW_Rawdata = 1; + } else { + if ((TestResult_FWMutual == -1) || (TestResult_FW_CC == -1)) + TestResult_FW_Rawdata = -1; + else + TestResult_FW_Rawdata = 0; + } + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + //---Noise Test--- + if (nvt_read_fw_noise(RawData_Diff) != 0) { + TestResult_Noise = 1; // 1: ERROR + TestResult_FW_DiffMax = 1; + TestResult_FW_DiffMin = 1; + } else { + TestResult_FW_DiffMax = RawDataTest_SinglePoint_Sub(RawData_Diff_Max, RecordResult_FW_DiffMax, X_Channel, Y_Channel, + PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N); + + TestResult_FW_DiffMin = RawDataTest_SinglePoint_Sub(RawData_Diff_Min, RecordResult_FW_DiffMin, X_Channel, Y_Channel, + PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N); + + if ((TestResult_FW_DiffMax == -1) || (TestResult_FW_DiffMin == -1)) + TestResult_Noise = -1; + else + TestResult_Noise = 0; + } + + //--Short Test--- + if (nvt_read_fw_short(RawData_Short) != 0) { + TestResult_Short = 1; // 1:ERROR + } else { + //---Self Test Check --- // 0:PASS, -1:FAIL + TestResult_Short = RawDataTest_SinglePoint_Sub(RawData_Short, RecordResult_Short, X_Channel, Y_Channel, + PS_Config_Lmt_Short_Rawdata_P, PS_Config_Lmt_Short_Rawdata_N); + } + + //---Open Test--- + if (nvt_read_fw_open(RawData_Open) != 0) { + TestResult_Open = 1; // 1:ERROR + } else { + //---Self Test Check --- // 0:PASS, -1:FAIL + TestResult_Open = RawDataTest_SinglePoint_Sub(RawData_Open, RecordResult_Open, X_Channel, Y_Channel, + PS_Config_Lmt_Open_Rawdata_P, PS_Config_Lmt_Open_Rawdata_N); + } + + //---Reset IC--- + nvt_bootloader_reset(); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + nvt_mp_test_result_printed = 0; + + return seq_open(file, &nvt_selftest_seq_ops); +} + +static const struct proc_ops nvt_selftest_fops = { + .proc_open = nvt_selftest_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +#ifdef CONFIG_OF +/******************************************************* +Description: + Novatek touchscreen parse AIN setting for array type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_ain(struct device_node *np, const char *name, uint8_t *array, int32_t size) +{ + struct property *data; + int32_t len, ret; + int32_t tmp[40]; + int32_t i; + + data = of_find_property(np, name, &len); + len /= sizeof(u32); + if ((!data) || (!len) || (len != size)) { + NVT_ERR("error find %s. len=%d\n", name, len); + return -1; + } else { + NVT_LOG("%s. len=%d\n", name, len); + ret = of_property_read_u32_array(np, name, tmp, len); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } + + for (i = 0; i < len; i++) + array[i] = tmp[i]; + +#if NVT_DEBUG + printk("[NVT-ts] %s = ", name); + nvt_print_result_log_in_one_line(array, len); + printk("\n"); +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for u32 type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_u32(struct device_node *np, const char *name, int32_t *para) +{ + int32_t ret; + + ret = of_property_read_u32(np, name, para); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } else { +#if NVT_DEBUG + NVT_LOG("%s=%d\n", name, *para); +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for array type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_array(struct device_node *np, const char *name, int32_t *array, + int32_t size) +{ + struct property *data; + int32_t len, ret; +#if NVT_DEBUG + int32_t j = 0; +#endif + + data = of_find_property(np, name, &len); + len /= sizeof(u32); + if ((!data) || (!len) || (len < size)) { + NVT_ERR("error find %s. len=%d\n", name, len); + return -1; + } else { + NVT_LOG("%s. len=%d\n", name, len); + ret = of_property_read_u32_array(np, name, array, len); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } + +#if NVT_DEBUG + NVT_LOG("%s =\n", name); + for (j = 0; j < Y_Channel; j++) { + nvt_print_data_log_in_one_line(array + j * X_Channel, X_Channel); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(array + Y_Channel * X_Channel, Key_Channel); + printk("\n"); +#endif +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse device tree mp function. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible) +{ + struct device_node *np = root; + struct device_node *child = NULL; + + NVT_LOG("Parse mp criteria for node %s\n", node_compatible); + + /* find each MP sub-nodes */ + for_each_child_of_node(root, child) { + /* find the specified node */ + if (of_device_is_compatible(child, node_compatible)) { + NVT_LOG("found child node %s\n", node_compatible); + np = child; + break; + } + } + if (child == NULL) { + NVT_ERR("Not found compatible node %s!\n", node_compatible); + return -1; + } + + /* MP Config*/ + if (nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE)) + return -1; + + if (nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE)) + return -1; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE)) + return -1; +#endif + + if (nvt_mp_parse_u32(np, "X_Channel", &X_Channel)) + return -1; + + if (nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel)) + return -1; + + if (nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE)) + return -1; + + if (nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE)) + return -1; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE)) + return -1; +#endif + + /* MP Criteria */ + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_P", PS_Config_Lmt_Short_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_N", PS_Config_Lmt_Short_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_P", PS_Config_Lmt_Open_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_N", PS_Config_Lmt_Open_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_P", PS_Config_Lmt_FW_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_N", PS_Config_Lmt_FW_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_P", PS_Config_Lmt_FW_CC_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_N", PS_Config_Lmt_FW_CC_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_P", PS_Config_Lmt_FW_Diff_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_N", PS_Config_Lmt_FW_Diff_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame)) + return -1; + + NVT_LOG("Parse mp criteria done!\n"); + + return 0; +} +#endif /* #ifdef CONFIG_OF */ + +/******************************************************* +Description: + Novatek touchscreen MP function proc. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_mp_proc_init(void) +{ + NVT_proc_selftest_entry = proc_create("nvt_selftest", 0444, NULL, &nvt_selftest_fops); + if (NVT_proc_selftest_entry == NULL) { + NVT_ERR("create /proc/nvt_selftest Failed!\n"); + return -1; + } else { + if(nvt_mp_buffer_init()) { + NVT_ERR("Allocate mp memory failed\n"); + return -1; + } + else { + NVT_LOG("create /proc/nvt_selftest Succeeded!\n"); + } + return 0; + } +} + +/******************************************************* +Description: + Novatek touchscreen MP function proc. file node + deinitial function. + +return: + n.a. +*******************************************************/ +void nvt_mp_proc_deinit(void) +{ + nvt_mp_buffer_deinit(); + + if (NVT_proc_selftest_entry != NULL) { + remove_proc_entry("nvt_selftest", NULL); + NVT_proc_selftest_entry = NULL; + NVT_LOG("Removed /proc/%s\n", "nvt_selftest"); + } +} +#endif /* #if NVT_TOUCH_MP */ diff --git a/nt36xxx/nt36xxx_mp_ctrlram.h b/nt36xxx/nt36xxx_mp_ctrlram.h new file mode 100644 index 0000000000..69c27cabcc --- /dev/null +++ b/nt36xxx/nt36xxx_mp_ctrlram.h @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 46179 $ + * $Date: 2019-06-14 13:47:17 +0800 (Fri, 14 Jun 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#if NVT_TOUCH_MP + +static uint32_t IC_X_CFG_SIZE = 18; +static uint32_t IC_Y_CFG_SIZE = 36; +static uint32_t IC_KEY_CFG_SIZE = 4; +static uint32_t X_Channel = 18; +static uint32_t Y_Channel = 36; +static uint32_t Key_Channel = TOUCH_KEY_NUM; +static uint8_t AIN_X[40] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; +static uint8_t AIN_Y[40] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + +#if TOUCH_KEY_NUM > 0 +static uint8_t AIN_KEY[8] = {0, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +#endif /* #if TOUCH_KEY_NUM > 0 */ + +static int32_t PS_Config_Lmt_Short_Rawdata_P[40 * 40] = { + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, +#if TOUCH_KEY_NUM > 0 + 14008,14008,14008, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = { + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, +#if TOUCH_KEY_NUM > 0 + 10000,10000,10000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = { + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, +#if TOUCH_KEY_NUM > 0 + 5120,5120,5120, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = { + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, +#if TOUCH_KEY_NUM > 0 + 50,50,50, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = { + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, +#if TOUCH_KEY_NUM > 0 + 2560,2560,2560, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = { + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +#if TOUCH_KEY_NUM > 0 + 240,240,240, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = { + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, +#if TOUCH_KEY_NUM > 0 + 314,314,314, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +#if TOUCH_KEY_NUM > 0 + 0,0,0, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_P[40 * 40] = { + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, +#if TOUCH_KEY_NUM > 0 + 75,75,75, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_N[40 *40] = { + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, +#if TOUCH_KEY_NUM > 0 + -75,-75,-75, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Diff_Test_Frame = 50; + +#endif /* #if NVT_TOUCH_MP */ diff --git a/nt36xxx/nt36xxx_spi.c b/nt36xxx/nt36xxx_spi.c new file mode 100644 index 0000000000..c26dbd70b2 --- /dev/null +++ b/nt36xxx/nt36xxx_spi.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define NVT_NT36XXX_SPI + +#include "nt36xxx.c" diff --git a/nt36xxx/nt36xxx_spi_ext_proc.c b/nt36xxx/nt36xxx_spi_ext_proc.c new file mode 100644 index 0000000000..a6ad2f6231 --- /dev/null +++ b/nt36xxx/nt36xxx_spi_ext_proc.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define NVT_NT36XXX_SPI + +#include "nt36xxx_ext_proc.c" diff --git a/nt36xxx/nt36xxx_spi_fw_update.c b/nt36xxx/nt36xxx_spi_fw_update.c new file mode 100644 index 0000000000..2a6c20e2fb --- /dev/null +++ b/nt36xxx/nt36xxx_spi_fw_update.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define NVT_NT36XXX_SPI + +#include "nt36xxx_fw_update.c" diff --git a/st/fts.c b/st/fts.c new file mode 100644 index 0000000000..f5dc14c681 --- /dev/null +++ b/st/fts.c @@ -0,0 +1,6066 @@ +// 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 +#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" + +#if defined(CONFIG_ST_TRUSTED_TOUCH) + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "linux/gunyah/gh_irq_lend.h" +#include "linux/gunyah/gh_msgq.h" +#include "linux/gunyah/gh_mem_notifier.h" +#include "linux/gunyah/gh_rm_drv.h" +#include +#endif + +#define LINK_KOBJ_NAME "tp" + +#define FTS_DVDD_VOL_MIN 1800000 +#define FTS_DVDD_VOL_MAX 1800000 +#define FTS_DVDD_LOAD 20000 +#define FTS_AVDD_VOL_MIN 3000000 +#define FTS_AVDD_VOL_MAX 3300000 +#define FTS_AVDD_LOAD 20000 + +/* + * 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 + +static void fts_interrupt_disable(struct fts_ts_info *info); +static void fts_interrupt_enable(struct fts_ts_info *info); +static irqreturn_t fts_interrupt_handler(int irq, void *handle); +static int fts_probe_delayed(struct fts_ts_info *info); + +#ifdef CONFIG_ST_TRUSTED_TOUCH + +static struct gh_acl_desc *fts_vm_get_acl(enum gh_vm_names vm_name) +{ + struct gh_acl_desc *acl_desc; + gh_vmid_t vmid; + + gh_rm_get_vmid(vm_name, &vmid); + + acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]), + GFP_KERNEL); + if (!acl_desc) + return ERR_PTR(ENOMEM); + + acl_desc->n_acl_entries = 1; + acl_desc->acl_entries[0].vmid = vmid; + acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W; + + return acl_desc; +} + +static struct gh_sgl_desc *fts_vm_get_sgl(struct trusted_touch_vm_info *vm_info) +{ + struct gh_sgl_desc *sgl_desc; + int i; + + sgl_desc = kzalloc(offsetof(struct gh_sgl_desc, + sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL); + if (!sgl_desc) + return ERR_PTR(ENOMEM); + + sgl_desc->n_sgl_entries = vm_info->iomem_list_size; + + for (i = 0; i < vm_info->iomem_list_size; i++) { + sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i]; + sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i]; + } + + return sgl_desc; +} + +static int fts_populate_vm_info(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + struct device_node *np = info->client->dev.of_node; + int num_regs, num_sizes = 0; + + vm_info = kzalloc(sizeof(struct trusted_touch_vm_info), GFP_KERNEL); + if (!vm_info) { + rc = -ENOMEM; + goto error; + } + + info->vm_info = vm_info; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH; + vm_info->vm_name = GH_TRUSTED_VM; + rc = of_property_read_u32(np, "st,trusted-touch-spi-irq", + &vm_info->hw_irq); + if (rc) { + pr_err("Failed to read trusted touch SPI irq:%d\n", rc); + goto vm_error; + } + num_regs = of_property_count_u32_elems(np, + "st,trusted-touch-io-bases"); + if (num_regs < 0) { + pr_err("Invalid number of IO regions specified\n"); + rc = -EINVAL; + goto vm_error; + } + + num_sizes = of_property_count_u32_elems(np, + "st,trusted-touch-io-sizes"); + if (num_sizes < 0) { + pr_err("Invalid number of IO regions specified\n"); + rc = -EINVAL; + goto vm_error; + } + + if (num_regs != num_sizes) { + pr_err("IO bases and sizes doe not match\n"); + rc = -EINVAL; + goto vm_error; + } + + vm_info->iomem_list_size = num_regs; + + vm_info->iomem_bases = kcalloc(num_regs, sizeof(*vm_info->iomem_bases), + GFP_KERNEL); + if (!vm_info->iomem_bases) { + rc = -ENOMEM; + goto vm_error; + } + + rc = of_property_read_u32_array(np, "st,trusted-touch-io-bases", + vm_info->iomem_bases, vm_info->iomem_list_size); + if (rc) { + pr_err("Failed to read trusted touch io bases:%d\n", rc); + goto io_bases_error; + } + + vm_info->iomem_sizes = kzalloc( + sizeof(*vm_info->iomem_sizes) * num_sizes, GFP_KERNEL); + if (!vm_info->iomem_sizes) { + rc = -ENOMEM; + goto io_bases_error; + } + + rc = of_property_read_u32_array(np, "st,trusted-touch-io-sizes", + vm_info->iomem_sizes, vm_info->iomem_list_size); + if (rc) { + pr_err("Failed to read trusted touch io sizes:%d\n", rc); + goto io_sizes_error; + } + return rc; + +io_sizes_error: + kfree(vm_info->iomem_sizes); +io_bases_error: + kfree(vm_info->iomem_bases); +vm_error: + kfree(vm_info); +error: + return rc; +} + +static void fts_destroy_vm_info(struct fts_ts_info *info) +{ + kfree(info->vm_info->iomem_sizes); + kfree(info->vm_info->iomem_bases); + kfree(info->vm_info); +} + +static void fts_vm_deinit(struct fts_ts_info *info) +{ + if (info->vm_info->mem_cookie) + gh_mem_notifier_unregister(info->vm_info->mem_cookie); + fts_destroy_vm_info(info); +} + +#ifdef CONFIG_ARCH_QTI_VM +static int fts_vm_mem_release(struct fts_ts_info *info); +static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info); + +static int fts_sgl_cmp(const void *a, const void *b) +{ + struct gh_sgl_entry *left = (struct gh_sgl_entry *)a; + struct gh_sgl_entry *right = (struct gh_sgl_entry *)b; + + return (left->ipa_base - right->ipa_base); +} + +static int fts_vm_compare_sgl_desc(struct gh_sgl_desc *expected, + struct gh_sgl_desc *received) +{ + int idx; + + if (expected->n_sgl_entries != received->n_sgl_entries) + return -E2BIG; + sort(received->sgl_entries, received->n_sgl_entries, + sizeof(received->sgl_entries[0]), fts_sgl_cmp, NULL); + sort(expected->sgl_entries, expected->n_sgl_entries, + sizeof(expected->sgl_entries[0]), fts_sgl_cmp, NULL); + + for (idx = 0; idx < expected->n_sgl_entries; idx++) { + struct gh_sgl_entry *left = &expected->sgl_entries[idx]; + struct gh_sgl_entry *right = &received->sgl_entries[idx]; + + if ((left->ipa_base != right->ipa_base) || + (left->size != right->size)) { + pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n", + left->ipa_base, right->ipa_base, + left->size, right->size); + return -EINVAL; + } + } + return 0; +} + +static int fts_vm_handle_vm_hardware(struct fts_ts_info *info) +{ + int rc = 0; + + if (atomic_read(&info->delayed_vm_probe_pending)) { + rc = fts_probe_delayed(info); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + return rc; + } + atomic_set(&info->delayed_vm_probe_pending, 0); + return rc; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + fts_interrupt_enable(info); + return rc; +} + +static void fts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_info *info = data; + struct irq_data *irq_data; + int irq = 0; + int const resource_timeout = msecs_to_jiffies(2000); + int rc = 0; + + irq = gh_irq_accept(info->vm_info->irq_label, -1, IRQ_TYPE_LEVEL_HIGH); + if (irq < 0) { + pr_err("failed to accept irq\n"); + goto irq_fail; + } + atomic_set(&info->vm_info->tvm_owns_irq, 1); + irq_data = irq_get_irq_data(irq); + if (!irq_data) { + pr_err("Invalid irq data for trusted touch\n"); + goto irq_fail; + } + if (!irq_data->hwirq) { + pr_err("Invalid irq in irq data\n"); + goto irq_fail; + } + if (irq_data->hwirq != info->vm_info->hw_irq) { + pr_err("Invalid irq lent\n"); + goto irq_fail; + } + + pr_debug("irq:returned from accept:%d\n", irq); + info->client->irq = irq; + if (!wait_for_completion_timeout(&info->resource_checkpoint, + resource_timeout)) { + pr_err("Resources not acquired in TVM\n"); + goto irq_fail; + } + + rc = fts_vm_handle_vm_hardware(info); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + goto irq_fail; + } + + atomic_set(&info->trusted_touch_enabled, 1); + return; +irq_fail: + fts_trusted_touch_vm_mode_disable(info); +} + +static void fts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_shared_payload *payload; + struct gh_sgl_desc *sgl_desc, *expected_sgl_desc; + struct gh_acl_desc *acl_desc; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_info *info; + int rc = 0; + + if (notif_type != GH_RM_NOTIF_MEM_SHARED || + tag != GH_MEM_NOTIFIER_TAG_TOUCH) { + pr_err("Invalid command passed from rm\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid entry data passed from rm\n"); + return; + } + + info = (struct fts_ts_info *)entry_data; + vm_info = info->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + payload = (struct gh_rm_notif_mem_shared_payload *)notif_msg; + if (payload->trans_type != GH_RM_TRANS_TYPE_LEND || + payload->label != TRUSTED_TOUCH_MEM_LABEL) { + pr_err("Invalid label or transaction type\n"); + goto onlend_fail; + } + + acl_desc = fts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("failed to populated acl data:rc=%d\n", + PTR_ERR(acl_desc)); + goto onlend_fail; + } + + sgl_desc = gh_rm_mem_accept(payload->mem_handle, GH_RM_MEM_TYPE_IO, + GH_RM_TRANS_TYPE_LEND, + GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | + GH_RM_MEM_ACCEPT_VALIDATE_LABEL | + GH_RM_MEM_ACCEPT_DONE, payload->label, acl_desc, + NULL, NULL, 0); + if (IS_ERR_OR_NULL(sgl_desc)) { + pr_err("failed to do mem accept :rc=%d\n", + PTR_ERR(sgl_desc)); + goto acl_fail; + } + atomic_set(&vm_info->tvm_owns_iomem, 1); + + /* Initiate i2c session on tvm */ + rc = pm_runtime_get_sync(info->client->adapter->dev.parent); + if (rc < 0) { + pr_err("failed to get sync rc:%d\n", rc); + (void)fts_vm_mem_release(info); + atomic_set(&info->vm_info->tvm_owns_iomem, 0); + goto acl_fail; + } + complete(&info->resource_checkpoint); + + expected_sgl_desc = fts_vm_get_sgl(vm_info); + if (fts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { + pr_err("IO sg list does not match\n"); + goto sgl_cmp_fail; + } + + vm_info->vm_mem_handle = payload->mem_handle; + kfree(expected_sgl_desc); + kfree(acl_desc); + return; + +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +onlend_fail: + fts_trusted_touch_vm_mode_disable(info); +} + +static int fts_vm_mem_release(struct fts_ts_info *info) +{ + int rc = 0; + + rc = gh_rm_mem_release(info->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("VM mem release failed: rc=%d\n", rc); + + rc = gh_rm_mem_notify(info->vm_info->vm_mem_handle, + GH_RM_MEM_NOTIFY_OWNER_RELEASED, + GH_MEM_NOTIFIER_TAG_TOUCH, 0); + if (rc) + pr_err("Failed to notify mem release to PVM: rc=%d\n"); + + info->vm_info->vm_mem_handle = 0; + return rc; +} + +static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info) +{ + int rc = 0; + + + if (atomic_read(&info->vm_info->tvm_owns_iomem) && + atomic_read(&info->vm_info->tvm_owns_irq)) + fts_interrupt_disable(info); + + if (atomic_read(&info->vm_info->tvm_owns_iomem)) { + flushFIFO(); + release_all_touches(info); + rc = fts_vm_mem_release(info); + if (rc) + pr_err("Failed to release mem rc:%d\n", rc); + else + atomic_set(&info->vm_info->tvm_owns_iomem, 0); + pm_runtime_put_sync(info->client->adapter->dev.parent); + } + + if (atomic_read(&info->vm_info->tvm_owns_irq)) { + rc = gh_irq_release(info->vm_info->irq_label); + if (rc) + pr_err("Failed to release irq rc:%d\n", rc); + else + atomic_set(&info->vm_info->tvm_owns_irq, 0); + + rc = gh_irq_release_notify(info->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); + } + atomic_set(&info->trusted_touch_enabled, 0); + reinit_completion(&info->resource_checkpoint); + pr_debug("trusted touch disabled\n"); +} + +static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&info->trusted_touch_enabled) == 0) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_trusted_touch_vm_mode_disable(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&info->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + pr_err("Cannot turnon trusted touch(vm mode) in VM\n"); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + dev_err(&info->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +#else + +static int fts_clk_prepare_enable(struct fts_ts_info *info) +{ + int ret; + + ret = clk_prepare_enable(info->iface_clk); + if (ret) { + dev_err(&info->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(info->core_clk); + if (ret) { + clk_disable_unprepare(info->iface_clk); + dev_err(&info->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void fts_clk_disable_unprepare(struct fts_ts_info *info) +{ + clk_disable_unprepare(info->core_clk); + clk_disable_unprepare(info->iface_clk); +} + +static int fts_bus_get(struct fts_ts_info *info) +{ + int rc = 0; + + mutex_lock(&info->fts_clk_io_ctrl_mutex); + rc = pm_runtime_get_sync(info->client->adapter->dev.parent); + if (rc >= 0 && info->core_clk != NULL && info->iface_clk != NULL) { + rc = fts_clk_prepare_enable(info); + if (rc) + pm_runtime_put_sync(info->client->adapter->dev.parent); + } + mutex_unlock(&info->fts_clk_io_ctrl_mutex); + return rc; +} + +static void fts_bus_put(struct fts_ts_info *info) +{ + mutex_lock(&info->fts_clk_io_ctrl_mutex); + if (info->core_clk != NULL && info->iface_clk != NULL) + fts_clk_disable_unprepare(info); + pm_runtime_put_sync(info->client->adapter->dev.parent); + mutex_unlock(&info->fts_clk_io_ctrl_mutex); +} + +static struct gh_notify_vmid_desc *fts_vm_get_vmid(gh_vmid_t vmid) +{ + struct gh_notify_vmid_desc *vmid_desc; + + vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc, + vmid_entries[1]), GFP_KERNEL); + if (!vmid_desc) + return ERR_PTR(ENOMEM); + + vmid_desc->n_vmid_entries = 1; + vmid_desc->vmid_entries[0].vmid = vmid; + return vmid_desc; +} + +static void fts_trusted_touch_complete(struct fts_ts_info *info) +{ + if (atomic_read(&info->vm_info->pvm_owns_iomem) && + atomic_read(&info->vm_info->pvm_owns_irq)) { + fts_interrupt_enable(info); + fts_bus_put(info); + complete(&info->trusted_touch_powerdown); + atomic_set(&info->trusted_touch_enabled, 0); + pr_debug("reenabled interrupts on PVM\n"); + } else { + pr_err("PVM does not own irq and IOMEM\n"); + } +} + +static void fts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_info *info = data; + int rc = 0; + + rc = gh_irq_reclaim(info->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + else + atomic_set(&info->vm_info->pvm_owns_irq, 1); +} + +static void fts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_released_payload *payload; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_info *info; + int rc = 0; + + if (notif_type != GH_RM_NOTIF_MEM_RELEASED || + tag != GH_MEM_NOTIFIER_TAG_TOUCH) { + pr_err(" Invalid tag or command passed\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err(" Invalid data or notification message\n"); + return; + } + + payload = (struct gh_rm_notif_mem_released_payload *)notif_msg; + info = (struct fts_ts_info *)entry_data; + vm_info = info->vm_info; + if (!vm_info) { + pr_err(" Invalid vm_info\n"); + return; + } + + if (payload->mem_handle != vm_info->vm_mem_handle) { + pr_err("Invalid mem handle detected\n"); + return; + } + + rc = gh_rm_mem_reclaim(payload->mem_handle, 0); + if (rc) { + pr_err("Trusted touch VM mem release failed rc:%d\n", rc); + return; + } + atomic_set(&vm_info->pvm_owns_iomem, 1); + vm_info->vm_mem_handle = 0; +} + +static int fts_vm_mem_lend(struct fts_ts_info *info) +{ + struct gh_acl_desc *acl_desc; + struct gh_sgl_desc *sgl_desc; + struct gh_notify_vmid_desc *vmid_desc; + gh_memparcel_handle_t mem_handle; + gh_vmid_t trusted_vmid; + int rc = 0; + + acl_desc = fts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("Failed to get acl of IO memories for Trusted touch\n"); + PTR_ERR(acl_desc); + return -EINVAL; + } + + sgl_desc = fts_vm_get_sgl(info->vm_info); + if (IS_ERR(sgl_desc)) { + pr_err("Failed to get sgl of IO memories for Trusted touch\n"); + PTR_ERR(sgl_desc); + rc = -EINVAL; + goto sgl_error; + } + + rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); + if (rc) { + pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", + rc); + goto error; + } + + gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); + + vmid_desc = fts_vm_get_vmid(trusted_vmid); + + rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED, + GH_MEM_NOTIFIER_TAG_TOUCH, vmid_desc); + if (rc) { + pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); + goto vmid_error; + } + + info->vm_info->vm_mem_handle = mem_handle; +vmid_error: + kfree(vmid_desc); +error: + kfree(sgl_desc); +sgl_error: + kfree(acl_desc); + + return rc; +} + +static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info = info->vm_info; + + /* i2c session start and resource acquire */ + if (fts_bus_get(info) < 0) { + dev_err(&info->client->dev, "fts_bus_get failed\n"); + rc = -EIO; + return rc; + } + + /* flush pending interurpts from FIFO */ + fts_interrupt_disable(info); + flushFIFO(); + release_all_touches(info); + + rc = fts_vm_mem_lend(info); + if (rc) { + pr_err("Failed to lend memory\n"); + return -EINVAL; + } + atomic_set(&vm_info->pvm_owns_iomem, 0); + + rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, + info->client->irq, &fts_vm_irq_on_release_callback, info); + if (rc) { + pr_err("Failed to lend irq\n"); + return -EINVAL; + } + atomic_set(&vm_info->pvm_owns_irq, 0); + + rc = gh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + return -EINVAL; + } + + reinit_completion(&info->trusted_touch_powerdown); + atomic_set(&info->trusted_touch_enabled, 1); + pr_debug("trusted touch enabled\n"); + return rc; +} + +static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&info->trusted_touch_enabled) == 0) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_trusted_touch_complete(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&info->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + err = fts_trusted_touch_vm_mode_enable(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + dev_err(&info->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} +#endif + +static int fts_vm_init(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + void *mem_cookie; + + rc = fts_populate_vm_info(info); + if (rc) { + pr_err("Cannot setup vm pipeline\n"); + rc = -EINVAL; + goto fail; + } + + vm_info = info->vm_info; +#ifdef CONFIG_ARCH_QTI_VM + mem_cookie = gh_mem_notifier_register(GH_MEM_NOTIFIER_TAG_TOUCH, + fts_vm_mem_on_lend_handler, info); + if (!mem_cookie) { + pr_err("Failed to register on lend mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM, + &fts_vm_irq_on_lend_callback, info); + atomic_set(&vm_info->tvm_owns_irq, 0); + atomic_set(&vm_info->tvm_owns_iomem, 0); + init_completion(&info->resource_checkpoint); +#else + mem_cookie = gh_mem_notifier_register(GH_MEM_NOTIFIER_TAG_TOUCH, + fts_vm_mem_on_release_handler, info); + if (!mem_cookie) { + pr_err("Failed to register on release mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + atomic_set(&vm_info->pvm_owns_irq, 1); + atomic_set(&vm_info->pvm_owns_iomem, 1); +#endif + return rc; +init_fail: + fts_vm_deinit(info); +fail: + return rc; +} + +static void fts_dt_parse_trusted_touch_info(struct fts_ts_info *info) +{ + struct device_node *np = info->client->dev.of_node; + int rc = 0; + const char *selection; + const char *environment; + + rc = of_property_read_string(np, "st,trusted-touch-mode", + &selection); + if (rc) { + dev_warn(&info->client->dev, + "%s: No trusted touch mode selection made\n", __func__); + } + + if (!strcmp(selection, "vm_mode")) { + atomic_set(&info->trusted_touch_mode, TRUSTED_TOUCH_VM_MODE); + pr_err("Selected trusted touch mode to VM mode\n"); + } else { + atomic_set(&info->trusted_touch_mode, TRUSTED_TOUCH_MODE_NONE); + pr_err("Invalid trusted_touch mode\n"); + } + + rc = of_property_read_string(np, "st,touch-environment", + &environment); + if (rc) { + dev_warn(&info->client->dev, + "%s: No trusted touch mode environment\n", __func__); + } + info->touch_environment = environment; + pr_err("Trusted touch environment:%s\n", + info->touch_environment); +} + +static void fts_trusted_touch_init(struct fts_ts_info *info) +{ + int rc = 0; + + atomic_set(&info->trusted_touch_initialized, 0); + init_completion(&info->trusted_touch_powerdown); + fts_dt_parse_trusted_touch_info(info); + + /* Get clocks */ + info->core_clk = devm_clk_get(info->client->dev.parent, + "m-ahb"); + if (IS_ERR(info->core_clk)) { + info->core_clk = NULL; + dev_warn(&info->client->dev, + "%s: core_clk is not defined\n", __func__); + } + + info->iface_clk = devm_clk_get(info->client->dev.parent, + "se-clk"); + if (IS_ERR(info->iface_clk)) { + info->iface_clk = NULL; + dev_warn(&info->client->dev, + "%s: iface_clk is not defined\n", __func__); + } + if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { + rc = fts_vm_init(info); + if (rc) + pr_err("Failed to init VM\n"); + } + atomic_set(&info->trusted_touch_initialized, 1); +} + +static ssize_t fts_trusted_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info; + + if (!client) + return scnprintf(buf, PAGE_SIZE, "client is null\n"); + + info = i2c_get_clientdata(client); + if (!info) { + logError(0, "info is null\n"); + return scnprintf(buf, PAGE_SIZE, "info is null\n"); + } + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_enabled)); +} + +static ssize_t fts_trusted_touch_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; + unsigned long value; + int err = 0; + + if (!client) + return -EIO; + info = i2c_get_clientdata(client); + if (!info) { + logError(0, "info is null\n"); + return -EIO; + } + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + +#ifdef CONFIG_ARCH_QTI_VM + err = fts_handle_trusted_touch_tvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in tvm\n"); + return -EINVAL; + } +#else + err = fts_handle_trusted_touch_pvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in pvm\n"); + return -EINVAL; + } +#endif + err = count; + return err; +} + +#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, 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 (typeOfComand[0]) { + case 0x13: + 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 0x15: + 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 0x14: + 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 0x16: + 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; + + default: + break; + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + 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 + +#ifdef CONFIG_ST_TRUSTED_TOUCH +static DEVICE_ATTR(trusted_touch_enable, + 0664, + fts_trusted_touch_enable_show, + fts_trusted_touch_enable_store); +#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 +#ifdef CONFIG_ST_TRUSTED_TOUCH + &dev_attr_trusted_touch_enable.attr, +#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 = 0; + u8 touchsize; + + if (!info->resume_bit && !info->aoi_notify_enabled) + return; + + 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--; + + if (info->sensor_sleep && info->aoi_notify_enabled) + if ((x < info->aoi_left || x > info->aoi_right) + || (y < info->aoi_top || y > info->aoi_bottom)) { + x = -x; + y = -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); + + 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; + + if (!info) { + pr_err("%s: Invalid info\n", __func__); + return IRQ_HANDLED; + } +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifndef CONFIG_ARCH_QTI_VM + if (atomic_read(&info->vm_info->pvm_owns_iomem) && + atomic_read(&info->vm_info->pvm_owns_irq) && + atomic_read(&info->trusted_touch_enabled)) { + pr_err("%s: Cannot service interrupts in PVM while trusted touch is enabled\n", + __func__); + return IRQ_HANDLED; + } +#endif +#endif + 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 +#ifdef CONFIG_ARCH_QTI_VM + logError(0, "%s Interrupt Mode\n", tag); + if (request_threaded_irq(info->client->irq, NULL, fts_interrupt_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, info->client->name, info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } +#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 +#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); + +#ifdef CONFIG_ST_TRUSTED_TOUCH + if (atomic_read(&info->trusted_touch_enabled)) + wait_for_completion_interruptible( + &info->trusted_touch_powerdown); +#endif + + __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, FTS_DVDD_LOAD); + 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, FTS_AVDD_LOAD); + 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 PTR_ERR(panel); +} + +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_delayed(struct fts_ts_info *info) +{ + int error = 0; + int retval = 0; + +/* Avoid setting up hardware for TVM during probe */ +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (!atomic_read(&info->delayed_vm_probe_pending)) { + atomic_set(&info->delayed_vm_probe_pending, 1); + return 0; + } + goto tvm_setup; +#endif +#endif + 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 Exit_1; + } + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, + "%s %s: ERROR Failed to enable regulators\n", + tag, __func__); + goto Exit_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 Exit_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); + } + } + +#ifdef CONFIG_ARCH_QTI_VM +tvm_setup: +#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; +#ifdef CONFIG_ARCH_QTI_VM + return error; +#endif + goto Exit_3; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + return error; + +Exit_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); + +Exit_2: + fts_get_reg(info, false); +Exit_1: + return error; +} + +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 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 can't 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 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_1; + } + + 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 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; + +#ifdef CONFIG_ST_TRUSTED_TOUCH + fts_trusted_touch_init(info); + mutex_init(&(info->fts_clk_io_ctrl_mutex)); +#endif + + 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; + } + + /* I2C cmd */ + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + +#ifdef SCRIPTLESS + 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 + 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 + + 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; + } + + info->aoi_class = class_create(THIS_MODULE, "android_touch"); + if (info->aoi_class) { + info->aoi_dev = device_create(info->aoi_class, + NULL, DCHIP_ID_0, info, "touch"); + if (!IS_ERR(info->aoi_dev)) { + dev_set_drvdata(info->aoi_dev, info); + + error = sysfs_create_group(&info->aoi_dev->kobj, + &aoi_enable_attr_group); + } + } + + error = fts_probe_delayed(info); + if (error) { + logError(1, "%s ERROR: Failed to enable resources\n", + tag); + goto ProbeErrorExit_11; + } + 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: +#ifdef CONFIG_ST_TRUSTED_TOUCH + fts_vm_deinit(info); +#endif + /* fb_unregister_client(&info->notifier); */ + 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_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; + + error = check_dt(dp); + if (error == -EPROBE_DEFER) + return error; + + if (error) { + 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); + + if (info->aoi_dev) { + sysfs_remove_group(&info->aoi_dev->kobj, + &aoi_enable_attr_group); + info->aoi_dev = NULL; + } + + if (info->aoi_class) { + device_destroy(info->aoi_class, DCHIP_ID_0); + info->aoi_class = NULL; + } + +#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); +} + +module_init(fts_driver_init); +module_exit(fts_driver_exit); + +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/st/fts.h b/st/fts.h new file mode 100644 index 0000000000..817096209a --- /dev/null +++ b/st/fts.h @@ -0,0 +1,396 @@ +/* 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 + +#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); +}; + +enum trusted_touch_mode_config { + TRUSTED_TOUCH_VM_MODE, + TRUSTED_TOUCH_MODE_NONE +}; + +#ifdef CONFIG_ST_TRUSTED_TOUCH +#define TRUSTED_TOUCH_MEM_LABEL 0x7 + +struct trusted_touch_vm_info { + enum gh_irq_label irq_label; + enum gh_vm_names vm_name; + u32 hw_irq; + gh_memparcel_handle_t vm_mem_handle; + u32 *iomem_bases; + u32 *iomem_sizes; + u32 iomem_list_size; + void *mem_cookie; +#ifdef CONFIG_ARCH_QTI_VM + atomic_t tvm_owns_iomem; + atomic_t tvm_owns_irq; +#else + atomic_t pvm_owns_iomem; + atomic_t pvm_owns_irq; +#endif +}; +#endif + +/* + * 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 */ + struct class *aoi_class; + struct device *aoi_dev; + int aoi_left; + int aoi_top; + int aoi_bottom; + int aoi_right; + +#ifdef CONFIG_ST_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + const char *touch_environment; + struct completion trusted_touch_powerdown; + struct completion resource_checkpoint; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif +}; + +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); +void release_all_touches(struct fts_ts_info *info); +/*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; +extern struct attribute_group aoi_enable_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/st/fts_aoi_event.c b/st/fts_aoi_event.c new file mode 100644 index 0000000000..1a37f659e1 --- /dev/null +++ b/st/fts_aoi_event.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, 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, +}; + +static ssize_t enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + int enable; + + if (kstrtoint(buf, 10, &enable)) + return -EINVAL; + + if (!enable && info->aoi_notify_enabled) { + info->aoi_left = 0; + info->aoi_top = 0; + info->aoi_right = 0; + info->aoi_bottom = 0; + info->aoi_notify_enabled = false; + } else { + info->aoi_left = 0; + info->aoi_top = 0; + info->aoi_right = X_AXIS_MAX; + info->aoi_bottom = Y_AXIS_MAX; + info->aoi_notify_enabled = true; + } + + return count; +} + +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + size_t len = 0; + + len = scnprintf(buf, PAGE_SIZE, + "%d", + info->aoi_notify_enabled); + + return len; +} + +static DEVICE_ATTR_RW(enable); + +static struct attribute *aoi_enable_attributes[] = { + &dev_attr_aoi_set.attr, + &dev_attr_enable.attr, + NULL, +}; + +struct attribute_group aoi_enable_attr_group = { + .attrs = aoi_enable_attributes, +}; diff --git a/st/fts_driver_test.c b/st/fts_driver_test.c new file mode 100644 index 0000000000..fa9ed503cf --- /dev/null +++ b/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/st/fts_gui.c b/st/fts_gui.c new file mode 100644 index 0000000000..4adec7d96e --- /dev/null +++ b/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/st/fts_lib/ftsCompensation.c b/st/fts_lib/ftsCompensation.c new file mode 100644 index 0000000000..2bffaa9060 --- /dev/null +++ b/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/st/fts_lib/ftsCompensation.h b/st/fts_lib/ftsCompensation.h new file mode 100644 index 0000000000..fcd1cccfe5 --- /dev/null +++ b/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/st/fts_lib/ftsCrossCompile.c b/st/fts_lib/ftsCrossCompile.c new file mode 100644 index 0000000000..e44899c0a6 --- /dev/null +++ b/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/st/fts_lib/ftsCrossCompile.h b/st/fts_lib/ftsCrossCompile.h new file mode 100644 index 0000000000..15f51b3d72 --- /dev/null +++ b/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/st/fts_lib/ftsError.c b/st/fts_lib/ftsError.c new file mode 100644 index 0000000000..80919322e2 --- /dev/null +++ b/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/st/fts_lib/ftsError.h b/st/fts_lib/ftsError.h new file mode 100644 index 0000000000..c96a587691 --- /dev/null +++ b/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/st/fts_lib/ftsFlash.c b/st/fts_lib/ftsFlash.c new file mode 100644 index 0000000000..9fc3926dec --- /dev/null +++ b/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/st/fts_lib/ftsFlash.h b/st/fts_lib/ftsFlash.h new file mode 100644 index 0000000000..844c5da532 --- /dev/null +++ b/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/st/fts_lib/ftsFrame.c b/st/fts_lib/ftsFrame.c new file mode 100644 index 0000000000..ffcf34925e --- /dev/null +++ b/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/st/fts_lib/ftsFrame.h b/st/fts_lib/ftsFrame.h new file mode 100644 index 0000000000..6945a619eb --- /dev/null +++ b/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/st/fts_lib/ftsGesture.c b/st/fts_lib/ftsGesture.c new file mode 100644 index 0000000000..dfd2dae00c --- /dev/null +++ b/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/st/fts_lib/ftsGesture.h b/st/fts_lib/ftsGesture.h new file mode 100644 index 0000000000..c5c2a6abbf --- /dev/null +++ b/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/st/fts_lib/ftsHardware.h b/st/fts_lib/ftsHardware.h new file mode 100644 index 0000000000..553cf6d576 --- /dev/null +++ b/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/st/fts_lib/ftsIO.c b/st/fts_lib/ftsIO.c new file mode 100644 index 0000000000..ba0fb18e33 --- /dev/null +++ b/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/st/fts_lib/ftsIO.h b/st/fts_lib/ftsIO.h new file mode 100644 index 0000000000..efba899ea2 --- /dev/null +++ b/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/st/fts_lib/ftsSoftware.h b/st/fts_lib/ftsSoftware.h new file mode 100644 index 0000000000..0c52982856 --- /dev/null +++ b/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/st/fts_lib/ftsTest.c b/st/fts_lib/ftsTest.c new file mode 100644 index 0000000000..baa0772cdb --- /dev/null +++ b/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/st/fts_lib/ftsTest.h b/st/fts_lib/ftsTest.h new file mode 100644 index 0000000000..0e9334d452 --- /dev/null +++ b/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/st/fts_lib/ftsTime.c b/st/fts_lib/ftsTime.c new file mode 100644 index 0000000000..07d1bf5dba --- /dev/null +++ b/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/st/fts_lib/ftsTime.h b/st/fts_lib/ftsTime.h new file mode 100644 index 0000000000..d98dd2a9fc --- /dev/null +++ b/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/st/fts_lib/ftsTool.c b/st/fts_lib/ftsTool.c new file mode 100644 index 0000000000..543682d1e3 --- /dev/null +++ b/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/st/fts_lib/ftsTool.h b/st/fts_lib/ftsTool.h new file mode 100644 index 0000000000..350cbd57e1 --- /dev/null +++ b/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 diff --git a/synaptics_dsx/synaptics_dsx_active_pen.c b/synaptics_dsx/synaptics_dsx_active_pen.c new file mode 100644 index 0000000000..6cd855043b --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_active_pen.c @@ -0,0 +1,606 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define APEN_PHYS_NAME "synaptics_dsx/active_pen" + +#define ACTIVE_PEN_MAX_PRESSURE_16BIT 65535 +#define ACTIVE_PEN_MAX_PRESSURE_8BIT 255 + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + }; + unsigned char data[2]; + }; +}; + +struct apen_data_8b_pressure { + union { + struct { + unsigned char status_pen:1; + unsigned char status_invert:1; + unsigned char status_barrel:1; + unsigned char status_reserved:5; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char pressure_msb; + unsigned char battery_state; + unsigned char pen_id_0_7; + unsigned char pen_id_8_15; + unsigned char pen_id_16_23; + unsigned char pen_id_24_31; + } __packed; + unsigned char data[11]; + }; +}; + +struct apen_data { + union { + struct { + unsigned char status_pen:1; + unsigned char status_invert:1; + unsigned char status_barrel:1; + unsigned char status_reserved:5; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char pressure_lsb; + unsigned char pressure_msb; + unsigned char battery_state; + unsigned char pen_id_0_7; + unsigned char pen_id_8_15; + unsigned char pen_id_16_23; + unsigned char pen_id_24_31; + } __packed; + unsigned char data[12]; + }; +}; + +struct synaptics_rmi4_apen_handle { + bool apen_present; + unsigned char intr_mask; + unsigned char battery_state; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short apen_data_addr; + unsigned short max_pressure; + unsigned int pen_id; + struct input_dev *apen_dev; + struct apen_data *apen_data; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_apen_handle *apen; + +DECLARE_COMPLETION(apen_remove_complete); + +static void apen_lift(void) +{ + input_report_key(apen->apen_dev, BTN_TOUCH, 0); + input_report_key(apen->apen_dev, BTN_TOOL_PEN, 0); + input_report_key(apen->apen_dev, BTN_TOOL_RUBBER, 0); + input_sync(apen->apen_dev); + apen->apen_present = false; +} + +static void apen_report(void) +{ + int retval; + int x; + int y; + int pressure; + static int invert = -1; + struct apen_data_8b_pressure *apen_data_8b; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->apen_data_addr, + apen->apen_data->data, + sizeof(apen->apen_data->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read active pen data\n", + __func__); + return; + } + + if (apen->apen_data->status_pen == 0) { + if (apen->apen_present) + apen_lift(); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: No active pen data\n", + __func__); + + return; + } + + x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb); + y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb); + + if ((x == -1) && (y == -1)) { + if (apen->apen_present) + apen_lift(); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Active pen in range but no valid x & y\n", + __func__); + + return; + } + + if (!apen->apen_present) + invert = -1; + + if (invert != -1 && invert != apen->apen_data->status_invert) + apen_lift(); + + invert = apen->apen_data->status_invert; + + if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) { + pressure = (apen->apen_data->pressure_msb << 8) | + apen->apen_data->pressure_lsb; + apen->battery_state = apen->apen_data->battery_state; + apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) | + (apen->apen_data->pen_id_16_23 << 16) | + (apen->apen_data->pen_id_8_15 << 8) | + apen->apen_data->pen_id_0_7; + } else { + apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data; + pressure = apen_data_8b->pressure_msb; + apen->battery_state = apen_data_8b->battery_state; + apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) | + (apen_data_8b->pen_id_16_23 << 16) | + (apen_data_8b->pen_id_8_15 << 8) | + apen_data_8b->pen_id_0_7; + } + + input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0); + input_report_key(apen->apen_dev, + apen->apen_data->status_invert > 0 ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1); + input_report_key(apen->apen_dev, + BTN_STYLUS, apen->apen_data->status_barrel > 0 ? + 1 : 0); + input_report_abs(apen->apen_dev, ABS_X, x); + input_report_abs(apen->apen_dev, ABS_Y, y); + input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure); + + input_sync(apen->apen_dev); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n", + __func__, + apen->apen_data->status_pen, + apen->apen_data->status_invert, + apen->apen_data->status_barrel, + x, y, pressure); + + apen->apen_present = true; +} + +static void apen_set_params(void) +{ + input_set_abs_params(apen->apen_dev, ABS_X, 0, + apen->rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(apen->apen_dev, ABS_Y, 0, + apen->rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(apen->apen_dev, ABS_PRESSURE, 0, + apen->max_pressure, 0, 0); +} + +static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8) +{ + int retval; + unsigned char ii; + unsigned char data_reg_presence; + unsigned char size_of_query_9; + unsigned char *query_9; + unsigned char *data_desc; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + data_reg_presence = query_8->data[1]; + + size_of_query_9 = query_8->size_of_query9; + query_9 = kmalloc(size_of_query_9, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->query_base_addr + 9, + query_9, + size_of_query_9); + if (retval < 0) + goto exit; + + data_desc = query_9; + + for (ii = 0; ii < 6; ii++) { + if (!(data_reg_presence & (1 << ii))) + continue; /* The data register is not present */ + data_desc++; /* Jump over the size entry */ + while (*data_desc & (1 << 7)) + data_desc++; + data_desc++; /* Go to the next descriptor */ + } + + data_desc++; /* Jump over the size entry */ + /* Check for the presence of subpackets 1 and 2 */ + if ((*data_desc & (3 << 1)) == (3 << 1)) + apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT; + else + apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT; + +exit: + kfree(query_9); + + return retval; +} + +static int apen_reg_init(void) +{ + int retval; + unsigned char data_offset; + unsigned char size_of_query8; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->query_base_addr + 7, + &size_of_query8, + sizeof(size_of_query8)); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->query_base_addr + 8, + query_8.data, + sizeof(query_8.data)); + if (retval < 0) + return retval; + + if ((size_of_query8 >= 2) && (query_8.data6_is_present)) { + data_offset = query_8.data0_is_present + + query_8.data1_is_present + + query_8.data2_is_present + + query_8.data3_is_present + + query_8.data4_is_present + + query_8.data5_is_present; + apen->apen_data_addr = apen->data_base_addr + data_offset; + retval = apen_pressure(&query_8); + if (retval < 0) + return retval; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Active pen support unavailable\n", + __func__); + retval = -ENODEV; + } + + return retval; +} + +static int apen_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + struct synaptics_rmi4_fn_desc fd; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&fd, + sizeof(fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, fd.fn_number); + switch (fd.fn_number) { + case SYNAPTICS_RMI4_F12: + goto f12_found; + } + } else { + break; + } + + intr_count += fd.intr_src_count; + } + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F12\n", + __func__); + return -EINVAL; + +f12_found: + apen->query_base_addr = fd.query_base_addr | (page << 8); + apen->control_base_addr = fd.ctrl_base_addr | (page << 8); + apen->data_base_addr = fd.data_base_addr | (page << 8); + apen->command_base_addr = fd.cmd_base_addr | (page << 8); + + retval = apen_reg_init(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize active pen registers\n", + __func__); + return retval; + } + + apen->intr_mask = 0; + intr_src = fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + apen->intr_mask |= 1 << ii; + } + + rmi4_data->intr_mask[0] |= apen->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static void synaptics_rmi4_apen_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!apen) + return; + + if (apen->intr_mask & intr_mask) + apen_report(); + + return; +} + +static int synaptics_rmi4_apen_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (apen) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + apen = kzalloc(sizeof(*apen), GFP_KERNEL); + if (!apen) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for apen\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + apen->apen_data = kzalloc(sizeof(*(apen->apen_data)), GFP_KERNEL); + if (!apen->apen_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for apen_data\n", + __func__); + retval = -ENOMEM; + goto exit_free_apen; + } + + apen->rmi4_data = rmi4_data; + + retval = apen_scan_pdt(); + if (retval < 0) + goto exit_free_apen_data; + + apen->apen_dev = input_allocate_device(); + if (apen->apen_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate active pen device\n", + __func__); + retval = -ENOMEM; + goto exit_free_apen_data; + } + + apen->apen_dev->name = ACTIVE_PEN_DRIVER_NAME; + apen->apen_dev->phys = APEN_PHYS_NAME; + apen->apen_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + apen->apen_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + apen->apen_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(apen->apen_dev, rmi4_data); + + set_bit(EV_KEY, apen->apen_dev->evbit); + set_bit(EV_ABS, apen->apen_dev->evbit); + set_bit(BTN_TOUCH, apen->apen_dev->keybit); + set_bit(BTN_TOOL_PEN, apen->apen_dev->keybit); + set_bit(BTN_TOOL_RUBBER, apen->apen_dev->keybit); + set_bit(BTN_STYLUS, apen->apen_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, apen->apen_dev->propbit); +#endif + + apen_set_params(); + + retval = input_register_device(apen->apen_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register active pen device\n", + __func__); + goto exit_free_input_device; + } + + return 0; + +exit_free_input_device: + input_free_device(apen->apen_dev); + +exit_free_apen_data: + kfree(apen->apen_data); + +exit_free_apen: + kfree(apen); + apen = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_apen_remove(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + goto exit; + + input_unregister_device(apen->apen_dev); + kfree(apen->apen_data); + kfree(apen); + apen = NULL; + +exit: + complete(&apen_remove_complete); +} + +static void synaptics_rmi4_apen_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) { + synaptics_rmi4_apen_init(rmi4_data); + return; + } + + apen_lift(); + + apen_scan_pdt(); +} + +static void synaptics_rmi4_apen_reinit(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + return; + + apen_lift(); +} + +static void synaptics_rmi4_apen_e_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + return; + + apen_lift(); +} + +static void synaptics_rmi4_apen_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + return; + + apen_lift(); +} + +static struct synaptics_rmi4_exp_fn active_pen_module = { + .fn_type = RMI_ACTIVE_PEN, + .init = synaptics_rmi4_apen_init, + .remove = synaptics_rmi4_apen_remove, + .reset = synaptics_rmi4_apen_reset, + .reinit = synaptics_rmi4_apen_reinit, + .early_suspend = synaptics_rmi4_apen_e_suspend, + .suspend = synaptics_rmi4_apen_suspend, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_apen_attn, +}; + +static int __init rmi4_active_pen_module_init(void) +{ + synaptics_rmi4_new_function(&active_pen_module, true); + + return 0; +} + +static void __exit rmi4_active_pen_module_exit(void) +{ + synaptics_rmi4_new_function(&active_pen_module, false); + + wait_for_completion(&apen_remove_complete); +} + +module_init(rmi4_active_pen_module_init); +module_exit(rmi4_active_pen_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Active Pen Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_core.c b/synaptics_dsx/synaptics_dsx_core.c new file mode 100644 index 0000000000..065cdbb7da --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_core.c @@ -0,0 +1,5079 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" +#ifdef KERNEL_ABOVE_2_6_38 +#include +#endif + +#include + +#define INPUT_PHYS_NAME "synaptics_dsx/touch_input" +#define STYLUS_PHYS_NAME "synaptics_dsx/stylus" + +#define VIRTUAL_KEY_MAP_FILE_NAME "virtualkeys." PLATFORM_DRIVER_NAME + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +/* +#define USE_DATA_SERVER +*/ + +#define WAKEUP_GESTURE false + +#define NO_0D_WHILE_2D +#define REPORT_2D_Z +#define REPORT_2D_W +/* +#define REPORT_2D_PRESSURE +*/ + +#define F12_DATA_15_WORKAROUND + +#define IGNORE_FN_INIT_FAILURE +#define FB_READY_RESET +#define FB_READY_WAIT_MS 100 +#define FB_READY_TIMEOUT_S 30 +#ifdef SYNA_TDDI +#define TDDI_LPWG_WAIT_US 10 +#endif +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define REBUILD_WORK_DELAY_MS 500 /* ms */ + +#define EXP_FN_WORK_DELAY_MS 500 /* ms */ +#define MAX_F11_TOUCH_WIDTH 15 +#define MAX_F12_TOUCH_WIDTH 255 + +#define CHECK_STATUS_TIMEOUT_MS 100 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 + +#define STATUS_NO_ERROR 0x00 +#define STATUS_RESET_OCCURRED 0x01 +#define STATUS_INVALID_CONFIG 0x02 +#define STATUS_DEVICE_FAILURE 0x03 +#define STATUS_CONFIG_CRC_FAILURE 0x04 +#define STATUS_FIRMWARE_CRC_FAILURE 0x05 +#define STATUS_CRC_IN_PROGRESS 0x06 + +#define NORMAL_OPERATION (0 << 0) +#define SENSOR_SLEEP (1 << 0) +#define NO_SLEEP_OFF (0 << 2) +#define NO_SLEEP_ON (1 << 2) +#define CONFIGURED (1 << 7) + +#define F11_CONTINUOUS_MODE 0x00 +#define F11_WAKEUP_GESTURE_MODE 0x04 +#define F12_CONTINUOUS_MODE 0x00 +#define F12_WAKEUP_GESTURE_MODE 0x02 +#define F12_UDG_DETECT 0x0f + +static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data, + bool *was_in_bl_mode); +static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data, + bool rebuild); + +#ifdef CONFIG_DRM +static void synaptics_rmi4_dsi_panel_notifier_cb( + enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *client_data); +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +#ifndef CONFIG_FB +#define USE_EARLYSUSPEND +#endif +#endif + +#ifdef USE_EARLYSUSPEND +static int synaptics_rmi4_early_suspend(struct early_suspend *h); + +static int synaptics_rmi4_late_resume(struct early_suspend *h); +#endif + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); + +static void synaptics_rmi4_defer_probe(struct work_struct *work); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#ifdef USE_DATA_SERVER +static ssize_t synaptics_rmi4_synad_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_query_0_5 { + union { + struct { + /* query 0 */ + unsigned char f11_query0_b0__2:3; + unsigned char has_query_9:1; + unsigned char has_query_11:1; + unsigned char has_query_12:1; + unsigned char has_query_27:1; + unsigned char has_query_28:1; + + /* query 1 */ + unsigned char num_of_fingers:3; + unsigned char has_rel:1; + unsigned char has_abs:1; + unsigned char has_gestures:1; + unsigned char has_sensitibity_adjust:1; + unsigned char f11_query1_b7:1; + + /* query 2 */ + unsigned char num_of_x_electrodes; + + /* query 3 */ + unsigned char num_of_y_electrodes; + + /* query 4 */ + unsigned char max_electrodes:7; + unsigned char f11_query4_b7:1; + + /* query 5 */ + unsigned char abs_data_size:2; + unsigned char has_anchored_finger:1; + unsigned char has_adj_hyst:1; + unsigned char has_dribble:1; + unsigned char has_bending_correction:1; + unsigned char has_large_object_suppression:1; + unsigned char has_jitter_filter:1; + } __packed; + unsigned char data[6]; + }; +}; + +struct synaptics_rmi4_f11_query_7_8 { + union { + struct { + /* query 7 */ + unsigned char has_single_tap:1; + unsigned char has_tap_and_hold:1; + unsigned char has_double_tap:1; + unsigned char has_early_tap:1; + unsigned char has_flick:1; + unsigned char has_press:1; + unsigned char has_pinch:1; + unsigned char has_chiral_scroll:1; + + /* query 8 */ + unsigned char has_palm_detect:1; + unsigned char has_rotate:1; + unsigned char has_touch_shapes:1; + unsigned char has_scroll_zones:1; + unsigned char individual_scroll_zones:1; + unsigned char has_multi_finger_scroll:1; + unsigned char has_multi_finger_scroll_edge_motion:1; + unsigned char has_multi_finger_scroll_inertia:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f11_query_9 { + union { + struct { + unsigned char has_pen:1; + unsigned char has_proximity:1; + unsigned char has_large_object_sensitivity:1; + unsigned char has_suppress_on_large_object_detect:1; + unsigned char has_two_pen_thresholds:1; + unsigned char has_contact_geometry:1; + unsigned char has_pen_hover_discrimination:1; + unsigned char has_pen_hover_and_edge_filters:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_query_12 { + union { + struct { + unsigned char has_small_object_detection:1; + unsigned char has_small_object_detection_tuning:1; + unsigned char has_8bit_w:1; + unsigned char has_2d_adjustable_mapping:1; + unsigned char has_general_information_2:1; + unsigned char has_physical_properties:1; + unsigned char has_finger_limit:1; + unsigned char has_linear_cofficient_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_query_27 { + union { + struct { + unsigned char f11_query27_b0:1; + unsigned char has_pen_position_correction:1; + unsigned char has_pen_jitter_filter_coefficient:1; + unsigned char has_group_decomposition:1; + unsigned char has_wakeup_gesture:1; + unsigned char has_small_finger_correction:1; + unsigned char has_data_37:1; + unsigned char f11_query27_b7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_ctrl_6_9 { + union { + struct { + unsigned char sensor_max_x_pos_7_0; + unsigned char sensor_max_x_pos_11_8:4; + unsigned char f11_ctrl7_b4__7:4; + unsigned char sensor_max_y_pos_7_0; + unsigned char sensor_max_y_pos_11_8:4; + unsigned char f11_ctrl9_b4__7:4; + } __packed; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f11_data_1_5 { + union { + struct { + unsigned char x_position_11_4; + unsigned char y_position_11_4; + unsigned char x_position_3_0:4; + unsigned char y_position_3_0:4; + unsigned char wx:4; + unsigned char wy:4; + unsigned char z; + } __packed; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + struct { + unsigned char ctrl24_is_present:1; + unsigned char ctrl25_is_present:1; + unsigned char ctrl26_is_present:1; + unsigned char ctrl27_is_present:1; + unsigned char ctrl28_is_present:1; + unsigned char ctrl29_is_present:1; + unsigned char ctrl30_is_present:1; + unsigned char ctrl31_is_present:1; + } __packed; + struct { + unsigned char ctrl32_is_present:1; + unsigned char ctrl33_is_present:1; + unsigned char ctrl34_is_present:1; + unsigned char ctrl35_is_present:1; + unsigned char ctrl36_is_present:1; + unsigned char ctrl37_is_present:1; + unsigned char ctrl38_is_present:1; + unsigned char ctrl39_is_present:1; + } __packed; + struct { + unsigned char ctrl40_is_present:1; + unsigned char ctrl41_is_present:1; + unsigned char ctrl42_is_present:1; + unsigned char ctrl43_is_present:1; + unsigned char ctrl44_is_present:1; + unsigned char ctrl45_is_present:1; + unsigned char ctrl46_is_present:1; + unsigned char ctrl47_is_present:1; + } __packed; + struct { + unsigned char ctrl48_is_present:1; + unsigned char ctrl49_is_present:1; + unsigned char ctrl50_is_present:1; + unsigned char ctrl51_is_present:1; + unsigned char ctrl52_is_present:1; + unsigned char ctrl53_is_present:1; + unsigned char ctrl54_is_present:1; + unsigned char ctrl55_is_present:1; + } __packed; + struct { + unsigned char ctrl56_is_present:1; + unsigned char ctrl57_is_present:1; + unsigned char ctrl58_is_present:1; + unsigned char ctrl59_is_present:1; + unsigned char ctrl60_is_present:1; + unsigned char ctrl61_is_present:1; + unsigned char ctrl62_is_present:1; + unsigned char ctrl63_is_present:1; + } __packed; + }; + unsigned char data[9]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + struct { + unsigned char data8_is_present:1; + unsigned char data9_is_present:1; + unsigned char data10_is_present:1; + unsigned char data11_is_present:1; + unsigned char data12_is_present:1; + unsigned char data13_is_present:1; + unsigned char data14_is_present:1; + unsigned char data15_is_present:1; + } __packed; + struct { + unsigned char data16_is_present:1; + unsigned char data17_is_present:1; + unsigned char data18_is_present:1; + unsigned char data19_is_present:1; + unsigned char data20_is_present:1; + unsigned char data21_is_present:1; + unsigned char data22_is_present:1; + unsigned char data23_is_present:1; + } __packed; + struct { + unsigned char data24_is_present:1; + unsigned char data25_is_present:1; + unsigned char data26_is_present:1; + unsigned char data27_is_present:1; + unsigned char data28_is_present:1; + unsigned char data29_is_present:1; + unsigned char data30_is_present:1; + unsigned char data31_is_present:1; + } __packed; + }; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_8 { + union { + struct { + unsigned char max_x_coord_lsb; + unsigned char max_x_coord_msb; + unsigned char max_y_coord_lsb; + unsigned char max_y_coord_msb; + unsigned char rx_pitch_lsb; + unsigned char rx_pitch_msb; + unsigned char tx_pitch_lsb; + unsigned char tx_pitch_msb; + unsigned char low_rx_clip; + unsigned char high_rx_clip; + unsigned char low_tx_clip; + unsigned char high_tx_clip; + unsigned char num_of_rx; + unsigned char num_of_tx; + }; + unsigned char data[14]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_23 { + union { + struct { + unsigned char finger_enable:1; + unsigned char active_stylus_enable:1; + unsigned char palm_enable:1; + unsigned char unclassified_object_enable:1; + unsigned char hovering_finger_enable:1; + unsigned char gloved_finger_enable:1; + unsigned char f12_ctr23_00_b6__7:2; + unsigned char max_reported_objects; + unsigned char f12_ctr23_02_b0:1; + unsigned char report_active_stylus_as_finger:1; + unsigned char report_palm_as_finger:1; + unsigned char report_unclassified_object_as_finger:1; + unsigned char report_hovering_finger_as_finger:1; + unsigned char report_gloved_finger_as_finger:1; + unsigned char report_narrow_object_swipe_as_finger:1; + unsigned char report_handedge_as_finger:1; + unsigned char cover_enable:1; + unsigned char stylus_enable:1; + unsigned char eraser_enable:1; + unsigned char small_object_enable:1; + unsigned char f12_ctr23_03_b4__7:4; + unsigned char report_cover_as_finger:1; + unsigned char report_stylus_as_finger:1; + unsigned char report_eraser_as_finger:1; + unsigned char report_small_object_as_finger:1; + unsigned char f12_ctr23_04_b4__7:4; + }; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_31 { + union { + struct { + unsigned char max_x_coord_lsb; + unsigned char max_x_coord_msb; + unsigned char max_y_coord_lsb; + unsigned char max_y_coord_msb; + unsigned char rx_pitch_lsb; + unsigned char rx_pitch_msb; + unsigned char rx_clip_low; + unsigned char rx_clip_high; + unsigned char wedge_clip_low; + unsigned char wedge_clip_high; + unsigned char num_of_p; + unsigned char num_of_q; + }; + unsigned char data[12]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_58 { + union { + struct { + unsigned char reporting_format; + unsigned char f12_ctr58_00_reserved; + unsigned char min_force_lsb; + unsigned char min_force_msb; + unsigned char max_force_lsb; + unsigned char max_force_msb; + unsigned char light_press_threshold_lsb; + unsigned char light_press_threshold_msb; + unsigned char light_press_hysteresis_lsb; + unsigned char light_press_hysteresis_msb; + unsigned char hard_press_threshold_lsb; + unsigned char hard_press_threshold_msb; + unsigned char hard_press_hysteresis_lsb; + unsigned char hard_press_hysteresis_msb; + }; + unsigned char data[14]; + }; +}; + +struct synaptics_rmi4_f12_finger_data { + unsigned char object_type_and_status; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; +#ifdef REPORT_2D_Z + unsigned char z; +#endif +#ifdef REPORT_2D_W + unsigned char wx; + unsigned char wy; +#endif +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char f1a_query0_b3__4:2; + unsigned char has_query4:1; + unsigned char has_query3:1; + unsigned char has_query2:1; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_query_4 { + union { + struct { + unsigned char has_ctrl19:1; + unsigned char f1a_query4_b1__4:4; + unsigned char has_ctrl24:1; + unsigned char f1a_query4_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char button_int_enable; + unsigned char multi_button; + unsigned char *txrx_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char max_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fhandler { + struct synaptics_rmi4_exp_fn *exp_fn; + bool insert; + bool remove; + struct list_head link; +}; + +struct synaptics_rmi4_exp_fn_data { + bool initialized; + bool queue_work; + struct mutex mutex; + struct list_head list; + struct delayed_work work; + struct workqueue_struct *workqueue; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_exp_fn_data exp_data; + +static struct synaptics_dsx_button_map *vir_button_map; + +#ifdef USE_DATA_SERVER +static pid_t synad_pid; +static struct task_struct *synad_task; +static struct siginfo interrupt_signal; +#endif + +static struct device_attribute attrs[] = { + __ATTR(reset, 0220, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, 0444, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, 0444, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, 0444, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, 0664, + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), + __ATTR(suspend, 0220, + synaptics_rmi4_show_error, + synaptics_rmi4_suspend_store), + __ATTR(wake_gesture, 0664, + synaptics_rmi4_wake_gesture_show, + synaptics_rmi4_wake_gesture_store), +#ifdef USE_DATA_SERVER + __ATTR(synad_pid, 0220, + synaptics_rmi4_show_error, + synaptics_rmi4_synad_pid_store), +#endif +}; + +static struct kobj_attribute virtual_key_map_attr = { + .attr = { + .name = VIRTUAL_KEY_MAP_FILE_NAME, + .mode = 0444, + }, + .show = synaptics_rmi4_virtual_key_map_show, +}; + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (kstrtouint(buf, 10, &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", + (rmi4_data->rmi4_mod_info.product_info[0]), + (rmi4_data->rmi4_mod_info.product_info[1])); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->firmware_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct synaptics_rmi4_f01_device_status device_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + device_status.data, + sizeof(device_status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device status, error = %d\n", + __func__, retval); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + device_status.flash_prog); +} + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + if (list_empty(&rmi->support_fn_list)) + return -ENODEV; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + +static ssize_t synaptics_rmi4_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + synaptics_rmi4_suspend(dev); + else if (input == 0) + synaptics_rmi4_resume(dev); + else + return -EINVAL; + + return count; +} + +static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->enable_wakeup_gesture); +} + +static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) + rmi4_data->enable_wakeup_gesture = input; + + return count; +} + +#ifdef USE_DATA_SERVER +static ssize_t synaptics_rmi4_synad_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + synad_pid = input; + + if (synad_pid) { + synad_task = pid_task(find_vpid(synad_pid), PIDTYPE_PID); + if (!synad_task) + return -EINVAL; + } + + return count; +} +#endif + +static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ii; + int cnt; + int count = 0; + + for (ii = 0; ii < vir_button_map->nbuttons; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, "0x01:%d:%d:%d:%d:%d\n", + vir_button_map->map[ii * 5 + 0], + vir_button_map->map[ii * 5 + 1], + vir_button_map->map[ii * 5 + 2], + vir_button_map->map[ii * 5 + 3], + vir_button_map->map[ii * 5 + 4]); + buf += cnt; + count += cnt; + } + + return count; +} + +static int synaptics_rmi4_f11_wg(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + unsigned char reporting_control; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F11) + break; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base, + &reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + reporting_control = (reporting_control & ~MASK_3BIT); + if (enable) + reporting_control |= F11_WAKEUP_GESTURE_MODE; + else + reporting_control |= F11_CONTINUOUS_MODE; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fhandler->full_addr.ctrl_base, + &reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + return retval; +} + +static int synaptics_rmi4_f12_wg(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + unsigned char offset; + unsigned char reporting_control[3]; + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F12) + break; + } + + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + offset = extra_data->ctrl20_offset; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + offset, + reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + if (enable) + reporting_control[rmi4_data->set_wakeup_gesture] = + F12_WAKEUP_GESTURE_MODE; + else + reporting_control[rmi4_data->set_wakeup_gesture] = + F12_CONTINUOUS_MODE; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fhandler->full_addr.ctrl_base + offset, + reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + return retval; +} + +static void synaptics_rmi4_wakeup_gesture(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + if (rmi4_data->f11_wakeup_gesture) + synaptics_rmi4_f11_wg(rmi4_data, enable); + else if (rmi4_data->f12_wakeup_gesture) + synaptics_rmi4_f12_wg(rmi4_data, enable); +} + +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char finger_status_reg[3]; + unsigned char detected_gestures; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int temp; + struct synaptics_rmi4_f11_data_1_5 data; + struct synaptics_rmi4_f11_extra_data *extra_data; + + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + + extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra; + + if (rmi4_data->suspend && rmi4_data->enable_wakeup_gesture) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data38_offset, + &detected_gestures, + sizeof(detected_gestures)); + if (retval < 0) + return 0; + + if (detected_gestures) { + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 1); + input_sync(rmi4_data->input_dev); + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0); + input_sync(rmi4_data->input_dev); + rmi4_data->suspend = false; + } +/* synaptics_rmi4_wakeup_gesture(rmi4_data, false); */ + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, finger_status); +#endif + + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * sizeof(data.data)); + retval = synaptics_rmi4_reg_read(rmi4_data, + data_offset, + data.data, + sizeof(data.data)); + if (retval < 0) { + touch_count = 0; + goto exit; + } + + x = (data.x_position_11_4 << 4) | data.x_position_3_0; + y = (data.y_position_11_4 << 4) | data.y_position_3_0; + wx = data.wx; + wy = data.wy; + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = x; + x = y; + y = temp; + temp = wx; + wx = wy; + wy = temp; + } + + if (rmi4_data->hw_if->board_data->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->hw_if->board_data->y_flip) + y = rmi4_data->sensor_max_y - y; + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + touch_count++; + } + } + + if (touch_count == 0) { + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_sync(rmi4_data->input_dev); + +exit: + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + return touch_count; +} + +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char index; + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned char size_of_2d_data; + unsigned char gesture_type; + unsigned short data_addr; + int x; + int y; + int wx; + int wy; + int temp; +#if defined(REPORT_2D_PRESSURE) || defined(F51_DISCRETE_FORCE) + int pressure; +#endif +#ifdef REPORT_2D_PRESSURE + unsigned char f_fingers; + unsigned char f_lsb; + unsigned char f_msb; + unsigned char *f_data; +#endif +#ifdef F51_DISCRETE_FORCE + unsigned char force_level; +#endif + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_f12_finger_data *data; + struct synaptics_rmi4_f12_finger_data *finger_data; + static unsigned char finger_presence; + static unsigned char stylus_presence; +#ifdef F12_DATA_15_WORKAROUND + static unsigned char objects_already_present; +#endif + + fingers_to_process = fhandler->num_of_data_points; + data_addr = fhandler->full_addr.data_base; + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data); + + if (rmi4_data->suspend && rmi4_data->enable_wakeup_gesture) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data4_offset, + rmi4_data->gesture_detection, + sizeof(rmi4_data->gesture_detection)); + if (retval < 0) + return 0; + + gesture_type = rmi4_data->gesture_detection[0]; + + if (gesture_type && gesture_type != F12_UDG_DETECT) { + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 1); + input_sync(rmi4_data->input_dev); + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0); + input_sync(rmi4_data->input_dev); + /* synaptics_rmi4_wakeup_gesture(rmi4_data, false); */ + /* rmi4_data->suspend = false; */ + } + + return 0; + } + + /* Determine the total number of fingers to process */ + if (extra_data->data15_size) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data15_offset, + extra_data->data15_data, + extra_data->data15_size); + if (retval < 0) + return 0; + + /* Start checking from the highest bit */ + index = extra_data->data15_size - 1; /* Highest byte */ + finger = (fingers_to_process - 1) % 8; /* Highest bit */ + do { + if (extra_data->data15_data[index] & (1 << finger)) + break; + + if (finger) { + finger--; + } else if (index > 0) { + index--; /* Move to the next lower byte */ + finger = 7; + } + + fingers_to_process--; + } while (fingers_to_process); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Number of fingers to process = %d\n", + __func__, fingers_to_process); + } + +#ifdef F12_DATA_15_WORKAROUND + fingers_to_process = max(fingers_to_process, objects_already_present); +#endif + + if (!fingers_to_process) { + synaptics_rmi4_free_fingers(rmi4_data); + finger_presence = 0; + stylus_presence = 0; + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data1_offset, + (unsigned char *)fhandler->data, + fingers_to_process * size_of_2d_data); + if (retval < 0) + return 0; + + data = (struct synaptics_rmi4_f12_finger_data *)fhandler->data; + +#ifdef REPORT_2D_PRESSURE + if (rmi4_data->report_pressure) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data29_offset, + extra_data->data29_data, + extra_data->data29_size); + if (retval < 0) + return 0; + } +#endif + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + + for (finger = 0; finger < fingers_to_process; finger++) { + finger_data = data + finger; + finger_status = finger_data->object_type_and_status; + +#ifdef F12_DATA_15_WORKAROUND + objects_already_present = finger + 1; +#endif + + x = (finger_data->x_msb << 8) | (finger_data->x_lsb); + y = (finger_data->y_msb << 8) | (finger_data->y_lsb); +#ifdef REPORT_2D_W + wx = finger_data->wx; + wy = finger_data->wy; +#endif + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = x; + x = y; + y = temp; + temp = wx; + wx = wy; + wy = temp; + } + + if (rmi4_data->hw_if->board_data->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->hw_if->board_data->y_flip) + y = rmi4_data->sensor_max_y - y; + + switch (finger_status) { + case F12_FINGER_STATUS: + case F12_GLOVED_FINGER_STATUS: + /* Stylus has priority over fingers */ + if (stylus_presence) + break; +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 1); +#endif + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); +#ifdef REPORT_2D_W + if (rmi4_data->wedge_sensor) { + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, wx); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, wx); + } else { + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, + max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, + min(wx, wy)); + } +#endif +#ifdef REPORT_2D_PRESSURE + if (rmi4_data->report_pressure) { + f_fingers = extra_data->data29_size / 2; + f_data = extra_data->data29_data; + if (finger + 1 > f_fingers) { + pressure = 1; + } else { + f_lsb = finger * 2; + f_msb = finger * 2 + 1; + pressure = (int)f_data[f_lsb] << 0 | + (int)f_data[f_msb] << 8; + } + pressure = pressure > 0 ? pressure : 1; + if (pressure > rmi4_data->force_max) + pressure = rmi4_data->force_max; + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, pressure); + } +#elif defined(F51_DISCRETE_FORCE) + if (finger == 0) { + retval = synaptics_rmi4_reg_read(rmi4_data, + FORCE_LEVEL_ADDR, + &force_level, + sizeof(force_level)); + if (retval < 0) + return 0; + pressure = force_level > 0 ? force_level : 1; + } else { + pressure = 1; + } + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, pressure); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + finger_presence = 1; + touch_count++; + break; + case F12_PALM_STATUS: + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d: x = %d, y = %d, wx = %d, wy = %d\n", + __func__, finger, + x, y, wx, wy); + break; + case F12_STYLUS_STATUS: + case F12_ERASER_STATUS: + if (!rmi4_data->stylus_enable) + break; + /* Stylus has priority over fingers */ + if (finger_presence) { + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + synaptics_rmi4_free_fingers(rmi4_data); + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + finger_presence = 0; + } + if (stylus_presence) {/* Allow one stylus at a timee */ + if (finger + 1 != stylus_presence) + break; + } + input_report_key(rmi4_data->stylus_dev, + BTN_TOUCH, 1); + if (finger_status == F12_STYLUS_STATUS) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_PEN, 1); + } else { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_RUBBER, 1); + } + input_report_abs(rmi4_data->stylus_dev, + ABS_X, x); + input_report_abs(rmi4_data->stylus_dev, + ABS_Y, y); + input_sync(rmi4_data->stylus_dev); + + stylus_presence = finger + 1; + touch_count++; + break; + default: +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); +#endif + break; + } + } + + if (touch_count == 0) { + finger_presence = 0; +#ifdef F12_DATA_15_WORKAROUND + objects_already_present = 0; +#endif + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + if (rmi4_data->stylus_enable) { + stylus_presence = 0; + input_report_key(rmi4_data->stylus_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_PEN, 0); + if (rmi4_data->eraser_enable) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_RUBBER, 0); + } + input_sync(rmi4_data->stylus_dev); + } + } + + input_sync(rmi4_data->input_dev); + + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + return touch_count; +} + +static int synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static unsigned char do_once = 1; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (do_once) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + do_once = 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read button data registers\n", + __func__); + return retval; + } + + data = f1a->button_data_buffer; + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); +#endif + } + + if (touch_count) + input_sync(rmi4_data->input_dev); + + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + return retval; +} + +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char touch_count_2d; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + case SYNAPTICS_RMI4_F12: + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; +#ifdef USE_DATA_SERVER + case SYNAPTICS_RMI4_F21: + if (synad_pid) + send_sig_info(SIGIO, &interrupt_signal, synad_task); + break; +#endif + default: + break; + } +} + +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data, + bool report) +{ + int retval; + unsigned char data[MAX_INTR_REGISTERS + 1]; + unsigned char *intr = &data[1]; + bool was_in_bl_mode; + struct synaptics_rmi4_f01_device_status status; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + data, + rmi4_data->num_of_intr_regs + 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read interrupt status\n", + __func__); + return retval; + } + + status.data[0] = data[0]; + if (status.status_code == STATUS_CRC_IN_PROGRESS) { + retval = synaptics_rmi4_check_status(rmi4_data, + &was_in_bl_mode); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to check status\n", + __func__); + return retval; + } + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device status\n", + __func__); + return retval; + } + } + if (status.unconfigured && !status.flash_prog) { + pr_notice("%s: spontaneous reset detected\n", __func__); + retval = synaptics_rmi4_reinit_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reinit device\n", + __func__); + } + } + + if (!report) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + intr[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler); + } + } + } + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) { + if (!exp_fhandler->insert && + !exp_fhandler->remove && + (exp_fhandler->exp_fn->attn != NULL)) + exp_fhandler->exp_fn->attn(rmi4_data, intr[0]); + } + } + mutex_unlock(&exp_data.mutex); + + return retval; +} + +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state) + goto exit; + + synaptics_rmi4_sensor_report(rmi4_data, true); + +exit: + return IRQ_HANDLED; +} + +static int synaptics_rmi4_int_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short intr_addr; + + intr_mask = rmi4_data->intr_mask; + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (enable) { + retval = synaptics_rmi4_reg_write(rmi4_data, + intr_addr, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_write(rmi4_data, + intr_addr, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + } + } + } + + return retval; +} + +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable, bool attn_only) +{ + int retval = 0; + unsigned char data[MAX_INTR_REGISTERS]; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + mutex_lock(&(rmi4_data->rmi4_irq_enable_mutex)); + + if (attn_only) { + retval = synaptics_rmi4_int_enable(rmi4_data, enable); + goto exit; + } + + if (enable) { + if (rmi4_data->irq_enabled) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Interrupt already enabled\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_int_enable(rmi4_data, false); + if (retval < 0) + goto exit; + + /* Clear interrupts */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + data, + rmi4_data->num_of_intr_regs); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read interrupt status\n", + __func__); + goto exit; + } + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, bdata->irq_flags, + PLATFORM_DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create irq thread\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_int_enable(rmi4_data, true); + if (retval < 0) + goto exit; + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + } + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_irq_enable_mutex)); + + return retval; +} + +static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + unsigned char ii; + unsigned char intr_offset; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < (fd->intr_src_count + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; +} + +static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->data = NULL; + fhandler->extra = NULL; + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + rmi4_data->f01_query_base_addr = fd->query_base_addr; + rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr; + rmi4_data->f01_data_base_addr = fd->data_base_addr; + rmi4_data->f01_cmd_base_addr = fd->cmd_base_addr; + + return 0; +} + +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + int temp; + unsigned char offset; + unsigned char fingers_supported; + struct synaptics_rmi4_f11_extra_data *extra_data; + struct synaptics_rmi4_f11_query_0_5 query_0_5; + struct synaptics_rmi4_f11_query_7_8 query_7_8; + struct synaptics_rmi4_f11_query_9 query_9; + struct synaptics_rmi4_f11_query_12 query_12; + struct synaptics_rmi4_f11_query_27 query_27; + struct synaptics_rmi4_f11_ctrl_6_9 control_6_9; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL); + if (!fhandler->extra) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fhandler->extra\n", + __func__); + return -ENOMEM; + } + extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base, + query_0_5.data, + sizeof(query_0_5.data)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if (query_0_5.num_of_fingers <= 4) + fhandler->num_of_data_points = query_0_5.num_of_fingers + 1; + else if (query_0_5.num_of_fingers == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + 6, + control_6_9.data, + sizeof(control_6_9.data)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = control_6_9.sensor_max_x_pos_7_0 | + (control_6_9.sensor_max_x_pos_11_8 << 8); + rmi4_data->sensor_max_y = control_6_9.sensor_max_y_pos_7_0 | + (control_6_9.sensor_max_y_pos_11_8 << 8); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + rmi4_data->max_touch_width = MAX_F11_TOUCH_WIDTH; + + if (bdata->swap_axes) { + temp = rmi4_data->sensor_max_x; + rmi4_data->sensor_max_x = rmi4_data->sensor_max_y; + rmi4_data->sensor_max_y = temp; + } + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + fhandler->data = NULL; + + offset = sizeof(query_0_5.data); + + /* query 6 */ + if (query_0_5.has_rel) + offset += 1; + + /* queries 7 8 */ + if (query_0_5.has_gestures) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_7_8.data, + sizeof(query_7_8.data)); + if (retval < 0) + return retval; + + offset += sizeof(query_7_8.data); + } + + /* query 9 */ + if (query_0_5.has_query_9) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_9.data, + sizeof(query_9.data)); + if (retval < 0) + return retval; + + offset += sizeof(query_9.data); + } + + /* query 10 */ + if (query_0_5.has_gestures && query_7_8.has_touch_shapes) + offset += 1; + + /* query 11 */ + if (query_0_5.has_query_11) + offset += 1; + + /* query 12 */ + if (query_0_5.has_query_12) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_12.data, + sizeof(query_12.data)); + if (retval < 0) + return retval; + + offset += sizeof(query_12.data); + } + + /* query 13 */ + if (query_0_5.has_jitter_filter) + offset += 1; + + /* query 14 */ + if (query_0_5.has_query_12 && query_12.has_general_information_2) + offset += 1; + + /* queries 15 16 17 18 19 20 21 22 23 24 25 26*/ + if (query_0_5.has_query_12 && query_12.has_physical_properties) + offset += 12; + + /* query 27 */ + if (query_0_5.has_query_27) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_27.data, + sizeof(query_27.data)); + if (retval < 0) + return retval; + + rmi4_data->f11_wakeup_gesture = query_27.has_wakeup_gesture; + } + + if (!rmi4_data->f11_wakeup_gesture) + return retval; + + /* data 0 */ + fingers_supported = fhandler->num_of_data_points; + offset = (fingers_supported + 3) / 4; + + /* data 1 2 3 4 5 */ + offset += 5 * fingers_supported; + + /* data 6 7 */ + if (query_0_5.has_rel) + offset += 2 * fingers_supported; + + /* data 8 */ + if (query_0_5.has_gestures && query_7_8.data[0]) + offset += 1; + + /* data 9 */ + if (query_0_5.has_gestures && (query_7_8.data[0] || query_7_8.data[1])) + offset += 1; + + /* data 10 */ + if (query_0_5.has_gestures && + (query_7_8.has_pinch || query_7_8.has_flick)) + offset += 1; + + /* data 11 12 */ + if (query_0_5.has_gestures && + (query_7_8.has_flick || query_7_8.has_rotate)) + offset += 2; + + /* data 13 */ + if (query_0_5.has_gestures && query_7_8.has_touch_shapes) + offset += (fingers_supported + 3) / 4; + + /* data 14 15 */ + if (query_0_5.has_gestures && + (query_7_8.has_scroll_zones || + query_7_8.has_multi_finger_scroll || + query_7_8.has_chiral_scroll)) + offset += 2; + + /* data 16 17 */ + if (query_0_5.has_gestures && + (query_7_8.has_scroll_zones && + query_7_8.individual_scroll_zones)) + offset += 2; + + /* data 18 19 20 21 22 23 24 25 26 27 */ + if (query_0_5.has_query_9 && query_9.has_contact_geometry) + offset += 10 * fingers_supported; + + /* data 28 */ + if (query_0_5.has_bending_correction || + query_0_5.has_large_object_suppression) + offset += 1; + + /* data 29 30 31 */ + if (query_0_5.has_query_9 && query_9.has_pen_hover_discrimination) + offset += 3; + + /* data 32 */ + if (query_0_5.has_query_12 && + query_12.has_small_object_detection_tuning) + offset += 1; + + /* data 33 34 */ + if (query_0_5.has_query_27 && query_27.f11_query27_b0) + offset += 2; + + /* data 35 */ + if (query_0_5.has_query_12 && query_12.has_8bit_w) + offset += fingers_supported; + + /* data 36 */ + if (query_0_5.has_bending_correction) + offset += 1; + + /* data 37 */ + if (query_0_5.has_query_27 && query_27.has_data_37) + offset += 1; + + /* data 38 */ + if (query_0_5.has_query_27 && query_27.has_wakeup_gesture) + extra_data->data38_offset = offset; + + return retval; +} + +static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data, + unsigned short ctrl28) +{ + int retval; + static unsigned short ctrl_28_address; + + if (ctrl28) + ctrl_28_address = ctrl28; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_28_address, + &rmi4_data->report_enable, + sizeof(rmi4_data->report_enable)); + if (retval < 0) + return retval; + + return retval; +} + +static int synaptics_rmi4_f12_find_sub(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *presence, unsigned char presence_size, + unsigned char structure_offset, unsigned char reg, + unsigned char sub) +{ + int retval; + unsigned char cnt; + unsigned char regnum; + unsigned char bitnum; + unsigned char p_index; + unsigned char s_index; + unsigned char offset; + unsigned char max_reg; + unsigned char *structure; + + max_reg = (presence_size - 1) * 8 - 1; + + if (reg > max_reg) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Register number (%d) over limit\n", + __func__, reg); + return -EINVAL; + } + + p_index = reg / 8 + 1; + bitnum = reg % 8; + if ((presence[p_index] & (1 << bitnum)) == 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Register %d is not present\n", + __func__, reg); + return -EINVAL; + } + + structure = kmalloc(presence[0], GFP_KERNEL); + if (!structure) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for structure register\n", + __func__); + return -ENOMEM; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + structure_offset, + structure, + presence[0]); + if (retval < 0) + goto exit; + + s_index = 0; + + for (regnum = 0; regnum < reg; regnum++) { + p_index = regnum / 8 + 1; + bitnum = regnum % 8; + if ((presence[p_index] & (1 << bitnum)) == 0x00) + continue; + + if (structure[s_index] == 0x00) + s_index += 3; + else + s_index++; + + while (structure[s_index] & ~MASK_7BIT) + s_index++; + + s_index++; + } + + cnt = 0; + s_index++; + offset = sub / 7; + bitnum = sub % 7; + + do { + if (cnt == offset) { + if (structure[s_index + cnt] & (1 << bitnum)) + retval = 1; + else + retval = 0; + goto exit; + } + cnt++; + } while (structure[s_index + cnt - 1] & ~MASK_7BIT); + + retval = 0; + +exit: + kfree(structure); + + return retval; +} + +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval = 0; + int temp; + unsigned char subpacket; + unsigned char ctrl_23_size; + unsigned char size_of_2d_data; + unsigned char size_of_query5; + unsigned char size_of_query8; + unsigned char ctrl_8_offset; + unsigned char ctrl_20_offset; + unsigned char ctrl_23_offset; + unsigned char ctrl_28_offset; + unsigned char ctrl_31_offset; + unsigned char ctrl_58_offset; + unsigned char num_of_fingers; + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_f12_query_5 *query_5 = NULL; + struct synaptics_rmi4_f12_query_8 *query_8 = NULL; + struct synaptics_rmi4_f12_ctrl_8 *ctrl_8 = NULL; + struct synaptics_rmi4_f12_ctrl_23 *ctrl_23 = NULL; + struct synaptics_rmi4_f12_ctrl_31 *ctrl_31 = NULL; + struct synaptics_rmi4_f12_ctrl_58 *ctrl_58 = NULL; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL); + if (!fhandler->extra) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fhandler->extra\n", + __func__); + return -ENOMEM; + } + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data); + + query_5 = kzalloc(sizeof(*query_5), GFP_KERNEL); + if (!query_5) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_5\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + query_8 = kzalloc(sizeof(*query_8), GFP_KERNEL); + if (!query_8) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_8\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_8 = kzalloc(sizeof(*ctrl_8), GFP_KERNEL); + if (!ctrl_8) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_8\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_23 = kzalloc(sizeof(*ctrl_23), GFP_KERNEL); + if (!ctrl_23) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_23\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_31 = kzalloc(sizeof(*ctrl_31), GFP_KERNEL); + if (!ctrl_31) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_31\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_58 = kzalloc(sizeof(*ctrl_58), GFP_KERNEL); + if (!ctrl_58) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_58\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 4, + &size_of_query5, + sizeof(size_of_query5)); + if (retval < 0) + goto exit; + + if (size_of_query5 > sizeof(query_5->data)) + size_of_query5 = sizeof(query_5->data); + memset(query_5->data, 0x00, sizeof(query_5->data)); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 5, + query_5->data, + size_of_query5); + if (retval < 0) + goto exit; + + ctrl_8_offset = query_5->ctrl0_is_present + + query_5->ctrl1_is_present + + query_5->ctrl2_is_present + + query_5->ctrl3_is_present + + query_5->ctrl4_is_present + + query_5->ctrl5_is_present + + query_5->ctrl6_is_present + + query_5->ctrl7_is_present; + + ctrl_20_offset = ctrl_8_offset + + query_5->ctrl8_is_present + + query_5->ctrl9_is_present + + query_5->ctrl10_is_present + + query_5->ctrl11_is_present + + query_5->ctrl12_is_present + + query_5->ctrl13_is_present + + query_5->ctrl14_is_present + + query_5->ctrl15_is_present + + query_5->ctrl16_is_present + + query_5->ctrl17_is_present + + query_5->ctrl18_is_present + + query_5->ctrl19_is_present; + + ctrl_23_offset = ctrl_20_offset + + query_5->ctrl20_is_present + + query_5->ctrl21_is_present + + query_5->ctrl22_is_present; + + ctrl_28_offset = ctrl_23_offset + + query_5->ctrl23_is_present + + query_5->ctrl24_is_present + + query_5->ctrl25_is_present + + query_5->ctrl26_is_present + + query_5->ctrl27_is_present; + + ctrl_31_offset = ctrl_28_offset + + query_5->ctrl28_is_present + + query_5->ctrl29_is_present + + query_5->ctrl30_is_present; + + ctrl_58_offset = ctrl_31_offset + + query_5->ctrl31_is_present + + query_5->ctrl32_is_present + + query_5->ctrl33_is_present + + query_5->ctrl34_is_present + + query_5->ctrl35_is_present + + query_5->ctrl36_is_present + + query_5->ctrl37_is_present + + query_5->ctrl38_is_present + + query_5->ctrl39_is_present + + query_5->ctrl40_is_present + + query_5->ctrl41_is_present + + query_5->ctrl42_is_present + + query_5->ctrl43_is_present + + query_5->ctrl44_is_present + + query_5->ctrl45_is_present + + query_5->ctrl46_is_present + + query_5->ctrl47_is_present + + query_5->ctrl48_is_present + + query_5->ctrl49_is_present + + query_5->ctrl50_is_present + + query_5->ctrl51_is_present + + query_5->ctrl52_is_present + + query_5->ctrl53_is_present + + query_5->ctrl54_is_present + + query_5->ctrl55_is_present + + query_5->ctrl56_is_present + + query_5->ctrl57_is_present; + + ctrl_23_size = 2; + for (subpacket = 2; subpacket <= 4; subpacket++) { + retval = synaptics_rmi4_f12_find_sub(rmi4_data, + fhandler, query_5->data, sizeof(query_5->data), + 6, 23, subpacket); + if (retval == 1) + ctrl_23_size++; + else if (retval < 0) + goto exit; + + } + + retval = synaptics_rmi4_f12_find_sub(rmi4_data, + fhandler, query_5->data, sizeof(query_5->data), + 6, 20, 0); + if (retval == 1) + rmi4_data->set_wakeup_gesture = 2; + else if (retval == 0) + rmi4_data->set_wakeup_gesture = 0; + else if (retval < 0) + goto exit; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_23_offset, + ctrl_23->data, + ctrl_23_size); + if (retval < 0) + goto exit; + + /* Maximum number of fingers supported */ + fhandler->num_of_data_points = min_t(unsigned char, + ctrl_23->max_reported_objects, + (unsigned char)F12_FINGERS_TO_SUPPORT); + + num_of_fingers = fhandler->num_of_data_points; + rmi4_data->num_of_fingers = num_of_fingers; + + rmi4_data->stylus_enable = ctrl_23->stylus_enable; + rmi4_data->eraser_enable = ctrl_23->eraser_enable; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 7, + &size_of_query8, + sizeof(size_of_query8)); + if (retval < 0) + goto exit; + + if (size_of_query8 > sizeof(query_8->data)) + size_of_query8 = sizeof(query_8->data); + memset(query_8->data, 0x00, sizeof(query_8->data)); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 8, + query_8->data, + size_of_query8); + if (retval < 0) + goto exit; + + /* Determine the presence of the Data0 register */ + extra_data->data1_offset = query_8->data0_is_present; + + if ((size_of_query8 >= 3) && (query_8->data15_is_present)) { + extra_data->data15_offset = query_8->data0_is_present + + query_8->data1_is_present + + query_8->data2_is_present + + query_8->data3_is_present + + query_8->data4_is_present + + query_8->data5_is_present + + query_8->data6_is_present + + query_8->data7_is_present + + query_8->data8_is_present + + query_8->data9_is_present + + query_8->data10_is_present + + query_8->data11_is_present + + query_8->data12_is_present + + query_8->data13_is_present + + query_8->data14_is_present; + extra_data->data15_size = (num_of_fingers + 7) / 8; + } else { + extra_data->data15_size = 0; + } + +#ifdef REPORT_2D_PRESSURE + if ((size_of_query8 >= 5) && (query_8->data29_is_present)) { + extra_data->data29_offset = query_8->data0_is_present + + query_8->data1_is_present + + query_8->data2_is_present + + query_8->data3_is_present + + query_8->data4_is_present + + query_8->data5_is_present + + query_8->data6_is_present + + query_8->data7_is_present + + query_8->data8_is_present + + query_8->data9_is_present + + query_8->data10_is_present + + query_8->data11_is_present + + query_8->data12_is_present + + query_8->data13_is_present + + query_8->data14_is_present + + query_8->data15_is_present + + query_8->data16_is_present + + query_8->data17_is_present + + query_8->data18_is_present + + query_8->data19_is_present + + query_8->data20_is_present + + query_8->data21_is_present + + query_8->data22_is_present + + query_8->data23_is_present + + query_8->data24_is_present + + query_8->data25_is_present + + query_8->data26_is_present + + query_8->data27_is_present + + query_8->data28_is_present; + extra_data->data29_size = 0; + for (subpacket = 0; subpacket <= num_of_fingers; subpacket++) { + retval = synaptics_rmi4_f12_find_sub(rmi4_data, + fhandler, query_8->data, + sizeof(query_8->data), + 9, 29, subpacket); + if (retval == 1) + extra_data->data29_size += 2; + else if (retval < 0) + goto exit; + } + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_58_offset, + ctrl_58->data, + sizeof(ctrl_58->data)); + if (retval < 0) + goto exit; + rmi4_data->force_min = + (int)(ctrl_58->min_force_lsb << 0) | + (int)(ctrl_58->min_force_msb << 8); + rmi4_data->force_max = + (int)(ctrl_58->max_force_lsb << 0) | + (int)(ctrl_58->max_force_msb << 8); + rmi4_data->report_pressure = true; + } else { + extra_data->data29_size = 0; + rmi4_data->report_pressure = false; + } +#endif + + rmi4_data->report_enable = RPT_DEFAULT; +#ifdef REPORT_2D_Z + rmi4_data->report_enable |= RPT_Z; +#endif +#ifdef REPORT_2D_W + rmi4_data->report_enable |= (RPT_WX | RPT_WY); +#endif + + retval = synaptics_rmi4_f12_set_enables(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_28_offset); + if (retval < 0) + goto exit; + + if (query_5->ctrl8_is_present) { + rmi4_data->wedge_sensor = false; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_8_offset, + ctrl_8->data, + sizeof(ctrl_8->data)); + if (retval < 0) + goto exit; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = + ((unsigned int)ctrl_8->max_x_coord_lsb << 0) | + ((unsigned int)ctrl_8->max_x_coord_msb << 8); + rmi4_data->sensor_max_y = + ((unsigned int)ctrl_8->max_y_coord_lsb << 0) | + ((unsigned int)ctrl_8->max_y_coord_msb << 8); + + rmi4_data->max_touch_width = MAX_F12_TOUCH_WIDTH; + } else { + rmi4_data->wedge_sensor = true; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_31_offset, + ctrl_31->data, + sizeof(ctrl_31->data)); + if (retval < 0) + goto exit; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = + ((unsigned int)ctrl_31->max_x_coord_lsb << 0) | + ((unsigned int)ctrl_31->max_x_coord_msb << 8); + rmi4_data->sensor_max_y = + ((unsigned int)ctrl_31->max_y_coord_lsb << 0) | + ((unsigned int)ctrl_31->max_y_coord_msb << 8); + + rmi4_data->max_touch_width = MAX_F12_TOUCH_WIDTH; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + if (bdata->swap_axes) { + temp = rmi4_data->sensor_max_x; + rmi4_data->sensor_max_x = rmi4_data->sensor_max_y; + rmi4_data->sensor_max_y = temp; + } + + rmi4_data->f12_wakeup_gesture = query_5->ctrl27_is_present; + if (rmi4_data->f12_wakeup_gesture) { + extra_data->ctrl20_offset = ctrl_20_offset; + extra_data->data4_offset = query_8->data0_is_present + + query_8->data1_is_present + + query_8->data2_is_present + + query_8->data3_is_present; + } + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + /* Allocate memory for finger data storage space */ + fhandler->data_size = num_of_fingers * size_of_2d_data; + fhandler->data = kmalloc(fhandler->data_size, GFP_KERNEL); + if (!fhandler->data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fhandler->data\n", + __func__); + retval = -ENOMEM; + goto exit; + } + +exit: + kfree(query_5); + kfree(query_8); + kfree(ctrl_8); + kfree(ctrl_23); + kfree(ctrl_31); + kfree(ctrl_58); + + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + fhandler->extra = NULL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->max_count = f1a->button_query.max_button_count + 1; + + f1a->button_control.txrx_map = kzalloc(f1a->max_count * 2, GFP_KERNEL); + if (!f1a->button_control.txrx_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for tx rx mapping\n", + __func__); + return -ENOMEM; + } + + f1a->button_bitmask_size = (f1a->max_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->max_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f1a_button_map(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char ii; + unsigned char offset = 0; + struct synaptics_rmi4_f1a_query_4 query_4; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + rmi4_data->valid_button_count = f1a->valid_button_count; + + offset = f1a->button_query.has_general_control + + f1a->button_query.has_interrupt_enable + + f1a->button_query.has_multibutton_select; + + if (f1a->button_query.has_tx_rx_map) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + offset, + f1a->button_control.txrx_map, + f1a->max_count * 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tx rx mapping\n", + __func__); + return retval; + } + + rmi4_data->button_txrx_mapping = f1a->button_control.txrx_map; + } + + if (f1a->button_query.has_query4) { + offset = 2 + f1a->button_query.has_query2 + + f1a->button_query.has_query3; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_4.data, + sizeof(query_4.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read button features 4\n", + __func__); + return retval; + } + + if (query_4.has_ctrl24) + rmi4_data->external_afe_buttons = true; + else + rmi4_data->external_afe_buttons = false; + } + + if (!bdata->cap_button_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: cap_button_map is NULL in board file\n", + __func__); + return -ENODEV; + } else if (!bdata->cap_button_map->map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (bdata->cap_button_map->nbuttons != f1a->max_count) { + f1a->valid_button_count = min(f1a->max_count, + bdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->max_count; + } + + for (ii = 0; ii < f1a->valid_button_count; ii++) + f1a->button_map[ii] = bdata->cap_button_map->map[ii]; + + rmi4_data->valid_button_count = f1a->valid_button_count; + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_control.txrx_map); + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_f1a_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static void synaptics_rmi4_empty_fn_list(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_fn *fhandler_temp; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry_safe(fhandler, + fhandler_temp, + &rmi->support_fn_list, + link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + synaptics_rmi4_f1a_kfree(fhandler); + } else { + kfree(fhandler->extra); + kfree(fhandler->data); + } + list_del(&fhandler->link); + kfree(fhandler); + } + } + INIT_LIST_HEAD(&rmi->support_fn_list); +} + +static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data, + bool *was_in_bl_mode) +{ + int retval; + int timeout = CHECK_STATUS_TIMEOUT_MS; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + while (status.status_code == STATUS_CRC_IN_PROGRESS) { + if (timeout > 0) + msleep(20); + else + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + timeout -= 20; + } + + if (timeout != CHECK_STATUS_TIMEOUT_MS) + *was_in_bl_mode = true; + + if (status.flash_prog == 1) { + rmi4_data->flash_prog_mode = true; + pr_notice("%s: In flash prog mode, status = 0x%02x\n", + __func__, + status.status_code); + } else { + rmi4_data->flash_prog_mode = false; + } + + return 0; +} + +static int synaptics_rmi4_set_configured(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set configured\n", + __func__); + return retval; + } + + rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON; + device_ctrl |= CONFIGURED; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set configured\n", + __func__); + } + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kzalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char page_number; + unsigned char intr_count; + unsigned char *f01_query; + unsigned short pdt_entry_addr; + bool f01found; + bool f35found; + bool was_in_bl_mode; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + +rescan_pdt: + f01found = false; + f35found = false; + was_in_bl_mode = false; + intr_count = 0; + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + pdt_entry_addr &= ~(MASK_8BIT << 8); + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + if (rmi_fd.intr_src_count == 0) + break; + + f01found = true; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f01_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_check_status(rmi4_data, + &was_in_bl_mode); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to check status\n", + __func__); + return retval; + } + + if (was_in_bl_mode) { + kfree(fhandler); + fhandler = NULL; + goto rescan_pdt; + } + + if (rmi4_data->flash_prog_mode) + goto flash_prog_mode; + + break; + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { +#ifdef IGNORE_FN_INIT_FAILURE + kfree(fhandler); + fhandler = NULL; +#else + return retval; +#endif + } + break; +#ifdef USE_DATA_SERVER + case SYNAPTICS_RMI4_F21: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + fhandler->fn_number = rmi_fd.fn_number; + fhandler->num_of_data_sources = + rmi_fd.intr_src_count; + + synaptics_rmi4_set_intr_mask(fhandler, &rmi_fd, + intr_count); + break; +#endif + case SYNAPTICS_RMI4_F35: + f35found = true; + break; +#ifdef F51_DISCRETE_FORCE + case SYNAPTICS_RMI4_F51: + rmi4_data->f51_query_base_addr = + rmi_fd.query_base_addr | + (page_number << 8); + break; +#endif + } + + /* Accumulate the interrupt count */ + intr_count += rmi_fd.intr_src_count; + + if (fhandler && rmi_fd.intr_src_count) { + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + if (!f01found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F01\n", + __func__); + if (!f35found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + pr_notice("%s: In microbootloader mode\n", + __func__); + return 0; + } + } + +flash_prog_mode: + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + + f01_query = kmalloc(F01_STD_QUERY_LEN, GFP_KERNEL); + if (!f01_query) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for f01_query\n", + __func__); + return -ENOMEM; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + F01_STD_QUERY_LEN); + if (retval < 0) { + kfree(f01_query); + return retval; + } + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + rmi->product_info[0] = f01_query[2]; + rmi->product_info[1] = f01_query[3]; + retval = secure_memcpy(rmi->product_id_string, + sizeof(rmi->product_id_string), + &f01_query[11], + F01_STD_QUERY_LEN - 11, + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy product ID string\n", + __func__); + } + + kfree(f01_query); + + if (rmi->manufacturer_id != 1) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Non-Synaptics device found, manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + rmi4_data->firmware_id = (unsigned int)rmi->build_id[0] + + (unsigned int)rmi->build_id[1] * 0x100 + + (unsigned int)rmi->build_id[2] * 0x10000; + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + } + + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) + rmi4_data->enable_wakeup_gesture = WAKEUP_GESTURE; + else + rmi4_data->enable_wakeup_gesture = false; + + synaptics_rmi4_set_configured(rmi4_data); + + return 0; +} + +static int synaptics_rmi4_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, sizeof(buf), "dsx_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + pr_err("%s: Failed to get gpio %d (code: %d)", + __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + pr_err("%s: Failed to set gpio %d direction", + __func__, gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return retval; +} + +static void synaptics_rmi4_set_params(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + rmi4_data->max_touch_width, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + rmi4_data->max_touch_width, 0, 0); +#endif + + rmi4_data->input_settings.sensor_max_x = rmi4_data->sensor_max_x; + rmi4_data->input_settings.sensor_max_y = rmi4_data->sensor_max_y; + rmi4_data->input_settings.max_touch_width = rmi4_data->max_touch_width; + +#ifdef REPORT_2D_PRESSURE + if (rmi4_data->report_pressure) { + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, rmi4_data->force_min, + rmi4_data->force_max, 0, 0); + + rmi4_data->input_settings.force_min = rmi4_data->force_min; + rmi4_data->input_settings.force_max = rmi4_data->force_max; + } +#elif defined(F51_DISCRETE_FORCE) + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + FORCE_LEVEL_MAX, 0, 0); +#endif + +#ifdef TYPE_B_PROTOCOL +#ifdef KERNEL_ABOVE_3_6 + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, INPUT_MT_DIRECT); +#else + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers); +#endif +#endif + + rmi4_data->input_settings.num_of_fingers = rmi4_data->num_of_fingers; + + f1a = NULL; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + f1a = fhandler->data; + } + } + + if (f1a) { + for (ii = 0; ii < f1a->valid_button_count; ii++) { + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + + rmi4_data->input_settings.valid_button_count = + f1a->valid_button_count; + } + + if (vir_button_map->nbuttons) { + for (ii = 0; ii < vir_button_map->nbuttons; ii++) { + set_bit(vir_button_map->map[ii * 5], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, vir_button_map->map[ii * 5]); + } + } + + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) { + set_bit(KEY_WAKEUP, rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_WAKEUP); + } +} + +static int synaptics_rmi4_set_input_dev(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + rmi4_data->input_dev = input_allocate_device(); + if (rmi4_data->input_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate input device\n", + __func__); + retval = -ENOMEM; + goto err_input_device; + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + + rmi4_data->input_dev->name = PLATFORM_DRIVER_NAME; + rmi4_data->input_dev->phys = INPUT_PHYS_NAME; + rmi4_data->input_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + rmi4_data->input_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + rmi4_data->input_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(rmi4_data->input_dev, rmi4_data); + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + + if (bdata->max_y_for_2d >= 0) + rmi4_data->sensor_max_y = bdata->max_y_for_2d; + + synaptics_rmi4_set_params(rmi4_data); + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register input device\n", + __func__); + goto err_register_input; + } + + rmi4_data->input_settings.stylus_enable = rmi4_data->stylus_enable; + rmi4_data->input_settings.eraser_enable = rmi4_data->eraser_enable; + + if (!rmi4_data->stylus_enable) + return 0; + + rmi4_data->stylus_dev = input_allocate_device(); + if (rmi4_data->stylus_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate stylus device\n", + __func__); + retval = -ENOMEM; + goto err_stylus_device; + } + + rmi4_data->stylus_dev->name = STYLUS_DRIVER_NAME; + rmi4_data->stylus_dev->phys = STYLUS_PHYS_NAME; + rmi4_data->stylus_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + rmi4_data->stylus_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + rmi4_data->stylus_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(rmi4_data->stylus_dev, rmi4_data); + + set_bit(EV_KEY, rmi4_data->stylus_dev->evbit); + set_bit(EV_ABS, rmi4_data->stylus_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->stylus_dev->keybit); + set_bit(BTN_TOOL_PEN, rmi4_data->stylus_dev->keybit); + if (rmi4_data->eraser_enable) + set_bit(BTN_TOOL_RUBBER, rmi4_data->stylus_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->stylus_dev->propbit); +#endif + + input_set_abs_params(rmi4_data->stylus_dev, ABS_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->stylus_dev, ABS_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + + retval = input_register_device(rmi4_data->stylus_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register stylus device\n", + __func__); + goto err_register_stylus; + } + + return 0; + +err_register_stylus: + rmi4_data->stylus_dev = NULL; + +err_stylus_device: + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + +err_register_input: +err_query_device: + synaptics_rmi4_empty_fn_list(rmi4_data); + input_free_device(rmi4_data->input_dev); + +err_input_device: + return retval; +} + +static int synaptics_rmi4_set_gpio(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + retval = synaptics_rmi4_gpio_setup( + bdata->irq_gpio, + true, 0, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure attention GPIO\n", + __func__); + goto err_gpio_irq; + } + + if (bdata->power_gpio >= 0) { + retval = synaptics_rmi4_gpio_setup( + bdata->power_gpio, + true, 1, !bdata->power_on_state); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure power GPIO\n", + __func__); + goto err_gpio_power; + } + } + + if (bdata->reset_gpio >= 0) { + retval = synaptics_rmi4_gpio_setup( + bdata->reset_gpio, + true, 1, !bdata->reset_on_state); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure reset GPIO\n", + __func__); + goto err_gpio_reset; + } + } + + if (bdata->power_gpio >= 0) { + gpio_set_value(bdata->power_gpio, bdata->power_on_state); + msleep(bdata->power_delay_ms); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + return 0; + +err_gpio_reset: + if (bdata->power_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0); + +err_gpio_power: + synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0); + +err_gpio_irq: + return retval; +} + +static int synaptics_dsx_pinctrl_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + rmi4_data->ts_pinctrl = devm_pinctrl_get((rmi4_data->pdev->dev.parent)); + if (IS_ERR_OR_NULL(rmi4_data->ts_pinctrl)) { + retval = PTR_ERR(rmi4_data->ts_pinctrl); + dev_err(rmi4_data->pdev->dev.parent, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + rmi4_data->pinctrl_state_active + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_active)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_active); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + rmi4_data->pinctrl_state_suspend + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_suspend)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_suspend); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + rmi4_data->pinctrl_state_release + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_release); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(rmi4_data->ts_pinctrl); +err_pinctrl_get: + rmi4_data->ts_pinctrl = NULL; + return retval; +} + +static int synaptics_rmi4_regulator_configure(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + + if (enable) { + retval = regulator_set_load(rmi4_data->pwr_reg, + 20000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator load avdd\n", + __func__); + return retval; + } + + retval = regulator_set_voltage(rmi4_data->pwr_reg, + 3296000, + 3304000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator voltage avdd\n", + __func__); + goto err_avdd_load; + } + + retval = regulator_set_load(rmi4_data->bus_reg, + 62000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator load vdd\n", + __func__); + goto err_avdd_load; + } + + retval = regulator_set_voltage(rmi4_data->bus_reg, + 1800000, + 1800000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator voltage vdd\n", + __func__); + goto err_vdd_load; + } + + } else { + retval = regulator_set_load(rmi4_data->pwr_reg, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set inactive regulator load avdd\n", + __func__); + return retval; + } + + retval = regulator_set_load(rmi4_data->bus_reg, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set inactive regulator load vdd\n", + __func__); + return retval; + } + + } + return retval; + +err_vdd_load: + regulator_set_load(rmi4_data->bus_reg, 0); +err_avdd_load: + regulator_set_load(rmi4_data->pwr_reg, 0); + return retval; +} + +static int synaptics_rmi4_get_reg(struct synaptics_rmi4_data *rmi4_data, + bool get) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { + rmi4_data->pwr_reg = regulator_get(rmi4_data->pdev->dev.parent, + bdata->pwr_reg_name); + if (IS_ERR(rmi4_data->pwr_reg)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get power regulator\n", + __func__); + retval = PTR_ERR(rmi4_data->pwr_reg); + goto regulator_put; + } + } + + retval = regulator_set_load(rmi4_data->pwr_reg, + 20000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator current avdd\n", + __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(rmi4_data->pwr_reg, + 3296000, + 3304000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator voltage avdd\n", + __func__); + goto regulator_put; + } + + if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { + rmi4_data->bus_reg = regulator_get(rmi4_data->pdev->dev.parent, + bdata->bus_reg_name); + if (IS_ERR(rmi4_data->bus_reg)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get bus pullup regulator\n", + __func__); + retval = PTR_ERR(rmi4_data->bus_reg); + goto regulator_put; + } + } + + retval = regulator_set_load(rmi4_data->bus_reg, + 62000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator current vdd\n", + __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(rmi4_data->bus_reg, + 1800000, + 1800000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator voltage vdd\n", + __func__); + goto regulator_put; + } + + return 0; + +regulator_put: + if (rmi4_data->pwr_reg) { + regulator_put(rmi4_data->pwr_reg); + rmi4_data->pwr_reg = NULL; + } + + if (rmi4_data->bus_reg) { + regulator_put(rmi4_data->bus_reg); + rmi4_data->bus_reg = NULL; + } + + return retval; +} + +static int synaptics_rmi4_enable_reg(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (rmi4_data->bus_reg && rmi4_data->vdd_status == 0) { + retval = regulator_enable(rmi4_data->bus_reg); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable bus pullup regulator\n", + __func__); + goto exit; + } + rmi4_data->vdd_status = 1; + } + + if (rmi4_data->pwr_reg && rmi4_data->avdd_status == 0) { + retval = regulator_enable(rmi4_data->pwr_reg); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable power regulator\n", + __func__); + goto disable_bus_reg; + } + rmi4_data->avdd_status = 1; + msleep(bdata->power_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (rmi4_data->pwr_reg && rmi4_data->avdd_status == 1) { + regulator_disable(rmi4_data->pwr_reg); + rmi4_data->avdd_status = 0; + } + +disable_bus_reg: + if (rmi4_data->bus_reg && rmi4_data->vdd_status == 1) { + regulator_disable(rmi4_data->bus_reg); + rmi4_data->vdd_status = 0; + } + +exit: + synaptics_rmi4_regulator_configure(rmi4_data, false); + return retval; +} + +static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char ii; + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + +#ifdef TYPE_B_PROTOCOL + for (ii = 0; ii < rmi4_data->num_of_fingers; ii++) { + input_mt_slot(rmi4_data->input_dev, ii); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); + } +#endif + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + + if (rmi4_data->stylus_enable) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_PEN, 0); + if (rmi4_data->eraser_enable) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_RUBBER, 0); + } + input_sync(rmi4_data->stylus_dev); + } + + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + rmi4_data->fingers_on_2d = false; + + return 0; +} + +static int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char command = 0x01; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) + return retval; + + msleep(rmi4_data->hw_if->board_data->reset_delay_ms); + + if (rmi4_data->hw_if->ui_hw_init) { + retval = rmi4_data->hw_if->ui_hw_init(rmi4_data); + if (retval < 0) + return retval; + } + + return 0; +} + +static int synaptics_rmi4_do_rebuild(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_input_settings *settings; + + settings = &(rmi4_data->input_settings); + + if (settings->num_of_fingers != rmi4_data->num_of_fingers) + return 1; + + if (settings->valid_button_count != rmi4_data->valid_button_count) + return 1; + + if (settings->max_touch_width != rmi4_data->max_touch_width) + return 1; + + if (settings->sensor_max_x != rmi4_data->sensor_max_x) + return 1; + + if (settings->sensor_max_y != rmi4_data->sensor_max_y) + return 1; + + if (settings->force_min != rmi4_data->force_min) + return 1; + + if (settings->force_max != rmi4_data->force_max) + return 1; + + if (settings->stylus_enable != rmi4_data->stylus_enable) + return 1; + + if (settings->eraser_enable != rmi4_data->eraser_enable) + return 1; + + return 0; +} + +static void synaptics_rmi4_rebuild_work(struct work_struct *work) +{ + int retval; + unsigned char attr_count; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct synaptics_rmi4_data *rmi4_data = + container_of(delayed_work, struct synaptics_rmi4_data, + rb_work); + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + mutex_lock(&exp_data.mutex); + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->remove != NULL) + exp_fhandler->exp_fn->remove(rmi4_data); + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + synaptics_rmi4_free_fingers(rmi4_data); + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + if (rmi4_data->stylus_enable) { + input_unregister_device(rmi4_data->stylus_dev); + rmi4_data->stylus_dev = NULL; + } + + retval = synaptics_rmi4_set_input_dev(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up input device\n", + __func__); + goto exit; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit; + } + } + + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->init != NULL) + exp_fhandler->exp_fn->init(rmi4_data); + } + +exit: + synaptics_rmi4_irq_enable(rmi4_data, true, false); + + mutex_unlock(&exp_data.mutex); + + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); +} + +static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + synaptics_rmi4_free_fingers(rmi4_data); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F12) { + synaptics_rmi4_f12_set_enables(rmi4_data, 0); + break; + } + } + } + + retval = synaptics_rmi4_int_enable(rmi4_data, true); + if (retval < 0) + goto exit; + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->reinit != NULL) + exp_fhandler->exp_fn->reinit(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + synaptics_rmi4_set_configured(rmi4_data); + + retval = 0; + +exit: + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + return retval; +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data, + bool rebuild) +{ + int retval; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + + retval = synaptics_rmi4_sw_reset(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + goto exit; + } + + synaptics_rmi4_free_fingers(rmi4_data); + + synaptics_rmi4_empty_fn_list(rmi4_data); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to query device\n", + __func__); + goto exit; + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->reset != NULL) + exp_fhandler->exp_fn->reset(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + retval = 0; + +exit: + synaptics_rmi4_irq_enable(rmi4_data, true, false); + + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + + if (rebuild && synaptics_rmi4_do_rebuild(rmi4_data)) { + queue_delayed_work(rmi4_data->rb_workqueue, + &rmi4_data->rb_work, + msecs_to_jiffies(REBUILD_WORK_DELAY_MS)); + } + + return retval; +} + +#ifdef FB_READY_RESET +static void synaptics_rmi4_reset_work(struct work_struct *work) +{ + int retval = 0; + unsigned int timeout; + struct synaptics_rmi4_data *rmi4_data = + container_of(work, struct synaptics_rmi4_data, + reset_work); + + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1; + + while (!rmi4_data->fb_ready) { + msleep(FB_READY_WAIT_MS); + timeout--; + if (timeout == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for FB ready\n", + __func__); + goto err; + } + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + } + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); +err: + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for FB ready\n", + __func__); + +} +#endif + +static int synaptics_rmi4_sleep_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + unsigned char device_ctrl; + unsigned char no_sleep_setting = rmi4_data->no_sleep_setting; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device control\n", + __func__); + return retval; + } + + device_ctrl = device_ctrl & ~MASK_3BIT; + if (enable) + device_ctrl = device_ctrl | SENSOR_SLEEP; + else + device_ctrl = device_ctrl | no_sleep_setting | NORMAL_OPERATION; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write device control\n", + __func__); + return retval; + } + + rmi4_data->sensor_sleep = enable; + + return retval; +} + +static void synaptics_rmi4_exp_fn_work(struct work_struct *work) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler_temp; + struct synaptics_rmi4_data *rmi4_data = exp_data.rmi4_data; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + mutex_lock(&rmi4_data->rmi4_reset_mutex); + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry_safe(exp_fhandler, + exp_fhandler_temp, + &exp_data.list, + link) { + if ((exp_fhandler->exp_fn->init != NULL) && + exp_fhandler->insert) { + exp_fhandler->exp_fn->init(rmi4_data); + exp_fhandler->insert = false; + } else if ((exp_fhandler->exp_fn->remove != NULL) && + exp_fhandler->remove) { + exp_fhandler->exp_fn->remove(rmi4_data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + } + } + mutex_unlock(&exp_data.mutex); + mutex_unlock(&rmi4_data->rmi4_reset_mutex); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); +} + +void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn, + bool insert) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + + if (!exp_data.initialized) { + mutex_init(&exp_data.mutex); + INIT_LIST_HEAD(&exp_data.list); + exp_data.initialized = true; + } + + mutex_lock(&exp_data.mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("%s: Failed to alloc mem for expansion function\n", + __func__); + goto exit; + } + exp_fhandler->exp_fn = exp_fn; + exp_fhandler->insert = true; + exp_fhandler->remove = false; + list_add_tail(&exp_fhandler->link, &exp_data.list); + } else if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) { + if (exp_fhandler->exp_fn->fn_type == exp_fn->fn_type) { + exp_fhandler->insert = false; + exp_fhandler->remove = true; + goto exit; + } + } + } + +exit: + mutex_unlock(&exp_data.mutex); + + if (exp_data.queue_work) { + queue_delayed_work(exp_data.workqueue, + &exp_data.work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + } +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + +#ifdef CONFIG_DRM +static void synaptics_register_for_panel_events( + struct synaptics_rmi4_data *rmi4_data, struct device *dev) +{ + void *cookie; + + cookie = panel_event_notifier_register( + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel, + &synaptics_rmi4_dsi_panel_notifier_cb, + &rmi4_data->fb_notifier); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + pr_debug("Registered for panel notifications on panel: 0x%x\n", + active_panel); + rmi4_data->notifier_cookie = cookie; + +} +#endif + +static int synaptics_rmi4_probe(struct platform_device *pdev) +{ + int retval = 0; + struct synaptics_rmi4_data *rmi4_data; + const struct synaptics_dsx_hw_interface *hw_if; + const struct synaptics_dsx_board_data *bdata; + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + dev_err(&pdev->dev, + "%s: No hardware interface found\n", + __func__); + return -EINVAL; + } + + bdata = hw_if->board_data; + if (!bdata) { + dev_err(&pdev->dev, + "%s: No board data found\n", + __func__); + return -EINVAL; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&pdev->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + + rmi4_data->pdev = pdev; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->hw_if = hw_if; + rmi4_data->suspend = false; + rmi4_data->irq_enabled = false; + rmi4_data->fingers_on_2d = false; + + rmi4_data->reset_device = synaptics_rmi4_reset_device; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->sleep_enable = synaptics_rmi4_sleep_enable; + rmi4_data->report_touch = synaptics_rmi4_report_touch; + + mutex_init(&(rmi4_data->rmi4_reset_mutex)); + mutex_init(&(rmi4_data->rmi4_report_mutex)); + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->rmi4_exp_init_mutex)); + mutex_init(&(rmi4_data->rmi4_irq_enable_mutex)); + + platform_set_drvdata(pdev, rmi4_data); + + vir_button_map = bdata->vir_button_map; + + rmi4_data->initialized = false; + +#ifdef CONFIG_DRM + if (active_panel) { + pr_debug("panel detected, registering notifier\n"); + synaptics_register_for_panel_events(rmi4_data, + &pdev->dev); + } else { + pr_debug("panel not detected, freeing data\n"); + retval = -1; + goto err_drm_reg; + } +#endif + rmi4_data->rmi4_probe_wq = create_singlethread_workqueue( + "Synaptics_rmi4_probe_wq"); + if (!rmi4_data->rmi4_probe_wq) { + dev_err(&pdev->dev, + "%s: Failed to create probe workqueue\n", + __func__); + goto err_probe_wq; + } + INIT_WORK(&rmi4_data->rmi4_probe_work, synaptics_rmi4_defer_probe); + queue_work(rmi4_data->rmi4_probe_wq, &rmi4_data->rmi4_probe_work); + + return retval; + +err_probe_wq: +#ifdef CONFIG_DRM + if (active_panel && rmi4_data->notifier_cookie) { + pr_debug("unregistering panel notifications\n"); + panel_event_notifier_unregister( + &rmi4_data->notifier_cookie); + } +#endif +err_drm_reg: + kfree(rmi4_data); + + return retval; +} + +static void synaptics_rmi4_defer_probe(struct work_struct *work) +{ + int retval; + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data = container_of(work, + struct synaptics_rmi4_data, rmi4_probe_work); + struct platform_device *pdev; + const struct synaptics_dsx_hw_interface *hw_if; + const struct synaptics_dsx_board_data *bdata; + + pdev = rmi4_data->pdev; + hw_if = rmi4_data->hw_if; + bdata = hw_if->board_data; + + init_completion(&rmi4_data->drm_init_done); + retval = wait_for_completion_interruptible(&rmi4_data->drm_init_done); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Wait for DRM init was interrupted\n", + __func__); + goto err_drm_init_wait; + } + + retval = synaptics_rmi4_get_reg(rmi4_data, true); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to get regulators\n", + __func__); + goto err_get_reg; + } + + retval = synaptics_rmi4_enable_reg(rmi4_data, true); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to enable regulators\n", + __func__); + goto err_enable_reg; + } + + retval = synaptics_rmi4_set_gpio(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up GPIO's\n", + __func__); + goto err_set_gpio; + } + + retval = synaptics_dsx_pinctrl_init(rmi4_data); + if (!retval && rmi4_data->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(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + + if (hw_if->ui_hw_init) { + retval = hw_if->ui_hw_init(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to initialize hardware interface\n", + __func__); + goto err_ui_hw_init; + } + } + + retval = synaptics_rmi4_set_input_dev(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up input device\n", + __func__); + goto err_set_input_dev; + } + +#ifdef USE_EARLYSUSPEND + rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend; + rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume; + register_early_suspend(&rmi4_data->early_suspend); +#endif + + if (!exp_data.initialized) { + mutex_init(&exp_data.mutex); + INIT_LIST_HEAD(&exp_data.list); + exp_data.initialized = true; + } + + rmi4_data->irq = gpio_to_irq(bdata->irq_gpio); + + retval = synaptics_rmi4_irq_enable(rmi4_data, true, false); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to enable attention interrupt\n", + __func__); + goto err_enable_irq; + } + + if (vir_button_map->nbuttons) { + rmi4_data->board_prop_dir = kobject_create_and_add( + "board_properties", NULL); + if (!rmi4_data->board_prop_dir) { + dev_err(&pdev->dev, + "%s: Failed to create board_properties directory\n", + __func__); + goto err_virtual_buttons; + } else { + retval = sysfs_create_file(rmi4_data->board_prop_dir, + &virtual_key_map_attr.attr); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to create virtual key map file\n", + __func__); + goto err_virtual_buttons; + } + } + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + +#ifdef USE_DATA_SERVER + memset(&interrupt_signal, 0, sizeof(interrupt_signal)); + interrupt_signal.si_signo = SIGIO; + interrupt_signal.si_code = SI_USER; +#endif + + rmi4_data->rb_workqueue = + create_singlethread_workqueue("dsx_rebuild_workqueue"); + INIT_DELAYED_WORK(&rmi4_data->rb_work, synaptics_rmi4_rebuild_work); + + exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue"); + INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work); + exp_data.rmi4_data = rmi4_data; + exp_data.queue_work = true; + queue_delayed_work(exp_data.workqueue, + &exp_data.work, + 0); + +#ifdef FB_READY_RESET + rmi4_data->reset_workqueue = + create_singlethread_workqueue("dsx_reset_workqueue"); + INIT_WORK(&rmi4_data->reset_work, synaptics_rmi4_reset_work); + queue_work(rmi4_data->reset_workqueue, &rmi4_data->reset_work); +#endif + rmi4_data->initialized = true; + + return; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +err_virtual_buttons: + if (rmi4_data->board_prop_dir) { + sysfs_remove_file(rmi4_data->board_prop_dir, + &virtual_key_map_attr.attr); + kobject_put(rmi4_data->board_prop_dir); + } + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + +err_enable_irq: +#ifdef USE_EARLYSUSPEND + unregister_early_suspend(&rmi4_data->early_suspend); +#endif + + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + if (rmi4_data->stylus_enable) { + input_unregister_device(rmi4_data->stylus_dev); + rmi4_data->stylus_dev = NULL; + } + +err_set_input_dev: + synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->reset_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0); + +err_ui_hw_init: +err_set_gpio: + synaptics_rmi4_enable_reg(rmi4_data, false); + synaptics_rmi4_regulator_configure(rmi4_data, false); + + if (rmi4_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + devm_pinctrl_put(rmi4_data->ts_pinctrl); + rmi4_data->ts_pinctrl = NULL; + } else { + if (pinctrl_select_state( + rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_release)) + dev_err(&pdev->dev, + "%s: Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + } + } + +err_enable_reg: + synaptics_rmi4_get_reg(rmi4_data, false); + +err_get_reg: +err_drm_init_wait: +#ifdef CONFIG_DRM + if (active_panel && rmi4_data->notifier_cookie) { + pr_debug("unregistering panel_events\n"); + panel_event_notifier_unregister(&rmi4_data->notifier_cookie); + } +#endif + cancel_work_sync(&rmi4_data->rmi4_probe_work); + destroy_workqueue(rmi4_data->rmi4_probe_wq); + kfree(rmi4_data); +} + +static int synaptics_rmi4_remove(struct platform_device *pdev) +{ + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + +#ifdef FB_READY_RESET + cancel_work_sync(&rmi4_data->reset_work); + flush_workqueue(rmi4_data->reset_workqueue); + destroy_workqueue(rmi4_data->reset_workqueue); +#endif + + cancel_delayed_work_sync(&exp_data.work); + flush_workqueue(exp_data.workqueue); + destroy_workqueue(exp_data.workqueue); + + cancel_delayed_work_sync(&rmi4_data->rb_work); + flush_workqueue(rmi4_data->rb_workqueue); + destroy_workqueue(rmi4_data->rb_workqueue); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + if (rmi4_data->board_prop_dir) { + sysfs_remove_file(rmi4_data->board_prop_dir, + &virtual_key_map_attr.attr); + kobject_put(rmi4_data->board_prop_dir); + } + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + +#ifdef CONFIG_DRM + if (active_panel) { + pr_debug("unregistering panel_events\n"); + panel_event_notifier_unregister(&rmi4_data->notifier_cookie); + } +#endif + +#ifdef USE_EARLYSUSPEND + unregister_early_suspend(&rmi4_data->early_suspend); +#endif + + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + if (rmi4_data->stylus_enable) { + input_unregister_device(rmi4_data->stylus_dev); + rmi4_data->stylus_dev = NULL; + } + + synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->reset_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0); + + if (rmi4_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + devm_pinctrl_put(rmi4_data->ts_pinctrl); + rmi4_data->ts_pinctrl = NULL; + } else { + pinctrl_select_state( + rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_release); + } + } + + synaptics_rmi4_enable_reg(rmi4_data, false); + synaptics_rmi4_regulator_configure(rmi4_data, false); + synaptics_rmi4_get_reg(rmi4_data, false); + + cancel_work_sync(&rmi4_data->rmi4_probe_work); + destroy_workqueue(rmi4_data->rmi4_probe_wq); + + kfree(rmi4_data); + + return 0; +} + +#ifdef CONFIG_DRM +static void synaptics_rmi4_dsi_panel_notifier_cb( + enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *client_data) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(client_data, struct synaptics_rmi4_data, + fb_notifier); + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + pr_debug("Notification type: %d, early trigger:%d\n", + notification->notif_type, + notification->notif_data.early_trigger); + + if (!client_data) { + pr_err("No client data\n"); + return; + } + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (rmi4_data->initialized) { + synaptics_rmi4_regulator_configure(rmi4_data, true); + synaptics_rmi4_resume(&rmi4_data->pdev->dev); + } else { + complete(&rmi4_data->drm_init_done); + } + rmi4_data->fb_ready = true; + break; + case DRM_PANEL_EVENT_BLANK: + if (rmi4_data->initialized) { + synaptics_rmi4_suspend(&rmi4_data->pdev->dev); + synaptics_rmi4_regulator_configure(rmi4_data, false); + } + rmi4_data->fb_ready = false; + break; + case DRM_PANEL_EVENT_BLANK_LP: + pr_debug("received lp event\n"); + break; + case DRM_PANEL_EVENT_FPS_CHANGE: + pr_debug("received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + pr_debug("notification serviced :%d\n", + notification->notif_type); + break; + } + +} +#endif + +#ifdef USE_EARLYSUSPEND +static int synaptics_rmi4_early_suspend(struct early_suspend *h) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + unsigned char device_ctrl; + + if (rmi4_data->stay_awake) + return retval; + + if (rmi4_data->enable_wakeup_gesture) { + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + enable_irq_wake(rmi4_data->irq); + goto exit; + } + +#ifdef SYNA_TDDI + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + usleep(TDDI_LPWG_WAIT_US); +#endif + synaptics_rmi4_irq_enable(rmi4_data, false, false); + synaptics_rmi4_sleep_enable(rmi4_data, true); + synaptics_rmi4_free_fingers(rmi4_data); + +exit: + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->early_suspend != NULL) + exp_fhandler->exp_fn->early_suspend(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = true; + + return retval; +} + +static int synaptics_rmi4_late_resume(struct early_suspend *h) +{ +#ifdef FB_READY_RESET + int retval; +#endif + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + + if (rmi4_data->stay_awake) + return retval; + + if (rmi4_data->enable_wakeup_gesture) { + disable_irq_wake(rmi4_data->irq); + goto exit; + } + + rmi4_data->current_page = MASK_8BIT; + + if (rmi4_data->suspend) { + synaptics_rmi4_sleep_enable(rmi4_data, false); + synaptics_rmi4_irq_enable(rmi4_data, true, false); + } + +exit: +#ifdef FB_READY_RESET + if (rmi4_data->suspend) { + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + } + } +#endif + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->late_resume != NULL) + exp_fhandler->exp_fn->late_resume(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = false; + + return retval; +} +#endif + +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + unsigned char device_ctrl; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (rmi4_data->stay_awake) + return 0; + + if (rmi4_data->enable_wakeup_gesture) { + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + enable_irq_wake(rmi4_data->irq); + goto exit; + } + + if (!rmi4_data->suspend) { +#ifdef SYNA_TDDI + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + usleep(TDDI_LPWG_WAIT_US); +#endif + synaptics_rmi4_irq_enable(rmi4_data, false, false); + synaptics_rmi4_sleep_enable(rmi4_data, true); + synaptics_rmi4_free_fingers(rmi4_data); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + } + + synaptics_rmi4_enable_reg(rmi4_data, false); + +exit: + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->suspend != NULL) + exp_fhandler->exp_fn->suspend(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = true; + + return 0; +} + +static int synaptics_rmi4_resume(struct device *dev) +{ +#ifdef FB_READY_RESET + int retval; +#endif + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + if (rmi4_data->stay_awake) + return 0; + + if (rmi4_data->enable_wakeup_gesture) { + disable_irq_wake(rmi4_data->irq); + synaptics_rmi4_wakeup_gesture(rmi4_data, false); + goto exit; + } + + synaptics_rmi4_enable_reg(rmi4_data, true); + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + + rmi4_data->current_page = MASK_8BIT; + + synaptics_rmi4_sleep_enable(rmi4_data, false); + synaptics_rmi4_irq_enable(rmi4_data, true, false); + +exit: +#ifdef FB_READY_RESET + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + } +#endif + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->resume != NULL) + exp_fhandler->exp_fn->resume(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = false; + + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static struct platform_driver synaptics_rmi4_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, +}; + +static int __init synaptics_rmi4_init(void) +{ + int retval; + + retval = synaptics_rmi4_bus_init(); + if (retval) + return retval; + + return platform_driver_register(&synaptics_rmi4_driver); +} + +static void __exit synaptics_rmi4_exit(void) +{ + platform_driver_unregister(&synaptics_rmi4_driver); + + synaptics_rmi4_bus_exit(); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Touch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_core.h b/synaptics_dsx/synaptics_dsx_core.h new file mode 100644 index 0000000000..409227d6fe --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_core.h @@ -0,0 +1,536 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#ifndef _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DS4 (1 << 0) +#define SYNAPTICS_DS5 (1 << 1) +#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5) +#define SYNAPTICS_DSX_DRIVER_VERSION 0x2070 + +#include +#ifdef CONFIG_FB +#include +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)) +#define KERNEL_ABOVE_3_6 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#define PDT_PROPS (0X00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x00D0) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define ADDRESS_LEN (2) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1A) +#define SYNAPTICS_RMI4_F21 (0x21) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F35 (0x35) +#define SYNAPTICS_RMI4_F38 (0x38) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) +#define SYNAPTICS_RMI4_FDB (0xDB) + +#define PRODUCT_INFO_SIZE 2 +#define PRODUCT_ID_SIZE 10 +#define BUILD_ID_SIZE 3 + +#define F12_FINGERS_TO_SUPPORT 10 +#define F12_NO_OBJECT_STATUS 0x00 +#define F12_FINGER_STATUS 0x01 +#define F12_ACTIVE_STYLUS_STATUS 0x02 +#define F12_PALM_STATUS 0x03 +#define F12_HOVERING_FINGER_STATUS 0x05 +#define F12_GLOVED_FINGER_STATUS 0x06 +#define F12_NARROW_OBJECT_STATUS 0x07 +#define F12_HAND_EDGE_STATUS 0x08 +#define F12_COVER_STATUS 0x0A +#define F12_STYLUS_STATUS 0x0B +#define F12_ERASER_STATUS 0x0C +#define F12_SMALL_OBJECT_STATUS 0x0D + +#define F12_GESTURE_DETECTION_LEN 5 + +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +enum exp_fn { + RMI_DEV = 0, + RMI_FW_UPDATER, + RMI_TEST_REPORTING, + RMI_PROXIMITY, + RMI_ACTIVE_PEN, + RMI_GESTURE, + RMI_VIDEO, + RMI_DEBUG, + RMI_LAST, +}; + +extern struct drm_panel *active_panel; + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT entry + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_version: version of function + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + union { + struct { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; + } __packed; + unsigned char data[6]; + }; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for command registers + * @ctrl_base: 16-bit base address for control registers + * @data_base: 16-bit base address for data registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_f11_extra_data - extra data of F$11 + * @data38_offset: offset to F11_2D_DATA38 register + */ +struct synaptics_rmi4_f11_extra_data { + unsigned char data38_offset; +}; + +/* + * struct synaptics_rmi4_f12_extra_data - extra data of F$12 + * @data1_offset: offset to F12_2D_DATA01 register + * @data4_offset: offset to F12_2D_DATA04 register + * @data15_offset: offset to F12_2D_DATA15 register + * @data15_size: size of F12_2D_DATA15 register + * @data15_data: buffer for reading F12_2D_DATA15 register + * @data29_offset: offset to F12_2D_DATA29 register + * @data29_size: size of F12_2D_DATA29 register + * @data29_data: buffer for reading F12_2D_DATA29 register + * @ctrl20_offset: offset to F12_2D_CTRL20 register + */ +struct synaptics_rmi4_f12_extra_data { + unsigned char data1_offset; + unsigned char data4_offset; + unsigned char data15_offset; + unsigned char data15_size; + unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8]; + unsigned char data29_offset; + unsigned char data29_size; + unsigned char data29_data[F12_FINGERS_TO_SUPPORT * 2]; + unsigned char ctrl20_offset; +}; + +/* + * struct synaptics_rmi4_fn - RMI function handler + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + * @extra: pointer to extra data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; + void *extra; +}; + +/* + * struct synaptics_rmi4_input_settings - current input settings + * @num_of_fingers: maximum number of fingers for 2D touch + * @valid_button_count: number of valid 0D buttons + * @max_touch_width: maximum touch width + * @sensor_max_x: maximum x coordinate for 2D touch + * @sensor_max_y: maximum y coordinate for 2D touch + * @force_min: minimum force value + * @force_max: maximum force value + * @stylus_enable: flag to indicate reporting of stylus data + * @eraser_enable: flag to indicate reporting of eraser data + */ +struct synaptics_rmi4_input_settings { + unsigned char num_of_fingers; + unsigned char valid_button_count; + unsigned char max_touch_width; + int sensor_max_x; + int sensor_max_y; + int force_min; + int force_max; + bool stylus_enable; + bool eraser_enable; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: RMI protocol major version number + * @version_minor: RMI protocol minor version number + * @manufacturer_id: manufacturer ID + * @product_props: product properties + * @product_info: product information + * @product_id_string: product ID + * @build_id: firmware build ID + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[PRODUCT_INFO_SIZE]; + unsigned char product_id_string[PRODUCT_ID_SIZE + 1]; + unsigned char build_id[BUILD_ID_SIZE]; + struct list_head support_fn_list; +}; + +/* + * struct synaptics_rmi4_data - RMI4 device instance data + * @pdev: pointer to platform device + * @input_dev: pointer to associated input device + * @stylus_dev: pointer to associated stylus device + * @hw_if: pointer to hardware interface data + * @rmi4_mod_info: device information + * @board_prop_dir: /sys/board_properties directory for virtual key map file + * @pwr_reg: pointer to regulator for power control + * @bus_reg: pointer to regulator for bus pullup control + * @rmi4_reset_mutex: mutex for software reset + * @rmi4_report_mutex: mutex for input event reporting + * @rmi4_io_ctrl_mutex: mutex for communication interface I/O + * @rmi4_exp_init_mutex: mutex for expansion function module initialization + * @rmi4_irq_enable_mutex: mutex for enabling/disabling interrupt + * @rb_work: work for rebuilding input device + * @rb_workqueue: workqueue for rebuilding input device + * @fb_notifier: framebuffer notifier client + * @reset_work: work for issuing reset after display framebuffer ready + * @reset_workqueue: workqueue for issuing reset after display framebuffer ready + * @early_suspend: early suspend power management + * @current_page: current RMI page for register access + * @button_0d_enabled: switch for enabling 0d button support + * @num_of_tx: number of Tx channels for 2D touch + * @num_of_rx: number of Rx channels for 2D touch + * @num_of_fingers: maximum number of fingers for 2D touch + * @max_touch_width: maximum touch width + * @valid_button_count: number of valid 0D buttons + * @report_enable: input data to report for F$12 + * @no_sleep_setting: default setting of NoSleep in F01_RMI_CTRL00 register + * @gesture_detection: detected gesture type and properties + * @intr_mask: interrupt enable mask + * @button_txrx_mapping: Tx Rx mapping of 0D buttons + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f$01 + * @f01_cmd_base_addr: command base address for f$01 + * @f01_ctrl_base_addr: control base address for f$01 + * @f01_data_base_addr: data base address for f$01 + * @f51_query_base_addr: query base address for f$51 + * @firmware_id: firmware build ID + * @irq: attention interrupt + * @sensor_max_x: maximum x coordinate for 2D touch + * @sensor_max_y: maximum y coordinate for 2D touch + * @force_min: minimum force value + * @force_max: maximum force value + * @set_wakeup_gesture: location of set wakeup gesture + * @flash_prog_mode: flag to indicate flash programming mode status + * @irq_enabled: flag to indicate attention interrupt enable status + * @fingers_on_2d: flag to indicate presence of fingers in 2D area + * @suspend: flag to indicate whether in suspend state + * @sensor_sleep: flag to indicate sleep state of sensor + * @stay_awake: flag to indicate whether to stay awake during suspend + * @fb_ready: flag to indicate whether display framebuffer in ready state + * @f11_wakeup_gesture: flag to indicate support for wakeup gestures in F$11 + * @f12_wakeup_gesture: flag to indicate support for wakeup gestures in F$12 + * @enable_wakeup_gesture: flag to indicate usage of wakeup gestures + * @wedge_sensor: flag to indicate use of wedge sensor + * @report_pressure: flag to indicate reporting of pressure data + * @stylus_enable: flag to indicate reporting of stylus data + * @eraser_enable: flag to indicate reporting of eraser data + * @external_afe_buttons: flag to indicate presence of external AFE buttons + * @notifier_cookie: touch notifier for panel events + * @reset_device: pointer to device reset function + * @irq_enable: pointer to interrupt enable function + * @sleep_enable: pointer to sleep enable function + * @report_touch: pointer to touch reporting function + */ +struct synaptics_rmi4_data { + bool initialized; + struct platform_device *pdev; + struct input_dev *input_dev; + struct input_dev *stylus_dev; + const struct synaptics_dsx_hw_interface *hw_if; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct synaptics_rmi4_input_settings input_settings; + struct kobject *board_prop_dir; + struct regulator *pwr_reg; + struct regulator *bus_reg; + struct mutex rmi4_reset_mutex; + struct mutex rmi4_report_mutex; + struct mutex rmi4_io_ctrl_mutex; + struct mutex rmi4_exp_init_mutex; + struct mutex rmi4_irq_enable_mutex; + struct delayed_work rb_work; + struct workqueue_struct *rb_workqueue; + struct work_struct rmi4_probe_work; + struct workqueue_struct *rmi4_probe_wq; + struct completion drm_init_done; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + struct notifier_block fb_notifier; + struct work_struct reset_work; + struct workqueue_struct *reset_workqueue; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char num_of_tx; + unsigned char num_of_rx; + unsigned char num_of_fingers; + unsigned char max_touch_width; + unsigned char valid_button_count; + unsigned char report_enable; + unsigned char no_sleep_setting; + unsigned char gesture_detection[F12_GESTURE_DETECTION_LEN]; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned char *button_txrx_mapping; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; +#ifdef F51_DISCRETE_FORCE + unsigned short f51_query_base_addr; +#endif + unsigned int firmware_id; + int irq; + int sensor_max_x; + int sensor_max_y; + int force_min; + int force_max; + int set_wakeup_gesture; + int avdd_status; + int vdd_status; + bool flash_prog_mode; + bool irq_enabled; + bool fingers_on_2d; + bool suspend; + bool sensor_sleep; + bool stay_awake; + bool fb_ready; + bool f11_wakeup_gesture; + bool f12_wakeup_gesture; + bool enable_wakeup_gesture; + bool wedge_sensor; + bool report_pressure; + bool stylus_enable; + bool eraser_enable; + bool external_afe_buttons; + void *notifier_cookie; + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data, + bool rebuild); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable, + bool attn_only); + int (*sleep_enable)(struct synaptics_rmi4_data *rmi4_data, + bool enable); + void (*report_touch)(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler); +}; + +struct synaptics_dsx_bus_access { + unsigned char type; + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned int length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned int length); +}; + +struct synaptics_dsx_hw_interface { + struct synaptics_dsx_board_data *board_data; + const struct synaptics_dsx_bus_access *bus_access; + int (*bl_hw_init)(struct synaptics_rmi4_data *rmi4_data); + int (*ui_hw_init)(struct synaptics_rmi4_data *rmi4_data); +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + int (*init)(struct synaptics_rmi4_data *rmi4_data); + void (*remove)(struct synaptics_rmi4_data *rmi4_data); + void (*reset)(struct synaptics_rmi4_data *rmi4_data); + void (*reinit)(struct synaptics_rmi4_data *rmi4_data); + void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*resume)(struct synaptics_rmi4_data *rmi4_data); + void (*late_resume)(struct synaptics_rmi4_data *rmi4_data); + void (*attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); +}; + +int synaptics_rmi4_bus_init(void); + +void synaptics_rmi4_bus_exit(void); + +void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module, + bool insert); + +int synaptics_fw_updater(const unsigned char *fw_data); + +static inline int synaptics_rmi4_reg_read( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned int len) +{ + return rmi4_data->hw_if->bus_access->read(rmi4_data, addr, data, len); +} + +static inline int synaptics_rmi4_reg_write( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned int len) +{ + return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len); +} + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + dev_warn(dev, "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + dev_warn(dev, "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) + return -EINVAL; + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +#endif diff --git a/synaptics_dsx/synaptics_dsx_fw_update.c b/synaptics_dsx/synaptics_dsx_fw_update.c new file mode 100644 index 0000000000..52ca0ac86b --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_fw_update.c @@ -0,0 +1,5797 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define FW_IHEX_NAME "synaptics/startup_fw_update.bin" +#define FW_IMAGE_NAME "synaptics/startup_fw_update.img" + +#define ENABLE_SYS_REFLASH false +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define IMAGE_HEADER_VERSION_05 0x05 +#define IMAGE_HEADER_VERSION_06 0x06 +#define IMAGE_HEADER_VERSION_10 0x10 + +#define IMAGE_AREA_OFFSET 0x100 +#define LOCKDOWN_SIZE 0x50 + +#define MAX_UTILITY_PARAMS 20 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 +#define V5V6_CONFIG_ID_SIZE 4 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_NUMBER_OFFSET 0 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_PROPERTIES_2_OFFSET 4 +#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5 +#define V6_BLOCK_NUMBER_OFFSET 0 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define V7_CONFIG_ID_SIZE 32 + +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 + +#define V7_PARTITION_SUPPORT_BYTES 4 + +#define F35_ERROR_CODE_OFFSET 0 +#define F35_FLASH_STATUS_OFFSET 5 +#define F35_CHUNK_NUM_LSB_OFFSET 0 +#define F35_CHUNK_NUM_MSB_OFFSET 1 +#define F35_CHUNK_DATA_OFFSET 2 +#define F35_CHUNK_COMMAND_OFFSET 18 + +#define F35_CHUNK_SIZE 16 +#define F35_ERASE_ALL_WAIT_MS 5000 +#define F35_RESET_WAIT_MS 250 + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define INT_DISABLE_WAIT_MS 20 +#define ENTER_FLASH_PROG_WAIT_MS 20 +#define READ_CONFIG_WAIT_MS 20 + +static int fwu_do_reflash(void); + +static int fwu_recovery_check_status(void); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_utility_parameter_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#ifdef SYNA_TDDI +static ssize_t fwu_sysfs_write_lockdown_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_lockdown_code_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif + +#endif + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, + BL_V8 = 8, +}; + +enum flash_area { + NONE = 0, + UI_FIRMWARE, + UI_CONFIG, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +enum config_area { + UI_CONFIG_AREA = 0, + PM_CONFIG_AREA, + BL_CONFIG_AREA, + DP_CONFIG_AREA, + FLASH_CONFIG_AREA, +#ifdef SYNA_TDDI + TDDI_FORCE_CONFIG_AREA, + TDDI_LCM_DATA_AREA, + TDDI_OEM_DATA_AREA, +#endif + UPP_AREA, +}; + +enum v7_status { + SUCCESS = 0x00, + DEVICE_NOT_IN_BOOTLOADER_MODE, + INVALID_PARTITION, + INVALID_COMMAND, + INVALID_BLOCK_OFFSET, + INVALID_TRANSFER, + NOT_ERASED, + FLASH_PROGRAMMING_KEY_INCORRECT, + BAD_PARTITION_TABLE, + CHECKSUM_FAILED, + FLASH_HARDWARE_FAILURE = 0x1f, +}; + +enum v7_partition_id { + BOOTLOADER_PARTITION = 0x01, + DEVICE_CONFIG_PARTITION, + FLASH_CONFIG_PARTITION, + MANUFACTURING_BLOCK_PARTITION, + GUEST_SERIALIZATION_PARTITION, + GLOBAL_PARAMETERS_PARTITION, + CORE_CODE_PARTITION, + CORE_CONFIG_PARTITION, + GUEST_CODE_PARTITION, + DISPLAY_CONFIG_PARTITION, + EXTERNAL_TOUCH_AFE_CONFIG_PARTITION, + UTILITY_PARAMETER_PARTITION, +}; + +enum v7_flash_command { + CMD_V7_IDLE = 0x00, + CMD_V7_ENTER_BL, + CMD_V7_READ, + CMD_V7_WRITE, + CMD_V7_ERASE, + CMD_V7_ERASE_AP, + CMD_V7_SENSOR_ID, +}; + +enum v5v6_flash_command { + CMD_V5V6_IDLE = 0x0, + CMD_V5V6_WRITE_FW = 0x2, + CMD_V5V6_ERASE_ALL = 0x3, + CMD_V5V6_WRITE_LOCKDOWN = 0x4, + CMD_V5V6_READ_CONFIG = 0x5, + CMD_V5V6_WRITE_CONFIG = 0x6, + CMD_V5V6_ERASE_UI_CONFIG = 0x7, + CMD_V5V6_ERASE_BL_CONFIG = 0x9, + CMD_V5V6_ERASE_DISP_CONFIG = 0xa, + CMD_V5V6_ERASE_GUEST_CODE = 0xb, + CMD_V5V6_WRITE_GUEST_CODE = 0xc, + CMD_V5V6_ERASE_CHIP = 0x0d, + CMD_V5V6_ENABLE_FLASH_PROG = 0xf, +#ifdef SYNA_TDDI + CMD_V5V6_ERASE_FORCE_CONFIG = 0x11, + CMD_V5V6_READ_FORCE_CONFIG = 0x12, + CMD_V5V6_WRITE_FORCE_CONFIG = 0x13, + CMD_V5V6_ERASE_LOCKDOWN_DATA = 0x1a, + CMD_V5V6_READ_LOCKDOWN_DATA = 0x1b, + CMD_V5V6_WRITE_LOCKDOWN_DATA = 0x1c, + CMD_V5V6_ERASE_LCM_DATA = 0x1d, + CMD_V5V6_ERASE_OEM_DATA = 0x1e, +#endif +}; + +enum flash_command { + CMD_IDLE = 0, + CMD_WRITE_FW, + CMD_WRITE_CONFIG, + CMD_WRITE_LOCKDOWN, + CMD_WRITE_GUEST_CODE, + CMD_WRITE_BOOTLOADER, + CMD_WRITE_UTILITY_PARAM, + CMD_READ_CONFIG, + CMD_ERASE_ALL, + CMD_ERASE_UI_FIRMWARE, + CMD_ERASE_UI_CONFIG, + CMD_ERASE_BL_CONFIG, + CMD_ERASE_DISP_CONFIG, + CMD_ERASE_FLASH_CONFIG, + CMD_ERASE_GUEST_CODE, + CMD_ERASE_BOOTLOADER, + CMD_ERASE_UTILITY_PARAMETER, + CMD_ENABLE_FLASH_PROG, +#ifdef SYNA_TDDI + CMD_ERASE_CHIP, + CMD_ERASE_FORCE_CONFIG, + CMD_READ_FORCE_CONFIG, + CMD_WRITE_FORCE_CONFIG, + CMD_ERASE_LOCKDOWN_DATA, + CMD_READ_LOCKDOWN_DATA, + CMD_WRITE_LOCKDOWN_DATA, + CMD_ERASE_LCM_DATA, + CMD_READ_LCM_DATA, + CMD_WRITE_LCM_DATA, + CMD_ERASE_OEM_DATA, + CMD_READ_OEM_DATA, + CMD_WRITE_OEM_DATA, +#endif +}; + +enum f35_flash_command { + CMD_F35_IDLE = 0x0, + CMD_F35_RESERVED = 0x1, + CMD_F35_WRITE_CHUNK = 0x2, + CMD_F35_ERASE_ALL = 0x3, + CMD_F35_RESET = 0x10, +}; + +enum container_id { + TOP_LEVEL_CONTAINER = 0, + UI_CONTAINER, + UI_CONFIG_CONTAINER, + BL_CONTAINER, + BL_IMAGE_CONTAINER, + BL_CONFIG_CONTAINER, + BL_LOCKDOWN_INFO_CONTAINER, + PERMANENT_CONFIG_CONTAINER, + GUEST_CODE_CONTAINER, + BL_PROTOCOL_DESCRIPTOR_CONTAINER, + UI_PROTOCOL_DESCRIPTOR_CONTAINER, + RMI_SELF_DISCOVERY_CONTAINER, + RMI_PAGE_CONTENT_CONTAINER, + GENERAL_INFORMATION_CONTAINER, + DEVICE_CONFIG_CONTAINER, + FLASH_CONFIG_CONTAINER, + GUEST_SERIALIZATION_CONTAINER, + GLOBAL_PARAMETERS_CONTAINER, + CORE_CODE_CONTAINER, + CORE_CONFIG_CONTAINER, + DISPLAY_CONFIG_CONTAINER, + EXTERNAL_TOUCH_AFE_CONFIG_CONTAINER, + UTILITY_CONTAINER, + UTILITY_PARAMETER_CONTAINER, +}; + +enum utility_parameter_id { + UNUSED = 0, + FORCE_PARAMETER, + ANTI_BENDING_PARAMETER, +}; + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct partition_table { + unsigned char partition_id:5; + unsigned char byte_0_reserved:3; + unsigned char byte_1_reserved; + unsigned char partition_length_7_0; + unsigned char partition_length_15_8; + unsigned char start_physical_address_7_0; + unsigned char start_physical_address_15_8; + unsigned char partition_properties_7_0; + unsigned char partition_properties_15_8; +} __packed; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_0 { + union { + struct { + unsigned char subpacket_1_size:3; + unsigned char has_config_id:1; + unsigned char f34_query0_b4:1; + unsigned char has_thqa:1; + unsigned char f34_query0_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_1_7 { + union { + struct { + /* query 1 */ + unsigned char bl_minor_revision; + unsigned char bl_major_revision; + + /* query 2 */ + unsigned char bl_fw_id_7_0; + unsigned char bl_fw_id_15_8; + unsigned char bl_fw_id_23_16; + unsigned char bl_fw_id_31_24; + + /* query 3 */ + unsigned char minimum_write_size; + unsigned char block_size_7_0; + unsigned char block_size_15_8; + unsigned char flash_page_size_7_0; + unsigned char flash_page_size_15_8; + + /* query 4 */ + unsigned char adjustable_partition_area_size_7_0; + unsigned char adjustable_partition_area_size_15_8; + + /* query 5 */ + unsigned char flash_config_length_7_0; + unsigned char flash_config_length_15_8; + + /* query 6 */ + unsigned char payload_length_7_0; + unsigned char payload_length_15_8; + + /* query 7 */ + unsigned char f34_query7_b0:1; + unsigned char has_bootloader:1; + unsigned char has_device_config:1; + unsigned char has_flash_config:1; + unsigned char has_manufacturing_block:1; + unsigned char has_guest_serialization:1; + unsigned char has_global_parameters:1; + unsigned char has_core_code:1; + unsigned char has_core_config:1; + unsigned char has_guest_code:1; + unsigned char has_display_config:1; + unsigned char f34_query7_b11__15:5; + unsigned char f34_query7_b16__23; + unsigned char f34_query7_b24__31; + } __packed; + unsigned char data[21]; + }; +}; + +struct f34_v7_data0 { + union { + struct { + unsigned char operation_status:5; + unsigned char device_cfg_status:2; + unsigned char bl_mode:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_data_1_5 { + union { + struct { + unsigned char partition_id:5; + unsigned char f34_data1_b5__7:3; + unsigned char block_offset_7_0; + unsigned char block_offset_15_8; + unsigned char transfer_length_7_0; + unsigned char transfer_length_15_8; + unsigned char command; + unsigned char payload_0; + unsigned char payload_1; + } __packed; + unsigned char data[8]; + }; +}; + +struct f34_v5v6_flash_properties { + union { + struct { + unsigned char reg_map:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_pm_config:1; + unsigned char has_bl_config:1; + unsigned char has_disp_config:1; + unsigned char has_ctrl1:1; + unsigned char has_query4:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v5v6_flash_properties_2 { + union { + struct { + unsigned char has_guest_code:1; + unsigned char f34_query4_b1:1; + unsigned char has_gesture_config:1; + unsigned char has_force_config:1; + unsigned char has_lockdown_data:1; + unsigned char has_lcm_data:1; + unsigned char has_oem_data:1; + unsigned char f34_query4_b7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct register_offset { + unsigned char properties; + unsigned char properties_2; + unsigned char block_size; + unsigned char block_count; + unsigned char gc_block_count; + unsigned char flash_status; + unsigned char partition_id; + unsigned char block_number; + unsigned char transfer_length; + unsigned char flash_cmd; + unsigned char payload; +}; + +struct block_count { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short pm_config; + unsigned short fl_config; + unsigned short bl_image; + unsigned short bl_config; + unsigned short utility_param; + unsigned short lockdown; + unsigned short guest_code; +#ifdef SYNA_TDDI + unsigned short tddi_force_config; + unsigned short tddi_lockdown_data; + unsigned short tddi_lcm_data; + unsigned short tddi_oem_data; +#endif + unsigned short total_count; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short pm_config; + unsigned short fl_config; + unsigned short bl_image; + unsigned short bl_config; + unsigned short utility_param; + unsigned short lockdown; + unsigned short guest_code; +}; + +struct container_descriptor { + unsigned char content_checksum[4]; + unsigned char container_id[2]; + unsigned char minor_version; + unsigned char major_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char container_option_flags[4]; + unsigned char content_options_length[4]; + unsigned char content_options_address[4]; + unsigned char content_length[4]; + unsigned char content_address[4]; +}; + +struct image_header_10 { + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char minor_header_version; + unsigned char major_header_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char top_level_container_start_addr[4]; +}; + +struct image_header_05_06 { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_bootloader:1; + unsigned char options_guest_code:1; + unsigned char options_tddi:1; + unsigned char options_reserved:4; + unsigned char header_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char bootloader_addr[4]; + unsigned char bootloader_size[4]; + unsigned char ui_addr[4]; + unsigned char ui_size[4]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + union { + struct { + unsigned char cstmr_product_id[PRODUCT_ID_SIZE]; + unsigned char reserved_4a_4f[6]; + }; + struct { + unsigned char dsp_cfg_addr[4]; + unsigned char dsp_cfg_size[4]; + unsigned char reserved_48_4f[8]; + }; + }; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_guest_code; + bool contains_disp_config; + bool contains_perm_config; + bool contains_flash_config; + bool contains_utility_param; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int disp_config_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + unsigned char utility_param_id[MAX_UTILITY_PARAMS]; + struct block_data bootloader; + struct block_data utility; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data pm_config; + struct block_data fl_config; + struct block_data bl_image; + struct block_data bl_config; + struct block_data utility_param[MAX_UTILITY_PARAMS]; + struct block_data lockdown; + struct block_data guest_code; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool bl_mode_device; + bool force_update; + bool do_lockdown; + bool has_guest_code; +#ifdef SYNA_TDDI + bool has_force_config; + bool has_lockdown_data; + bool has_lcm_data; + bool has_oem_data; +#endif + bool has_utility_param; + bool new_partition_table; + bool incompatible_partition_tables; + bool write_bootloader; + unsigned int data_pos; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char config_id[32]; + unsigned char flash_status; + unsigned char partitions; +#ifdef F51_DISCRETE_FORCE + unsigned char *cal_data; + unsigned short cal_data_off; + unsigned short cal_data_size; + unsigned short cal_data_buf_size; + unsigned short cal_packet_data_size; +#endif + unsigned short block_size; + unsigned short config_size; + unsigned short config_area; + unsigned short config_block_count; + unsigned short flash_config_length; + unsigned short payload_length; + unsigned short partition_table_bytes; + unsigned short read_config_buf_size; + const unsigned char *config_data; + const unsigned char *image; + unsigned char *image_name; + unsigned int image_size; + struct image_metadata img; + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct f34_v5v6_flash_properties flash_properties; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_fn_desc f35_fd; + struct synaptics_rmi4_data *rmi4_data; + struct workqueue_struct *fwu_workqueue; + struct work_struct fwu_work; +}; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = 0664, + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; +#endif + +static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + __ATTR(dorecovery, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_do_recovery_store), + __ATTR(doreflash, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_do_reflash_store), + __ATTR(writeconfig, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_write_config_store), + __ATTR(readconfig, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_read_config_store), + __ATTR(configarea, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_config_area_store), + __ATTR(imagename, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_image_name_store), + __ATTR(imagesize, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_image_size_store), + __ATTR(blocksize, 0444, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, 0444, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, 0444, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, 0444, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, 0444, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, 0444, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(uppblockcount, 0444, + fwu_sysfs_utility_parameter_block_count_show, + synaptics_rmi4_store_error), + __ATTR(guestcodeblockcount, 0444, + fwu_sysfs_guest_code_block_count_show, + synaptics_rmi4_store_error), + __ATTR(writeguestcode, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_write_guest_code_store), +#ifdef SYNA_TDDI + __ATTR(lockdowncode, 0664, + fwu_sysfs_read_lockdown_code_show, + fwu_sysfs_write_lockdown_code_store), +#endif +#endif +}; + +static struct synaptics_rmi4_fwu_handle *fwu; + +DECLARE_COMPLETION(fwu_remove_complete); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +DEFINE_MUTEX(fwu_sysfs_mutex); +#endif + +static void calculate_checksum(unsigned short *data, unsigned long len, + unsigned long *result) +{ + unsigned long temp; + unsigned long sum1 = 0xffff; + unsigned long sum2 = 0xffff; + + *result = 0xffffffff; + + while (len--) { + temp = *data; + sum1 += temp; + sum2 += sum1; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + data++; + } + + *result = sum2 << 16 | sum1; +} + +static void convert_to_little_endian(unsigned char *dest, unsigned long src) +{ + dest[0] = (unsigned char)(src & 0xff); + dest[1] = (unsigned char)((src >> 8) & 0xff); + dest[2] = (unsigned char)((src >> 16) & 0xff); + dest[3] = (unsigned char)((src >> 24) & 0xff); +} + +static unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +#ifdef F51_DISCRETE_FORCE +static int fwu_f51_force_data_init(void) +{ + int retval; + unsigned char query_count; + unsigned char packet_info; + unsigned char offset[2]; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f51_query_base_addr + 7, + offset, + sizeof(offset)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read force data offset\n", + __func__); + return retval; + } + + fwu->cal_data_off = offset[0] | offset[1] << 8; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f51_query_base_addr, + &query_count, + sizeof(query_count)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read number of F51 query registers\n", + __func__); + return retval; + } + + if (query_count >= 10) { + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f51_query_base_addr + 9, + &packet_info, + sizeof(packet_info)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F51 packet register info\n", + __func__); + return retval; + } + + if (packet_info & MASK_1BIT) { + fwu->cal_packet_data_size = packet_info >> 1; + fwu->cal_packet_data_size *= 2; + } else { + fwu->cal_packet_data_size = 0; + } + } else { + fwu->cal_packet_data_size = 0; + } + + fwu->cal_data_size = CAL_DATA_SIZE + fwu->cal_packet_data_size; + if (fwu->cal_data_size > fwu->cal_data_buf_size) { + kfree(fwu->cal_data); + fwu->cal_data_buf_size = fwu->cal_data_size; + fwu->cal_data = kmalloc(fwu->cal_data_buf_size, GFP_KERNEL); + if (!fwu->cal_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu->cal_data\n", + __func__); + fwu->cal_data_buf_size = 0; + return -ENOMEM; + } + } + + return 0; +} +#endif + +static int fwu_allocate_read_config_buf(unsigned int count) +{ + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (count > fwu->read_config_buf_size) { + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(count, GFP_KERNEL); + if (!fwu->read_config_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = count; + } + + return 0; +} + +static void fwu_compare_partition_tables(void) +{ + fwu->incompatible_partition_tables = false; + + if (fwu->phyaddr.bl_image != fwu->img.phyaddr.bl_image) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.lockdown != fwu->img.phyaddr.lockdown) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.bl_config != fwu->img.phyaddr.bl_config) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.utility_param != fwu->img.phyaddr.utility_param) + fwu->incompatible_partition_tables = true; + + if (fwu->bl_version == BL_V7) { + if (fwu->phyaddr.fl_config != fwu->img.phyaddr.fl_config) + fwu->incompatible_partition_tables = true; + } + + fwu->new_partition_table = false; + + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) + fwu->new_partition_table = true; + else if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) + fwu->new_partition_table = true; + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) + fwu->new_partition_table = true; + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) + fwu->new_partition_table = true; + } +} + +static void fwu_parse_partition_table(const unsigned char *partition_table, + struct block_count *blkcount, struct physical_address *phyaddr) +{ + unsigned char ii; + unsigned char index; + unsigned char offset; + unsigned short partition_length; + unsigned short physical_address; + struct partition_table *ptable; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + for (ii = 0; ii < fwu->partitions; ii++) { + index = ii * 8 + 2; + ptable = (struct partition_table *)&partition_table[index]; + partition_length = ptable->partition_length_15_8 << 8 | + ptable->partition_length_7_0; + physical_address = ptable->start_physical_address_15_8 << 8 | + ptable->start_physical_address_7_0; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Partition entry %d:\n", + __func__, ii); + for (offset = 0; offset < 8; offset++) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: 0x%02x\n", + __func__, + partition_table[index + offset]); + } + switch (ptable->partition_id) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + blkcount->total_count += partition_length; + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + blkcount->total_count += partition_length; + break; + case BOOTLOADER_PARTITION: + blkcount->bl_image = partition_length; + phyaddr->bl_image = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Bootloader block count: %d\n", + __func__, blkcount->bl_image); + blkcount->total_count += partition_length; + break; + case UTILITY_PARAMETER_PARTITION: + blkcount->utility_param = partition_length; + phyaddr->utility_param = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Utility parameter block count: %d\n", + __func__, blkcount->utility_param); + blkcount->total_count += partition_length; + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + blkcount->total_count += partition_length; + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + phyaddr->fl_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + blkcount->total_count += partition_length; + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + blkcount->total_count += partition_length; + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + phyaddr->pm_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + blkcount->total_count += partition_length; + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + phyaddr->bl_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + blkcount->total_count += partition_length; + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + phyaddr->lockdown = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + blkcount->total_count += partition_length; + break; + }; + } +} + +static void fwu_parse_image_header_10_utility(const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = fwu->img.utility.size / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + if (ii >= MAX_UTILITY_PARAMS) + continue; + addr = le_to_uint(fwu->img.utility.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UTILITY_PARAMETER_CONTAINER: + fwu->img.utility_param[ii].data = content; + fwu->img.utility_param[ii].size = length; + fwu->img.utility_param_id[ii] = content[0]; + break; + default: + break; + }; + } +} + +static void fwu_parse_image_header_10_bootloader(const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = (fwu->img.bootloader.size - 4) / 4; + + for (ii = 1; ii <= num_of_containers; ii++) { + addr = le_to_uint(fwu->img.bootloader.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case BL_IMAGE_CONTAINER: + fwu->img.bl_image.data = content; + fwu->img.bl_image.size = length; + break; + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + fwu->img.bl_config.data = content; + fwu->img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + fwu->img.lockdown.data = content; + fwu->img.lockdown.size = length; + break; + default: + break; + }; + } +} + +static void fwu_parse_image_header_10(void) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const unsigned char *image; + const unsigned char *content; + struct container_descriptor *descriptor; + struct image_header_10 *header; + + image = fwu->image; + header = (struct image_header_10 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + /* address of top level container */ + offset = le_to_uint(header->top_level_container_start_addr); + descriptor = (struct container_descriptor *)(image + offset); + + /* address of top level container content */ + offset = le_to_uint(descriptor->content_address); + num_of_containers = le_to_uint(descriptor->content_length) / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + addr = le_to_uint(image + offset); + offset += 4; + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + fwu->img.ui_firmware.data = content; + fwu->img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + fwu->img.ui_config.data = content; + fwu->img.ui_config.size = length; + break; + case BL_CONTAINER: + fwu->img.bl_version = *content; + fwu->img.bootloader.data = content; + fwu->img.bootloader.size = length; + fwu_parse_image_header_10_bootloader(image); + break; + case UTILITY_CONTAINER: + fwu->img.utility.data = content; + fwu->img.utility.size = length; + fwu_parse_image_header_10_utility(image); + break; + case GUEST_CODE_CONTAINER: + fwu->img.contains_guest_code = true; + fwu->img.guest_code.data = content; + fwu->img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + fwu->img.contains_disp_config = true; + fwu->img.dp_config.data = content; + fwu->img.dp_config.size = length; + break; + case PERMANENT_CONFIG_CONTAINER: + case GUEST_SERIALIZATION_CONTAINER: + fwu->img.contains_perm_config = true; + fwu->img.pm_config.data = content; + fwu->img.pm_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + fwu->img.contains_flash_config = true; + fwu->img.fl_config.data = content; + fwu->img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + fwu->img.contains_firmware_id = true; + fwu->img.firmware_id = le_to_uint(content + 4); + break; + default: + break; + } + } +} + +static void fwu_parse_image_header_05_06(void) +{ + int retval; + const unsigned char *image; + struct image_header_05_06 *header; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + image = fwu->image; + header = (struct image_header_05_06 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + fwu->img.bl_version = header->header_version; + + fwu->img.contains_bootloader = header->options_bootloader; + if (fwu->img.contains_bootloader) + fwu->img.bootloader_size = le_to_uint(header->bootloader_size); + + fwu->img.ui_firmware.size = le_to_uint(header->firmware_size); + if (fwu->img.ui_firmware.size) { + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + if (fwu->img.contains_bootloader) + fwu->img.ui_firmware.data += fwu->img.bootloader_size; + } + + if ((fwu->img.bl_version == BL_V6) && header->options_tddi) + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + + fwu->img.ui_config.size = le_to_uint(header->config_size); + if (fwu->img.ui_config.size) { + fwu->img.ui_config.data = fwu->img.ui_firmware.data + + fwu->img.ui_firmware.size; + } + + if (fwu->img.contains_bootloader || header->options_tddi) + fwu->img.contains_disp_config = true; + else + fwu->img.contains_disp_config = false; + + if (fwu->img.contains_disp_config) { + fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr); + fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size); + fwu->img.dp_config.data = image + fwu->img.disp_config_offset; + } else { + retval = secure_memcpy(fwu->img.cstmr_product_id, + sizeof(fwu->img.cstmr_product_id), + header->cstmr_product_id, + sizeof(header->cstmr_product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy custom product ID string\n", + __func__); + } + fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0; + } + + fwu->img.contains_firmware_id = header->options_firmware_id; + if (fwu->img.contains_firmware_id) + fwu->img.firmware_id = le_to_uint(header->firmware_id); + + retval = secure_memcpy(fwu->img.product_id, + sizeof(fwu->img.product_id), + header->product_id, + sizeof(header->product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy product ID string\n", + __func__); + } + fwu->img.product_id[PRODUCT_ID_SIZE] = 0; + + fwu->img.lockdown.size = LOCKDOWN_SIZE; + fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE; +} + +static int fwu_parse_image_info(void) +{ + struct image_header_10 *header; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + header = (struct image_header_10 *)fwu->image; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + fwu_parse_image_header_10(); + break; + case IMAGE_HEADER_VERSION_05: + case IMAGE_HEADER_VERSION_06: + fwu_parse_image_header_05_06(); + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Unsupported image file format (0x%02x)\n", + __func__, header->major_header_version); + return -EINVAL; + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) { + if (!fwu->img.contains_flash_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No flash config found in firmware image\n", + __func__); + return -EINVAL; + } + + fwu_parse_partition_table(fwu->img.fl_config.data, + &fwu->img.blkcount, &fwu->img.phyaddr); + + if (fwu->img.blkcount.utility_param) + fwu->img.contains_utility_param = true; + + fwu_compare_partition_tables(); + } else { + fwu->new_partition_table = false; + fwu->incompatible_partition_tables = false; + } + + return 0; +} + +static int fwu_read_flash_status(void) +{ + int retval; + unsigned char status; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + fwu->in_bl_mode = status >> 7; + + if (fwu->bl_version == BL_V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == BL_V6) + fwu->flash_status = status & MASK_3BIT; + else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + fwu->flash_status = status & MASK_5BIT; + + if (fwu->write_bootloader) + fwu->flash_status = 0x00; + + if (fwu->flash_status != 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash status = %d, command = 0x%02x\n", + __func__, fwu->flash_status, fwu->command); + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) { + if (fwu->flash_status == 0x08) + fwu->flash_status = 0x00; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash command\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) + fwu->command = command & MASK_4BIT; + else if (fwu->bl_version == BL_V6) + fwu->command = command & MASK_6BIT; + else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + fwu->command = command; + + if (fwu->write_bootloader) + fwu->command = 0x00; + + return 0; +} + +static int fwu_wait_for_idle(int timeout_ms, bool poll) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + do { + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); + + count++; + if (poll || (count == timeout_count)) + fwu_read_flash_status(); + + if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00)) + return 0; + } while (count < timeout_count); + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for idle status\n", + __func__); + + return -ETIMEDOUT; +} + +static int fwu_write_f34_v7_command_single_transaction(unsigned char cmd) +{ + int retval; + unsigned char data_base; + struct f34_v7_data_1_5 data_1_5; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + + switch (cmd) { + case CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BOOTLOADER: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UTILITY_PARAMETER: + data_1_5.partition_id = UTILITY_PARAMETER_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + }; + + data_1_5.payload_0 = fwu->bootloader_id[0]; + data_1_5.payload_1 = fwu->bootloader_id[1]; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.partition_id, + data_1_5.data, + sizeof(data_1_5.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write single transaction command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v7_command(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_LOCKDOWN: + case CMD_WRITE_GUEST_CODE: + case CMD_WRITE_BOOTLOADER: + case CMD_WRITE_UTILITY_PARAM: + command = CMD_V7_WRITE; + break; + case CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ERASE_BOOTLOADER: + case CMD_ERASE_UTILITY_PARAMETER: + command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + fwu->command = command; + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ERASE_BOOTLOADER: + case CMD_ERASE_UTILITY_PARAMETER: + case CMD_ENABLE_FLASH_PROG: + retval = fwu_write_f34_v7_command_single_transaction(cmd); + if (retval < 0) + return retval; + else + return 0; + default: + break; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v5v6_command(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_IDLE: + command = CMD_V5V6_IDLE; + break; + case CMD_WRITE_FW: + command = CMD_V5V6_WRITE_FW; + break; + case CMD_WRITE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_WRITE_LOCKDOWN: + command = CMD_V5V6_WRITE_LOCKDOWN; + break; + case CMD_WRITE_GUEST_CODE: + command = CMD_V5V6_WRITE_GUEST_CODE; + break; + case CMD_READ_CONFIG: + command = CMD_V5V6_READ_CONFIG; + break; + case CMD_ERASE_ALL: + command = CMD_V5V6_ERASE_ALL; + break; + case CMD_ERASE_UI_CONFIG: + command = CMD_V5V6_ERASE_UI_CONFIG; + break; + case CMD_ERASE_DISP_CONFIG: + command = CMD_V5V6_ERASE_DISP_CONFIG; + break; + case CMD_ERASE_GUEST_CODE: + command = CMD_V5V6_ERASE_GUEST_CODE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V5V6_ENABLE_FLASH_PROG; + break; +#ifdef SYNA_TDDI + case CMD_ERASE_CHIP: + command = CMD_V5V6_ERASE_CHIP; + break; + case CMD_ERASE_FORCE_CONFIG: + command = CMD_V5V6_ERASE_FORCE_CONFIG; + break; + case CMD_READ_FORCE_CONFIG: + command = CMD_V5V6_READ_FORCE_CONFIG; + break; + case CMD_WRITE_FORCE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_ERASE_LOCKDOWN_DATA: + command = CMD_V5V6_ERASE_LOCKDOWN_DATA; + break; + case CMD_READ_LOCKDOWN_DATA: + command = CMD_V5V6_READ_LOCKDOWN_DATA; + break; + case CMD_WRITE_LOCKDOWN_DATA: + command = CMD_V5V6_WRITE_LOCKDOWN_DATA; + break; + case CMD_ERASE_LCM_DATA: + command = CMD_V5V6_ERASE_LCM_DATA; + break; + case CMD_ERASE_OEM_DATA: + command = CMD_V5V6_ERASE_OEM_DATA; + break; +#endif + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_GUEST_CODE: +#ifdef SYNA_TDDI + case CMD_ERASE_CHIP: + case CMD_ERASE_FORCE_CONFIG: + case CMD_ERASE_LOCKDOWN_DATA: + case CMD_ERASE_LCM_DATA: + case CMD_ERASE_OEM_DATA: +#endif + case CMD_ENABLE_FLASH_PROG: + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.payload, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + break; + default: + break; + }; + + fwu->command = command; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command(unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_command(cmd); + else + retval = fwu_write_f34_v5v6_command(cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char partition; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case CMD_WRITE_CONFIG: + case CMD_READ_CONFIG: + if (fwu->config_area == UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (fwu->config_area == DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (fwu->config_area == PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (fwu->config_area == BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (fwu->config_area == FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + else if (fwu->config_area == UPP_AREA) + partition = UTILITY_PARAMETER_PARTITION; + break; + case CMD_WRITE_LOCKDOWN: + partition = DEVICE_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_WRITE_BOOTLOADER: + partition = BOOTLOADER_PARTITION; + break; + case CMD_WRITE_UTILITY_PARAM: + partition = UTILITY_PARAMETER_PARTITION; + break; + case CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ERASE_BOOTLOADER: + partition = BOOTLOADER_PARTITION; + break; + case CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.partition_id, + &partition, + sizeof(partition)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write partition ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_partition_id(unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_partition_id(cmd); + else + retval = 0; + + return retval; +} + +static int fwu_read_f34_v7_partition_table(unsigned char *partition_table) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + fwu->config_area = FLASH_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT); + length[1] = (unsigned char)(fwu->flash_config_length >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(CMD_READ_CONFIG); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command\n", + __func__); + return retval; + } + + msleep(READ_CONFIG_WAIT_MS); + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + fwu->off.payload, + partition_table, + fwu->partition_table_bytes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_v7_queries(void) +{ + int retval; + unsigned char ii; + unsigned char query_base; + unsigned char index; + unsigned char offset; + unsigned char *ptable; + struct f34_v7_query_0 query_0; + struct f34_v7_query_1_7 query_1_7; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + query_base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + query_base, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read query 0\n", + __func__); + return retval; + } + + offset = query_0.subpacket_1_size + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + query_base + offset, + query_1_7.data, + sizeof(query_1_7.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + fwu->bootloader_id[0] = query_1_7.bl_minor_revision; + fwu->bootloader_id[1] = query_1_7.bl_major_revision; + + if (fwu->bootloader_id[1] == BL_V8) + fwu->bl_version = BL_V8; + + fwu->block_size = query_1_7.block_size_15_8 << 8 | + query_1_7.block_size_7_0; + + fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 | + query_1_7.flash_config_length_7_0; + + fwu->payload_length = query_1_7.payload_length_15_8 << 8 | + query_1_7.payload_length_7_0; + + fwu->off.flash_status = V7_FLASH_STATUS_OFFSET; + fwu->off.partition_id = V7_PARTITION_ID_OFFSET; + fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET; + fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + fwu->off.flash_cmd = V7_COMMAND_OFFSET; + fwu->off.payload = V7_PAYLOAD_OFFSET; + + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES; + + fwu->partitions = 0; + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) { + for (ii = 0; ii < 8; ii++) { + if (query_1_7.data[index + offset] & (1 << ii)) + fwu->partitions++; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Supported partitions: 0x%02x\n", + __func__, query_1_7.data[index + offset]); + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!ptable) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for partition table\n", + __func__); + return -ENOMEM; + } + + retval = fwu_read_f34_v7_partition_table(ptable); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read partition table\n", + __func__); + kfree(ptable); + return retval; + } + + fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr); + + if (fwu->blkcount.dp_config) + fwu->flash_properties.has_disp_config = 1; + else + fwu->flash_properties.has_disp_config = 0; + + if (fwu->blkcount.pm_config) + fwu->flash_properties.has_pm_config = 1; + else + fwu->flash_properties.has_pm_config = 0; + + if (fwu->blkcount.bl_config) + fwu->flash_properties.has_bl_config = 1; + else + fwu->flash_properties.has_bl_config = 0; + + if (fwu->blkcount.guest_code) + fwu->has_guest_code = 1; + else + fwu->has_guest_code = 0; + + if (fwu->blkcount.utility_param) + fwu->has_utility_param = 1; + else + fwu->has_utility_param = 0; + + kfree(ptable); + + return 0; +} + +static int fwu_read_f34_v5v6_queries(void) +{ + int retval; + unsigned char count; + unsigned char base; + unsigned char offset; + unsigned char buf[10]; + struct f34_v5v6_flash_properties_2 properties_2; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + V5V6_BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) { + fwu->off.properties = V5_PROPERTIES_OFFSET; + fwu->off.block_size = V5_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V5_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == BL_V6) { + fwu->off.properties = V6_PROPERTIES_OFFSET; + fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET; + fwu->off.block_size = V6_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V6_BLOCK_COUNT_OFFSET; + fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_size, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == BL_V5) { + fwu->off.flash_cmd = fwu->off.payload + fwu->block_size; + fwu->off.flash_status = fwu->off.flash_cmd; + } else if (fwu->bl_version == BL_V6) { + fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET; + fwu->off.flash_status = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties.has_pm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_disp_config) + count += 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_count, + buf, + count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.ui_firmware, &(buf[0])); + batohs(&fwu->blkcount.ui_config, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_pm_config) { + batohs(&fwu->blkcount.pm_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->blkcount.bl_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_disp_config) + batohs(&fwu->blkcount.dp_config, &(buf[count])); + + fwu->has_guest_code = false; +#ifdef SYNA_TDDI + fwu->has_force_config = false; + fwu->has_lockdown_data = false; + fwu->has_lcm_data = false; + fwu->has_oem_data = false; +#endif + + if (fwu->flash_properties.has_query4) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties_2, + properties_2.data, + sizeof(properties_2.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties 2\n", + __func__); + return retval; + } + offset = fwu->off.properties_2 + 1; + count = 0; + if (properties_2.has_guest_code) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read guest code block count\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.guest_code, &(buf[0])); + count++; + fwu->has_guest_code = true; + } +#ifdef SYNA_TDDI + if (properties_2.has_force_config) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi force block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_force_config, &(buf[0])); + count++; + fwu->has_force_config = true; + } + if (properties_2.has_lockdown_data) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi lockdown block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_lockdown_data, &(buf[0])); + count++; + fwu->has_lockdown_data = true; + } + if (properties_2.has_lcm_data) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi lcm block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_lcm_data, &(buf[0])); + count++; + fwu->has_lcm_data = true; + } + if (properties_2.has_oem_data) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi oem block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_oem_data, &(buf[0])); + fwu->has_oem_data = true; + } +#endif + } + + fwu->has_utility_param = false; + + return 0; +} + +static int fwu_read_f34_queries(void) +{ + int retval; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + + if (fwu->bl_version == BL_V7) + retval = fwu_read_f34_v7_queries(); + else + retval = fwu_read_f34_v5v6_queries(); + + return retval; +} + +static int fwu_write_f34_v7_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short left_bytes; + unsigned short write_size; + unsigned short max_write_size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + do { + if (remaining / fwu->payload_length) + transfer = fwu->payload_length; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write transfer length (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command (remaining = %d)\n", + __func__, remaining); + return retval; + } + +#ifdef MAX_WRITE_SIZE + max_write_size = MAX_WRITE_SIZE; + if (max_write_size >= transfer * fwu->block_size) + max_write_size = transfer * fwu->block_size; + else if (max_write_size > fwu->block_size) + max_write_size -= max_write_size % fwu->block_size; + else + max_write_size = fwu->block_size; +#else + max_write_size = transfer * fwu->block_size; +#endif + left_bytes = transfer * fwu->block_size; + + do { + if (left_bytes / max_write_size) + write_size = max_write_size; + else + write_size = left_bytes; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.payload, + block_ptr, + write_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block data (remaining = %d)\n", + __func__, remaining); + return retval; + } + + block_ptr += write_size; + left_bytes -= write_size; + } while (left_bytes); + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (remaining = %d)\n", + __func__, remaining); + return retval; + } + + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_write_f34_v5v6_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.payload, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block data (block %d)\n", + __func__, blk); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command for block %d\n", + __func__, blk); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (block %d)\n", + __func__, blk); + return retval; + } + + block_ptr += fwu->block_size; + } + + return 0; +} + +static int fwu_write_f34_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_blocks(block_ptr, block_cnt, cmd); + else + retval = fwu_write_f34_v5v6_blocks(block_ptr, block_cnt, cmd); + + return retval; +} + +static int fwu_read_f34_v7_blocks(unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + do { + if (remaining / fwu->payload_length) + transfer = fwu->payload_length; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write transfer length (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + fwu->off.payload, + &fwu->read_config_buf[index], + transfer * fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data (remaining = %d)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_v5v6_blocks(unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write read config command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + fwu->off.payload, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data (block %d)\n", + __func__, blk); + return retval; + } + + index += fwu->block_size; + } + + return 0; +} + +static int fwu_read_f34_blocks(unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_read_f34_v7_blocks(block_cnt, cmd); + else + retval = fwu_read_f34_v5v6_blocks(block_cnt, cmd); + + return retval; +} + +static int fwu_get_image_firmware_id(unsigned int *fw_id) +{ + int retval; + unsigned char index = 0; + char *strptr; + char *firmware_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->img.contains_firmware_id) { + *fw_id = fwu->img.firmware_id; + } else { + strptr = strnstr(fwu->image_name, "PR", MAX_IMAGE_NAME_LEN); + if (!strptr) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No valid PR number (PRxxxxxxx) found in image file name (%s)\n", + __func__, fwu->image_name); + return -EINVAL; + } + + strptr += 2; + firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL); + if (!firmware_id) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for firmware_id\n", + __func__); + return -ENOMEM; + } + while (strptr[index] >= '0' && strptr[index] <= '9') { + firmware_id[index] = strptr[index]; + index++; + if (index == MAX_FIRMWARE_ID_LEN - 1) + break; + } + + retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id); + kfree(firmware_id); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to obtain image firmware ID\n", + __func__); + return -EINVAL; + } + } + + return 0; +} + +static int fwu_get_device_config_id(void) +{ + int retval; + unsigned char config_id_size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + fwu->config_id, + config_id_size); + if (retval < 0) + return retval; + + return 0; +} + +static enum flash_area fwu_go_nogo(void) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char ii; + unsigned char config_id_size; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->bl_mode_device) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device firmware ID */ + device_fw_id = rmi4_data->firmware_id; + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + retval = fwu_get_image_firmware_id(&image_fw_id); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image firmware ID = %d\n", + __func__, image_fw_id); + + if (image_fw_id > device_fw_id) { + flash_area = UI_FIRMWARE; + goto exit; + } else if (image_fw_id < device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image firmware ID older than device firmware ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + /* Get device config ID */ + retval = fwu_get_device_config_id(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + for (ii = 0; ii < config_id_size; ii++) { + if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) { + flash_area = UI_CONFIG; + goto exit; + } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) { + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: No need to do reflash\n", + __func__); + } else { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware and config" : + "UI config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + bool f35found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->in_ub_mode = false; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + switch (rmi_fd.fn_version) { + case F34_V0: + fwu->bl_version = BL_V5; + break; + case F34_V1: + fwu->bl_version = BL_V6; + break; + case F34_V2: + fwu->bl_version = BL_V7; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Unrecognized F34 version\n", + __func__); + return -EINVAL; + } + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + case SYNAPTICS_RMI4_F35: + f35found = true; + fwu->f35_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f35_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f35_fd.data_base_addr = + rmi_fd.data_base_addr; + fwu->f35_fd.cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + } + } else { + break; + } + + intr_count += rmi_fd.intr_src_count; + } + + if (!f01found || !f34found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find both F01 and F34\n", + __func__); + if (!f35found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + fwu->in_ub_mode = true; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + fwu_recovery_check_status(); + return 0; + } + } + + rmi4_data->intr_mask[0] |= fwu->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_enter_flash_prog(void) +{ + int retval; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_read_flash_status(); + if (retval < 0) + return retval; + + if (fwu->in_bl_mode) + return 0; + + retval = rmi4_data->irq_enable(rmi4_data, false, true); + if (retval < 0) + return retval; + + msleep(INT_DISABLE_WAIT_MS); + + retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG); + if (retval < 0) + return retval; + + retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false); + if (retval < 0) + return retval; + + if (!fwu->in_bl_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: BL mode not entered\n", + __func__); + return -EINVAL; + } + + if (rmi4_data->hw_if->bl_hw_init) { + retval = rmi4_data->hw_if->bl_hw_init(rmi4_data); + if (retval < 0) + return retval; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + return retval; + + retval = fwu_read_f34_queries(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F01 device control\n", + __func__); + return retval; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write F01 device control\n", + __func__); + return retval; + } + + msleep(ENTER_FLASH_PROG_WAIT_MS); + + return retval; +} + +static int fwu_check_ui_firmware_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.ui_firmware.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_firmware) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: UI firmware size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_ui_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.ui_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: UI configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_dp_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.dp_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.dp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Display configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_check_pm_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.pm_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.pm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Permanent configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} +#endif + +static int fwu_check_bl_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.bl_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.bl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_guest_code_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.guest_code.size / fwu->block_size; + if (block_count != fwu->blkcount.guest_code) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Guest code size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_erase_configuration(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_UI_CONFIG); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG); + if (retval < 0) + return retval; + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG); + if (retval < 0) + return retval; + break; + case FLASH_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + break; + case UPP_AREA: + retval = fwu_write_f34_command(CMD_ERASE_UTILITY_PARAMETER); + if (retval < 0) + return retval; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return retval; +} + +static int fwu_erase_bootloader(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_command(CMD_ERASE_BOOTLOADER); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +#ifdef SYNA_TDDI +static int fwu_erase_lockdown_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_command(CMD_ERASE_LOCKDOWN_DATA); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + msleep(100); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +#endif + +static int fwu_erase_guest_code(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_command(CMD_ERASE_GUEST_CODE); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +static int fwu_erase_all(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->bl_version == BL_V7) { + retval = fwu_write_f34_command(CMD_ERASE_UI_FIRMWARE); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } else { + retval = fwu_write_f34_command(CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (!(fwu->bl_version == BL_V8 && + fwu->flash_status == BAD_PARTITION_TABLE)) { + if (retval < 0) + return retval; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + if (fwu->bl_version == BL_V8) + return 0; + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code) { + retval = fwu_erase_guest_code(); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_write_firmware(void) +{ + unsigned short firmware_block_count; + + firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size; + + return fwu_write_f34_blocks((unsigned char *)fwu->img.ui_firmware.data, + firmware_block_count, CMD_WRITE_FW); +} + +static int fwu_write_bootloader(void) +{ + int retval; + unsigned short bootloader_block_count; + + bootloader_block_count = fwu->img.bl_image.size / fwu->block_size; + + fwu->write_bootloader = true; + retval = fwu_write_f34_blocks((unsigned char *)fwu->img.bl_image.data, + bootloader_block_count, CMD_WRITE_BOOTLOADER); + fwu->write_bootloader = false; + + return retval; +} + +static int fwu_write_utility_parameter(void) +{ + int retval; + unsigned char ii; + unsigned char checksum_array[4]; + unsigned char *pbuf; + unsigned short remaining_size; + unsigned short utility_param_size; + unsigned long checksum; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + utility_param_size = fwu->blkcount.utility_param * fwu->block_size; + retval = fwu_allocate_read_config_buf(utility_param_size); + if (retval < 0) + return retval; + memset(fwu->read_config_buf, 0x00, utility_param_size); + + pbuf = fwu->read_config_buf; + remaining_size = utility_param_size - 4; + + for (ii = 0; ii < MAX_UTILITY_PARAMS; ii++) { + if (fwu->img.utility_param_id[ii] == UNUSED) + continue; + +#ifdef F51_DISCRETE_FORCE + if (fwu->img.utility_param_id[ii] == FORCE_PARAMETER) { + if (fwu->bl_mode_device) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device in bootloader mode, skipping calibration data restoration\n", + __func__); + goto image_param; + } + retval = secure_memcpy(&(pbuf[4]), + remaining_size - 4, + fwu->cal_data, + fwu->cal_data_buf_size, + fwu->cal_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy force calibration data\n", + __func__); + return retval; + } + pbuf[0] = FORCE_PARAMETER; + pbuf[1] = 0x00; + pbuf[2] = (4 + fwu->cal_data_size) / 2; + pbuf += (fwu->cal_data_size + 4); + remaining_size -= (fwu->cal_data_size + 4); + continue; + } +image_param: +#endif + + retval = secure_memcpy(pbuf, + remaining_size, + fwu->img.utility_param[ii].data, + fwu->img.utility_param[ii].size, + fwu->img.utility_param[ii].size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy utility parameter data\n", + __func__); + return retval; + } + pbuf += fwu->img.utility_param[ii].size; + remaining_size -= fwu->img.utility_param[ii].size; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((utility_param_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[utility_param_size - 4] = checksum_array[0]; + fwu->read_config_buf[utility_param_size - 3] = checksum_array[1]; + fwu->read_config_buf[utility_param_size - 2] = checksum_array[2]; + fwu->read_config_buf[utility_param_size - 1] = checksum_array[3]; + + retval = fwu_write_f34_blocks((unsigned char *)fwu->read_config_buf, + fwu->blkcount.utility_param, CMD_WRITE_UTILITY_PARAM); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_configuration(void) +{ + return fwu_write_f34_blocks((unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG); +} + +static int fwu_write_ui_configuration(void) +{ + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->img.ui_config.data; + fwu->config_size = fwu->img.ui_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +static int fwu_write_dp_configuration(void) +{ + fwu->config_area = DP_CONFIG_AREA; + fwu->config_data = fwu->img.dp_config.data; + fwu->config_size = fwu->img.dp_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_write_pm_configuration(void) +{ + fwu->config_area = PM_CONFIG_AREA; + fwu->config_data = fwu->img.pm_config.data; + fwu->config_size = fwu->img.pm_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +#ifdef SYNA_TDDI +static int fwu_write_tddi_lockdown_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_blocks(fwu->read_config_buf, + fwu->blkcount.tddi_lockdown_data, + CMD_WRITE_LOCKDOWN_DATA); + if (retval < 0) + return retval; + rmi4_data->reset_device(rmi4_data, false); + return 0; +} +#endif +#endif + +static int fwu_write_flash_configuration(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + rmi4_data->reset_device(rmi4_data, false); + + return 0; +} + +static int fwu_write_guest_code(void) +{ + int retval; + unsigned short guest_code_block_count; + + guest_code_block_count = fwu->img.guest_code.size / fwu->block_size; + + retval = fwu_write_f34_blocks((unsigned char *)fwu->img.guest_code.data, + guest_code_block_count, CMD_WRITE_GUEST_CODE); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_lockdown(void) +{ + unsigned short lockdown_block_count; + + lockdown_block_count = fwu->img.lockdown.size / fwu->block_size; + + return fwu_write_f34_blocks((unsigned char *)fwu->img.lockdown.data, + lockdown_block_count, CMD_WRITE_LOCKDOWN); +} + +static int fwu_write_partition_table_v8(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + rmi4_data->reset_device(rmi4_data, false); + + return 0; +} + +static int fwu_write_partition_table_v7(void) +{ + int retval; + unsigned short block_count; + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_flash_configuration(); + if (retval < 0) + return retval; + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_bl_area_v7(void) +{ + int retval; + bool has_utility_param; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + has_utility_param = fwu->has_utility_param; + + if (fwu->has_utility_param) { + fwu->config_area = UPP_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + fwu->config_area = BL_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + fwu->config_area = FLASH_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_erase_bootloader(); + if (retval < 0) + return retval; + + retval = fwu_write_bootloader(); + if (retval < 0) + return retval; + + msleep(rmi4_data->hw_if->board_data->reset_delay_ms); + rmi4_data->reset_device(rmi4_data, false); + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + rmi4_data->reset_device(rmi4_data, false); + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->img.bl_config.data; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + if (fwu->img.contains_utility_param) { + retval = fwu_write_utility_parameter(); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_do_reflash(void) +{ + int retval; + bool do_bl_update = false; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!fwu->new_partition_table) { + retval = fwu_check_ui_firmware_size(); + if (retval < 0) + return retval; + + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + return retval; + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_check_dp_configuration_size(); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_check_guest_code_size(); + if (retval < 0) + return retval; + } + } else if (fwu->bl_version == BL_V7) { + retval = fwu_check_bl_configuration_size(); + if (retval < 0) + return retval; + } + + if (!fwu->has_utility_param && fwu->img.contains_utility_param) { + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + do_bl_update = true; + } + + if (fwu->has_utility_param && !fwu->img.contains_utility_param) { + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + do_bl_update = true; + } + + if (!do_bl_update && fwu->incompatible_partition_tables) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Incompatible partition tables\n", + __func__); + return -EINVAL; + } else if (!do_bl_update && fwu->new_partition_table) { + if (!fwu->force_update) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Partition table mismatch\n", + __func__); + return -EINVAL; + } + } + + retval = fwu_erase_all(); + if (retval < 0) + return retval; + + if (do_bl_update) { + retval = fwu_write_bl_area_v7(); + if (retval < 0) + return retval; + pr_notice("%s: Bootloader area programmed\n", __func__); + } else if (fwu->bl_version == BL_V7 && fwu->new_partition_table) { + retval = fwu_write_partition_table_v7(); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } else if (fwu->bl_version == BL_V8) { + retval = fwu_write_partition_table_v8(); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } + + fwu->config_area = UI_CONFIG_AREA; + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_write_dp_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Display configuration programmed\n", __func__); + } + + retval = fwu_write_ui_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_write_guest_code(); + if (retval < 0) + return retval; + pr_notice("%s: Guest code programmed\n", __func__); + } + + retval = fwu_write_firmware(); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_do_read_config(void) +{ + int retval; + unsigned short block_count; + unsigned short config_area; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->blkcount.ui_config; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.dp_config; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.pm_config; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.bl_config; + break; + case UPP_AREA: + if (!fwu->has_utility_param) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Utility parameter not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.utility_param; + break; +#ifdef SYNA_TDDI + case TDDI_FORCE_CONFIG_AREA: + if (!fwu->has_force_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: force configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.tddi_force_config; + break; + case TDDI_OEM_DATA_AREA: + if (!fwu->has_oem_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: oem data not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.tddi_oem_data; + break; + case TDDI_LCM_DATA_AREA: + if (!fwu->has_lcm_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: lcm data not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.tddi_lcm_data; + break; +#endif + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + if (block_count == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid block count\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + if (fwu->bl_version == BL_V5 || fwu->bl_version == BL_V6) { + config_area = fwu->config_area; + retval = fwu_enter_flash_prog(); + fwu->config_area = config_area; + if (retval < 0) + goto exit; + } + + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + +exit: + if (fwu->bl_version == BL_V5 || fwu->bl_version == BL_V6) + rmi4_data->reset_device(rmi4_data, false); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} + +#ifdef SYNA_TDDI +static int fwu_do_read_tddi_lockdown_data(void) +{ + int retval = -EINVAL; + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->blkcount.tddi_lockdown_data; + fwu->config_size = fwu->block_size * block_count; + + if (fwu->bl_version != BL_V6) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data in bl v.%d\n", + __func__, + fwu->bl_version); + goto exit; + } else if (!fwu->has_lockdown_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data\n", __func__); + goto exit; + } + + kfree(fwu->read_config_buf); + + fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL); + + if (!fwu->read_config_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + retval = -ENOMEM; + goto exit; + } + fwu->read_config_buf_size = fwu->config_size; + retval = fwu_read_f34_blocks(block_count, CMD_READ_LOCKDOWN_DATA); +exit: + return retval; +} + +int get_tddi_lockdown_data(unsigned char *lockdown_data, unsigned short leng) +{ + int retval; + + retval = fwu_do_read_tddi_lockdown_data(); + if (retval < 0) + return retval; + memcpy(lockdown_data, fwu->read_config_buf, leng); + return retval; +} + +int set_tddi_lockdown_data(unsigned char *lockdown_data, unsigned short leng) +{ + int retval = -EINVAL; + unsigned long checksum; + unsigned char checksum_array[4]; + unsigned short blk_cnt; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->bl_version != BL_V6) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data in bl v.%d\n", + __func__, + fwu->bl_version); + goto exit; + } else if (!fwu->has_lockdown_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data\n", __func__); + goto exit; + } + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + retval = fwu_erase_lockdown_data(); + if (retval < 0) + goto exit; + + blk_cnt = fwu->blkcount.tddi_lockdown_data; + + fwu->config_size = fwu->blkcount.tddi_lockdown_data * fwu->block_size; + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + memset(fwu->read_config_buf, 0x00, fwu->config_size); + retval = secure_memcpy(fwu->read_config_buf, fwu->config_size, + lockdown_data, leng, leng); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy tddi lockdwon data\n", + __func__); + goto exit; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((fwu->config_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[blk_cnt * fwu->block_size - 4] = checksum_array[0]; + fwu->read_config_buf[blk_cnt * fwu->block_size - 3] = checksum_array[1]; + fwu->read_config_buf[blk_cnt * fwu->block_size - 2] = checksum_array[2]; + fwu->read_config_buf[blk_cnt * fwu->block_size - 1] = checksum_array[3]; + retval = fwu_write_tddi_lockdown_data(); +exit: + return retval; +} +#endif +#endif + +static int fwu_do_lockdown_v7(void) +{ + int retval; + struct f34_v7_data0 status; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + status.data, + sizeof(status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + if (status.device_cfg_status == 2) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +static int fwu_do_lockdown_v5v6(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; +#ifdef SYNA_TDDI + unsigned char *img_ld; + + img_ld = (unsigned char *)fwu->img.lockdown.data; + if (fwu->has_lockdown_data) { + retval = set_tddi_lockdown_data(img_ld, + LOCKDOWN_SIZE); + if (retval < 0) + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write lockdown data\n", + __func__); + return retval; + } +#endif + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if (fwu->flash_properties.unlocked == 0) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +#ifdef F51_DISCRETE_FORCE +static int fwu_do_restore_f51_cal_data(void) +{ + int retval; + unsigned char checksum_array[4]; + unsigned short block_count; + unsigned long checksum; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->blkcount.ui_config; + fwu->config_size = fwu->block_size * block_count; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = secure_memcpy(&fwu->read_config_buf[fwu->cal_data_off], + fwu->cal_data_size, fwu->cal_data, + fwu->cal_data_buf_size, fwu->cal_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to restore calibration data\n", + __func__); + return retval; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((fwu->config_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[fwu->config_size - 4] = checksum_array[0]; + fwu->read_config_buf[fwu->config_size - 3] = checksum_array[1]; + fwu->read_config_buf[fwu->config_size - 2] = checksum_array[2]; + fwu->read_config_buf[fwu->config_size - 1] = checksum_array[3]; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + return 0; +} +#endif + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_start_write_guest_code(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_parse_image_info(); + if (retval < 0) + return -EINVAL; + + if (!fwu->has_guest_code) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Guest code not supported\n", + __func__); + return -EINVAL; + } + + if (!fwu->img.contains_guest_code) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No guest code in firmware image\n", + __func__); + return -EINVAL; + } + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write guest code process\n", __func__); + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + retval = fwu_check_guest_code_size(); + if (retval < 0) + goto exit; + + retval = fwu_erase_guest_code(); + if (retval < 0) + goto exit; + + retval = fwu_write_guest_code(); + if (retval < 0) + goto exit; + + pr_notice("%s: Guest code programmed\n", __func__); + +exit: + rmi4_data->reset_device(rmi4_data, false); + + pr_notice("%s: End of write guest code process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} + +static int fwu_start_write_config(void) +{ + int retval; + unsigned short config_area; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_parse_image_info(); + if (retval < 0) + return -EINVAL; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + device_fw_id = rmi4_data->firmware_id; + retval = fwu_get_image_firmware_id(&image_fw_id); + if (retval < 0) + return retval; + if (device_fw_id != image_fw_id) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Device and image firmware IDs don't match\n", + __func__); + return -EINVAL; + } + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_disp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No display configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_dp_configuration_size(); + if (retval < 0) + return retval; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_perm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No permanent configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_pm_configuration_size(); + if (retval < 0) + return retval; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Configuration not supported\n", + __func__); + return -EINVAL; + } + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write config process\n", __func__); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + if (fwu->config_area != PM_CONFIG_AREA) { + retval = fwu_erase_configuration(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to erase config\n", + __func__); + goto exit; + } + } + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_ui_configuration(); + if (retval < 0) + goto exit; + break; + case DP_CONFIG_AREA: + retval = fwu_write_dp_configuration(); + if (retval < 0) + goto exit; + break; + case PM_CONFIG_AREA: + retval = fwu_write_pm_configuration(); + if (retval < 0) + goto exit; + break; + } + + pr_notice("%s: Config written\n", __func__); + +exit: + switch (fwu->config_area) { + case UI_CONFIG_AREA: + rmi4_data->reset_device(rmi4_data, true); + break; + case DP_CONFIG_AREA: + case PM_CONFIG_AREA: + rmi4_data->reset_device(rmi4_data, false); + break; + } + + pr_notice("%s: End of write config process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} +#endif + +static int fwu_start_reflash(void) +{ + int retval = 0; + enum flash_area flash_area; + bool do_rebuild = false; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of reflash process\n", __func__); + + if (fwu->image == NULL) { + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + FW_IMAGE_NAME, sizeof(FW_IMAGE_NAME), + sizeof(FW_IMAGE_NAME)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy image file name\n", + __func__); + goto exit; + } + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->pdev->dev.parent); + if (retval != 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Firmware image %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Firmware image size = %d\n", + __func__, (unsigned int)fw_entry->size); + + fwu->image = fw_entry->data; + } + + retval = fwu_parse_image_info(); + if (retval < 0) + goto exit; + + if (fwu->blkcount.total_count != fwu->img.blkcount.total_count) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash size mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (fwu->bl_version != fwu->img.bl_version) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader version mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = fwu_read_flash_status(); + if (retval < 0) + goto exit; + + if (fwu->in_bl_mode) { + fwu->bl_mode_device = true; + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device in bootloader mode\n", + __func__); + } else { + fwu->bl_mode_device = false; + } + + flash_area = fwu_go_nogo(); + + if (flash_area != NONE) { + retval = fwu_enter_flash_prog(); + if (retval < 0) { + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + } + +#ifdef F51_DISCRETE_FORCE + if (flash_area != NONE && !fwu->bl_mode_device) { + fwu->config_size = fwu->block_size * fwu->blkcount.ui_config; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) { + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + + retval = fwu_read_f34_blocks(fwu->blkcount.ui_config, + CMD_READ_CONFIG); + if (retval < 0) { + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + + retval = secure_memcpy(fwu->cal_data, fwu->cal_data_buf_size, + &fwu->read_config_buf[fwu->cal_data_off], + fwu->cal_data_size, fwu->cal_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to save calibration data\n", + __func__); + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + } +#endif + + switch (flash_area) { + case UI_FIRMWARE: + do_rebuild = true; + retval = fwu_do_reflash(); +#ifdef F51_DISCRETE_FORCE + if (retval < 0) + break; + + if (fwu->has_utility_param || fwu->img.contains_utility_param) + break; + + rmi4_data->reset_device(rmi4_data, false); + + if (fwu->bl_mode_device || fwu->in_bl_mode) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device in bootloader mode, skipping calibration data restoration\n", + __func__); + break; + } + + retval = fwu_do_restore_f51_cal_data(); +#endif + break; + case UI_CONFIG: + do_rebuild = true; + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + break; + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + break; + retval = fwu_write_ui_configuration(); +#ifdef F51_DISCRETE_FORCE + if (retval < 0) + break; + + if (fwu->has_utility_param) + break; + + retval = fwu_do_restore_f51_cal_data(); +#endif + break; + case NONE: + default: + break; + } + + if (retval < 0) { + do_rebuild = false; + rmi4_data->reset_device(rmi4_data, false); + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) { + switch (fwu->bl_version) { + case BL_V5: + case BL_V6: + retval = fwu_do_lockdown_v5v6(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do lockdown\n", + __func__); + } + rmi4_data->reset_device(rmi4_data, false); + break; + case BL_V7: + case BL_V8: + retval = fwu_do_lockdown_v7(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do lockdown\n", + __func__); + } + rmi4_data->reset_device(rmi4_data, false); + break; + default: + break; + } + } + +exit: + if (fw_entry) + release_firmware(fw_entry); + + if (do_rebuild) + rmi4_data->reset_device(rmi4_data, true); + + pr_notice("%s: End of reflash process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} + +static int fwu_recovery_check_status(void) +{ + int retval; + unsigned char data_base; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f35_fd.data_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + F35_ERROR_CODE_OFFSET, + &status, + 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read status\n", + __func__); + return retval; + } + + status = status & MASK_5BIT; + + if (status != 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Recovery mode status = %d\n", + __func__, status); + return -EINVAL; + } + + return 0; +} + +static int fwu_recovery_erase_completion(void) +{ + int retval; + unsigned char data_base; + unsigned char command; + unsigned char status; + unsigned int timeout = F35_ERASE_ALL_WAIT_MS / 20; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f35_fd.data_base_addr; + + do { + command = 0x01; + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f35_fd.cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue command\n", + __func__); + return retval; + } + + do { + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f35_fd.cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read command status\n", + __func__); + return retval; + } + + if ((command & 0x01) == 0x00) + break; + + msleep(20); + timeout--; + } while (timeout > 0); + + if (timeout == 0) + goto exit; + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + F35_FLASH_STATUS_OFFSET, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + if ((status & 0x01) == 0x00) + break; + + msleep(20); + timeout--; + } while (timeout > 0); + +exit: + if (timeout == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for flash erase completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int fwu_recovery_erase_all(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char command = CMD_F35_ERASE_ALL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue erase all command\n", + __func__); + return retval; + } + + if (fwu->f35_fd.cmd_base_addr) { + retval = fwu_recovery_erase_completion(); + if (retval < 0) + return retval; + } else { + msleep(F35_ERASE_ALL_WAIT_MS); + } + + retval = fwu_recovery_check_status(); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_recovery_write_chunk(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char chunk_number[] = {0, 0}; + unsigned char chunk_spare; + unsigned char chunk_size; + unsigned char buf[F35_CHUNK_SIZE + 1]; + unsigned short chunk; + unsigned short chunk_total; + unsigned short bytes_written = 0; + unsigned char *chunk_ptr = (unsigned char *)fwu->image; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_NUM_LSB_OFFSET, + chunk_number, + sizeof(chunk_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk number\n", + __func__); + return retval; + } + + buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK; + + chunk_total = fwu->image_size / F35_CHUNK_SIZE; + chunk_spare = fwu->image_size % F35_CHUNK_SIZE; + if (chunk_spare) + chunk_total++; + + for (chunk = 0; chunk < chunk_total; chunk++) { + if (chunk_spare && chunk == chunk_total - 1) + chunk_size = chunk_spare; + else + chunk_size = F35_CHUNK_SIZE; + + memset(buf, 0x00, F35_CHUNK_SIZE); + secure_memcpy(buf, sizeof(buf), chunk_ptr, + fwu->image_size - bytes_written, + chunk_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_DATA_OFFSET, + buf, + sizeof(buf)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk data (chunk %d)\n", + __func__, chunk); + return retval; + } + chunk_ptr += chunk_size; + bytes_written += chunk_size; + } + + retval = fwu_recovery_check_status(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_recovery_reset(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char command = CMD_F35_RESET; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + return retval; + } + + msleep(F35_RESET_WAIT_MS); + + return 0; +} + +static int fwu_start_recovery(void) +{ + int retval; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of recovery process\n", __func__); + + if (fwu->image == NULL) { + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + FW_IHEX_NAME, sizeof(FW_IHEX_NAME), + sizeof(FW_IHEX_NAME)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy ihex file name\n", + __func__); + goto exit; + } + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Requesting firmware ihex %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->pdev->dev.parent); + if (retval != 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Firmware ihex %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Firmware image size = %d\n", + __func__, (unsigned int)fw_entry->size); + + fwu->image = fw_entry->data; + fwu->image_size = fw_entry->size; + } + + retval = rmi4_data->irq_enable(rmi4_data, false, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable interrupt\n", + __func__); + goto exit; + } + + retval = fwu_recovery_erase_all(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do erase all in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: External flash erased\n", __func__); + + retval = fwu_recovery_write_chunk(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk data in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Chunk data programmed\n", __func__); + + retval = fwu_recovery_reset(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reset device in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Recovery mode reset issued\n", __func__); + + rmi4_data->reset_device(rmi4_data, true); + + retval = 0; + +exit: + if (fw_entry) + release_firmware(fw_entry); + + pr_notice("%s: End of recovery process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} + +int synaptics_fw_updater(const unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + if (fwu->in_ub_mode) { + fwu->image = NULL; + retval = fwu_start_recovery(); + if (retval < 0) + return retval; + } + + fwu->image = fw_data; + + retval = fwu_start_reflash(); + + fwu->image = NULL; + + return retval; +} +EXPORT_SYMBOL(synaptics_fw_updater); + +#ifdef DO_STARTUP_FW_UPDATE +static void fwu_startup_fw_update_work(struct work_struct *work) +{ + static unsigned char do_once = 1; +#ifdef WAIT_FOR_FB_READY + unsigned int timeout; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; +#endif + + if (!do_once) + return; + do_once = 0; + +#ifdef WAIT_FOR_FB_READY + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1; + + while (!rmi4_data->fb_ready) { + msleep(FB_READY_WAIT_MS); + timeout--; + if (timeout == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for FB ready\n", + __func__); + return; + } + } +#endif + + synaptics_fw_updater(NULL); +} +#endif + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (count < fwu->config_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, (unsigned int)count); + retval = -EINVAL; + goto exit; + } + + retval = secure_memcpy(buf, count, fwu->read_config_buf, + fwu->read_config_buf_size, fwu->config_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy config data\n", + __func__); + goto exit; + } else { + retval = fwu->config_size; + } + +exit: + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos], + fwu->image_size - fwu->data_pos, buf, count, count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy image data\n", + __func__); + goto exit; + } else { + retval = count; + } + + fwu->data_pos += count; + +exit: + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (!fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not in microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + retval = fwu_start_recovery(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do recovery\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + if (input & LOCKDOWN) { + fwu->do_lockdown = true; + input &= ~LOCKDOWN; + } + + if ((input != NORMAL) && (input != FORCE)) { + retval = -EINVAL; + goto exit; + } + + if (input == FORCE) + fwu->force_update = true; + + retval = synaptics_fw_updater(fwu->image); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + retval = fwu_start_write_config(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = fwu_do_read_config(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + fwu->config_area = config_area; + + mutex_unlock(&fwu_sysfs_mutex); + + return count; +} + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + buf, count, count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy image file name\n", + __func__); + } else { + retval = count; + } + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = sstrtoul(buf, 10, &size); + if (retval) + return retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for image data\n", + __func__); + retval = -ENOMEM; + } + retval = count; + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_utility_parameter_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.utility_param); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + retval = fwu_start_write_guest_code(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write guest code\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +#ifdef SYNA_TDDI +static ssize_t fwu_sysfs_read_lockdown_code_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned short lockdown_data_size; + unsigned char *lockdown_data; + char ld_val[2]; + int retval = 0; + int i = 0; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + lockdown_data_size = fwu->blkcount.tddi_lockdown_data * fwu->block_size; + lockdown_data = kzalloc(lockdown_data_size, GFP_KERNEL); + if (!lockdown_data) { + mutex_unlock(&fwu_sysfs_mutex); + return -ENOMEM; + } + + if (get_tddi_lockdown_data(lockdown_data, lockdown_data_size) < 0) { + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return -EINVAL; + } + + for (i = 0; i < lockdown_data_size; i++) { + retval += snprintf(ld_val, PAGE_SIZE, "%02x", + *(lockdown_data + i)); + strlcat(buf, ld_val, lockdown_data_size); + } + *(buf + retval) = '\n'; + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return retval + 1; +} + +static ssize_t fwu_sysfs_write_lockdown_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned short lockdown_data_size = (count - 1) / 2; + unsigned char *lockdown_data; + unsigned char temp[2]; + int ld_val; + int i = 0; + + for (i = 0; i < (count - 1); i++) { + if (((*buf >= '0') && (*buf <= '9')) || + (('a' < *buf) && (*buf > 'f')) || + (('A' < *buf) && (*buf > 'F'))) + continue; + else + return -EINVAL; + } + + if (count % 2 != 1) + return -EINVAL; + + lockdown_data = kzalloc(lockdown_data_size, GFP_KERNEL); + if (!lockdown_data) + return -ENOMEM; + + for (i = 0; i < lockdown_data_size; i++) { + memcpy(temp, (buf + 2 * i), sizeof(temp)); + if (kstrtoint(temp, 16, &ld_val) == 1) + *(lockdown_data + i) = ld_val & 0xff; + } + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (set_tddi_lockdown_data(lockdown_data, lockdown_data_size) < 0) { + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return -EINVAL; + } + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return count; +} +#endif +#endif +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!fwu) + return; + + if (fwu->intr_mask & intr_mask) + fwu_read_flash_status(); + + return; +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + struct pdt_properties pdt_props; + + if (fwu) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL); + if (!fwu->image_name) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for image name\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + fwu->rmi4_data = rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_mem; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(); + if (retval < 0) + goto exit_free_mem; + + retval = fwu_get_device_config_id(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device config ID\n", + __func__); + goto exit_free_mem; + } + } + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + fwu->initialized = true; + +#ifdef DO_STARTUP_FW_UPDATE + fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue"); + INIT_WORK(&fwu->fwu_work, fwu_startup_fw_update_work); + queue_work(fwu->fwu_workqueue, + &fwu->fwu_work); +#endif + +#ifdef F51_DISCRETE_FORCE + fwu_read_flash_status(); + if (!fwu->in_bl_mode) { + retval = fwu_f51_force_data_init(); + if (retval < 0) + goto exit_free_mem; + } +#endif + + if (ENABLE_SYS_REFLASH == false) + return 0; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, + &dev_attr_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_mem; + } +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif + +exit_free_mem: + kfree(fwu->image_name); + +exit_free_fwu: + kfree(fwu); + fwu = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!fwu) + goto exit; + +#ifdef DO_STARTUP_FW_UPDATE + cancel_work_sync(&fwu->fwu_work); + flush_workqueue(fwu->fwu_workqueue); + destroy_workqueue(fwu->fwu_workqueue); +#endif + +#ifdef F51_DISCRETE_FORCE + kfree(fwu->cal_data); +#endif + kfree(fwu->read_config_buf); + kfree(fwu->image_name); + kfree(fwu); + fwu = NULL; + + if (ENABLE_SYS_REFLASH == false) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif + +exit: + complete(&fwu_remove_complete); +} + +static void synaptics_rmi4_fwu_reset(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (!fwu) { + synaptics_rmi4_fwu_init(rmi4_data); + return; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + return; + + if (!fwu->in_ub_mode) + fwu_read_f34_queries(); + +#ifdef F51_DISCRETE_FORCE + fwu_read_flash_status(); + if (!fwu->in_bl_mode) + fwu_f51_force_data_init(); +#endif + + return; +} + +static struct synaptics_rmi4_exp_fn fwu_module = { + .fn_type = RMI_FW_UPDATER, + .init = synaptics_rmi4_fwu_init, + .remove = synaptics_rmi4_fwu_remove, + .reset = synaptics_rmi4_fwu_reset, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_fwu_attn, +}; + +static int __init rmi4_fw_update_module_init(void) +{ + synaptics_rmi4_new_function(&fwu_module, true); + + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + synaptics_rmi4_new_function(&fwu_module, false); + + wait_for_completion(&fwu_remove_complete); +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX FW Update Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_gesture.c b/synaptics_dsx/synaptics_dsx_gesture.c new file mode 100644 index 0000000000..64f960efb1 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_gesture.c @@ -0,0 +1,2291 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define GESTURE_PHYS_NAME "synaptics_dsx/gesture" + +#define TUNING_SYSFS_DIR_NAME "tuning" + +#define STORE_GESTURES +#ifdef STORE_GESTURES +#define GESTURES_TO_STORE 10 +#endif + +#define CTRL23_FINGER_REPORT_ENABLE_BIT 0 +#define CTRL27_UDG_ENABLE_BIT 4 +#define WAKEUP_GESTURE_MODE 0x02 + +static ssize_t udg_sysfs_engine_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_detection_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_detection_score_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_detection_index_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_registration_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_registration_begin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_registration_status_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_max_index_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_detection_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_template_valid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_valid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_template_clear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_trace_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t udg_sysfs_template_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t udg_sysfs_trace_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t udg_sysfs_template_displacement_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_displacement_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_scale_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_scale_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_threshold_factor_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_threshold_factor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static int udg_read_tuning_params(void); + +static int udg_write_tuning_params(void); + +static int udg_detection_enable(bool enable); + +static int udg_engine_enable(bool enable); + +static int udg_set_index(unsigned char index); + +#ifdef STORE_GESTURES +static int udg_read_valid_data(void); +static int udg_write_valid_data(void); +static int udg_read_template_data(unsigned char index); +static int udg_write_template_data(void); +#endif + +enum gesture_type { + DETECTION = 0x0f, + REGISTRATION = 0x10, +}; + +struct udg_tuning { + union { + struct { + unsigned char maximum_number_of_templates; + unsigned char template_size; + unsigned char template_disp_lsb; + unsigned char template_disp_msb; + unsigned char rotation_inv_lsb; + unsigned char rotation_inv_msb; + unsigned char scale_inv_lsb; + unsigned char scale_inv_msb; + unsigned char thres_factor_lsb; + unsigned char thres_factor_msb; + unsigned char metric_thres_lsb; + unsigned char metric_thres_msb; + unsigned char inter_stroke_lsb; + unsigned char inter_stroke_msb; + } __packed; + unsigned char data[14]; + }; +}; + +struct udg_addr { + unsigned short data_4; + unsigned short ctrl_18; + unsigned short ctrl_20; + unsigned short ctrl_23; + unsigned short ctrl_27; + unsigned short ctrl_41; + unsigned short trace_x; + unsigned short trace_y; + unsigned short trace_segment; + unsigned short template_helper; + unsigned short template_data; + unsigned short template_flags; +}; + +struct synaptics_rmi4_f12_query_0 { + union { + struct { + struct { + unsigned char has_register_descriptors:1; + unsigned char has_closed_cover:1; + unsigned char has_fast_glove_detect:1; + unsigned char has_dribble:1; + unsigned char has_4p4_jitter_filter_strength:1; + unsigned char f12_query0_s0_b5__7:3; + } __packed; + struct { + unsigned char max_num_templates:4; + unsigned char f12_query0_s1_b4__7:4; + unsigned char template_size_lsb; + unsigned char template_size_msb; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + struct { + unsigned char ctrl24_is_present:1; + unsigned char ctrl25_is_present:1; + unsigned char ctrl26_is_present:1; + unsigned char ctrl27_is_present:1; + unsigned char ctrl28_is_present:1; + unsigned char ctrl29_is_present:1; + unsigned char ctrl30_is_present:1; + unsigned char ctrl31_is_present:1; + } __packed; + struct { + unsigned char ctrl32_is_present:1; + unsigned char ctrl33_is_present:1; + unsigned char ctrl34_is_present:1; + unsigned char ctrl35_is_present:1; + unsigned char ctrl36_is_present:1; + unsigned char ctrl37_is_present:1; + unsigned char ctrl38_is_present:1; + unsigned char ctrl39_is_present:1; + } __packed; + struct { + unsigned char ctrl40_is_present:1; + unsigned char ctrl41_is_present:1; + unsigned char ctrl42_is_present:1; + unsigned char ctrl43_is_present:1; + unsigned char ctrl44_is_present:1; + unsigned char ctrl45_is_present:1; + unsigned char ctrl46_is_present:1; + unsigned char ctrl47_is_present:1; + } __packed; + }; + unsigned char data[7]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + struct { + unsigned char data8_is_present:1; + unsigned char data9_is_present:1; + unsigned char data10_is_present:1; + unsigned char data11_is_present:1; + unsigned char data12_is_present:1; + unsigned char data13_is_present:1; + unsigned char data14_is_present:1; + unsigned char data15_is_present:1; + } __packed; + struct { + unsigned char data16_is_present:1; + unsigned char data17_is_present:1; + unsigned char data18_is_present:1; + unsigned char data19_is_present:1; + unsigned char data20_is_present:1; + unsigned char data21_is_present:1; + unsigned char data22_is_present:1; + unsigned char data23_is_present:1; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f12_control_41 { + union { + struct { + unsigned char enable_registration:1; + unsigned char template_index:4; + unsigned char begin:1; + unsigned char f12_ctrl41_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_udg_handle { + atomic_t attn_event; + unsigned char intr_mask; + unsigned char report_flags; + unsigned char object_type_enable1; + unsigned char object_type_enable2; + unsigned char trace_size; + unsigned char template_index; + unsigned char max_num_templates; + unsigned char detection_score; + unsigned char detection_index; + unsigned char detection_status; + unsigned char registration_status; + unsigned char *ctrl_buf; + unsigned char *trace_data_buf; + unsigned char *template_data_buf; +#ifdef STORE_GESTURES + unsigned char gestures_to_store; + unsigned char *storage_buf; + unsigned char valid_buf[2]; +#endif + unsigned short trace_data_buf_size; + unsigned short template_size; + unsigned short template_data_size; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short ctrl_18_sub10_off; + unsigned short ctrl_20_sub1_off; + unsigned short ctrl_23_sub3_off; + unsigned short ctrl_27_sub5_off; + struct input_dev *udg_dev; + struct kobject *tuning_dir; + struct udg_addr addr; + struct udg_tuning tuning; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct device_attribute attrs[] = { + __ATTR(engine_enable, 0220, + synaptics_rmi4_show_error, + udg_sysfs_engine_enable_store), + __ATTR(detection_enable, 0220, + synaptics_rmi4_show_error, + udg_sysfs_detection_enable_store), + __ATTR(detection_score, 0444, + udg_sysfs_detection_score_show, + synaptics_rmi4_store_error), + __ATTR(detection_index, 0444, + udg_sysfs_detection_index_show, + synaptics_rmi4_store_error), + __ATTR(registration_enable, 0220, + synaptics_rmi4_show_error, + udg_sysfs_registration_enable_store), + __ATTR(registration_begin, 0220, + synaptics_rmi4_show_error, + udg_sysfs_registration_begin_store), + __ATTR(registration_status, 0444, + udg_sysfs_registration_status_show, + synaptics_rmi4_store_error), + __ATTR(template_size, 0444, + udg_sysfs_template_size_show, + synaptics_rmi4_store_error), + __ATTR(template_max_index, 0444, + udg_sysfs_template_max_index_show, + synaptics_rmi4_store_error), + __ATTR(template_detection, 0444, + udg_sysfs_template_detection_show, + synaptics_rmi4_store_error), + __ATTR(template_index, 0220, + synaptics_rmi4_show_error, + udg_sysfs_template_index_store), + __ATTR(template_valid, 0664, + udg_sysfs_template_valid_show, + udg_sysfs_template_valid_store), + __ATTR(template_clear, 0220, + synaptics_rmi4_show_error, + udg_sysfs_template_clear_store), + __ATTR(trace_size, 0444, + udg_sysfs_trace_size_show, + synaptics_rmi4_store_error), +}; + +static struct bin_attribute template_data = { + .attr = { + .name = "template_data", + .mode = 0664, + }, + .size = 0, + .read = udg_sysfs_template_data_show, + .write = udg_sysfs_template_data_store, +}; + +static struct bin_attribute trace_data = { + .attr = { + .name = "trace_data", + .mode = 0444, + }, + .size = 0, + .read = udg_sysfs_trace_data_show, + .write = NULL, +}; + +static struct device_attribute params[] = { + __ATTR(template_displacement, 0664, + udg_sysfs_template_displacement_show, + udg_sysfs_template_displacement_store), + __ATTR(rotation_invariance, 0664, + udg_sysfs_rotation_invariance_show, + udg_sysfs_rotation_invariance_store), + __ATTR(scale_invariance, 0664, + udg_sysfs_scale_invariance_show, + udg_sysfs_scale_invariance_store), + __ATTR(threshold_factor, 0664, + udg_sysfs_threshold_factor_show, + udg_sysfs_threshold_factor_store), + __ATTR(match_metric_threshold, 0664, + udg_sysfs_match_metric_threshold_show, + udg_sysfs_match_metric_threshold_store), + __ATTR(max_inter_stroke_time, 0664, + udg_sysfs_max_inter_stroke_time_show, + udg_sysfs_max_inter_stroke_time_store), +}; + +static struct synaptics_rmi4_udg_handle *udg; + +static unsigned char ctrl_18_sub_size[] = {10, 10, 10, 2, 3, 4, 3, 3, 1, 1}; +static unsigned char ctrl_20_sub_size[] = {2}; +static unsigned char ctrl_23_sub_size[] = {1, 1, 1}; +static unsigned char ctrl_27_sub_size[] = {1, 5, 2, 1, 7}; + +DECLARE_COMPLETION(udg_remove_complete); + +static ssize_t udg_sysfs_engine_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool enable; + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + enable = true; + else if (input == 0) + enable = false; + else + return -EINVAL; + + retval = udg_engine_enable(enable); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_detection_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool enable; + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + enable = true; + else if (input == 0) + enable = false; + else + return -EINVAL; + + udg->detection_status = 0; + + retval = udg_detection_enable(enable); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_detection_score_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_score); +} + +static ssize_t udg_sysfs_detection_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_index); +} + +static ssize_t udg_sysfs_registration_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool enable; + unsigned int input; + struct synaptics_rmi4_f12_control_41 control_41; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + enable = true; + else if (input == 0) + enable = false; + else + return -EINVAL; + + if (enable) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[0] = 0; + udg->ctrl_buf[0] |= (1 << CTRL23_FINGER_REPORT_ENABLE_BIT); + if (udg->ctrl_23_sub3_off) + udg->ctrl_buf[udg->ctrl_23_sub3_off] = 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[0] = udg->object_type_enable1; + if (udg->ctrl_23_sub3_off) { + udg->ctrl_buf[udg->ctrl_23_sub3_off] = + udg->object_type_enable2; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + control_41.enable_registration = enable ? 1 : 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_registration_begin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool begin; + unsigned int input; + struct synaptics_rmi4_f12_control_41 control_41; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + begin = true; + else if (input == 0) + begin = false; + else + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + control_41.begin = begin ? 1 : 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_registration_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%02x\n", udg->registration_status); +} + +static ssize_t udg_sysfs_template_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->template_size); +} + +static ssize_t udg_sysfs_template_max_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->max_num_templates - 1); +} + +static ssize_t udg_sysfs_template_detection_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + int attn_event; + unsigned char detection_status; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + attn_event = atomic_read(&udg->attn_event); + atomic_set(&udg->attn_event, 0); + + if (attn_event == 0) + return snprintf(buf, PAGE_SIZE, "0\n"); + + if (udg->detection_status == 0) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.data_4, + rmi4_data->gesture_detection, + sizeof(rmi4_data->gesture_detection)); + if (retval < 0) + return retval; + + udg->detection_status = rmi4_data->gesture_detection[0]; + } + + detection_status = udg->detection_status; + udg->detection_status = 0; + + switch (detection_status) { + case DETECTION: + udg->detection_score = rmi4_data->gesture_detection[1]; + udg->detection_index = rmi4_data->gesture_detection[4]; + udg->trace_size = rmi4_data->gesture_detection[3]; + break; + case REGISTRATION: + udg->registration_status = rmi4_data->gesture_detection[1]; + udg->trace_size = rmi4_data->gesture_detection[3]; + break; + default: + return snprintf(buf, PAGE_SIZE, "0\n"); + } + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", detection_status); +} + +static ssize_t udg_sysfs_template_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long index; + + retval = sstrtoul(buf, 10, &index); + if (retval) + return retval; + + retval = udg_set_index((unsigned char)index); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_template_valid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned char valid; + unsigned char offset; + unsigned char byte_num; + unsigned char template_flags[2]; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + byte_num = udg->template_index / 8; + offset = udg->template_index % 8; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_flags, + template_flags, + sizeof(template_flags)); + if (retval < 0) + return retval; + + valid = (template_flags[byte_num] & (1 << offset)) >> offset; + + return snprintf(buf, PAGE_SIZE, "%u\n", valid); +} + +static ssize_t udg_sysfs_template_valid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long valid; + unsigned char offset; + unsigned char byte_num; + unsigned char template_flags[2]; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = sstrtoul(buf, 10, &valid); + if (retval) + return retval; + + if (valid > 0) + valid = 1; + + byte_num = udg->template_index / 8; + offset = udg->template_index % 8; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_flags, + template_flags, + sizeof(template_flags)); + if (retval < 0) + return retval; + + if (valid) + template_flags[byte_num] |= (1 << offset); + else + template_flags[byte_num] &= ~(1 << offset); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_flags, + template_flags, + sizeof(template_flags)); + if (retval < 0) + return retval; + +#ifdef STORE_GESTURES + udg_read_valid_data(); +#endif + + return count; +} + +static ssize_t udg_sysfs_template_clear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + const char cmd[] = {'0', 0}; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + memset(udg->template_data_buf, 0x00, udg->template_data_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_data, + udg->template_data_buf, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to clear template data\n", + __func__); + return retval; + } + + retval = udg_sysfs_template_valid_store(dev, attr, cmd, 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to clear valid bit\n", + __func__); + return retval; + } + +#ifdef STORE_GESTURES + udg_read_template_data(udg->template_index); + udg_read_valid_data(); +#endif + + return count; +} + +static ssize_t udg_sysfs_trace_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->trace_size); +} + +static ssize_t udg_sysfs_trace_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned short index = 0; + unsigned short trace_data_size; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + trace_data_size = udg->trace_size * 5; + + if (trace_data_size == 0) + return -EINVAL; + + if (count < trace_data_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, (unsigned int)count); + return -EINVAL; + } + + if (udg->trace_data_buf_size < trace_data_size) { + if (udg->trace_data_buf_size) + kfree(udg->trace_data_buf); + udg->trace_data_buf = kzalloc(trace_data_size, GFP_KERNEL); + if (!udg->trace_data_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for trace data buffer\n", + __func__); + udg->trace_data_buf_size = 0; + return -ENOMEM; + } + udg->trace_data_buf_size = trace_data_size; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.trace_x, + &udg->trace_data_buf[index], + udg->trace_size * 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read trace X data\n", + __func__); + return retval; + } + index += udg->trace_size * 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.trace_y, + &udg->trace_data_buf[index], + udg->trace_size * 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read trace Y data\n", + __func__); + return retval; + } + index += udg->trace_size * 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.trace_segment, + &udg->trace_data_buf[index], + udg->trace_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read trace segment data\n", + __func__); + return retval; + } + + retval = secure_memcpy(buf, count, udg->trace_data_buf, + udg->trace_data_buf_size, trace_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy trace data\n", + __func__); + return retval; + } + + return trace_data_size; +} + +static ssize_t udg_sysfs_template_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (count < udg->template_data_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, (unsigned int)count); + return -EINVAL; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_data, + udg->template_data_buf, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read template data\n", + __func__); + return retval; + } + + retval = secure_memcpy(buf, count, udg->template_data_buf, + udg->template_data_size, udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy template data\n", + __func__); + return retval; + } + +#ifdef STORE_GESTURES + udg_read_template_data(udg->template_index); + udg_read_valid_data(); +#endif + + return udg->template_data_size; +} + +static ssize_t udg_sysfs_template_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = secure_memcpy(udg->template_data_buf, udg->template_data_size, + buf, count, count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy template data\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_data, + udg->template_data_buf, + count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write template data\n", + __func__); + return retval; + } + +#ifdef STORE_GESTURES + udg_read_template_data(udg->template_index); + udg_read_valid_data(); +#endif + + return count; +} + +static ssize_t udg_sysfs_template_displacement_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short template_displacement; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + template_displacement = + ((unsigned short)udg->tuning.template_disp_lsb << 0) | + ((unsigned short)udg->tuning.template_disp_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", template_displacement); +} + +static ssize_t udg_sysfs_template_displacement_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.template_disp_lsb = (unsigned char)(input >> 0); + udg->tuning.template_disp_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short rotation_invariance; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + rotation_invariance = + ((unsigned short)udg->tuning.rotation_inv_lsb << 0) | + ((unsigned short)udg->tuning.rotation_inv_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", rotation_invariance); +} + +static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.rotation_inv_lsb = (unsigned char)(input >> 0); + udg->tuning.rotation_inv_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_scale_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short scale_invariance; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + scale_invariance = + ((unsigned short)udg->tuning.scale_inv_lsb << 0) | + ((unsigned short)udg->tuning.scale_inv_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", scale_invariance); +} + +static ssize_t udg_sysfs_scale_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.scale_inv_lsb = (unsigned char)(input >> 0); + udg->tuning.scale_inv_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_threshold_factor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short threshold_factor; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + threshold_factor = + ((unsigned short)udg->tuning.thres_factor_lsb << 0) | + ((unsigned short)udg->tuning.thres_factor_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", threshold_factor); +} + +static ssize_t udg_sysfs_threshold_factor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.thres_factor_lsb = (unsigned char)(input >> 0); + udg->tuning.thres_factor_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short match_metric_threshold; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + match_metric_threshold = + ((unsigned short)udg->tuning.metric_thres_lsb << 0) | + ((unsigned short)udg->tuning.metric_thres_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", match_metric_threshold); +} + +static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.metric_thres_lsb = (unsigned char)(input >> 0); + udg->tuning.metric_thres_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short max_inter_stroke_time; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + max_inter_stroke_time = + ((unsigned short)udg->tuning.inter_stroke_lsb << 0) | + ((unsigned short)udg->tuning.inter_stroke_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", max_inter_stroke_time); +} + +static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.inter_stroke_lsb = (unsigned char)(input >> 0); + udg->tuning.inter_stroke_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static int udg_ctrl_subpacket(unsigned char ctrlreg, + unsigned char subpacket, + struct synaptics_rmi4_f12_query_5 *query_5) +{ + int retval; + unsigned char cnt; + unsigned char regnum; + unsigned char bitnum; + unsigned char q5_index; + unsigned char q6_index; + unsigned char offset; + unsigned char max_ctrlreg; + unsigned char *query_6; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + max_ctrlreg = (sizeof(query_5->data) - 1) * 8 - 1; + + if (ctrlreg > max_ctrlreg) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Control register number (%d) over limit\n", + __func__, ctrlreg); + return -EINVAL; + } + + q5_index = ctrlreg / 8 + 1; + bitnum = ctrlreg % 8; + if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Control %d is not present\n", + __func__, ctrlreg); + return -EINVAL; + } + + query_6 = kmalloc(query_5->size_of_query6, GFP_KERNEL); + if (!query_6) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query 6\n", + __func__); + return -ENOMEM; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 6, + query_6, + query_5->size_of_query6); + if (retval < 0) + goto exit; + + q6_index = 0; + + for (regnum = 0; regnum < ctrlreg; regnum++) { + q5_index = regnum / 8 + 1; + bitnum = regnum % 8; + if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) + continue; + + if (query_6[q6_index] == 0x00) + q6_index += 3; + else + q6_index++; + + while (query_6[q6_index] & ~MASK_7BIT) + q6_index++; + + q6_index++; + } + + cnt = 0; + q6_index++; + offset = subpacket / 7; + bitnum = subpacket % 7; + + do { + if (cnt == offset) { + if (query_6[q6_index + cnt] & (1 << bitnum)) + retval = 1; + else + retval = 0; + goto exit; + } + cnt++; + } while (query_6[q6_index + cnt - 1] & ~MASK_7BIT); + + retval = 0; + +exit: + kfree(query_6); + + return retval; +} + +static int udg_read_tuning_params(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_18, + udg->ctrl_buf, + udg->ctrl_18_sub10_off + sizeof(struct udg_tuning)); + if (retval < 0) + return retval; + + secure_memcpy(udg->tuning.data, + sizeof(udg->tuning.data), + (unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off], + sizeof(struct udg_tuning), + sizeof(struct udg_tuning)); + + return 0; +} + +static int udg_write_tuning_params(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + secure_memcpy((unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off], + sizeof(struct udg_tuning), + udg->tuning.data, + sizeof(udg->tuning.data), + sizeof(struct udg_tuning)); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_18, + udg->ctrl_buf, + udg->ctrl_18_sub10_off + sizeof(struct udg_tuning)); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_detection_enable(bool enable) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_20, + udg->ctrl_buf, + udg->ctrl_20_sub1_off + 1); + if (retval < 0) + return retval; + + if (enable) + udg->ctrl_buf[udg->ctrl_20_sub1_off] = WAKEUP_GESTURE_MODE; + else + udg->ctrl_buf[udg->ctrl_20_sub1_off] = udg->report_flags; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_20, + udg->ctrl_buf, + udg->ctrl_20_sub1_off + 1); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_engine_enable(bool enable) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (enable) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[udg->ctrl_27_sub5_off] |= + (1 << CTRL27_UDG_ENABLE_BIT); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[udg->ctrl_27_sub5_off] &= + ~(1 << CTRL27_UDG_ENABLE_BIT); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + } + + return 0; +} + +static void udg_report(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + atomic_set(&udg->attn_event, 1); + + if (rmi4_data->suspend) { + if (rmi4_data->gesture_detection[0] == 0) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.data_4, + rmi4_data->gesture_detection, + sizeof(rmi4_data->gesture_detection)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read gesture detection\n", + __func__); + return; + } + } + + udg->detection_status = rmi4_data->gesture_detection[0]; + rmi4_data->gesture_detection[0] = 0; + + if (udg->detection_status == DETECTION) { + input_report_key(udg->udg_dev, KEY_WAKEUP, 1); + input_sync(udg->udg_dev); + input_report_key(udg->udg_dev, KEY_WAKEUP, 0); + input_sync(udg->udg_dev); + rmi4_data->suspend = false; + } + } + + return; +} + +static int udg_set_index(unsigned char index) +{ + int retval; + struct synaptics_rmi4_f12_control_41 control_41; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (index >= udg->max_num_templates) + return -EINVAL; + + udg->template_index = index; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + control_41.template_index = udg->template_index; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + return 0; +} + +#ifdef STORE_GESTURES +static int udg_read_valid_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_flags, + udg->valid_buf, + sizeof(udg->valid_buf)); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_write_valid_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_flags, + udg->valid_buf, + sizeof(udg->valid_buf)); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_read_template_data(unsigned char index) +{ + int retval; + unsigned char *storage; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + udg_set_index(index); + storage = &(udg->storage_buf[index * udg->template_data_size]); + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_data, + storage, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read template data\n", + __func__); + return retval; + } + + return 0; +} + +static int udg_write_template_data(void) +{ + int retval; + unsigned char ii; + unsigned char *storage; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + for (ii = 0; ii < udg->gestures_to_store; ii++) { + udg_set_index(ii); + storage = &(udg->storage_buf[ii * udg->template_data_size]); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_data, + storage, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write template data\n", + __func__); + return retval; + } + } + + return 0; +} +#endif + +static int udg_reg_init(void) +{ + int retval; + unsigned char ii; + unsigned char data_offset; + unsigned char size_of_query; + unsigned char ctrl_18_offset; + unsigned char ctrl_20_offset; + unsigned char ctrl_23_offset; + unsigned char ctrl_27_offset; + unsigned char ctrl_41_offset; + struct synaptics_rmi4_f12_query_0 query_0; + struct synaptics_rmi4_f12_query_5 query_5; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 7, + &size_of_query, + sizeof(size_of_query)); + if (retval < 0) + return retval; + + if (size_of_query < 4) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: User defined gesture support unavailable (missing data registers)\n", + __func__); + retval = -ENODEV; + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 8, + query_8.data, + sizeof(query_8.data)); + if (retval < 0) + return retval; + + if ((query_8.data16_is_present) && + (query_8.data17_is_present) && + (query_8.data18_is_present) && + (query_8.data19_is_present) && + (query_8.data20_is_present) && + (query_8.data21_is_present)) { + data_offset = query_8.data0_is_present + + query_8.data1_is_present + + query_8.data2_is_present + + query_8.data3_is_present; + udg->addr.data_4 = udg->data_base_addr + data_offset; + data_offset = data_offset + + query_8.data4_is_present + + query_8.data5_is_present + + query_8.data6_is_present + + query_8.data7_is_present + + query_8.data8_is_present + + query_8.data9_is_present + + query_8.data10_is_present + + query_8.data11_is_present + + query_8.data12_is_present + + query_8.data13_is_present + + query_8.data14_is_present + + query_8.data15_is_present; + udg->addr.trace_x = udg->data_base_addr + data_offset; + udg->addr.trace_y = udg->addr.trace_x + 1; + udg->addr.trace_segment = udg->addr.trace_y + 1; + udg->addr.template_helper = udg->addr.trace_segment + 1; + udg->addr.template_data = udg->addr.template_helper + 1; + udg->addr.template_flags = udg->addr.template_data + 1; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: User defined gesture support unavailable (missing data registers)\n", + __func__); + retval = -ENODEV; + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 4, + &size_of_query, + sizeof(size_of_query)); + if (retval < 0) + return retval; + + if (size_of_query < 7) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: User defined gesture support unavailable (missing control registers)\n", + __func__); + retval = -ENODEV; + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 5, + query_5.data, + sizeof(query_5.data)); + if (retval < 0) + return retval; + + ctrl_18_offset = query_5.ctrl0_is_present + + query_5.ctrl1_is_present + + query_5.ctrl2_is_present + + query_5.ctrl3_is_present + + query_5.ctrl4_is_present + + query_5.ctrl5_is_present + + query_5.ctrl6_is_present + + query_5.ctrl7_is_present + + query_5.ctrl8_is_present + + query_5.ctrl9_is_present + + query_5.ctrl10_is_present + + query_5.ctrl11_is_present + + query_5.ctrl12_is_present + + query_5.ctrl13_is_present + + query_5.ctrl14_is_present + + query_5.ctrl15_is_present + + query_5.ctrl16_is_present + + query_5.ctrl17_is_present; + + ctrl_20_offset = ctrl_18_offset + + query_5.ctrl18_is_present + + query_5.ctrl19_is_present; + + ctrl_23_offset = ctrl_20_offset + + query_5.ctrl20_is_present + + query_5.ctrl21_is_present + + query_5.ctrl22_is_present; + + ctrl_27_offset = ctrl_23_offset+ + query_5.ctrl23_is_present + + query_5.ctrl24_is_present + + query_5.ctrl25_is_present + + query_5.ctrl26_is_present; + + ctrl_41_offset = ctrl_27_offset+ + query_5.ctrl27_is_present + + query_5.ctrl28_is_present + + query_5.ctrl29_is_present + + query_5.ctrl30_is_present + + query_5.ctrl31_is_present + + query_5.ctrl32_is_present + + query_5.ctrl33_is_present + + query_5.ctrl34_is_present + + query_5.ctrl35_is_present + + query_5.ctrl36_is_present + + query_5.ctrl37_is_present + + query_5.ctrl38_is_present + + query_5.ctrl39_is_present + + query_5.ctrl40_is_present; + + udg->addr.ctrl_18 = udg->control_base_addr + ctrl_18_offset; + udg->addr.ctrl_20 = udg->control_base_addr + ctrl_20_offset; + udg->addr.ctrl_23 = udg->control_base_addr + ctrl_23_offset; + udg->addr.ctrl_27 = udg->control_base_addr + ctrl_27_offset; + udg->addr.ctrl_41 = udg->control_base_addr + ctrl_41_offset; + + udg->ctrl_18_sub10_off = 0; + for (ii = 0; ii < 10; ii++) { + retval = udg_ctrl_subpacket(18, ii, &query_5); + if (retval == 1) + udg->ctrl_18_sub10_off += ctrl_18_sub_size[ii]; + else if (retval < 0) + return retval; + } + + udg->ctrl_20_sub1_off = 0; + for (ii = 0; ii < 1; ii++) { + retval = udg_ctrl_subpacket(20, ii, &query_5); + if (retval == 1) + udg->ctrl_20_sub1_off += ctrl_20_sub_size[ii]; + else if (retval < 0) + return retval; + } + + udg->ctrl_23_sub3_off = 0; + for (ii = 0; ii < 3; ii++) { + retval = udg_ctrl_subpacket(23, ii, &query_5); + if (retval == 1) + udg->ctrl_23_sub3_off += ctrl_23_sub_size[ii]; + else if (retval < 0) + return retval; + } + + retval = udg_ctrl_subpacket(23, 3, &query_5); + if (retval == 0) + udg->ctrl_23_sub3_off = 0; + else if (retval < 0) + return retval; + + udg->ctrl_27_sub5_off = 0; + for (ii = 0; ii < 5; ii++) { + retval = udg_ctrl_subpacket(27, ii, &query_5); + if (retval == 1) + udg->ctrl_27_sub5_off += ctrl_27_sub_size[ii]; + else if (retval < 0) + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 0, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) + return retval; + + udg->max_num_templates = query_0.max_num_templates; + udg->template_size = + ((unsigned short)query_0.template_size_lsb << 0) | + ((unsigned short)query_0.template_size_msb << 8); + udg->template_data_size = udg->template_size * 4 * 2 + 4 + 1; + +#ifdef STORE_GESTURES + udg->gestures_to_store = udg->max_num_templates; + if (GESTURES_TO_STORE < udg->gestures_to_store) + udg->gestures_to_store = GESTURES_TO_STORE; +#endif + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_20, + udg->ctrl_buf, + udg->ctrl_20_sub1_off + 1); + if (retval < 0) + return retval; + + udg->report_flags = udg->ctrl_buf[udg->ctrl_20_sub1_off]; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + + udg->object_type_enable1 = udg->ctrl_buf[0]; + if (udg->ctrl_23_sub3_off) + udg->object_type_enable2 = udg->ctrl_buf[udg->ctrl_23_sub3_off]; + + return retval; +} + +static int udg_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + struct synaptics_rmi4_fn_desc fd; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&fd, + sizeof(fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, fd.fn_number); + switch (fd.fn_number) { + case SYNAPTICS_RMI4_F12: + goto f12_found; + break; + } + } else { + break; + } + + intr_count += fd.intr_src_count; + } + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F12\n", + __func__); + return -EINVAL; + +f12_found: + udg->query_base_addr = fd.query_base_addr | (page << 8); + udg->control_base_addr = fd.ctrl_base_addr | (page << 8); + udg->data_base_addr = fd.data_base_addr | (page << 8); + udg->command_base_addr = fd.cmd_base_addr | (page << 8); + + retval = udg_reg_init(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize user defined gesture registers\n", + __func__); + return retval; + } + + udg->intr_mask = 0; + intr_src = fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + udg->intr_mask |= 1 << ii; + } + + rmi4_data->intr_mask[0] |= udg->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &rmi4_data->intr_mask[0], + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static void synaptics_rmi4_udg_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!udg) + return; + + if (udg->intr_mask & intr_mask) + udg_report(); + + return; +} + +static int synaptics_rmi4_udg_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char size; + unsigned char attr_count; + unsigned char param_count; + + if (udg) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + udg = kzalloc(sizeof(*udg), GFP_KERNEL); + if (!udg) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for udg\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + size = 0; + for (ii = 0; ii < sizeof(ctrl_18_sub_size); ii++) + size += ctrl_18_sub_size[ii]; + size += sizeof(struct udg_tuning); + udg->ctrl_buf = kzalloc(size, GFP_KERNEL); + if (!udg->ctrl_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_buf\n", + __func__); + retval = -ENOMEM; + goto exit_free_udg; + } + + udg->rmi4_data = rmi4_data; + + retval = udg_scan_pdt(); + if (retval < 0) + goto exit_free_ctrl_buf; + + udg->template_data_buf = kzalloc(udg->template_data_size, GFP_KERNEL); + if (!udg->template_data_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for template_data_buf\n", + __func__); + retval = -ENOMEM; + goto exit_free_ctrl_buf; + } + +#ifdef STORE_GESTURES + udg->storage_buf = kzalloc( + udg->template_data_size * udg->gestures_to_store, + GFP_KERNEL); + if (!udg->storage_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for storage_buf\n", + __func__); + kfree(udg->template_data_buf); + retval = -ENOMEM; + goto exit_free_ctrl_buf; + } +#endif + + udg->udg_dev = input_allocate_device(); + if (udg->udg_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate gesture device\n", + __func__); + retval = -ENOMEM; + goto exit_free_template_data_buf; + } + + udg->udg_dev->name = GESTURE_DRIVER_NAME; + udg->udg_dev->phys = GESTURE_PHYS_NAME; + udg->udg_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + udg->udg_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + udg->udg_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(udg->udg_dev, rmi4_data); + + set_bit(EV_KEY, udg->udg_dev->evbit); + set_bit(KEY_WAKEUP, udg->udg_dev->keybit); + input_set_capability(udg->udg_dev, EV_KEY, KEY_WAKEUP); + + retval = input_register_device(udg->udg_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register gesture device\n", + __func__); + input_free_device(udg->udg_dev); + goto exit_free_template_data_buf; + } + + udg->tuning_dir = kobject_create_and_add(TUNING_SYSFS_DIR_NAME, + &udg->udg_dev->dev.kobj); + if (!udg->tuning_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create tuning sysfs directory\n", + __func__); + goto exit_unregister_input_device; + } + + retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &template_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create template data bin file\n", + __func__); + goto exit_remove_sysfs_directory; + } + + retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &trace_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create trace data bin file\n", + __func__); + goto exit_remove_bin_file; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&udg->udg_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + for (param_count = 0; param_count < ARRAY_SIZE(params); param_count++) { + retval = sysfs_create_file(udg->tuning_dir, + ¶ms[param_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create tuning parameters\n", + __func__); + retval = -ENODEV; + goto exit_remove_params; + } + } + + retval = udg_engine_enable(true); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable gesture engine\n", + __func__); + goto exit_remove_params; + } + + return 0; + +exit_remove_params: + for (param_count--; param_count >= 0; param_count--) { + sysfs_remove_file(udg->tuning_dir, + ¶ms[param_count].attr); + } + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&udg->udg_dev->dev.kobj, + &attrs[attr_count].attr); + } + + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data); + +exit_remove_bin_file: + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data); + +exit_remove_sysfs_directory: + kobject_put(udg->tuning_dir); + +exit_unregister_input_device: + input_unregister_device(udg->udg_dev); + +exit_free_template_data_buf: +#ifdef STORE_GESTURES + kfree(udg->storage_buf); +#endif + kfree(udg->template_data_buf); + +exit_free_ctrl_buf: + kfree(udg->ctrl_buf); + +exit_free_udg: + kfree(udg); + udg = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_udg_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char count; + + if (!udg) + goto exit; + + for (count = 0; count < ARRAY_SIZE(params); count++) { + sysfs_remove_file(udg->tuning_dir, + ¶ms[count].attr); + } + + for (count = 0; count < ARRAY_SIZE(attrs); count++) { + sysfs_remove_file(&udg->udg_dev->dev.kobj, + &attrs[count].attr); + } + + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data); + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data); + kobject_put(udg->tuning_dir); + + input_unregister_device(udg->udg_dev); +#ifdef STORE_GESTURES + kfree(udg->storage_buf); +#endif + kfree(udg->template_data_buf); + kfree(udg->trace_data_buf); + kfree(udg->ctrl_buf); + kfree(udg); + udg = NULL; + +exit: + complete(&udg_remove_complete); +} + +static void synaptics_rmi4_udg_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) { + synaptics_rmi4_udg_init(rmi4_data); + return; + } + + udg_scan_pdt(); + udg_engine_enable(true); +#ifdef STORE_GESTURES + udg_write_template_data(); + udg_write_valid_data(); +#endif +} + +static void synaptics_rmi4_udg_reinit(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + udg_engine_enable(true); +#ifdef STORE_GESTURES + udg_write_template_data(); + udg_write_valid_data(); +#endif +} + +static void synaptics_rmi4_udg_e_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + rmi4_data->sleep_enable(rmi4_data, false); + rmi4_data->irq_enable(rmi4_data, true, false); + enable_irq_wake(rmi4_data->irq); + + udg_engine_enable(true); + udg_detection_enable(true); +} + +static void synaptics_rmi4_udg_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + rmi4_data->sleep_enable(rmi4_data, false); + rmi4_data->irq_enable(rmi4_data, true, false); + enable_irq_wake(rmi4_data->irq); + + udg_engine_enable(true); + udg_detection_enable(true); +} + +static void synaptics_rmi4_udg_resume(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + disable_irq_wake(rmi4_data->irq); + udg_detection_enable(false); +} + +static void synaptics_rmi4_udg_l_resume(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + disable_irq_wake(rmi4_data->irq); + udg_detection_enable(false); +} + +static struct synaptics_rmi4_exp_fn gesture_module = { + .fn_type = RMI_GESTURE, + .init = synaptics_rmi4_udg_init, + .remove = synaptics_rmi4_udg_remove, + .reset = synaptics_rmi4_udg_reset, + .reinit = synaptics_rmi4_udg_reinit, + .early_suspend = synaptics_rmi4_udg_e_suspend, + .suspend = synaptics_rmi4_udg_suspend, + .resume = synaptics_rmi4_udg_resume, + .late_resume = synaptics_rmi4_udg_l_resume, + .attn = synaptics_rmi4_udg_attn, +}; + +static int __init rmi4_gesture_module_init(void) +{ + synaptics_rmi4_new_function(&gesture_module, true); + + return 0; +} + +static void __exit rmi4_gesture_module_exit(void) +{ + synaptics_rmi4_new_function(&gesture_module, false); + + wait_for_completion(&udg_remove_complete); +} + +module_init(rmi4_gesture_module_init); +module_exit(rmi4_gesture_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX User Defined Gesture Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_i2c.c b/synaptics_dsx/synaptics_dsx_i2c.c new file mode 100644 index 0000000000..bc37323c1c --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_i2c.c @@ -0,0 +1,672 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" +#include "linux/moduleparam.h" + +#define SYN_I2C_RETRY_TIMES 10 +#define rd_msgs 1 + +#ifdef CONFIG_DRM +#include +struct drm_panel *active_panel; +#endif + +static unsigned char *wr_buf; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_i2c_device; + + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) +{ + int retval; + u32 value; + const char *name; + struct property *prop; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + + retval = of_property_read_u32(np, "synaptics,irq-on-state", + &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n", + __func__); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n", + __func__); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n", + __func__); + return retval; + } + bdata->reset_on_state = value; + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n", + __func__); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n", + __func__); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,max-y-for-2d", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,max-y-for-2d", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n", + __func__); + return retval; + } + bdata->max_y_for_2d = value; + } else { + bdata->max_y_for_2d = -1; + } + + bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes"); + bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip"); + bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip"); + + prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ub-i2c-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n", + __func__); + return retval; + } + bdata->ub_i2c_addr = (unsigned short)value; + } else { + bdata->ub_i2c_addr = -1; + } + + prop = of_find_property(np, "synaptics,cap-button-codes", NULL); + if (prop && prop->length) { + bdata->cap_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->cap_button_map->map) + return -ENOMEM; + bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); + retval = of_property_read_u32_array(np, + "synaptics,cap-button-codes", + bdata->cap_button_map->map, + bdata->cap_button_map->nbuttons); + if (retval < 0) { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + } else { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + + prop = of_find_property(np, "synaptics,vir-button-codes", NULL); + if (prop && prop->length) { + bdata->vir_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->vir_button_map->map) + return -ENOMEM; + bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); + bdata->vir_button_map->nbuttons /= 5; + retval = of_property_read_u32_array(np, + "synaptics,vir-button-codes", + bdata->vir_button_map->map, + bdata->vir_button_map->nbuttons * 5); + if (retval < 0) { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + } else { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + + return 0; +} +#endif + +static int synaptics_rmi4_i2c_alloc_buf(struct synaptics_rmi4_data *rmi4_data, + unsigned int count) +{ + static unsigned int buf_size; + + if (count > buf_size) { + if (buf_size) + kfree(wr_buf); + wr_buf = kzalloc(count, GFP_KERNEL); + if (!wr_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for buffer\n", + __func__); + buf_size = 0; + return -ENOMEM; + } + buf_size = count; + } + + return 0; +} + +static void synaptics_rmi4_i2c_check_addr(struct synaptics_rmi4_data *rmi4_data, + struct i2c_client *i2c) +{ + if (hw_if.board_data->ub_i2c_addr == -1) + return; + + if (hw_if.board_data->i2c_addr == i2c->addr) + hw_if.board_data->i2c_addr = hw_if.board_data->ub_i2c_addr; + else + hw_if.board_data->i2c_addr = i2c->addr; +} + +static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr) +{ + int retval = 0; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[2]; + + msg[0].addr = hw_if.board_data->i2c_addr; + msg[0].flags = 0; + msg[0].len = PAGE_SELECT_LEN; + msg[0].buf = buf; + + page = ((addr >> 8) & MASK_8BIT); + buf[0] = MASK_8BIT; + buf[1] = page; + + if (page != rmi4_data->current_page) { + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) { + rmi4_data->current_page = page; + retval = PAGE_SELECT_LEN; + break; + } + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + + if (retry == SYN_I2C_RETRY_TIMES / 2) { + synaptics_rmi4_i2c_check_addr(rmi4_data, i2c); + msg[0].addr = hw_if.board_data->i2c_addr; + } + } + } else { + retval = PAGE_SELECT_LEN; + } + + return retval; +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval = 0; + unsigned char retry; + unsigned char buf; + unsigned char index = 0; + unsigned char xfer_msgs; + unsigned char remaining_msgs; + unsigned short i2c_addr; + unsigned short data_offset = 0; + unsigned int remaining_length = length; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_adapter *adap = i2c->adapter; + struct i2c_msg msg[rd_msgs + 1]; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + msg[0].addr = hw_if.board_data->i2c_addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf; + msg[rd_msgs].addr = hw_if.board_data->i2c_addr; + msg[rd_msgs].flags = I2C_M_RD; + msg[rd_msgs].len = (unsigned short)remaining_length; + msg[rd_msgs].buf = &data[data_offset]; + + buf = addr & MASK_8BIT; + + remaining_msgs = rd_msgs + 1; + + while (remaining_msgs) { + xfer_msgs = remaining_msgs; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_transfer(adap, &msg[index], xfer_msgs); + if (retval == xfer_msgs) + break; + + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + + if (retry == SYN_I2C_RETRY_TIMES / 2) { + synaptics_rmi4_i2c_check_addr(rmi4_data, i2c); + i2c_addr = hw_if.board_data->i2c_addr; + msg[0].addr = i2c_addr; + msg[rd_msgs].addr = i2c_addr; + } + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C read over retry limit\n", + __func__); + retval = -EIO; + goto exit; + } + + remaining_msgs -= xfer_msgs; + index += xfer_msgs; + } + + retval = length; + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char retry; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[2]; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_i2c_alloc_buf(rmi4_data, length + 1); + if (retval < 0) + goto exit; + + retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + msg[0].addr = hw_if.board_data->i2c_addr; + msg[0].flags = 0; + msg[0].len = (unsigned short)(length + 1); + msg[0].buf = wr_buf; + + wr_buf[0] = addr & MASK_8BIT; + retval = secure_memcpy(&wr_buf[1], length, &data[0], length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + goto exit; + } + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) { + retval = length; + break; + } + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + + if (retry == SYN_I2C_RETRY_TIMES / 2) { + synaptics_rmi4_i2c_check_addr(rmi4_data, i2c); + msg[0].addr = hw_if.board_data->i2c_addr; + } + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C write over retry limit\n", + __func__); + retval = -EIO; + } + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +#ifdef CONFIG_DRM +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 0; + + 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 0; + } + } + + return PTR_ERR(panel); +} +#endif + +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 struct synaptics_dsx_bus_access bus_access = { + .type = BUS_I2C, + .read = synaptics_rmi4_i2c_read, + .write = synaptics_rmi4_i2c_write, +}; + +static void synaptics_rmi4_i2c_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_i2c_device); +} + +static int synaptics_rmi4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + struct device_node *dp = client->dev.of_node; + +#ifdef CONFIG_DRM + retval = check_dt(dp); + if (retval == -EPROBE_DEFER) + return retval; + + if (retval) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + retval = -EPROBE_DEFER; + else + retval = -ENODEV; + + return retval; + } +#endif + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data commands not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_i2c_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_i2c_device) { + dev_err(&client->dev, + "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF + if (client->dev.of_node) { + hw_if.board_data = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!hw_if.board_data) { + dev_err(&client->dev, + "%s: Failed to allocate memory for board data\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->cap_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for 0D button map\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->vir_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for virtual button map\n", + __func__); + return -ENOMEM; + } + parse_dt(&client->dev, hw_if.board_data); + } +#else + hw_if.board_data = client->dev.platform_data; +#endif + + hw_if.bus_access = &bus_access; + hw_if.board_data->i2c_addr = client->addr; + + synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_i2c_device->id = 0; + synaptics_dsx_i2c_device->num_resources = 0; + synaptics_dsx_i2c_device->dev.parent = &client->dev; + synaptics_dsx_i2c_device->dev.platform_data = &hw_if; + synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release; + + retval = platform_device_register(synaptics_dsx_i2c_device); + if (retval) { + dev_err(&client->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(synaptics_dsx_i2c_device); + + return 0; +} + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {I2C_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_of_match_table[] = { + { + .compatible = "synaptics,dsx-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); +#else +#define synaptics_rmi4_of_match_table NULL +#endif + +static struct i2c_driver synaptics_rmi4_i2c_driver = { + .driver = { + .name = I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = synaptics_rmi4_of_match_table, + }, + .probe = synaptics_rmi4_i2c_probe, + .remove = synaptics_rmi4_i2c_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + kfree(wr_buf); + + i2c_del_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_proximity.c b/synaptics_dsx/synaptics_dsx_proximity.c new file mode 100644 index 0000000000..9bc18fa8c2 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_proximity.c @@ -0,0 +1,673 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define PROX_PHYS_NAME "synaptics_dsx/proximity" + +#define HOVER_Z_MAX (255) + +#define HOVERING_FINGER_EN (1 << 4) + +static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static struct device_attribute attrs[] = { + __ATTR(hover_finger_en, 0664, + synaptics_rmi4_hover_finger_en_show, + synaptics_rmi4_hover_finger_en_store), +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + }; + unsigned char data[2]; + }; +}; + +struct prox_finger_data { + union { + struct { + unsigned char object_type_and_status; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char z; + } __packed; + unsigned char proximity_data[6]; + }; +}; + +struct synaptics_rmi4_prox_handle { + bool hover_finger_present; + bool hover_finger_en; + unsigned char intr_mask; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short hover_finger_en_addr; + unsigned short hover_finger_data_addr; + struct input_dev *prox_dev; + struct prox_finger_data *finger_data; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_prox_handle *prox; + +DECLARE_COMPLETION(prox_remove_complete); + +static void prox_hover_finger_lift(void) +{ + input_report_key(prox->prox_dev, BTN_TOUCH, 0); + input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0); + input_sync(prox->prox_dev); + prox->hover_finger_present = false; +} + +static void prox_hover_finger_report(void) +{ + int retval; + int x; + int y; + int z; + struct prox_finger_data *data; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + data = prox->finger_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->hover_finger_data_addr, + data->proximity_data, + sizeof(data->proximity_data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read hovering finger data\n", + __func__); + return; + } + + if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) { + if (prox->hover_finger_present) + prox_hover_finger_lift(); + + return; + } + + x = (data->x_msb << 8) | (data->x_lsb); + y = (data->y_msb << 8) | (data->y_lsb); + z = HOVER_Z_MAX - data->z; + + input_report_key(prox->prox_dev, BTN_TOUCH, 0); + input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1); + input_report_abs(prox->prox_dev, ABS_X, x); + input_report_abs(prox->prox_dev, ABS_Y, y); + input_report_abs(prox->prox_dev, ABS_DISTANCE, z); + + input_sync(prox->prox_dev); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: x = %d y = %d z = %d\n", + __func__, x, y, z); + + prox->hover_finger_present = true; +} + +static int prox_set_hover_finger_en(void) +{ + int retval; + unsigned char object_report_enable; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->hover_finger_en_addr, + &object_report_enable, + sizeof(object_report_enable)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read from object report enable register\n", + __func__); + return retval; + } + + if (prox->hover_finger_en) + object_report_enable |= HOVERING_FINGER_EN; + else + object_report_enable &= ~HOVERING_FINGER_EN; + + retval = synaptics_rmi4_reg_write(rmi4_data, + prox->hover_finger_en_addr, + &object_report_enable, + sizeof(object_report_enable)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write to object report enable register\n", + __func__); + return retval; + } + + return 0; +} + +static void prox_set_params(void) +{ + input_set_abs_params(prox->prox_dev, ABS_X, 0, + prox->rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(prox->prox_dev, ABS_Y, 0, + prox->rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0, + HOVER_Z_MAX, 0, 0); +} + +static int prox_reg_init(void) +{ + int retval; + unsigned char ctrl_23_offset; + unsigned char data_1_offset; + struct synaptics_rmi4_f12_query_5 query_5; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->query_base_addr + 5, + query_5.data, + sizeof(query_5.data)); + if (retval < 0) + return retval; + + ctrl_23_offset = query_5.ctrl0_is_present + + query_5.ctrl1_is_present + + query_5.ctrl2_is_present + + query_5.ctrl3_is_present + + query_5.ctrl4_is_present + + query_5.ctrl5_is_present + + query_5.ctrl6_is_present + + query_5.ctrl7_is_present + + query_5.ctrl8_is_present + + query_5.ctrl9_is_present + + query_5.ctrl10_is_present + + query_5.ctrl11_is_present + + query_5.ctrl12_is_present + + query_5.ctrl13_is_present + + query_5.ctrl14_is_present + + query_5.ctrl15_is_present + + query_5.ctrl16_is_present + + query_5.ctrl17_is_present + + query_5.ctrl18_is_present + + query_5.ctrl19_is_present + + query_5.ctrl20_is_present + + query_5.ctrl21_is_present + + query_5.ctrl22_is_present; + + prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->query_base_addr + 8, + query_8.data, + sizeof(query_8.data)); + if (retval < 0) + return retval; + + data_1_offset = query_8.data0_is_present; + prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset; + + return retval; +} + +static int prox_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + struct synaptics_rmi4_fn_desc fd; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&fd, + sizeof(fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, fd.fn_number); + switch (fd.fn_number) { + case SYNAPTICS_RMI4_F12: + goto f12_found; + break; + } + } else { + break; + } + + intr_count += fd.intr_src_count; + } + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F12\n", + __func__); + return -EINVAL; + +f12_found: + prox->query_base_addr = fd.query_base_addr | (page << 8); + prox->control_base_addr = fd.ctrl_base_addr | (page << 8); + prox->data_base_addr = fd.data_base_addr | (page << 8); + prox->command_base_addr = fd.cmd_base_addr | (page << 8); + + retval = prox_reg_init(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize proximity registers\n", + __func__); + return retval; + } + + prox->intr_mask = 0; + intr_src = fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + prox->intr_mask |= 1 << ii; + } + + rmi4_data->intr_mask[0] |= prox->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!prox) + return -ENODEV; + + return snprintf(buf, PAGE_SIZE, "%u\n", + prox->hover_finger_en); +} + +static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + if (!prox) + return -ENODEV; + + if (kstrtouint(buf, 16, &input) != 1) + return -EINVAL; + + if (input == 1) + prox->hover_finger_en = true; + else if (input == 0) + prox->hover_finger_en = false; + else + return -EINVAL; + + retval = prox_set_hover_finger_en(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change hovering finger enable setting\n", + __func__); + return retval; + } + + return count; +} + +int synaptics_rmi4_prox_hover_finger_en(bool enable) +{ + int retval; + + if (!prox) + return -ENODEV; + + prox->hover_finger_en = enable; + + retval = prox_set_hover_finger_en(); + if (retval < 0) + return retval; + + return 0; +} +EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en); + +static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!prox) + return; + + if (prox->intr_mask & intr_mask) + prox_hover_finger_report(); +} + +static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + + if (prox) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + prox = kzalloc(sizeof(*prox), GFP_KERNEL); + if (!prox) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for prox\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL); + if (!prox->finger_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for finger_data\n", + __func__); + retval = -ENOMEM; + goto exit_free_prox; + } + + prox->rmi4_data = rmi4_data; + + retval = prox_scan_pdt(); + if (retval < 0) + goto exit_free_finger_data; + + prox->hover_finger_en = true; + + retval = prox_set_hover_finger_en(); + if (retval < 0) + return retval; + + prox->prox_dev = input_allocate_device(); + if (prox->prox_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate proximity device\n", + __func__); + retval = -ENOMEM; + goto exit_free_finger_data; + } + + prox->prox_dev->name = PROXIMITY_DRIVER_NAME; + prox->prox_dev->phys = PROX_PHYS_NAME; + prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(prox->prox_dev, rmi4_data); + + set_bit(EV_KEY, prox->prox_dev->evbit); + set_bit(EV_ABS, prox->prox_dev->evbit); + set_bit(BTN_TOUCH, prox->prox_dev->keybit); + set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit); +#endif + + prox_set_params(); + + retval = input_register_device(prox->prox_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register proximity device\n", + __func__); + goto exit_free_input_device; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_free_sysfs; + } + } + + return 0; + +exit_free_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + input_unregister_device(prox->prox_dev); + prox->prox_dev = NULL; + +exit_free_input_device: + if (prox->prox_dev) + input_free_device(prox->prox_dev); + +exit_free_finger_data: + kfree(prox->finger_data); + +exit_free_prox: + kfree(prox); + prox = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!prox) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + input_unregister_device(prox->prox_dev); + kfree(prox->finger_data); + kfree(prox); + prox = NULL; + +exit: + complete(&prox_remove_complete); +} + +static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) { + synaptics_rmi4_prox_init(rmi4_data); + return; + } + + prox_hover_finger_lift(); + + prox_scan_pdt(); + + prox_set_hover_finger_en(); +} + +static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) + return; + + prox_hover_finger_lift(); + + prox_set_hover_finger_en(); +} + +static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) + return; + + prox_hover_finger_lift(); +} + +static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) + return; + + prox_hover_finger_lift(); +} + +static struct synaptics_rmi4_exp_fn proximity_module = { + .fn_type = RMI_PROXIMITY, + .init = synaptics_rmi4_prox_init, + .remove = synaptics_rmi4_prox_remove, + .reset = synaptics_rmi4_prox_reset, + .reinit = synaptics_rmi4_prox_reinit, + .early_suspend = synaptics_rmi4_prox_e_suspend, + .suspend = synaptics_rmi4_prox_suspend, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_prox_attn, +}; + +static int __init rmi4_proximity_module_init(void) +{ + synaptics_rmi4_new_function(&proximity_module, true); + + return 0; +} + +static void __exit rmi4_proximity_module_exit(void) +{ + synaptics_rmi4_new_function(&proximity_module, false); + + wait_for_completion(&prox_remove_complete); +} + +module_init(rmi4_proximity_module_init); +module_exit(rmi4_proximity_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Proximity Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_rmi_dev.c b/synaptics_dsx/synaptics_dsx_rmi_dev.c new file mode 100644 index 0000000000..ca366d0dbc --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_rmi_dev.c @@ -0,0 +1,1075 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define SYSFS_FOLDER_NAME "rmidev" +#define DEV_NUMBER 1 +#define REG_ADDR_LIMIT 0xFFFF + +#define RMIDEV_MAJOR_NUM 0 + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_pid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_term_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_concurrent_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_concurrent_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct rmidev_handle { + dev_t dev_no; + pid_t pid; + unsigned char intr_mask; + unsigned char *tmpbuf; + unsigned int tmpbuf_size; + struct device dev; + struct synaptics_rmi4_data *rmi4_data; + struct kobject *sysfs_dir; + struct kernel_siginfo interrupt_signal; + struct kernel_siginfo terminate_signal; + struct task_struct *task; + void *data; + bool concurrent; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; +}; + +static struct bin_attribute attr_data = { + .attr = { + .name = "data", + .mode = 0664, + }, + .size = 0, + .read = rmidev_sysfs_data_show, + .write = rmidev_sysfs_data_store, +}; + +static struct device_attribute attrs[] = { + __ATTR(open, 0220, + synaptics_rmi4_show_error, + rmidev_sysfs_open_store), + __ATTR(release, 0220, + synaptics_rmi4_show_error, + rmidev_sysfs_release_store), + __ATTR(attn_state, 0444, + rmidev_sysfs_attn_state_show, + synaptics_rmi4_store_error), + __ATTR(pid, 0664, + rmidev_sysfs_pid_show, + rmidev_sysfs_pid_store), + __ATTR(term, 0220, + synaptics_rmi4_show_error, + rmidev_sysfs_term_store), + __ATTR(intr_mask, 0664, + rmidev_sysfs_intr_mask_show, + rmidev_sysfs_intr_mask_store), + __ATTR(concurrent, 0664, + rmidev_sysfs_concurrent_show, + rmidev_sysfs_concurrent_store), +}; + +static int rmidev_major_num = RMIDEV_MAJOR_NUM; + +static struct class *rmidev_device_class; + +static struct rmidev_handle *rmidev; + +DECLARE_COMPLETION(rmidev_remove_complete); + +static irqreturn_t rmidev_sysfs_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + sysfs_notify(&rmi4_data->input_dev->dev.kobj, + SYSFS_FOLDER_NAME, "attn_state"); + + return IRQ_HANDLED; +} + +static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status[MAX_INTR_REGISTERS]; + unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT; + + mutex_lock(&(rmi4_data->rmi4_irq_enable_mutex)); + + if (enable) { + if (rmi4_data->irq_enabled) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Interrupt already enabled\n", + __func__); + goto exit; + } + + /* Clear interrupts first */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + goto exit; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + rmidev_sysfs_irq, irq_flags, + PLATFORM_DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create irq thread\n", + __func__); + goto exit; + } + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + } + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_irq_enable_mutex)); + + return retval; +} + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned char intr_status = 0; + unsigned int length = (unsigned int)count; + unsigned short address = (unsigned short)pos; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (length > (REG_ADDR_LIMIT - address)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Out of register map limit\n", + __func__); + return -EINVAL; + } + + if (length) { + retval = synaptics_rmi4_reg_read(rmi4_data, + address, + (unsigned char *)buf, + length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + if (!rmidev->concurrent) + goto exit; + + if (address != rmi4_data->f01_data_base_addr) + goto exit; + + if (length <= 1) + goto exit; + + intr_status = buf[1]; + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & intr_status) { + rmi4_data->report_touch(rmi4_data, + fhandler); + } + } + } + } + +exit: + return length; +} + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int length = (unsigned int)count; + unsigned short address = (unsigned short)pos; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (length > (REG_ADDR_LIMIT - address)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Out of register map limit\n", + __func__); + return -EINVAL; + } + + if (length) { + retval = synaptics_rmi4_reg_write(rmi4_data, + address, + (unsigned char *)buf, + length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return length; +} + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + rmi4_data->irq_enable(rmi4_data, false, false); + rmidev_sysfs_irq_enable(rmi4_data, true); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev_sysfs_irq_enable(rmi4_data, false); + + rmi4_data->reset_device(rmi4_data, false); + + rmi4_data->stay_awake = false; + + return count; +} + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int attn_state; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + attn_state = gpio_get_value(bdata->irq_gpio); + + return snprintf(buf, PAGE_SIZE, "%u\n", attn_state); +} + +static ssize_t rmidev_sysfs_pid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", rmidev->pid); +} + +static ssize_t rmidev_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + rmidev->pid = input; + + if (rmidev->pid) { + rmidev->task = pid_task(find_vpid(rmidev->pid), PIDTYPE_PID); + if (!rmidev->task) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to locate PID of data logging tool\n", + __func__); + return -EINVAL; + } + } + + return count; +} + +static ssize_t rmidev_sysfs_term_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (rmidev->pid) + send_sig_info(SIGTERM, &rmidev->terminate_signal, rmidev->task); + + return count; +} + +static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%02x\n", rmidev->intr_mask); +} + +static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + rmidev->intr_mask = (unsigned char)input; + + return count; +} + +static ssize_t rmidev_sysfs_concurrent_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", rmidev->concurrent); +} + +static ssize_t rmidev_sysfs_concurrent_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + rmidev->concurrent = input > 0 ? true : false; + + return count; +} + +static int rmidev_allocate_buffer(int count) +{ + if (count + 1 > rmidev->tmpbuf_size) { + if (rmidev->tmpbuf_size) + kfree(rmidev->tmpbuf); + rmidev->tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!rmidev->tmpbuf) { + dev_err(rmidev->rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for buffer\n", + __func__); + rmidev->tmpbuf_size = 0; + return -ENOMEM; + } + rmidev->tmpbuf_size = count + 1; + } + + return 0; +} + +/* + * rmidev_llseek - set register address to access for RMI device + * + * @filp: pointer to file structure + * @off: + * if whence == SEEK_SET, + * off: 16-bit RMI register address + * if whence == SEEK_CUR, + * off: offset from current position + * if whence == SEEK_END, + * off: offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: read register data from RMI device + * + * @filp: pointer to file structure + * @buf: pointer to user space buffer + * @count: number of bytes to read + * @f_pos: starting RMI register address + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + unsigned char intr_status = 0; + unsigned short address; + struct rmidev_data *dev_data = filp->private_data; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + address = (unsigned short)(*f_pos); + + rmidev_allocate_buffer(count); + + retval = synaptics_rmi4_reg_read(rmidev->rmi4_data, + *f_pos, + rmidev->tmpbuf, + count); + if (retval < 0) + goto clean_up; + + if (copy_to_user(buf, rmidev->tmpbuf, count)) + retval = -EFAULT; + else + *f_pos += retval; + + if (!rmidev->concurrent) + goto clean_up; + + if (address != rmi4_data->f01_data_base_addr) + goto clean_up; + + if (count <= 1) + goto clean_up; + + intr_status = rmidev->tmpbuf[1]; + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & intr_status) { + rmi4_data->report_touch(rmi4_data, + fhandler); + } + } + } + } + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_write: write register data to RMI device + * + * @filp: pointer to file structure + * @buf: pointer to user space buffer + * @count: number of bytes to write + * @f_pos: starting RMI register address + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto unlock; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto unlock; + } + rmidev_allocate_buffer(count); + + if (copy_from_user(rmidev->tmpbuf, buf, count)) { + retval = -EFAULT; + goto unlock; + } + + retval = synaptics_rmi4_reg_write(rmidev->rmi4_data, + *f_pos, + rmidev->tmpbuf, + count); + if (retval >= 0) + *f_pos += retval; + +unlock: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + filp->private_data = dev_data; + + mutex_lock(&(dev_data->file_mutex)); + + rmi4_data->irq_enable(rmi4_data, false, false); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + rmi4_data->reset_device(rmi4_data, false); + + rmi4_data->stay_awake = false; + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + dev_t devno; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (dev_data) { + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + unregister_chrdev_region(devno, 1); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: rmidev device removed\n", + __func__); + } +} + +static char *rmi_char_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static int rmidev_create_device_class(void) +{ + if (rmidev_device_class != NULL) + return 0; + + rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); + + if (IS_ERR(rmidev_device_class)) { + pr_err("%s: Failed to create /dev/%s\n", + __func__, CHAR_DEVICE_NAME); + return -ENODEV; + } + + rmidev_device_class->devnode = rmi_char_devnode; + + return 0; +} + +static void rmidev_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!rmidev) + return; + + if (rmidev->pid && (rmidev->intr_mask & intr_mask)) + send_sig_info(SIGIO, &rmidev->interrupt_signal, rmidev->task); + + return; +} + +static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + dev_t dev_no; + unsigned char attr_count; + struct rmidev_data *dev_data; + struct device *device_ptr; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (rmidev) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for rmidev\n", + __func__); + retval = -ENOMEM; + goto err_rmidev; + } + + rmidev->rmi4_data = rmi4_data; + + memset(&rmidev->interrupt_signal, 0, sizeof(rmidev->interrupt_signal)); + rmidev->interrupt_signal.si_signo = SIGIO; + rmidev->interrupt_signal.si_code = SI_USER; + + memset(&rmidev->terminate_signal, 0, sizeof(rmidev->terminate_signal)); + rmidev->terminate_signal.si_signo = SIGTERM; + rmidev->terminate_signal.si_code = SI_USER; + + retval = rmidev_create_device_class(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create device class\n", + __func__); + goto err_device_class; + } + + if (rmidev_major_num) { + dev_no = MKDEV(rmidev_major_num, DEV_NUMBER); + retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); + } else { + retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate char device region\n", + __func__); + goto err_device_region; + } + + rmidev_major_num = MAJOR(dev_no); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Major number of rmidev = %d\n", + __func__, rmidev_major_num); + } + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for dev_data\n", + __func__); + retval = -ENOMEM; + goto err_dev_data; + } + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to add rmi char device\n", + __func__); + goto err_char_device; + } + + dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no)); + dev_data->device_class = rmidev_device_class; + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create rmi char device\n", + __func__); + retval = -ENODEV; + goto err_char_device; + } + + retval = gpio_export(bdata->irq_gpio, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(rmi4_data->input_dev->dev), + "attn", bdata->irq_gpio); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s Failed to create gpio symlink\n", + __func__); + } else { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Exported attention gpio %d\n", + __func__, bdata->irq_gpio); + } + } + + rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, + &rmi4_data->input_dev->dev.kobj); + if (!rmidev->sysfs_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs directory\n", + __func__); + retval = -ENODEV; + goto err_sysfs_dir; + } + + retval = sysfs_create_bin_file(rmidev->sysfs_dir, + &attr_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto err_sysfs_bin; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(rmidev->sysfs_dir, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); + + sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); + +err_sysfs_bin: + kobject_put(rmidev->sysfs_dir); + +err_sysfs_dir: + sysfs_remove_link(&(rmi4_data->input_dev->dev.kobj), "attn"); + gpio_unexport(bdata->irq_gpio); + +err_char_device: + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, 1); + +err_device_region: + if (rmidev_device_class != NULL) { + class_destroy(rmidev_device_class); + rmidev_device_class = NULL; + } + +err_device_class: + kfree(rmidev); + rmidev = NULL; + +err_rmidev: + return retval; +} + +static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + struct rmidev_data *dev_data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!rmidev) + goto exit; + + rmidev_major_num = RMIDEV_MAJOR_NUM; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); + + sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); + + kobject_put(rmidev->sysfs_dir); + + sysfs_remove_link(&(rmi4_data->input_dev->dev.kobj), "attn"); + gpio_unexport(bdata->irq_gpio); + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + if (rmidev_device_class != NULL) { + class_destroy(rmidev_device_class); + rmidev_device_class = NULL; + } + + kfree(rmidev->tmpbuf); + + kfree(rmidev); + rmidev = NULL; + +exit: + complete(&rmidev_remove_complete); +} + +static struct synaptics_rmi4_exp_fn rmidev_module = { + .fn_type = RMI_DEV, + .init = rmidev_init_device, + .remove = rmidev_remove_device, + .reset = NULL, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = rmidev_attn, +}; + +static int __init rmidev_module_init(void) +{ + synaptics_rmi4_new_function(&rmidev_module, true); + + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + synaptics_rmi4_new_function(&rmidev_module, false); + + wait_for_completion(&rmidev_remove_complete); +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c b/synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c new file mode 100644 index 0000000000..135dd81e13 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c @@ -0,0 +1,989 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SYN_I2C_RETRY_TIMES 10 + +#define REPORT_ID_GET_BLOB 0x07 +#define REPORT_ID_WRITE 0x09 +#define REPORT_ID_READ_ADDRESS 0x0a +#define REPORT_ID_READ_DATA 0x0b +#define REPORT_ID_SET_RMI_MODE 0x0f + +#define PREFIX_USAGE_PAGE_1BYTE 0x05 +#define PREFIX_USAGE_PAGE_2BYTES 0x06 +#define PREFIX_USAGE 0x09 +#define PREFIX_REPORT_ID 0x85 +#define PREFIX_REPORT_COUNT_1BYTE 0x95 +#define PREFIX_REPORT_COUNT_2BYTES 0x96 + +#define USAGE_GET_BLOB 0xc5 +#define USAGE_WRITE 0x02 +#define USAGE_READ_ADDRESS 0x03 +#define USAGE_READ_DATA 0x04 +#define USAGE_SET_MODE 0x06 + +#define FEATURE_REPORT_TYPE 0x03 + +#define VENDOR_DEFINED_PAGE 0xff00 + +#define BLOB_REPORT_SIZE 256 + +#define RESET_COMMAND 0x01 +#define GET_REPORT_COMMAND 0x02 +#define SET_REPORT_COMMAND 0x03 +#define SET_POWER_COMMAND 0x08 + +#define FINGER_MODE 0x00 +#define RMI_MODE 0x02 + +struct hid_report_info { + unsigned char get_blob_id; + unsigned char write_id; + unsigned char read_addr_id; + unsigned char read_data_id; + unsigned char set_mode_id; + unsigned int blob_size; +}; + +static struct hid_report_info hid_report; + +struct hid_device_descriptor { + unsigned short device_descriptor_length; + unsigned short format_version; + unsigned short report_descriptor_length; + unsigned short report_descriptor_index; + unsigned short input_register_index; + unsigned short input_report_max_length; + unsigned short output_register_index; + unsigned short output_report_max_length; + unsigned short command_register_index; + unsigned short data_register_index; + unsigned short vendor_id; + unsigned short product_id; + unsigned short version_id; + unsigned int reserved; +}; + +static struct hid_device_descriptor hid_dd; + +struct i2c_rw_buffer { + unsigned char *read; + unsigned char *write; + unsigned int read_size; + unsigned int write_size; +}; + +static struct i2c_rw_buffer buffer; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) +{ + int retval; + u32 value; + const char *name; + struct property *prop; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + + retval = of_property_read_u32(np, "synaptics,irq-on-state", + &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n", + __func__); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n", + __func__); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n", + __func__); + return retval; + } + bdata->reset_on_state = value; + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n", + __func__); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n", + __func__); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,dev-dscrptr-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,dev-dscrptr-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,dev-dscrptr-addr property\n", + __func__); + return retval; + } + bdata->device_descriptor_addr = (unsigned short)value; + } else { + bdata->device_descriptor_addr = 0; + } + + prop = of_find_property(np, "synaptics,max-y-for-2d", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,max-y-for-2d", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n", + __func__); + return retval; + } + bdata->max_y_for_2d = value; + } else { + bdata->max_y_for_2d = -1; + } + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ub-i2c-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n", + __func__); + return retval; + } + bdata->ub_i2c_addr = (unsigned short)value; + } else { + bdata->ub_i2c_addr = -1; + } + + prop = of_find_property(np, "synaptics,cap-button-codes", NULL); + if (prop && prop->length) { + bdata->cap_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->cap_button_map->map) + return -ENOMEM; + bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); + retval = of_property_read_u32_array(np, + "synaptics,cap-button-codes", + bdata->cap_button_map->map, + bdata->cap_button_map->nbuttons); + if (retval < 0) { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + } else { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + + prop = of_find_property(np, "synaptics,vir-button-codes", NULL); + if (prop && prop->length) { + bdata->vir_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->vir_button_map->map) + return -ENOMEM; + bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); + bdata->vir_button_map->nbuttons /= 5; + retval = of_property_read_u32_array(np, + "synaptics,vir-button-codes", + bdata->vir_button_map->map, + bdata->vir_button_map->nbuttons * 5); + if (retval < 0) { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + } else { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + + return 0; +} +#endif + +static int do_i2c_transfer(struct i2c_client *client, struct i2c_msg *msg) +{ + unsigned char retry; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + dev_err(&client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(&client->dev, + "%s: I2C transfer over retry limit\n", + __func__); + return -EIO; + } + + return 0; +} + +static int check_buffer(unsigned char **buffer, unsigned int *buffer_size, + unsigned int length) +{ + if (*buffer_size < length) { + if (*buffer_size) + kfree(*buffer); + *buffer = kzalloc(length, GFP_KERNEL); + if (!(*buffer)) + return -ENOMEM; + *buffer_size = length; + } + + return 0; +} + +static int generic_read(struct i2c_client *client, unsigned short length) +{ + int retval; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + } + }; + + check_buffer(&buffer.read, &buffer.read_size, length); + msg[0].buf = buffer.read; + + retval = do_i2c_transfer(client, msg); + + return retval; +} + +static int generic_write(struct i2c_client *client, unsigned short length) +{ + int retval; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = buffer.write, + } + }; + + retval = do_i2c_transfer(client, msg); + + return retval; +} + +static void traverse_report_descriptor(unsigned int *index) +{ + unsigned char size; + unsigned char *buf = buffer.read; + + size = buf[*index] & MASK_2BIT; + switch (size) { + case 0: /* 0 bytes */ + *index += 1; + break; + case 1: /* 1 byte */ + *index += 2; + break; + case 2: /* 2 bytes */ + *index += 3; + break; + case 3: /* 4 bytes */ + *index += 5; + break; + default: + break; + } +} + +static void find_blob_size(unsigned int index) +{ + unsigned int ii = index; + unsigned char *buf = buffer.read; + + while (ii < hid_dd.report_descriptor_length) { + if (buf[ii] == PREFIX_REPORT_COUNT_1BYTE) { + hid_report.blob_size = buf[ii + 1]; + return; + } else if (buf[ii] == PREFIX_REPORT_COUNT_2BYTES) { + hid_report.blob_size = buf[ii + 1] | (buf[ii + 2] << 8); + return; + } + traverse_report_descriptor(&ii); + } +} + +static void find_reports(unsigned int index) +{ + unsigned int ii = index; + unsigned char *buf = buffer.read; + static unsigned int report_id_index; + static unsigned char report_id; + static unsigned short usage_page; + + if (buf[ii] == PREFIX_REPORT_ID) { + report_id = buf[ii + 1]; + report_id_index = ii; + return; + } + + if (buf[ii] == PREFIX_USAGE_PAGE_1BYTE) { + usage_page = buf[ii + 1]; + return; + } else if (buf[ii] == PREFIX_USAGE_PAGE_2BYTES) { + usage_page = buf[ii + 1] | (buf[ii + 2] << 8); + return; + } + + if ((usage_page == VENDOR_DEFINED_PAGE) && (buf[ii] == PREFIX_USAGE)) { + switch (buf[ii + 1]) { + case USAGE_GET_BLOB: + hid_report.get_blob_id = report_id; + find_blob_size(report_id_index); + break; + case USAGE_WRITE: + hid_report.write_id = report_id; + break; + case USAGE_READ_ADDRESS: + hid_report.read_addr_id = report_id; + break; + case USAGE_READ_DATA: + hid_report.read_data_id = report_id; + break; + case USAGE_SET_MODE: + hid_report.set_mode_id = report_id; + break; + default: + break; + } + } +} + +static int parse_report_descriptor(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned int ii = 0; + unsigned char *buf; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + buffer.write[0] = hid_dd.report_descriptor_index & MASK_8BIT; + buffer.write[1] = hid_dd.report_descriptor_index >> 8; + retval = generic_write(i2c, 2); + if (retval < 0) + return retval; + retval = generic_read(i2c, hid_dd.report_descriptor_length); + if (retval < 0) + return retval; + + buf = buffer.read; + + hid_report.get_blob_id = REPORT_ID_GET_BLOB; + hid_report.write_id = REPORT_ID_WRITE; + hid_report.read_addr_id = REPORT_ID_READ_ADDRESS; + hid_report.read_data_id = REPORT_ID_READ_DATA; + hid_report.set_mode_id = REPORT_ID_SET_RMI_MODE; + hid_report.blob_size = BLOB_REPORT_SIZE; + + while (ii < hid_dd.report_descriptor_length) { + find_reports(ii); + traverse_report_descriptor(&ii); + } + + return 0; +} + +static int switch_to_rmi(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, 11); + + /* set rmi mode */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.set_mode_id; + buffer.write[3] = SET_REPORT_COMMAND; + buffer.write[4] = hid_report.set_mode_id; + buffer.write[5] = hid_dd.data_register_index & MASK_8BIT; + buffer.write[6] = hid_dd.data_register_index >> 8; + buffer.write[7] = 0x04; + buffer.write[8] = 0x00; + buffer.write[9] = hid_report.set_mode_id; + buffer.write[10] = RMI_MODE; + + retval = generic_write(i2c, 11); + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int check_report_mode(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned short report_size; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, 7); + + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.set_mode_id; + buffer.write[3] = GET_REPORT_COMMAND; + buffer.write[4] = hid_report.set_mode_id; + buffer.write[5] = hid_dd.data_register_index & MASK_8BIT; + buffer.write[6] = hid_dd.data_register_index >> 8; + + retval = generic_write(i2c, 7); + if (retval < 0) + goto exit; + + retval = generic_read(i2c, 2); + if (retval < 0) + goto exit; + + report_size = (buffer.read[1] << 8) | buffer.read[0]; + + retval = generic_write(i2c, 7); + if (retval < 0) + goto exit; + + retval = generic_read(i2c, report_size); + if (retval < 0) + goto exit; + + retval = buffer.read[3]; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Report mode = %d\n", + __func__, retval); + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int hid_i2c_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, 6); + + /* read device descriptor */ + buffer.write[0] = bdata->device_descriptor_addr & MASK_8BIT; + buffer.write[1] = bdata->device_descriptor_addr >> 8; + retval = generic_write(i2c, 2); + if (retval < 0) + goto exit; + retval = generic_read(i2c, sizeof(hid_dd)); + if (retval < 0) + goto exit; + retval = secure_memcpy((unsigned char *)&hid_dd, + sizeof(struct hid_device_descriptor), + buffer.read, + buffer.read_size, + sizeof(hid_dd)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy device descriptor data\n", + __func__); + goto exit; + } + + retval = parse_report_descriptor(rmi4_data); + if (retval < 0) + goto exit; + + /* set power */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = 0x00; + buffer.write[3] = SET_POWER_COMMAND; + retval = generic_write(i2c, 4); + if (retval < 0) + goto exit; + + /* reset */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = 0x00; + buffer.write[3] = RESET_COMMAND; + retval = generic_write(i2c, 4); + if (retval < 0) + goto exit; + + while (gpio_get_value(bdata->irq_gpio)) + msleep(20); + + retval = generic_read(i2c, hid_dd.input_report_max_length); + if (retval < 0) + goto exit; + + /* get blob */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.get_blob_id; + buffer.write[3] = 0x02; + buffer.write[4] = hid_dd.data_register_index & MASK_8BIT; + buffer.write[5] = hid_dd.data_register_index >> 8; + + retval = generic_write(i2c, 6); + if (retval < 0) + goto exit; + + msleep(20); + + retval = generic_read(i2c, hid_report.blob_size + 3); + if (retval < 0) + goto exit; + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize HID/I2C interface\n", + __func__); + return retval; + } + + retval = switch_to_rmi(rmi4_data); + + return retval; +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char retry; + unsigned char recover = 1; + unsigned short report_length; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[] = { + { + .addr = i2c->addr, + .flags = 0, + .len = hid_dd.output_report_max_length + 2, + }, + { + .addr = i2c->addr, + .flags = I2C_M_RD, + .len = (unsigned short)(length + 4), + }, + }; + +recover: + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, + hid_dd.output_report_max_length + 2); + msg[0].buf = buffer.write; + buffer.write[0] = hid_dd.output_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.output_register_index >> 8; + buffer.write[2] = hid_dd.output_report_max_length & MASK_8BIT; + buffer.write[3] = hid_dd.output_report_max_length >> 8; + buffer.write[4] = hid_report.read_addr_id; + buffer.write[5] = 0x00; + buffer.write[6] = addr & MASK_8BIT; + buffer.write[7] = addr >> 8; + buffer.write[8] = (unsigned char)length; + buffer.write[9] = (unsigned char)(length >> 8); + + check_buffer(&buffer.read, &buffer.read_size, length + 4); + msg[1].buf = buffer.read; + + retval = do_i2c_transfer(i2c, &msg[0]); + if (retval != 0) + goto exit; + + retry = 0; + do { + retval = do_i2c_transfer(i2c, &msg[1]); + if (retval == 0) + retval = length; + else + goto exit; + + report_length = (buffer.read[1] << 8) | buffer.read[0]; + if (report_length == hid_dd.input_report_max_length) { + retval = secure_memcpy(&data[0], length, + &buffer.read[4], buffer.read_size - 4, + length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + } else { + retval = length; + } + goto exit; + } + + msleep(20); + retry++; + } while (retry < SYN_I2C_RETRY_TIMES); + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to receive read report\n", + __func__); + retval = -EIO; + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + if ((retval != length) && (recover == 1)) { + recover = 0; + if (check_report_mode(rmi4_data) != RMI_MODE) { + retval = hid_i2c_init(rmi4_data); + if (retval == 0) + goto recover; + } + } + + return retval; +} + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char recover = 1; + unsigned int msg_length; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[] = { + { + .addr = i2c->addr, + .flags = 0, + } + }; + + if ((length + 10) < (hid_dd.output_report_max_length + 2)) + msg_length = hid_dd.output_report_max_length + 2; + else + msg_length = length + 10; + +recover: + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, msg_length); + msg[0].len = (unsigned short)msg_length; + msg[0].buf = buffer.write; + buffer.write[0] = hid_dd.output_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.output_register_index >> 8; + buffer.write[2] = hid_dd.output_report_max_length & MASK_8BIT; + buffer.write[3] = hid_dd.output_report_max_length >> 8; + buffer.write[4] = hid_report.write_id; + buffer.write[5] = 0x00; + buffer.write[6] = addr & MASK_8BIT; + buffer.write[7] = addr >> 8; + buffer.write[8] = (unsigned char)length; + buffer.write[9] = (unsigned char)(length >> 8); + retval = secure_memcpy(&buffer.write[10], buffer.write_size - 10, + &data[0], length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + } else { + retval = do_i2c_transfer(i2c, msg); + if (retval == 0) + retval = length; + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + if ((retval != length) && (recover == 1)) { + recover = 0; + if (check_report_mode(rmi4_data) != RMI_MODE) { + retval = hid_i2c_init(rmi4_data); + if (retval == 0) + goto recover; + } + } + + return retval; +} + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_I2C, + .read = synaptics_rmi4_i2c_read, + .write = synaptics_rmi4_i2c_write, +}; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_i2c_device; + +static void synaptics_rmi4_i2c_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_i2c_device); +} + +static int synaptics_rmi4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data commands not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_i2c_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_i2c_device) { + dev_err(&client->dev, + "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF + if (client->dev.of_node) { + hw_if.board_data = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!hw_if.board_data) { + dev_err(&client->dev, + "%s: Failed to allocate memory for board data\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->cap_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for 0D button map\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->vir_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for virtual button map\n", + __func__); + return -ENOMEM; + } + parse_dt(&client->dev, hw_if.board_data); + } +#else + hw_if.board_data = client->dev.platform_data; +#endif + + hw_if.bus_access = &bus_access; + hw_if.bl_hw_init = switch_to_rmi; + hw_if.ui_hw_init = hid_i2c_init; + + synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_i2c_device->id = 0; + synaptics_dsx_i2c_device->num_resources = 0; + synaptics_dsx_i2c_device->dev.parent = &client->dev; + synaptics_dsx_i2c_device->dev.platform_data = &hw_if; + synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release; + + retval = platform_device_register(synaptics_dsx_i2c_device); + if (retval) { + dev_err(&client->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_i2c_remove(struct i2c_client *client) +{ + if (buffer.read_size) + kfree(buffer.read); + + if (buffer.write_size) + kfree(buffer.write); + + platform_device_unregister(synaptics_dsx_i2c_device); + + return 0; +} + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {I2C_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_of_match_table[] = { + { + .compatible = "synaptics,dsx-rmi-hid-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); +#else +#define synaptics_rmi4_of_match_table NULL +#endif + +static struct i2c_driver synaptics_rmi4_i2c_driver = { + .driver = { + .name = I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = synaptics_rmi4_of_match_table, + }, + .probe = synaptics_rmi4_i2c_probe, + .remove = synaptics_rmi4_i2c_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_spi.c b/synaptics_dsx/synaptics_dsx_spi.c new file mode 100644 index 0000000000..7e957813dc --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_spi.c @@ -0,0 +1,698 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SPI_READ 0x80 +#define SPI_WRITE 0x00 + +static unsigned char *buf; + +static struct spi_transfer *xfer; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) +{ + int retval; + u32 value; + const char *name; + struct property *prop; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + + retval = of_property_read_u32(np, "synaptics,irq-on-state", + &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n", + __func__); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n", + __func__); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n", + __func__); + return retval; + } + bdata->reset_on_state = value; + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n", + __func__); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n", + __func__); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,byte-delay-us", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,byte-delay-us property\n", + __func__); + return retval; + } + bdata->byte_delay_us = value; + } else { + bdata->byte_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,block-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,block-delay-us", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,block-delay-us property\n", + __func__); + return retval; + } + bdata->block_delay_us = value; + } else { + bdata->block_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,address-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,address-delay-us", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,address-delay-us property\n", + __func__); + return retval; + } + bdata->addr_delay_us = value; + } else { + bdata->addr_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,max-y-for-2d", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,max-y-for-2d", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n", + __func__); + return retval; + } + bdata->max_y_for_2d = value; + } else { + bdata->max_y_for_2d = -1; + } + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ub-i2c-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n", + __func__); + return retval; + } + bdata->ub_i2c_addr = (unsigned short)value; + } else { + bdata->ub_i2c_addr = -1; + } + + prop = of_find_property(np, "synaptics,cap-button-codes", NULL); + if (prop && prop->length) { + bdata->cap_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->cap_button_map->map) + return -ENOMEM; + bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); + retval = of_property_read_u32_array(np, + "synaptics,cap-button-codes", + bdata->cap_button_map->map, + bdata->cap_button_map->nbuttons); + if (retval < 0) { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + } else { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + + prop = of_find_property(np, "synaptics,vir-button-codes", NULL); + if (prop && prop->length) { + bdata->vir_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->vir_button_map->map) + return -ENOMEM; + bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); + bdata->vir_button_map->nbuttons /= 5; + retval = of_property_read_u32_array(np, + "synaptics,vir-button-codes", + bdata->vir_button_map->map, + bdata->vir_button_map->nbuttons * 5); + if (retval < 0) { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + } else { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + + return 0; +} +#endif + +static int synaptics_rmi4_spi_alloc_buf(struct synaptics_rmi4_data *rmi4_data, + unsigned int size, unsigned int count) +{ + static unsigned int buf_size; + static unsigned int xfer_count; + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for buf\n", + __func__); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + if (count > xfer_count) { + if (xfer_count) + kfree(xfer); + xfer = kcalloc(count, sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfer) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for xfer\n", + __func__); + xfer_count = 0; + return -ENOMEM; + } + xfer_count = count; + } else { + memset(xfer, 0, count * sizeof(struct spi_transfer)); + } + + return 0; +} + +static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr) +{ + int retval; + unsigned int index; + unsigned int byte_count = PAGE_SELECT_LEN + 1; + unsigned char page; + struct spi_message msg; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + page = ((addr >> 8) & MASK_8BIT); + if ((page >> 7) == (rmi4_data->current_page >> 7)) + return PAGE_SELECT_LEN; + + spi_message_init(&msg); + + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count, + byte_count); + if (retval < 0) + return retval; + + buf[0] = SPI_WRITE; + buf[1] = MASK_8BIT; + buf[2] = page; + + if (bdata->byte_delay_us == 0) { + xfer[0].len = byte_count; + xfer[0].tx_buf = &buf[0]; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + for (index = 0; index < byte_count; index++) { + xfer[index].len = 1; + xfer[index].tx_buf = &buf[index]; + if (index == 1) + xfer[index].delay_usecs = bdata->addr_delay_us; + else + xfer[index].delay_usecs = bdata->byte_delay_us; + spi_message_add_tail(&xfer[index], &msg); + } + if (bdata->block_delay_us) + xfer[index - 1].delay_usecs = bdata->block_delay_us; + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + rmi4_data->current_page = page; + retval = PAGE_SELECT_LEN; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + return retval; +} + +static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int index; + unsigned int byte_count = length + ADDRESS_LEN; + unsigned char txbuf[ADDRESS_LEN]; + struct spi_message msg; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + spi_message_init(&msg); + + txbuf[0] = (addr >> 8) | SPI_READ; + txbuf[1] = addr & MASK_8BIT; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_spi_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return -EIO; + } + + if (bdata->byte_delay_us == 0) { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, length, + 2); + } else { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, length, + byte_count); + } + if (retval < 0) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return retval; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = ADDRESS_LEN; + xfer[0].tx_buf = &txbuf[0]; + spi_message_add_tail(&xfer[0], &msg); + xfer[1].len = length; + xfer[1].rx_buf = &buf[0]; + if (bdata->block_delay_us) + xfer[1].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[1], &msg); + } else { + for (index = 0; index < byte_count; index++) { + xfer[index].len = 1; + if (index < ADDRESS_LEN) + xfer[index].tx_buf = &txbuf[index]; + else + xfer[index].rx_buf = &buf[index - ADDRESS_LEN]; + if (index == 1) + xfer[index].delay_usecs = bdata->addr_delay_us; + else + xfer[index].delay_usecs = bdata->byte_delay_us; + spi_message_add_tail(&xfer[index], &msg); + } + if (bdata->block_delay_us) + xfer[index - 1].delay_usecs = bdata->block_delay_us; + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = secure_memcpy(data, length, buf, length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + } else { + retval = length; + } + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int index; + unsigned int byte_count = length + ADDRESS_LEN; + struct spi_message msg; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + spi_message_init(&msg); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_spi_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return -EIO; + } + + if (bdata->byte_delay_us == 0) { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count, + 1); + } else { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count, + byte_count); + } + if (retval < 0) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return retval; + } + + buf[0] = (addr >> 8) & ~SPI_READ; + buf[1] = addr & MASK_8BIT; + retval = secure_memcpy(&buf[ADDRESS_LEN], + byte_count - ADDRESS_LEN, data, length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return retval; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = byte_count; + xfer[0].tx_buf = &buf[0]; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(xfer, &msg); + } else { + for (index = 0; index < byte_count; index++) { + xfer[index].len = 1; + xfer[index].tx_buf = &buf[index]; + if (index == 1) + xfer[index].delay_usecs = bdata->addr_delay_us; + else + xfer[index].delay_usecs = bdata->byte_delay_us; + spi_message_add_tail(&xfer[index], &msg); + } + if (bdata->block_delay_us) + xfer[index - 1].delay_usecs = bdata->block_delay_us; + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_SPI, + .read = synaptics_rmi4_spi_read, + .write = synaptics_rmi4_spi_write, +}; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_spi_device; + +static void synaptics_rmi4_spi_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_spi_device); +} + +static int synaptics_rmi4_spi_probe(struct spi_device *spi) +{ + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + dev_err(&spi->dev, + "%s: Full duplex not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_spi_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_spi_device) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for synaptics_dsx_spi_device\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF + if (spi->dev.of_node) { + hw_if.board_data = devm_kzalloc(&spi->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!hw_if.board_data) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for board data\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->cap_button_map = devm_kzalloc(&spi->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->cap_button_map) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for 0D button map\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->vir_button_map = devm_kzalloc(&spi->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->vir_button_map) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for virtual button map\n", + __func__); + return -ENOMEM; + } + parse_dt(&spi->dev, hw_if.board_data); + } +#else + hw_if.board_data = spi->dev.platform_data; +#endif + + hw_if.bus_access = &bus_access; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + + retval = spi_setup(spi); + if (retval < 0) { + dev_err(&spi->dev, + "%s: Failed to perform SPI setup\n", + __func__); + return retval; + } + + synaptics_dsx_spi_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_spi_device->id = 0; + synaptics_dsx_spi_device->num_resources = 0; + synaptics_dsx_spi_device->dev.parent = &spi->dev; + synaptics_dsx_spi_device->dev.platform_data = &hw_if; + synaptics_dsx_spi_device->dev.release = synaptics_rmi4_spi_dev_release; + + retval = platform_device_register(synaptics_dsx_spi_device); + if (retval) { + dev_err(&spi->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_spi_remove(struct spi_device *spi) +{ + platform_device_unregister(synaptics_dsx_spi_device); + + return 0; +} + +static const struct spi_device_id synaptics_rmi4_id_table[] = { + {SPI_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_of_match_table[] = { + { + .compatible = "synaptics,dsx-spi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); +#else +#define synaptics_rmi4_of_match_table NULL +#endif + +static struct spi_driver synaptics_rmi4_spi_driver = { + .driver = { + .name = SPI_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = synaptics_rmi4_of_match_table, + }, + .probe = synaptics_rmi4_spi_probe, + .remove = synaptics_rmi4_spi_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return spi_register_driver(&synaptics_rmi4_spi_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + kfree(buf); + + kfree(xfer); + + spi_unregister_driver(&synaptics_rmi4_spi_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_test_reporting.c b/synaptics_dsx/synaptics_dsx_test_reporting.c new file mode 100644 index 0000000000..b4c4cc72be --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_test_reporting.c @@ -0,0 +1,5324 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SYSFS_FOLDER_NAME "f54" + +#define GET_REPORT_TIMEOUT_S 3 +#define CALIBRATION_TIMEOUT_S 10 +#define COMMAND_TIMEOUT_100MS 20 + +#define NO_SLEEP_OFF (0 << 2) +#define NO_SLEEP_ON (1 << 2) + +#define STATUS_IDLE 0 +#define STATUS_BUSY 1 +#define STATUS_ERROR 2 + +#define REPORT_INDEX_OFFSET 1 +#define REPORT_DATA_OFFSET 3 + +#define SENSOR_RX_MAPPING_OFFSET 1 +#define SENSOR_TX_MAPPING_OFFSET 2 + +#define COMMAND_GET_REPORT 1 +#define COMMAND_FORCE_CAL 2 +#define COMMAND_FORCE_UPDATE 4 + +#define CONTROL_NO_AUTO_CAL 1 + +#define CONTROL_0_SIZE 1 +#define CONTROL_1_SIZE 1 +#define CONTROL_2_SIZE 2 +#define CONTROL_3_SIZE 1 +#define CONTROL_4_6_SIZE 3 +#define CONTROL_7_SIZE 1 +#define CONTROL_8_9_SIZE 3 +#define CONTROL_10_SIZE 1 +#define CONTROL_11_SIZE 2 +#define CONTROL_12_13_SIZE 2 +#define CONTROL_14_SIZE 1 +#define CONTROL_15_SIZE 1 +#define CONTROL_16_SIZE 1 +#define CONTROL_17_SIZE 1 +#define CONTROL_18_SIZE 1 +#define CONTROL_19_SIZE 1 +#define CONTROL_20_SIZE 1 +#define CONTROL_21_SIZE 2 +#define CONTROL_22_26_SIZE 7 +#define CONTROL_27_SIZE 1 +#define CONTROL_28_SIZE 2 +#define CONTROL_29_SIZE 1 +#define CONTROL_30_SIZE 1 +#define CONTROL_31_SIZE 1 +#define CONTROL_32_35_SIZE 8 +#define CONTROL_36_SIZE 1 +#define CONTROL_37_SIZE 1 +#define CONTROL_38_SIZE 1 +#define CONTROL_39_SIZE 1 +#define CONTROL_40_SIZE 1 +#define CONTROL_41_SIZE 1 +#define CONTROL_42_SIZE 2 +#define CONTROL_43_54_SIZE 13 +#define CONTROL_55_56_SIZE 2 +#define CONTROL_57_SIZE 1 +#define CONTROL_58_SIZE 1 +#define CONTROL_59_SIZE 2 +#define CONTROL_60_62_SIZE 3 +#define CONTROL_63_SIZE 1 +#define CONTROL_64_67_SIZE 4 +#define CONTROL_68_73_SIZE 8 +#define CONTROL_70_73_SIZE 6 +#define CONTROL_74_SIZE 2 +#define CONTROL_75_SIZE 1 +#define CONTROL_76_SIZE 1 +#define CONTROL_77_78_SIZE 2 +#define CONTROL_79_83_SIZE 5 +#define CONTROL_84_85_SIZE 2 +#define CONTROL_86_SIZE 1 +#define CONTROL_87_SIZE 1 +#define CONTROL_88_SIZE 1 +#define CONTROL_89_SIZE 1 +#define CONTROL_90_SIZE 1 +#define CONTROL_91_SIZE 1 +#define CONTROL_92_SIZE 1 +#define CONTROL_93_SIZE 1 +#define CONTROL_94_SIZE 1 +#define CONTROL_95_SIZE 1 +#define CONTROL_96_SIZE 1 +#define CONTROL_97_SIZE 1 +#define CONTROL_98_SIZE 1 +#define CONTROL_99_SIZE 1 +#define CONTROL_100_SIZE 1 +#define CONTROL_101_SIZE 1 +#define CONTROL_102_SIZE 1 +#define CONTROL_103_SIZE 1 +#define CONTROL_104_SIZE 1 +#define CONTROL_105_SIZE 1 +#define CONTROL_106_SIZE 1 +#define CONTROL_107_SIZE 1 +#define CONTROL_108_SIZE 1 +#define CONTROL_109_SIZE 1 +#define CONTROL_110_SIZE 1 +#define CONTROL_111_SIZE 1 +#define CONTROL_112_SIZE 1 +#define CONTROL_113_SIZE 1 +#define CONTROL_114_SIZE 1 +#define CONTROL_115_SIZE 1 +#define CONTROL_116_SIZE 1 +#define CONTROL_117_SIZE 1 +#define CONTROL_118_SIZE 1 +#define CONTROL_119_SIZE 1 +#define CONTROL_120_SIZE 1 +#define CONTROL_121_SIZE 1 +#define CONTROL_122_SIZE 1 +#define CONTROL_123_SIZE 1 +#define CONTROL_124_SIZE 1 +#define CONTROL_125_SIZE 1 +#define CONTROL_126_SIZE 1 +#define CONTROL_127_SIZE 1 +#define CONTROL_128_SIZE 1 +#define CONTROL_129_SIZE 1 +#define CONTROL_130_SIZE 1 +#define CONTROL_131_SIZE 1 +#define CONTROL_132_SIZE 1 +#define CONTROL_133_SIZE 1 +#define CONTROL_134_SIZE 1 +#define CONTROL_135_SIZE 1 +#define CONTROL_136_SIZE 1 +#define CONTROL_137_SIZE 1 +#define CONTROL_138_SIZE 1 +#define CONTROL_139_SIZE 1 +#define CONTROL_140_SIZE 1 +#define CONTROL_141_SIZE 1 +#define CONTROL_142_SIZE 1 +#define CONTROL_143_SIZE 1 +#define CONTROL_144_SIZE 1 +#define CONTROL_145_SIZE 1 +#define CONTROL_146_SIZE 1 +#define CONTROL_147_SIZE 1 +#define CONTROL_148_SIZE 1 +#define CONTROL_149_SIZE 1 +#define CONTROL_150_SIZE 1 +#define CONTROL_151_SIZE 1 +#define CONTROL_152_SIZE 1 +#define CONTROL_153_SIZE 1 +#define CONTROL_154_SIZE 1 +#define CONTROL_155_SIZE 1 +#define CONTROL_156_SIZE 1 +#define CONTROL_157_158_SIZE 2 +#define CONTROL_163_SIZE 1 +#define CONTROL_165_SIZE 1 +#define CONTROL_166_SIZE 1 +#define CONTROL_167_SIZE 1 +#define CONTROL_168_SIZE 1 +#define CONTROL_169_SIZE 1 +#define CONTROL_171_SIZE 1 +#define CONTROL_172_SIZE 1 +#define CONTROL_173_SIZE 1 +#define CONTROL_174_SIZE 1 +#define CONTROL_175_SIZE 1 +#define CONTROL_176_SIZE 1 +#define CONTROL_177_178_SIZE 2 +#define CONTROL_179_SIZE 1 +#define CONTROL_182_SIZE 1 +#define CONTROL_183_SIZE 1 +#define CONTROL_185_SIZE 1 +#define CONTROL_186_SIZE 1 +#define CONTROL_187_SIZE 1 +#define CONTROL_188_SIZE 1 + +#define HIGH_RESISTANCE_DATA_SIZE 6 +#define FULL_RAW_CAP_MIN_MAX_DATA_SIZE 4 +#define TRX_OPEN_SHORT_DATA_SIZE 7 + +#define attrify(propname) (&dev_attr_##propname.attr) + +#define show_prototype(propname)\ +static ssize_t propname##_show(\ + struct device *dev,\ + struct device_attribute *attr,\ + char *buf);\ +\ +static struct device_attribute dev_attr_##propname =\ + __ATTR_RO(propname) + +#define store_prototype(propname)\ +static ssize_t propname##_store(\ + struct device *dev,\ + struct device_attribute *attr,\ + const char *buf, size_t count);\ +\ +static struct device_attribute dev_attr_##propname =\ + __ATTR_WO(propname) + +#define show_store_prototype(propname)\ +static ssize_t propname##_show(\ + struct device *dev,\ + struct device_attribute *attr,\ + char *buf);\ +\ +static ssize_t propname##_store(\ + struct device *dev,\ + struct device_attribute *attr,\ + const char *buf, size_t count);\ +\ +static struct device_attribute dev_attr_##propname =\ + __ATTR_RW(propname) + +#define disable_cbc(ctrl_num)\ +do {\ + retval = synaptics_rmi4_reg_read(rmi4_data,\ + f54->control.ctrl_num->address,\ + f54->control.ctrl_num->data,\ + sizeof(f54->control.ctrl_num->data));\ + if (retval < 0) {\ + dev_err(rmi4_data->pdev->dev.parent,\ + "%s: Failed to disable CBC (" #ctrl_num ")\n",\ + __func__);\ + return retval;\ + } \ + f54->control.ctrl_num->cbc_tx_carrier_selection = 0;\ + retval = synaptics_rmi4_reg_write(rmi4_data,\ + f54->control.ctrl_num->address,\ + f54->control.ctrl_num->data,\ + sizeof(f54->control.ctrl_num->data));\ + if (retval < 0) {\ + dev_err(rmi4_data->pdev->dev.parent,\ + "%s: Failed to disable CBC (" #ctrl_num ")\n",\ + __func__);\ + return retval;\ + } \ +} while (0) + +enum f54_report_types { + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_HIGH_RESISTANCE = 4, + F54_TX_TO_TX_SHORTS = 5, + F54_RX_TO_RX_SHORTS_1 = 7, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP_MIN_MAX = 13, + F54_RX_OPENS_1 = 14, + F54_TX_OPENS = 15, + F54_TX_TO_GND_SHORTS = 16, + F54_RX_TO_RX_SHORTS_2 = 17, + F54_RX_OPENS_2 = 18, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_NO_RX_COUPLING = 20, + F54_SENSOR_SPEED = 22, + F54_ADC_RANGE = 23, + F54_TRX_OPENS = 24, + F54_TRX_TO_GND_SHORTS = 25, + F54_TRX_SHORTS = 26, + F54_ABS_RAW_CAP = 38, + F54_ABS_DELTA_CAP = 40, + F54_ABS_HYBRID_DELTA_CAP = 59, + F54_ABS_HYBRID_RAW_CAP = 63, + F54_AMP_FULL_RAW_CAP = 78, + F54_AMP_RAW_ADC = 83, + F54_FULL_RAW_CAP_TDDI = 92, + INVALID_REPORT_TYPE = -1, +}; + +enum f54_afe_cal { + F54_AFE_CAL, + F54_AFE_IS_CAL, +}; + +struct f54_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char f54_query2_b0__1:2; + unsigned char has_baseline:1; + unsigned char has_image8:1; + unsigned char f54_query2_b4__5:2; + unsigned char has_image16:1; + unsigned char f54_query2_b7:1; + + /* queries 3.0 and 3.1 */ + unsigned short clock_rate; + + /* query 4 */ + unsigned char touch_controller_family; + + /* query 5 */ + unsigned char has_pixel_touch_threshold_adjustment:1; + unsigned char f54_query5_b1__7:7; + + /* query 6 */ + unsigned char has_sensor_assignment:1; + unsigned char has_interference_metric:1; + unsigned char has_sense_frequency_control:1; + unsigned char has_firmware_noise_mitigation:1; + unsigned char has_ctrl11:1; + unsigned char has_two_byte_report_rate:1; + unsigned char has_one_byte_report_rate:1; + unsigned char has_relaxation_control:1; + + /* query 7 */ + unsigned char curve_compensation_mode:2; + unsigned char f54_query7_b2__7:6; + + /* query 8 */ + unsigned char f54_query8_b0:1; + unsigned char has_iir_filter:1; + unsigned char has_cmn_removal:1; + unsigned char has_cmn_maximum:1; + unsigned char has_touch_hysteresis:1; + unsigned char has_edge_compensation:1; + unsigned char has_per_frequency_noise_control:1; + unsigned char has_enhanced_stretch:1; + + /* query 9 */ + unsigned char has_force_fast_relaxation:1; + unsigned char has_multi_metric_state_machine:1; + unsigned char has_signal_clarity:1; + unsigned char has_variance_metric:1; + unsigned char has_0d_relaxation_control:1; + unsigned char has_0d_acquisition_control:1; + unsigned char has_status:1; + unsigned char has_slew_metric:1; + + /* query 10 */ + unsigned char has_h_blank:1; + unsigned char has_v_blank:1; + unsigned char has_long_h_blank:1; + unsigned char has_startup_fast_relaxation:1; + unsigned char has_esd_control:1; + unsigned char has_noise_mitigation2:1; + unsigned char has_noise_state:1; + unsigned char has_energy_ratio_relaxation:1; + + /* query 11 */ + unsigned char has_excessive_noise_reporting:1; + unsigned char has_slew_option:1; + unsigned char has_two_overhead_bursts:1; + unsigned char has_query13:1; + unsigned char has_one_overhead_burst:1; + unsigned char f54_query11_b5:1; + unsigned char has_ctrl88:1; + unsigned char has_query15:1; + + /* query 12 */ + unsigned char number_of_sensing_frequencies:4; + unsigned char f54_query12_b4__7:4; + } __packed; + unsigned char data[14]; + }; +}; + +struct f54_query_13 { + union { + struct { + unsigned char has_ctrl86:1; + unsigned char has_ctrl87:1; + unsigned char has_ctrl87_sub0:1; + unsigned char has_ctrl87_sub1:1; + unsigned char has_ctrl87_sub2:1; + unsigned char has_cidim:1; + unsigned char has_noise_mitigation_enhancement:1; + unsigned char has_rail_im:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_15 { + union { + struct { + unsigned char has_ctrl90:1; + unsigned char has_transmit_strength:1; + unsigned char has_ctrl87_sub3:1; + unsigned char has_query16:1; + unsigned char has_query20:1; + unsigned char has_query21:1; + unsigned char has_query22:1; + unsigned char has_query25:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_16 { + union { + struct { + unsigned char has_query17:1; + unsigned char has_data17:1; + unsigned char has_ctrl92:1; + unsigned char has_ctrl93:1; + unsigned char has_ctrl94_query18:1; + unsigned char has_ctrl95_query19:1; + unsigned char has_ctrl99:1; + unsigned char has_ctrl100:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_21 { + union { + struct { + unsigned char has_abs_rx:1; + unsigned char has_abs_tx:1; + unsigned char has_ctrl91:1; + unsigned char has_ctrl96:1; + unsigned char has_ctrl97:1; + unsigned char has_ctrl98:1; + unsigned char has_data19:1; + unsigned char has_query24_data18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_22 { + union { + struct { + unsigned char has_packed_image:1; + unsigned char has_ctrl101:1; + unsigned char has_dynamic_sense_display_ratio:1; + unsigned char has_query23:1; + unsigned char has_ctrl103_query26:1; + unsigned char has_ctrl104:1; + unsigned char has_ctrl105:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_23 { + union { + struct { + unsigned char has_ctrl102:1; + unsigned char has_ctrl102_sub1:1; + unsigned char has_ctrl102_sub2:1; + unsigned char has_ctrl102_sub4:1; + unsigned char has_ctrl102_sub5:1; + unsigned char has_ctrl102_sub9:1; + unsigned char has_ctrl102_sub10:1; + unsigned char has_ctrl102_sub11:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_25 { + union { + struct { + unsigned char has_ctrl106:1; + unsigned char has_ctrl102_sub12:1; + unsigned char has_ctrl107:1; + unsigned char has_ctrl108:1; + unsigned char has_ctrl109:1; + unsigned char has_data20:1; + unsigned char f54_query25_b6:1; + unsigned char has_query27:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_27 { + union { + struct { + unsigned char has_ctrl110:1; + unsigned char has_data21:1; + unsigned char has_ctrl111:1; + unsigned char has_ctrl112:1; + unsigned char has_ctrl113:1; + unsigned char has_data22:1; + unsigned char has_ctrl114:1; + unsigned char has_query29:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_29 { + union { + struct { + unsigned char has_ctrl115:1; + unsigned char has_ground_ring_options:1; + unsigned char has_lost_bursts_tuning:1; + unsigned char has_aux_exvcom2_select:1; + unsigned char has_ctrl116:1; + unsigned char has_data23:1; + unsigned char has_ctrl117:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_30 { + union { + struct { + unsigned char has_ctrl118:1; + unsigned char has_ctrl119:1; + unsigned char has_ctrl120:1; + unsigned char has_ctrl121:1; + unsigned char has_ctrl122_query31:1; + unsigned char has_ctrl123:1; + unsigned char has_ctrl124:1; + unsigned char has_query32:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_32 { + union { + struct { + unsigned char has_ctrl125:1; + unsigned char has_ctrl126:1; + unsigned char has_ctrl127:1; + unsigned char has_abs_charge_pump_disable:1; + unsigned char has_query33:1; + unsigned char has_data24:1; + unsigned char has_query34:1; + unsigned char has_query35:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_33 { + union { + struct { + unsigned char has_ctrl128:1; + unsigned char has_ctrl129:1; + unsigned char has_ctrl130:1; + unsigned char has_ctrl131:1; + unsigned char has_ctrl132:1; + unsigned char has_ctrl133:1; + unsigned char has_ctrl134:1; + unsigned char has_query36:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_35 { + union { + struct { + unsigned char has_data25:1; + unsigned char has_ctrl135:1; + unsigned char has_ctrl136:1; + unsigned char has_ctrl137:1; + unsigned char has_ctrl138:1; + unsigned char has_ctrl139:1; + unsigned char has_data26:1; + unsigned char has_ctrl140:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_36 { + union { + struct { + unsigned char has_ctrl141:1; + unsigned char has_ctrl142:1; + unsigned char has_query37:1; + unsigned char has_ctrl143:1; + unsigned char has_ctrl144:1; + unsigned char has_ctrl145:1; + unsigned char has_ctrl146:1; + unsigned char has_query38:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_38 { + union { + struct { + unsigned char has_ctrl147:1; + unsigned char has_ctrl148:1; + unsigned char has_ctrl149:1; + unsigned char has_ctrl150:1; + unsigned char has_ctrl151:1; + unsigned char has_ctrl152:1; + unsigned char has_ctrl153:1; + unsigned char has_query39:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_39 { + union { + struct { + unsigned char has_ctrl154:1; + unsigned char has_ctrl155:1; + unsigned char has_ctrl156:1; + unsigned char has_ctrl160:1; + unsigned char has_ctrl157_ctrl158:1; + unsigned char f54_query39_b5__6:2; + unsigned char has_query40:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_40 { + union { + struct { + unsigned char has_ctrl169:1; + unsigned char has_ctrl163_query41:1; + unsigned char f54_query40_b2:1; + unsigned char has_ctrl165_query42:1; + unsigned char has_ctrl166:1; + unsigned char has_ctrl167:1; + unsigned char has_ctrl168:1; + unsigned char has_query43:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_43 { + union { + struct { + unsigned char f54_query43_b0__1:2; + unsigned char has_ctrl171:1; + unsigned char has_ctrl172_query44_query45:1; + unsigned char has_ctrl173:1; + unsigned char has_ctrl174:1; + unsigned char has_ctrl175:1; + unsigned char has_query46:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_46 { + union { + struct { + unsigned char has_ctrl176:1; + unsigned char has_ctrl177_ctrl178:1; + unsigned char has_ctrl179:1; + unsigned char f54_query46_b3:1; + unsigned char has_data27:1; + unsigned char has_data28:1; + unsigned char f54_query46_b6:1; + unsigned char has_query47:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_47 { + union { + struct { + unsigned char f54_query47_b0:1; + unsigned char has_ctrl182:1; + unsigned char has_ctrl183:1; + unsigned char f54_query47_b3:1; + unsigned char has_ctrl185:1; + unsigned char has_ctrl186:1; + unsigned char has_ctrl187:1; + unsigned char has_query49:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_49 { + union { + struct { + unsigned char f54_query49_b0__1:2; + unsigned char has_ctrl188:1; + unsigned char has_data31:1; + unsigned char f54_query49_b4__6:3; + unsigned char has_query50:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_50 { + union { + struct { + unsigned char f54_query50_b0__6:7; + unsigned char has_query51:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_51 { + union { + struct { + unsigned char f54_query51_b0__4:5; + unsigned char has_query53_query54_ctrl198:1; + unsigned char has_ctrl199:1; + unsigned char has_query55:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_55 { + union { + struct { + unsigned char has_query56:1; + unsigned char has_data33_data34:1; + unsigned char has_alt_report_rate:1; + unsigned char has_ctrl200:1; + unsigned char has_ctrl201_ctrl202:1; + unsigned char has_ctrl203:1; + unsigned char has_ctrl204:1; + unsigned char has_query57:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_57 { + union { + struct { + unsigned char has_ctrl205:1; + unsigned char has_ctrl206:1; + unsigned char has_usb_bulk_read:1; + unsigned char has_ctrl207:1; + unsigned char has_ctrl208:1; + unsigned char has_ctrl209:1; + unsigned char has_ctrl210:1; + unsigned char has_query58:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_58 { + union { + struct { + unsigned char has_query59:1; + unsigned char has_query60:1; + unsigned char has_ctrl211:1; + unsigned char has_ctrl212:1; + unsigned char has_hybrid_abs_tx_axis_filtering:1; + unsigned char has_hybrid_abs_tx_interpolation:1; + unsigned char has_ctrl213:1; + unsigned char has_query61:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_61 { + union { + struct { + unsigned char has_ctrl214:1; + unsigned char has_ctrl215_query62_query63:1; + unsigned char f54_query_61_b2:1; + unsigned char has_ctrl216:1; + unsigned char has_ctrl217:1; + unsigned char has_misc_host_ctrl:1; + unsigned char hybrid_abs_buttons:1; + unsigned char has_query64:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_64 { + union { + struct { + unsigned char has_ctrl101_sub1:1; + unsigned char has_ctrl220:1; + unsigned char has_ctrl221:1; + unsigned char has_ctrl222:1; + unsigned char has_ctrl219_sub1:1; + unsigned char has_ctrl103_sub3:1; + unsigned char has_ctrl224_ctrl226_ctrl227:1; + unsigned char has_query65:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_65 { + union { + struct { + unsigned char f54_query_65_b0__1:2; + unsigned char has_ctrl101_sub2:1; + unsigned char f54_query_65_b3__4:2; + unsigned char has_query66_ctrl231:1; + unsigned char has_ctrl232:1; + unsigned char has_query67:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_67 { + union { + struct { + unsigned char has_abs_doze_spatial_filter_en:1; + unsigned char has_abs_doze_avg_filter_enhancement_en:1; + unsigned char has_single_display_pulse:1; + unsigned char f54_query_67_b3__4:2; + unsigned char has_ctrl235_ctrl236:1; + unsigned char f54_query_67_b6:1; + unsigned char has_query68:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_68 { + union { + struct { + unsigned char f54_query_68_b0:1; + unsigned char has_ctrl238:1; + unsigned char has_ctrl238_sub1:1; + unsigned char has_ctrl238_sub2:1; + unsigned char has_ctrl239:1; + unsigned char has_freq_filter_bw_ext:1; + unsigned char is_tddi_hic:1; + unsigned char has_query69:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_69 { + union { + struct { + unsigned char has_ctrl240_sub0:1; + unsigned char has_ctrl240_sub1_sub2:1; + unsigned char has_ctrl240_sub3:1; + unsigned char has_ctrl240_sub4:1; + unsigned char f54_query_69_b4__7:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_data_31 { + union { + struct { + unsigned char is_calibration_crc:1; + unsigned char calibration_crc:1; + unsigned char short_test_row_number:5; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_7 { + union { + struct { + unsigned char cbc_cap:3; + unsigned char cbc_polarity:1; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl7_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_41 { + union { + struct { + unsigned char no_signal_clarity:1; + unsigned char f54_ctrl41_b1__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_57 { + union { + struct { + unsigned char cbc_cap:3; + unsigned char cbc_polarity:1; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl57_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_86 { + union { + struct { + unsigned char enable_high_noise_state:1; + unsigned char dynamic_sense_display_ratio:2; + unsigned char f54_ctrl86_b3__7:5; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_88 { + union { + struct { + unsigned char tx_low_reference_polarity:1; + unsigned char tx_high_reference_polarity:1; + unsigned char abs_low_reference_polarity:1; + unsigned char abs_polarity:1; + unsigned char cbc_polarity:1; + unsigned char cbc_tx_carrier_selection:1; + unsigned char charge_pump_enable:1; + unsigned char cbc_abs_auto_servo:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_110 { + union { + struct { + unsigned char active_stylus_rx_feedback_cap; + unsigned char active_stylus_rx_feedback_cap_reference; + unsigned char active_stylus_low_reference; + unsigned char active_stylus_high_reference; + unsigned char active_stylus_gain_control; + unsigned char active_stylus_gain_control_reference; + unsigned char active_stylus_timing_mode; + unsigned char active_stylus_discovery_bursts; + unsigned char active_stylus_detection_bursts; + unsigned char active_stylus_discovery_noise_multiplier; + unsigned char active_stylus_detection_envelope_min; + unsigned char active_stylus_detection_envelope_max; + unsigned char active_stylus_lose_count; + } __packed; + struct { + unsigned char data[13]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_149 { + union { + struct { + unsigned char trans_cbc_global_cap_enable:1; + unsigned char f54_ctrl149_b1__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_188 { + union { + struct { + unsigned char start_calibration:1; + unsigned char start_is_calibration:1; + unsigned char frequency:2; + unsigned char start_production_test:1; + unsigned char short_test_calibration:1; + unsigned char f54_ctrl188_b7:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control { + struct f54_control_7 *reg_7; + struct f54_control_41 *reg_41; + struct f54_control_57 *reg_57; + struct f54_control_86 *reg_86; + struct f54_control_88 *reg_88; + struct f54_control_110 *reg_110; + struct f54_control_149 *reg_149; + struct f54_control_188 *reg_188; +}; + +struct synaptics_rmi4_f54_handle { + bool no_auto_cal; + bool skip_preparation; + unsigned char status; + unsigned char intr_mask; + unsigned char intr_reg_num; + unsigned char tx_assigned; + unsigned char rx_assigned; + unsigned char *report_data; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short fifoindex; + unsigned int report_size; + unsigned int data_buffer_size; + unsigned int data_pos; + enum f54_report_types report_type; + struct f54_query query; + struct f54_query_13 query_13; + struct f54_query_15 query_15; + struct f54_query_16 query_16; + struct f54_query_21 query_21; + struct f54_query_22 query_22; + struct f54_query_23 query_23; + struct f54_query_25 query_25; + struct f54_query_27 query_27; + struct f54_query_29 query_29; + struct f54_query_30 query_30; + struct f54_query_32 query_32; + struct f54_query_33 query_33; + struct f54_query_35 query_35; + struct f54_query_36 query_36; + struct f54_query_38 query_38; + struct f54_query_39 query_39; + struct f54_query_40 query_40; + struct f54_query_43 query_43; + struct f54_query_46 query_46; + struct f54_query_47 query_47; + struct f54_query_49 query_49; + struct f54_query_50 query_50; + struct f54_query_51 query_51; + struct f54_query_55 query_55; + struct f54_query_57 query_57; + struct f54_query_58 query_58; + struct f54_query_61 query_61; + struct f54_query_64 query_64; + struct f54_query_65 query_65; + struct f54_query_67 query_67; + struct f54_query_68 query_68; + struct f54_query_69 query_69; + struct f54_data_31 data_31; + struct f54_control control; + struct mutex status_mutex; + struct kobject *sysfs_dir; + struct hrtimer watchdog; + struct work_struct timeout_work; + struct work_struct test_report_work; + struct workqueue_struct *test_report_workqueue; + struct synaptics_rmi4_data *rmi4_data; +}; + +struct f55_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char has_sensor_assignment:1; + unsigned char has_edge_compensation:1; + unsigned char curve_compensation_mode:2; + unsigned char has_ctrl6:1; + unsigned char has_alternate_transmitter_assignment:1; + unsigned char has_single_layer_multi_touch:1; + unsigned char has_query5:1; + } __packed; + unsigned char data[3]; + }; +}; + +struct f55_query_3 { + union { + struct { + unsigned char has_ctrl8:1; + unsigned char has_ctrl9:1; + unsigned char has_oncell_pattern_support:1; + unsigned char has_data0:1; + unsigned char has_single_wide_pattern_support:1; + unsigned char has_mirrored_tx_pattern_support:1; + unsigned char has_discrete_pattern_support:1; + unsigned char has_query9:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_5 { + union { + struct { + unsigned char has_corner_compensation:1; + unsigned char has_ctrl12:1; + unsigned char has_trx_configuration:1; + unsigned char has_ctrl13:1; + unsigned char f55_query5_b4:1; + unsigned char has_ctrl14:1; + unsigned char has_basis_function:1; + unsigned char has_query17:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_17 { + union { + struct { + unsigned char f55_query17_b0:1; + unsigned char has_ctrl16:1; + unsigned char has_ctrl18_ctrl19:1; + unsigned char has_ctrl17:1; + unsigned char has_ctrl20:1; + unsigned char has_ctrl21:1; + unsigned char has_ctrl22:1; + unsigned char has_query18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_18 { + union { + struct { + unsigned char has_ctrl23:1; + unsigned char has_ctrl24:1; + unsigned char has_query19:1; + unsigned char has_ctrl25:1; + unsigned char has_ctrl26:1; + unsigned char has_ctrl27_query20:1; + unsigned char has_ctrl28_query21:1; + unsigned char has_query22:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_22 { + union { + struct { + unsigned char has_ctrl29:1; + unsigned char has_query23:1; + unsigned char has_guard_disable:1; + unsigned char has_ctrl30:1; + unsigned char has_ctrl31:1; + unsigned char has_ctrl32:1; + unsigned char has_query24_through_query27:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_23 { + union { + struct { + unsigned char amp_sensor_enabled:1; + unsigned char image_transposed:1; + unsigned char first_column_at_left_side:1; + unsigned char size_of_column2mux:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_28 { + union { + struct { + unsigned char f55_query28_b0__4:5; + unsigned char has_ctrl37:1; + unsigned char has_query29:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_30 { + union { + struct { + unsigned char has_ctrl38:1; + unsigned char has_query31_query32:1; + unsigned char has_ctrl39:1; + unsigned char has_ctrl40:1; + unsigned char has_ctrl41:1; + unsigned char has_ctrl42:1; + unsigned char has_ctrl43_ctrl44:1; + unsigned char has_query33:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_33 { + union { + struct { + unsigned char has_extended_amp_pad:1; + unsigned char has_extended_amp_btn:1; + unsigned char has_ctrl45_ctrl46:1; + unsigned char f55_query33_b3:1; + unsigned char has_ctrl47_sub0_sub1:1; + unsigned char f55_query33_b5__7:3; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_control_43 { + union { + struct { + unsigned char swap_sensor_side:1; + unsigned char f55_ctrl43_b1__7:7; + unsigned char afe_l_mux_size:4; + unsigned char afe_r_mux_size:4; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f55_handle { + bool amp_sensor; + bool extended_amp; + bool has_force; + unsigned char size_of_column2mux; + unsigned char afe_mux_offset; + unsigned char force_tx_offset; + unsigned char force_rx_offset; + unsigned char *tx_assignment; + unsigned char *rx_assignment; + unsigned char *force_tx_assignment; + unsigned char *force_rx_assignment; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + struct f55_query query; + struct f55_query_3 query_3; + struct f55_query_5 query_5; + struct f55_query_17 query_17; + struct f55_query_18 query_18; + struct f55_query_22 query_22; + struct f55_query_23 query_23; + struct f55_query_28 query_28; + struct f55_query_30 query_30; + struct f55_query_33 query_33; +}; + +struct f21_query_2 { + union { + struct { + unsigned char size_of_query3; + struct { + unsigned char query0_is_present:1; + unsigned char query1_is_present:1; + unsigned char query2_is_present:1; + unsigned char query3_is_present:1; + unsigned char query4_is_present:1; + unsigned char query5_is_present:1; + unsigned char query6_is_present:1; + unsigned char query7_is_present:1; + } __packed; + struct { + unsigned char query8_is_present:1; + unsigned char query9_is_present:1; + unsigned char query10_is_present:1; + unsigned char query11_is_present:1; + unsigned char query12_is_present:1; + unsigned char query13_is_present:1; + unsigned char query14_is_present:1; + unsigned char query15_is_present:1; + } __packed; + }; + unsigned char data[3]; + }; +}; + +struct f21_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct f21_query_11 { + union { + struct { + unsigned char has_high_resolution_force:1; + unsigned char has_force_sensing_txrx_mapping:1; + unsigned char f21_query11_00_b2__7:6; + unsigned char f21_query11_00_reserved; + unsigned char max_number_of_force_sensors; + unsigned char max_number_of_force_txs; + unsigned char max_number_of_force_rxs; + unsigned char f21_query11_01_reserved; + } __packed; + unsigned char data[6]; + }; +}; + +struct synaptics_rmi4_f21_handle { + bool has_force; + unsigned char tx_assigned; + unsigned char rx_assigned; + unsigned char max_num_of_tx; + unsigned char max_num_of_rx; + unsigned char max_num_of_txrx; + unsigned char *force_txrx_assignment; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; +}; + +show_prototype(num_of_mapped_tx); +show_prototype(num_of_mapped_rx); +show_prototype(tx_mapping); +show_prototype(rx_mapping); +show_prototype(num_of_mapped_force_tx); +show_prototype(num_of_mapped_force_rx); +show_prototype(force_tx_mapping); +show_prototype(force_rx_mapping); +show_prototype(report_size); +show_prototype(status); + +store_prototype(do_preparation); +store_prototype(force_cal); +store_prototype(get_report); +store_prototype(resume_touch); +store_prototype(do_afe_calibration); + +show_store_prototype(report_type); +show_store_prototype(fifoindex); +show_store_prototype(no_auto_cal); +show_store_prototype(read_report); + +static struct attribute *attrs[] = { + attrify(num_of_mapped_tx), + attrify(num_of_mapped_rx), + attrify(tx_mapping), + attrify(rx_mapping), + attrify(num_of_mapped_force_tx), + attrify(num_of_mapped_force_rx), + attrify(force_tx_mapping), + attrify(force_rx_mapping), + attrify(report_size), + attrify(status), + attrify(do_preparation), + attrify(force_cal), + attrify(get_report), + attrify(resume_touch), + attrify(do_afe_calibration), + attrify(report_type), + attrify(fifoindex), + attrify(no_auto_cal), + attrify(read_report), + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static ssize_t test_sysfs_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute test_report_data = { + .attr = { + .name = "report_data", + .mode = 0444, + }, + .size = 0, + .read = test_sysfs_data_read, +}; + +static struct synaptics_rmi4_f54_handle *f54; +static struct synaptics_rmi4_f55_handle *f55; +static struct synaptics_rmi4_f21_handle *f21; + +DECLARE_COMPLETION(test_remove_complete); + +static bool test_report_type_valid(enum f54_report_types report_type) +{ + switch (report_type) { + case F54_8BIT_IMAGE: + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TX_TO_TX_SHORTS: + case F54_RX_TO_RX_SHORTS_1: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_RX_OPENS_1: + case F54_TX_OPENS: + case F54_TX_TO_GND_SHORTS: + case F54_RX_TO_RX_SHORTS_2: + case F54_RX_OPENS_2: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_NO_RX_COUPLING: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_TRX_OPENS: + case F54_TRX_TO_GND_SHORTS: + case F54_TRX_SHORTS: + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + case F54_AMP_FULL_RAW_CAP: + case F54_AMP_RAW_ADC: + case F54_FULL_RAW_CAP_TDDI: + return true; + default: + f54->report_type = INVALID_REPORT_TYPE; + f54->report_size = 0; + return false; + } +} + +static void test_set_report_size(void) +{ + int retval; + unsigned char tx = f54->tx_assigned; + unsigned char rx = f54->rx_assigned; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + f54->report_size = tx * rx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_NO_RX_COUPLING: + case F54_SENSOR_SPEED: + case F54_AMP_FULL_RAW_CAP: + case F54_AMP_RAW_ADC: + case F54_FULL_RAW_CAP_TDDI: + f54->report_size = 2 * tx * rx; + break; + case F54_HIGH_RESISTANCE: + f54->report_size = HIGH_RESISTANCE_DATA_SIZE; + break; + case F54_TX_TO_TX_SHORTS: + case F54_TX_OPENS: + case F54_TX_TO_GND_SHORTS: + f54->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX_SHORTS_1: + case F54_RX_OPENS_1: + if (rx < tx) + f54->report_size = 2 * rx * rx; + else + f54->report_size = 2 * tx * rx; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + f54->report_size = FULL_RAW_CAP_MIN_MAX_DATA_SIZE; + break; + case F54_RX_TO_RX_SHORTS_2: + case F54_RX_OPENS_2: + if (rx <= tx) + f54->report_size = 0; + else + f54->report_size = 2 * rx * (rx - tx); + break; + case F54_ADC_RANGE: + if (f54->query.has_signal_clarity) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_41->address, + f54->control.reg_41->data, + sizeof(f54->control.reg_41->data)); + if (retval < 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Failed to read control reg_41\n", + __func__); + f54->report_size = 0; + break; + } + if (!f54->control.reg_41->no_signal_clarity) { + if (tx % 4) + tx += 4 - (tx % 4); + } + } + f54->report_size = 2 * tx * rx; + break; + case F54_TRX_OPENS: + case F54_TRX_TO_GND_SHORTS: + case F54_TRX_SHORTS: + f54->report_size = TRX_OPEN_SHORT_DATA_SIZE; + break; + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + tx += f21->tx_assigned; + rx += f21->rx_assigned; + f54->report_size = 4 * (tx + rx); + break; + default: + f54->report_size = 0; + } +} + +static int test_set_interrupt(bool set) +{ + int retval; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short f01_ctrl_reg; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + intr_mask = rmi4_data->intr_mask; + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (!set) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + } + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (set) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } + } + } + + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (set) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &f54->intr_mask, + 1); + if (retval < 0) + return retval; + } + + return 0; +} + +static int test_wait_for_command_completion(void) +{ + int retval; + unsigned char value; + unsigned char timeout_count; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + timeout_count = 0; + do { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->command_base_addr, + &value, + sizeof(value)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read command register\n", + __func__); + return retval; + } + + if (value == 0x00) + break; + + msleep(100); + timeout_count++; + } while (timeout_count < COMMAND_TIMEOUT_100MS); + + if (timeout_count == COMMAND_TIMEOUT_100MS) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for command completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int test_do_command(unsigned char command) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command\n", + __func__); + return retval; + } + + retval = test_wait_for_command_completion(); + if (retval < 0) + return retval; + + return 0; +} + +static int test_do_preparation(void) +{ + int retval; + unsigned char value; + unsigned char zero = 0x00; + unsigned char device_ctrl; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set no sleep\n", + __func__); + return retval; + } + + device_ctrl |= NO_SLEEP_ON; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set no sleep\n", + __func__); + return retval; + } + + if (f54->skip_preparation) + return 0; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + case F54_FULL_RAW_CAP_TDDI: + break; + case F54_AMP_RAW_ADC: + if (f54->query_49.has_ctrl188) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + f54->control.reg_188->start_production_test = 1; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + } + break; + default: + if (f54->query.touch_controller_family == 1) + disable_cbc(reg_7); + else if (f54->query.has_ctrl88) + disable_cbc(reg_88); + + if (f54->query.has_0d_acquisition_control) + disable_cbc(reg_57); + + if ((f54->query.has_query15) && + (f54->query_15.has_query25) && + (f54->query_25.has_query27) && + (f54->query_27.has_query29) && + (f54->query_29.has_query30) && + (f54->query_30.has_query32) && + (f54->query_32.has_query33) && + (f54->query_33.has_query36) && + (f54->query_36.has_query38) && + (f54->query_38.has_ctrl149)) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_149->address, + &zero, + sizeof(f54->control.reg_149->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable global CBC\n", + __func__); + return retval; + } + } + + if (f54->query.has_signal_clarity) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_41->address, + &value, + sizeof(f54->control.reg_41->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable signal clarity\n", + __func__); + return retval; + } + value |= 0x01; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_41->address, + &value, + sizeof(f54->control.reg_41->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable signal clarity\n", + __func__); + return retval; + } + } + + retval = test_do_command(COMMAND_FORCE_UPDATE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do force update\n", + __func__); + return retval; + } + + retval = test_do_command(COMMAND_FORCE_CAL); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do force cal\n", + __func__); + return retval; + } + } + + return 0; +} + +static int test_do_afe_calibration(enum f54_afe_cal mode) +{ + int retval; + unsigned char timeout = CALIBRATION_TIMEOUT_S; + unsigned char timeout_count = 0; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to start calibration\n", + __func__); + return retval; + } + + if (mode == F54_AFE_CAL) + f54->control.reg_188->start_calibration = 1; + else if (mode == F54_AFE_IS_CAL) + f54->control.reg_188->start_is_calibration = 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to start calibration\n", + __func__); + return retval; + } + + do { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete calibration\n", + __func__); + return retval; + } + + if (mode == F54_AFE_CAL) { + if (!f54->control.reg_188->start_calibration) + break; + } else if (mode == F54_AFE_IS_CAL) { + if (!f54->control.reg_188->start_is_calibration) + break; + } + + if (timeout_count == timeout) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for calibration completion\n", + __func__); + return -EBUSY; + } + + timeout_count++; + msleep(1000); + } while (true); + + /* check CRC */ + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->data_31.address, + f54->data_31.data, + sizeof(f54->data_31.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read calibration CRC\n", + __func__); + return retval; + } + + if (mode == F54_AFE_CAL) { + if (f54->data_31.calibration_crc == 0) + return 0; + } else if (mode == F54_AFE_IS_CAL) { + if (f54->data_31.is_calibration_crc == 0) + return 0; + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read calibration CRC\n", + __func__); + + return -EINVAL; +} + +static int test_check_for_idle_status(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + switch (f54->status) { + case STATUS_IDLE: + retval = 0; + break; + case STATUS_BUSY: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Status busy\n", + __func__); + retval = -EINVAL; + break; + case STATUS_ERROR: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Status error\n", + __func__); + retval = -EINVAL; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid status (%d)\n", + __func__, f54->status); + retval = -EINVAL; + } + + return retval; +} + +static void test_timeout_work(struct work_struct *work) +{ + int retval; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + if (f54->status == STATUS_BUSY) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read command register\n", + __func__); + } else if (command & COMMAND_GET_REPORT) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report type not supported by FW\n", + __func__); + } else { + queue_work(f54->test_report_workqueue, + &f54->test_report_work); + goto exit; + } + f54->status = STATUS_ERROR; + f54->report_size = 0; + } + +exit: + mutex_unlock(&f54->status_mutex); +} + +static enum hrtimer_restart test_get_report_timeout(struct hrtimer *timer) +{ + schedule_work(&(f54->timeout_work)); + + return HRTIMER_NORESTART; +} + +static ssize_t num_of_mapped_tx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->tx_assigned); +} + +static ssize_t num_of_mapped_rx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rx_assigned); +} + +static ssize_t tx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char tx_num; + unsigned char tx_electrodes; + + if (!f55) + return -EINVAL; + + tx_electrodes = f55->query.num_of_tx_electrodes; + + for (ii = 0; ii < tx_electrodes; ii++) { + tx_num = f55->tx_assignment[ii]; + if (tx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", tx_num); + buf += cnt; + count += cnt; + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t rx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char rx_num; + unsigned char rx_electrodes; + + if (!f55) + return -EINVAL; + + rx_electrodes = f55->query.num_of_rx_electrodes; + + for (ii = 0; ii < rx_electrodes; ii++) { + rx_num = f55->rx_assignment[ii]; + if (rx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", rx_num); + buf += cnt; + count += cnt; + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t num_of_mapped_force_tx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f21->tx_assigned); +} + +static ssize_t num_of_mapped_force_rx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f21->rx_assigned); +} + +static ssize_t force_tx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char tx_num; + unsigned char tx_electrodes; + + if ((!f55 || !f55->has_force) && (!f21 || !f21->has_force)) + return -EINVAL; + + if (f55->has_force) { + tx_electrodes = f55->query.num_of_tx_electrodes; + + for (ii = 0; ii < tx_electrodes; ii++) { + tx_num = f55->force_tx_assignment[ii]; + if (tx_num == 0xff) { + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + } else { + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + tx_num); + } + buf += cnt; + count += cnt; + } + } else if (f21->has_force) { + tx_electrodes = f21->max_num_of_tx; + + for (ii = 0; ii < tx_electrodes; ii++) { + tx_num = f21->force_txrx_assignment[ii]; + if (tx_num == 0xff) { + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + } else { + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + tx_num); + } + buf += cnt; + count += cnt; + } + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t force_rx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char offset; + unsigned char rx_num; + unsigned char rx_electrodes; + + if ((!f55 || !f55->has_force) && (!f21 || !f21->has_force)) + return -EINVAL; + + if (f55->has_force) { + rx_electrodes = f55->query.num_of_rx_electrodes; + + for (ii = 0; ii < rx_electrodes; ii++) { + rx_num = f55->force_rx_assignment[ii]; + if (rx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + rx_num); + buf += cnt; + count += cnt; + } + } else if (f21->has_force) { + offset = f21->max_num_of_tx; + rx_electrodes = f21->max_num_of_rx; + + for (ii = offset; ii < (rx_electrodes + offset); ii++) { + rx_num = f21->force_txrx_assignment[ii]; + if (rx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + rx_num); + buf += cnt; + count += cnt; + } + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t report_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size); +} + +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + mutex_lock(&f54->status_mutex); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", f54->status); + + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t do_preparation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + retval = test_do_preparation(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do preparation\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t force_cal_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + retval = test_do_command(COMMAND_FORCE_CAL); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do force cal\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t get_report_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + if (!test_report_type_valid(f54->report_type)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid report type\n", + __func__); + retval = -EINVAL; + goto exit; + } + + test_set_interrupt(true); + + command = (unsigned char)COMMAND_GET_REPORT; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write get report command\n", + __func__); + goto exit; + } + + f54->status = STATUS_BUSY; + f54->report_size = 0; + f54->data_pos = 0; + + hrtimer_start(&f54->watchdog, + ktime_set(GET_REPORT_TIMEOUT_S, 0), + HRTIMER_MODE_REL); + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t resume_touch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char device_ctrl; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to restore no sleep setting\n", + __func__); + return retval; + } + + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + device_ctrl |= rmi4_data->no_sleep_setting; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to restore no sleep setting\n", + __func__); + return retval; + } + + test_set_interrupt(false); + + if (f54->skip_preparation) + return count; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + case F54_FULL_RAW_CAP_TDDI: + break; + case F54_AMP_RAW_ADC: + if (f54->query_49.has_ctrl188) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + f54->control.reg_188->start_production_test = 0; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + } + break; + default: + rmi4_data->reset_device(rmi4_data, false); + } + + return count; +} + +static ssize_t do_afe_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (!f54->query_49.has_ctrl188) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: F54_ANALOG_Ctrl188 not found\n", + __func__); + return -EINVAL; + } + + if (setting == 0 || setting == 1) + retval = test_do_afe_calibration((enum f54_afe_cal)setting); + else + return -EINVAL; + + if (retval) + return retval; + else + return count; +} + +static ssize_t report_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_type); +} + +static ssize_t report_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + if (!test_report_type_valid((enum f54_report_types)setting)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report type not supported by driver\n", + __func__); + retval = -EINVAL; + goto exit; + } + + f54->report_type = (enum f54_report_types)setting; + data = (unsigned char)setting; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->data_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write report type\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t fifoindex_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned char data[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->data_base_addr + REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read report index\n", + __func__); + return retval; + } + + batohs(&f54->fifoindex, data); + + return snprintf(buf, PAGE_SIZE, "%u\n", f54->fifoindex); +} + +static ssize_t fifoindex_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char data[2]; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + f54->fifoindex = setting; + + hstoba(data, (unsigned short)setting); + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->data_base_addr + REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write report index\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t no_auto_cal_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal); +} + +static ssize_t no_auto_cal_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting > 1) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read no auto cal setting\n", + __func__); + return retval; + } + + if (setting) + data |= CONTROL_NO_AUTO_CAL; + else + data &= ~CONTROL_NO_AUTO_CAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write no auto cal setting\n", + __func__); + return retval; + } + + f54->no_auto_cal = (setting == 1); + + return count; +} + +static ssize_t read_report_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int ii; + unsigned int jj; + int cnt; + int count = 0; + int tx_num = f54->tx_assigned; + int rx_num = f54->rx_assigned; + char *report_data_8; + short *report_data_16; + int *report_data_32; + unsigned short *report_data_u16; + unsigned int *report_data_u32; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + report_data_8 = (char *)f54->report_data; + for (ii = 0; ii < f54->report_size; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%03d: %d\n", + ii, *report_data_8); + report_data_8++; + buf += cnt; + count += cnt; + } + break; + case F54_AMP_RAW_ADC: + report_data_u16 = (unsigned short *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "tx = %d\nrx = %d\n", + tx_num, rx_num); + buf += cnt; + count += cnt; + + for (ii = 0; ii < tx_num; ii++) { + for (jj = 0; jj < (rx_num - 1); jj++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d ", + *report_data_u16); + report_data_u16++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d\n", + *report_data_u16); + report_data_u16++; + buf += cnt; + count += cnt; + } + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_NO_RX_COUPLING: + case F54_SENSOR_SPEED: + case F54_AMP_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_TDDI: + report_data_16 = (short *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "tx = %d\nrx = %d\n", + tx_num, rx_num); + buf += cnt; + count += cnt; + + for (ii = 0; ii < tx_num; ii++) { + for (jj = 0; jj < (rx_num - 1); jj++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d ", + *report_data_16); + report_data_16++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d\n", + *report_data_16); + report_data_16++; + buf += cnt; + count += cnt; + } + break; + case F54_HIGH_RESISTANCE: + case F54_FULL_RAW_CAP_MIN_MAX: + report_data_16 = (short *)f54->report_data; + for (ii = 0; ii < f54->report_size; ii += 2) { + cnt = snprintf(buf, PAGE_SIZE - count, "%03d: %d\n", + ii / 2, *report_data_16); + report_data_16++; + buf += cnt; + count += cnt; + } + break; + case F54_ABS_RAW_CAP: + case F54_ABS_HYBRID_RAW_CAP: + tx_num += f21->tx_assigned; + rx_num += f21->rx_assigned; + report_data_u32 = (unsigned int *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "rx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5u", + *report_data_u32); + report_data_u32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, "tx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5u", + *report_data_u32); + report_data_u32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + break; + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + tx_num += f21->tx_assigned; + rx_num += f21->rx_assigned; + report_data_32 = (int *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "rx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5d", + *report_data_32); + report_data_32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, "tx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5d", + *report_data_32); + report_data_32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + break; + default: + for (ii = 0; ii < f54->report_size; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%03d: 0x%02x\n", + ii, f54->report_data[ii]); + buf += cnt; + count += cnt; + } + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t read_report_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char timeout = GET_REPORT_TIMEOUT_S * 10; + unsigned char timeout_count; + const char cmd[] = {'1', 0}; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = report_type_store(dev, attr, buf, count); + if (retval < 0) + goto exit; + + retval = do_preparation_store(dev, attr, cmd, 1); + if (retval < 0) + goto exit; + + retval = get_report_store(dev, attr, cmd, 1); + if (retval < 0) + goto exit; + + timeout_count = 0; + do { + if (f54->status != STATUS_BUSY) + break; + msleep(100); + timeout_count++; + } while (timeout_count < timeout); + + if ((f54->status != STATUS_IDLE) || (f54->report_size == 0)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read report\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = resume_touch_store(dev, attr, cmd, 1); + if (retval < 0) + goto exit; + + return count; + +exit: + rmi4_data->reset_device(rmi4_data, false); + + return retval; +} + +static ssize_t test_sysfs_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int read_size; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + if (!f54->report_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report type %d data not available\n", + __func__, f54->report_type); + retval = -EINVAL; + goto exit; + } + + if ((f54->data_pos + count) > f54->report_size) + read_size = f54->report_size - f54->data_pos; + else + read_size = min_t(unsigned int, count, f54->report_size); + + retval = secure_memcpy(buf, count, f54->report_data + f54->data_pos, + f54->data_buffer_size - f54->data_pos, read_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy report data\n", + __func__); + goto exit; + } + f54->data_pos += read_size; + retval = read_size; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static void test_report_work(struct work_struct *work) +{ + int retval; + unsigned char report_index[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + if (f54->status != STATUS_BUSY) { + retval = f54->status; + goto exit; + } + + retval = test_wait_for_command_completion(); + if (retval < 0) { + retval = STATUS_ERROR; + goto exit; + } + + test_set_report_size(); + if (f54->report_size == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report data size = 0\n", + __func__); + retval = STATUS_ERROR; + goto exit; + } + + if (f54->data_buffer_size < f54->report_size) { + if (f54->data_buffer_size) + kfree(f54->report_data); + f54->report_data = kzalloc(f54->report_size, GFP_KERNEL); + if (!f54->report_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for data buffer\n", + __func__); + f54->data_buffer_size = 0; + retval = STATUS_ERROR; + goto exit; + } + f54->data_buffer_size = f54->report_size; + } + + report_index[0] = 0; + report_index[1] = 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->data_base_addr + REPORT_INDEX_OFFSET, + report_index, + sizeof(report_index)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write report data index\n", + __func__); + retval = STATUS_ERROR; + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->data_base_addr + REPORT_DATA_OFFSET, + f54->report_data, + f54->report_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read report data\n", + __func__); + retval = STATUS_ERROR; + goto exit; + } + + retval = STATUS_IDLE; + +exit: + mutex_unlock(&f54->status_mutex); + + if (retval == STATUS_ERROR) + f54->report_size = 0; + + f54->status = retval; +} + +static void test_remove_sysfs(void) +{ + sysfs_remove_group(f54->sysfs_dir, &attr_group); + sysfs_remove_bin_file(f54->sysfs_dir, &test_report_data); + kobject_put(f54->sysfs_dir); +} + +static int test_set_sysfs(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + f54->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, + &rmi4_data->input_dev->dev.kobj); + if (!f54->sysfs_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs directory\n", + __func__); + goto exit_directory; + } + + retval = sysfs_create_bin_file(f54->sysfs_dir, &test_report_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_bin_file; + } + + retval = sysfs_create_group(f54->sysfs_dir, &attr_group); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_attributes; + } + + return 0; + +exit_attributes: + sysfs_remove_group(f54->sysfs_dir, &attr_group); + sysfs_remove_bin_file(f54->sysfs_dir, &test_report_data); + +exit_bin_file: + kobject_put(f54->sysfs_dir); + +exit_directory: + return -ENODEV; +} + +static void test_free_control_mem(void) +{ + struct f54_control control = f54->control; + + kfree(control.reg_7); + kfree(control.reg_41); + kfree(control.reg_57); + kfree(control.reg_86); + kfree(control.reg_88); + kfree(control.reg_110); + kfree(control.reg_149); + kfree(control.reg_188); +} + +static void test_set_data(void) +{ + unsigned short reg_addr; + + reg_addr = f54->data_base_addr + REPORT_DATA_OFFSET + 1; + + /* data 4 */ + if (f54->query.has_sense_frequency_control) + reg_addr++; + + /* data 5 reserved */ + + /* data 6 */ + if (f54->query.has_interference_metric) + reg_addr += 2; + + /* data 7 */ + if (f54->query.has_one_byte_report_rate | + f54->query.has_two_byte_report_rate) + reg_addr++; + if (f54->query.has_two_byte_report_rate) + reg_addr++; + + /* data 8 */ + if (f54->query.has_variance_metric) + reg_addr += 2; + + /* data 9 */ + if (f54->query.has_multi_metric_state_machine) + reg_addr += 2; + + /* data 10 */ + if (f54->query.has_multi_metric_state_machine | + f54->query.has_noise_state) + reg_addr++; + + /* data 11 */ + if (f54->query.has_status) + reg_addr++; + + /* data 12 */ + if (f54->query.has_slew_metric) + reg_addr += 2; + + /* data 13 */ + if (f54->query.has_multi_metric_state_machine) + reg_addr += 2; + + /* data 14 */ + if (f54->query_13.has_cidim) + reg_addr++; + + /* data 15 */ + if (f54->query_13.has_rail_im) + reg_addr++; + + /* data 16 */ + if (f54->query_13.has_noise_mitigation_enhancement) + reg_addr++; + + /* data 17 */ + if (f54->query_16.has_data17) + reg_addr++; + + /* data 18 */ + if (f54->query_21.has_query24_data18) + reg_addr++; + + /* data 19 */ + if (f54->query_21.has_data19) + reg_addr++; + + /* data_20 */ + if (f54->query_25.has_ctrl109) + reg_addr++; + + /* data 21 */ + if (f54->query_27.has_data21) + reg_addr++; + + /* data 22 */ + if (f54->query_27.has_data22) + reg_addr++; + + /* data 23 */ + if (f54->query_29.has_data23) + reg_addr++; + + /* data 24 */ + if (f54->query_32.has_data24) + reg_addr++; + + /* data 25 */ + if (f54->query_35.has_data25) + reg_addr++; + + /* data 26 */ + if (f54->query_35.has_data26) + reg_addr++; + + /* data 27 */ + if (f54->query_46.has_data27) + reg_addr++; + + /* data 28 */ + if (f54->query_46.has_data28) + reg_addr++; + + /* data 29 30 reserved */ + + /* data 31 */ + if (f54->query_49.has_data31) { + f54->data_31.address = reg_addr; + reg_addr++; + } +} + +static int test_set_controls(void) +{ + int retval; + unsigned char length = 0; + unsigned char num_of_sensing_freqs; + unsigned short reg_addr = f54->control_base_addr; + struct f54_control *control = &f54->control; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + num_of_sensing_freqs = f54->query.number_of_sensing_frequencies; + + /* control 0 */ + reg_addr += CONTROL_0_SIZE; + + /* control 1 */ + if ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1)) + reg_addr += CONTROL_1_SIZE; + + /* control 2 */ + reg_addr += CONTROL_2_SIZE; + + /* control 3 */ + if (f54->query.has_pixel_touch_threshold_adjustment) + reg_addr += CONTROL_3_SIZE; + + /* controls 4 5 6 */ + if ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1)) + reg_addr += CONTROL_4_6_SIZE; + + /* control 7 */ + if (f54->query.touch_controller_family == 1) { + control->reg_7 = kzalloc(sizeof(*(control->reg_7)), + GFP_KERNEL); + if (!control->reg_7) + goto exit_no_mem; + control->reg_7->address = reg_addr; + reg_addr += CONTROL_7_SIZE; + } + + /* controls 8 9 */ + if ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1)) + reg_addr += CONTROL_8_9_SIZE; + + /* control 10 */ + if (f54->query.has_interference_metric) + reg_addr += CONTROL_10_SIZE; + + /* control 11 */ + if (f54->query.has_ctrl11) + reg_addr += CONTROL_11_SIZE; + + /* controls 12 13 */ + if (f54->query.has_relaxation_control) + reg_addr += CONTROL_12_13_SIZE; + + /* controls 14 15 16 */ + if (f54->query.has_sensor_assignment) { + reg_addr += CONTROL_14_SIZE; + reg_addr += CONTROL_15_SIZE * f54->query.num_of_rx_electrodes; + reg_addr += CONTROL_16_SIZE * f54->query.num_of_tx_electrodes; + } + + /* controls 17 18 19 */ + if (f54->query.has_sense_frequency_control) { + reg_addr += CONTROL_17_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_18_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_19_SIZE * num_of_sensing_freqs; + } + + /* control 20 */ + reg_addr += CONTROL_20_SIZE; + + /* control 21 */ + if (f54->query.has_sense_frequency_control) + reg_addr += CONTROL_21_SIZE; + + /* controls 22 23 24 25 26 */ + if (f54->query.has_firmware_noise_mitigation) + reg_addr += CONTROL_22_26_SIZE; + + /* control 27 */ + if (f54->query.has_iir_filter) + reg_addr += CONTROL_27_SIZE; + + /* control 28 */ + if (f54->query.has_firmware_noise_mitigation) + reg_addr += CONTROL_28_SIZE; + + /* control 29 */ + if (f54->query.has_cmn_removal) + reg_addr += CONTROL_29_SIZE; + + /* control 30 */ + if (f54->query.has_cmn_maximum) + reg_addr += CONTROL_30_SIZE; + + /* control 31 */ + if (f54->query.has_touch_hysteresis) + reg_addr += CONTROL_31_SIZE; + + /* controls 32 33 34 35 */ + if (f54->query.has_edge_compensation) + reg_addr += CONTROL_32_35_SIZE; + + /* control 36 */ + if ((f54->query.curve_compensation_mode == 1) || + (f54->query.curve_compensation_mode == 2)) { + if (f54->query.curve_compensation_mode == 1) { + length = max(f54->query.num_of_rx_electrodes, + f54->query.num_of_tx_electrodes); + } else if (f54->query.curve_compensation_mode == 2) { + length = f54->query.num_of_rx_electrodes; + } + reg_addr += CONTROL_36_SIZE * length; + } + + /* control 37 */ + if (f54->query.curve_compensation_mode == 2) + reg_addr += CONTROL_37_SIZE * f54->query.num_of_tx_electrodes; + + /* controls 38 39 40 */ + if (f54->query.has_per_frequency_noise_control) { + reg_addr += CONTROL_38_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_39_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_40_SIZE * num_of_sensing_freqs; + } + + /* control 41 */ + if (f54->query.has_signal_clarity) { + control->reg_41 = kzalloc(sizeof(*(control->reg_41)), + GFP_KERNEL); + if (!control->reg_41) + goto exit_no_mem; + control->reg_41->address = reg_addr; + reg_addr += CONTROL_41_SIZE; + } + + /* control 42 */ + if (f54->query.has_variance_metric) + reg_addr += CONTROL_42_SIZE; + + /* controls 43 44 45 46 47 48 49 50 51 52 53 54 */ + if (f54->query.has_multi_metric_state_machine) + reg_addr += CONTROL_43_54_SIZE; + + /* controls 55 56 */ + if (f54->query.has_0d_relaxation_control) + reg_addr += CONTROL_55_56_SIZE; + + /* control 57 */ + if (f54->query.has_0d_acquisition_control) { + control->reg_57 = kzalloc(sizeof(*(control->reg_57)), + GFP_KERNEL); + if (!control->reg_57) + goto exit_no_mem; + control->reg_57->address = reg_addr; + reg_addr += CONTROL_57_SIZE; + } + + /* control 58 */ + if (f54->query.has_0d_acquisition_control) + reg_addr += CONTROL_58_SIZE; + + /* control 59 */ + if (f54->query.has_h_blank) + reg_addr += CONTROL_59_SIZE; + + /* controls 60 61 62 */ + if ((f54->query.has_h_blank) || + (f54->query.has_v_blank) || + (f54->query.has_long_h_blank)) + reg_addr += CONTROL_60_62_SIZE; + + /* control 63 */ + if ((f54->query.has_h_blank) || + (f54->query.has_v_blank) || + (f54->query.has_long_h_blank) || + (f54->query.has_slew_metric) || + (f54->query.has_slew_option) || + (f54->query.has_noise_mitigation2)) + reg_addr += CONTROL_63_SIZE; + + /* controls 64 65 66 67 */ + if (f54->query.has_h_blank) + reg_addr += CONTROL_64_67_SIZE * 7; + else if ((f54->query.has_v_blank) || + (f54->query.has_long_h_blank)) + reg_addr += CONTROL_64_67_SIZE; + + /* controls 68 69 70 71 72 73 */ + if ((f54->query.has_h_blank) || + (f54->query.has_v_blank) || + (f54->query.has_long_h_blank)) { + if (f54->query_68.is_tddi_hic) + reg_addr += CONTROL_70_73_SIZE; + else + reg_addr += CONTROL_68_73_SIZE; + } + + /* control 74 */ + if (f54->query.has_slew_metric) + reg_addr += CONTROL_74_SIZE; + + /* control 75 */ + if (f54->query.has_enhanced_stretch) + reg_addr += CONTROL_75_SIZE * num_of_sensing_freqs; + + /* control 76 */ + if (f54->query.has_startup_fast_relaxation) + reg_addr += CONTROL_76_SIZE; + + /* controls 77 78 */ + if (f54->query.has_esd_control) + reg_addr += CONTROL_77_78_SIZE; + + /* controls 79 80 81 82 83 */ + if (f54->query.has_noise_mitigation2) + reg_addr += CONTROL_79_83_SIZE; + + /* controls 84 85 */ + if (f54->query.has_energy_ratio_relaxation) + reg_addr += CONTROL_84_85_SIZE; + + /* control 86 */ + if (f54->query_13.has_ctrl86) { + control->reg_86 = kzalloc(sizeof(*(control->reg_86)), + GFP_KERNEL); + if (!control->reg_86) + goto exit_no_mem; + control->reg_86->address = reg_addr; + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_86->address, + f54->control.reg_86->data, + sizeof(f54->control.reg_86->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read sense display ratio\n", + __func__); + return retval; + } + reg_addr += CONTROL_86_SIZE; + } + + /* control 87 */ + if (f54->query_13.has_ctrl87) + reg_addr += CONTROL_87_SIZE; + + /* control 88 */ + if (f54->query.has_ctrl88) { + control->reg_88 = kzalloc(sizeof(*(control->reg_88)), + GFP_KERNEL); + if (!control->reg_88) + goto exit_no_mem; + control->reg_88->address = reg_addr; + reg_addr += CONTROL_88_SIZE; + } + + /* control 89 */ + if (f54->query_13.has_cidim || + f54->query_13.has_noise_mitigation_enhancement || + f54->query_13.has_rail_im) + reg_addr += CONTROL_89_SIZE; + + /* control 90 */ + if (f54->query_15.has_ctrl90) + reg_addr += CONTROL_90_SIZE; + + /* control 91 */ + if (f54->query_21.has_ctrl91) + reg_addr += CONTROL_91_SIZE; + + /* control 92 */ + if (f54->query_16.has_ctrl92) + reg_addr += CONTROL_92_SIZE; + + /* control 93 */ + if (f54->query_16.has_ctrl93) + reg_addr += CONTROL_93_SIZE; + + /* control 94 */ + if (f54->query_16.has_ctrl94_query18) + reg_addr += CONTROL_94_SIZE; + + /* control 95 */ + if (f54->query_16.has_ctrl95_query19) + reg_addr += CONTROL_95_SIZE; + + /* control 96 */ + if (f54->query_21.has_ctrl96) + reg_addr += CONTROL_96_SIZE; + + /* control 97 */ + if (f54->query_21.has_ctrl97) + reg_addr += CONTROL_97_SIZE; + + /* control 98 */ + if (f54->query_21.has_ctrl98) + reg_addr += CONTROL_98_SIZE; + + /* control 99 */ + if (f54->query.touch_controller_family == 2) + reg_addr += CONTROL_99_SIZE; + + /* control 100 */ + if (f54->query_16.has_ctrl100) + reg_addr += CONTROL_100_SIZE; + + /* control 101 */ + if (f54->query_22.has_ctrl101) + reg_addr += CONTROL_101_SIZE; + + /* control 102 */ + if (f54->query_23.has_ctrl102) + reg_addr += CONTROL_102_SIZE; + + /* control 103 */ + if (f54->query_22.has_ctrl103_query26) { + f54->skip_preparation = true; + reg_addr += CONTROL_103_SIZE; + } + + /* control 104 */ + if (f54->query_22.has_ctrl104) + reg_addr += CONTROL_104_SIZE; + + /* control 105 */ + if (f54->query_22.has_ctrl105) + reg_addr += CONTROL_105_SIZE; + + /* control 106 */ + if (f54->query_25.has_ctrl106) + reg_addr += CONTROL_106_SIZE; + + /* control 107 */ + if (f54->query_25.has_ctrl107) + reg_addr += CONTROL_107_SIZE; + + /* control 108 */ + if (f54->query_25.has_ctrl108) + reg_addr += CONTROL_108_SIZE; + + /* control 109 */ + if (f54->query_25.has_ctrl109) + reg_addr += CONTROL_109_SIZE; + + /* control 110 */ + if (f54->query_27.has_ctrl110) { + control->reg_110 = kzalloc(sizeof(*(control->reg_110)), + GFP_KERNEL); + if (!control->reg_110) + goto exit_no_mem; + control->reg_110->address = reg_addr; + reg_addr += CONTROL_110_SIZE; + } + + /* control 111 */ + if (f54->query_27.has_ctrl111) + reg_addr += CONTROL_111_SIZE; + + /* control 112 */ + if (f54->query_27.has_ctrl112) + reg_addr += CONTROL_112_SIZE; + + /* control 113 */ + if (f54->query_27.has_ctrl113) + reg_addr += CONTROL_113_SIZE; + + /* control 114 */ + if (f54->query_27.has_ctrl114) + reg_addr += CONTROL_114_SIZE; + + /* control 115 */ + if (f54->query_29.has_ctrl115) + reg_addr += CONTROL_115_SIZE; + + /* control 116 */ + if (f54->query_29.has_ctrl116) + reg_addr += CONTROL_116_SIZE; + + /* control 117 */ + if (f54->query_29.has_ctrl117) + reg_addr += CONTROL_117_SIZE; + + /* control 118 */ + if (f54->query_30.has_ctrl118) + reg_addr += CONTROL_118_SIZE; + + /* control 119 */ + if (f54->query_30.has_ctrl119) + reg_addr += CONTROL_119_SIZE; + + /* control 120 */ + if (f54->query_30.has_ctrl120) + reg_addr += CONTROL_120_SIZE; + + /* control 121 */ + if (f54->query_30.has_ctrl121) + reg_addr += CONTROL_121_SIZE; + + /* control 122 */ + if (f54->query_30.has_ctrl122_query31) + reg_addr += CONTROL_122_SIZE; + + /* control 123 */ + if (f54->query_30.has_ctrl123) + reg_addr += CONTROL_123_SIZE; + + /* control 124 */ + if (f54->query_30.has_ctrl124) + reg_addr += CONTROL_124_SIZE; + + /* control 125 */ + if (f54->query_32.has_ctrl125) + reg_addr += CONTROL_125_SIZE; + + /* control 126 */ + if (f54->query_32.has_ctrl126) + reg_addr += CONTROL_126_SIZE; + + /* control 127 */ + if (f54->query_32.has_ctrl127) + reg_addr += CONTROL_127_SIZE; + + /* control 128 */ + if (f54->query_33.has_ctrl128) + reg_addr += CONTROL_128_SIZE; + + /* control 129 */ + if (f54->query_33.has_ctrl129) + reg_addr += CONTROL_129_SIZE; + + /* control 130 */ + if (f54->query_33.has_ctrl130) + reg_addr += CONTROL_130_SIZE; + + /* control 131 */ + if (f54->query_33.has_ctrl131) + reg_addr += CONTROL_131_SIZE; + + /* control 132 */ + if (f54->query_33.has_ctrl132) + reg_addr += CONTROL_132_SIZE; + + /* control 133 */ + if (f54->query_33.has_ctrl133) + reg_addr += CONTROL_133_SIZE; + + /* control 134 */ + if (f54->query_33.has_ctrl134) + reg_addr += CONTROL_134_SIZE; + + /* control 135 */ + if (f54->query_35.has_ctrl135) + reg_addr += CONTROL_135_SIZE; + + /* control 136 */ + if (f54->query_35.has_ctrl136) + reg_addr += CONTROL_136_SIZE; + + /* control 137 */ + if (f54->query_35.has_ctrl137) + reg_addr += CONTROL_137_SIZE; + + /* control 138 */ + if (f54->query_35.has_ctrl138) + reg_addr += CONTROL_138_SIZE; + + /* control 139 */ + if (f54->query_35.has_ctrl139) + reg_addr += CONTROL_139_SIZE; + + /* control 140 */ + if (f54->query_35.has_ctrl140) + reg_addr += CONTROL_140_SIZE; + + /* control 141 */ + if (f54->query_36.has_ctrl141) + reg_addr += CONTROL_141_SIZE; + + /* control 142 */ + if (f54->query_36.has_ctrl142) + reg_addr += CONTROL_142_SIZE; + + /* control 143 */ + if (f54->query_36.has_ctrl143) + reg_addr += CONTROL_143_SIZE; + + /* control 144 */ + if (f54->query_36.has_ctrl144) + reg_addr += CONTROL_144_SIZE; + + /* control 145 */ + if (f54->query_36.has_ctrl145) + reg_addr += CONTROL_145_SIZE; + + /* control 146 */ + if (f54->query_36.has_ctrl146) + reg_addr += CONTROL_146_SIZE; + + /* control 147 */ + if (f54->query_38.has_ctrl147) + reg_addr += CONTROL_147_SIZE; + + /* control 148 */ + if (f54->query_38.has_ctrl148) + reg_addr += CONTROL_148_SIZE; + + /* control 149 */ + if (f54->query_38.has_ctrl149) { + control->reg_149 = kzalloc(sizeof(*(control->reg_149)), + GFP_KERNEL); + if (!control->reg_149) + goto exit_no_mem; + control->reg_149->address = reg_addr; + reg_addr += CONTROL_149_SIZE; + } + + /* control 150 */ + if (f54->query_38.has_ctrl150) + reg_addr += CONTROL_150_SIZE; + + /* control 151 */ + if (f54->query_38.has_ctrl151) + reg_addr += CONTROL_151_SIZE; + + /* control 152 */ + if (f54->query_38.has_ctrl152) + reg_addr += CONTROL_152_SIZE; + + /* control 153 */ + if (f54->query_38.has_ctrl153) + reg_addr += CONTROL_153_SIZE; + + /* control 154 */ + if (f54->query_39.has_ctrl154) + reg_addr += CONTROL_154_SIZE; + + /* control 155 */ + if (f54->query_39.has_ctrl155) + reg_addr += CONTROL_155_SIZE; + + /* control 156 */ + if (f54->query_39.has_ctrl156) + reg_addr += CONTROL_156_SIZE; + + /* controls 157 158 */ + if (f54->query_39.has_ctrl157_ctrl158) + reg_addr += CONTROL_157_158_SIZE; + + /* controls 159 to 162 reserved */ + + /* control 163 */ + if (f54->query_40.has_ctrl163_query41) + reg_addr += CONTROL_163_SIZE; + + /* control 164 reserved */ + + /* control 165 */ + if (f54->query_40.has_ctrl165_query42) + reg_addr += CONTROL_165_SIZE; + + /* control 166 */ + if (f54->query_40.has_ctrl166) + reg_addr += CONTROL_166_SIZE; + + /* control 167 */ + if (f54->query_40.has_ctrl167) + reg_addr += CONTROL_167_SIZE; + + /* control 168 */ + if (f54->query_40.has_ctrl168) + reg_addr += CONTROL_168_SIZE; + + /* control 169 */ + if (f54->query_40.has_ctrl169) + reg_addr += CONTROL_169_SIZE; + + /* control 170 reserved */ + + /* control 171 */ + if (f54->query_43.has_ctrl171) + reg_addr += CONTROL_171_SIZE; + + /* control 172 */ + if (f54->query_43.has_ctrl172_query44_query45) + reg_addr += CONTROL_172_SIZE; + + /* control 173 */ + if (f54->query_43.has_ctrl173) + reg_addr += CONTROL_173_SIZE; + + /* control 174 */ + if (f54->query_43.has_ctrl174) + reg_addr += CONTROL_174_SIZE; + + /* control 175 */ + if (f54->query_43.has_ctrl175) + reg_addr += CONTROL_175_SIZE; + + /* control 176 */ + if (f54->query_46.has_ctrl176) + reg_addr += CONTROL_176_SIZE; + + /* controls 177 178 */ + if (f54->query_46.has_ctrl177_ctrl178) + reg_addr += CONTROL_177_178_SIZE; + + /* control 179 */ + if (f54->query_46.has_ctrl179) + reg_addr += CONTROL_179_SIZE; + + /* controls 180 to 181 reserved */ + + /* control 182 */ + if (f54->query_47.has_ctrl182) + reg_addr += CONTROL_182_SIZE; + + /* control 183 */ + if (f54->query_47.has_ctrl183) + reg_addr += CONTROL_183_SIZE; + + /* control 184 reserved */ + + /* control 185 */ + if (f54->query_47.has_ctrl185) + reg_addr += CONTROL_185_SIZE; + + /* control 186 */ + if (f54->query_47.has_ctrl186) + reg_addr += CONTROL_186_SIZE; + + /* control 187 */ + if (f54->query_47.has_ctrl187) + reg_addr += CONTROL_187_SIZE; + + /* control 188 */ + if (f54->query_49.has_ctrl188) { + control->reg_188 = kzalloc(sizeof(*(control->reg_188)), + GFP_KERNEL); + if (!control->reg_188) + goto exit_no_mem; + control->reg_188->address = reg_addr; + reg_addr += CONTROL_188_SIZE; + } + + return 0; + +exit_no_mem: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for control registers\n", + __func__); + return -ENOMEM; +} + +static int test_set_queries(void) +{ + int retval; + unsigned char offset; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr, + f54->query.data, + sizeof(f54->query.data)); + if (retval < 0) + return retval; + + offset = sizeof(f54->query.data); + + /* query 12 */ + if (f54->query.has_sense_frequency_control == 0) + offset -= 1; + + /* query 13 */ + if (f54->query.has_query13) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_13.data, + sizeof(f54->query_13.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 14 */ + if (f54->query_13.has_ctrl87) + offset += 1; + + /* query 15 */ + if (f54->query.has_query15) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_15.data, + sizeof(f54->query_15.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 16 */ + if (f54->query_15.has_query16) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_16.data, + sizeof(f54->query_16.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 17 */ + if (f54->query_16.has_query17) + offset += 1; + + /* query 18 */ + if (f54->query_16.has_ctrl94_query18) + offset += 1; + + /* query 19 */ + if (f54->query_16.has_ctrl95_query19) + offset += 1; + + /* query 20 */ + if (f54->query_15.has_query20) + offset += 1; + + /* query 21 */ + if (f54->query_15.has_query21) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_21.data, + sizeof(f54->query_21.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 22 */ + if (f54->query_15.has_query22) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_22.data, + sizeof(f54->query_22.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 23 */ + if (f54->query_22.has_query23) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_23.data, + sizeof(f54->query_23.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 24 */ + if (f54->query_21.has_query24_data18) + offset += 1; + + /* query 25 */ + if (f54->query_15.has_query25) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_25.data, + sizeof(f54->query_25.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 26 */ + if (f54->query_22.has_ctrl103_query26) + offset += 1; + + /* query 27 */ + if (f54->query_25.has_query27) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_27.data, + sizeof(f54->query_27.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 28 */ + if (f54->query_22.has_query28) + offset += 1; + + /* query 29 */ + if (f54->query_27.has_query29) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_29.data, + sizeof(f54->query_29.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 30 */ + if (f54->query_29.has_query30) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_30.data, + sizeof(f54->query_30.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 31 */ + if (f54->query_30.has_ctrl122_query31) + offset += 1; + + /* query 32 */ + if (f54->query_30.has_query32) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_32.data, + sizeof(f54->query_32.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 33 */ + if (f54->query_32.has_query33) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_33.data, + sizeof(f54->query_33.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 34 */ + if (f54->query_32.has_query34) + offset += 1; + + /* query 35 */ + if (f54->query_32.has_query35) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_35.data, + sizeof(f54->query_35.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 36 */ + if (f54->query_33.has_query36) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_36.data, + sizeof(f54->query_36.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 37 */ + if (f54->query_36.has_query37) + offset += 1; + + /* query 38 */ + if (f54->query_36.has_query38) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_38.data, + sizeof(f54->query_38.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 39 */ + if (f54->query_38.has_query39) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_39.data, + sizeof(f54->query_39.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 40 */ + if (f54->query_39.has_query40) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_40.data, + sizeof(f54->query_40.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 41 */ + if (f54->query_40.has_ctrl163_query41) + offset += 1; + + /* query 42 */ + if (f54->query_40.has_ctrl165_query42) + offset += 1; + + /* query 43 */ + if (f54->query_40.has_query43) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_43.data, + sizeof(f54->query_43.data)); + if (retval < 0) + return retval; + offset += 1; + } + + if (f54->query_43.has_ctrl172_query44_query45) + offset += 2; + + /* query 46 */ + if (f54->query_43.has_query46) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_46.data, + sizeof(f54->query_46.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 47 */ + if (f54->query_46.has_query47) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_47.data, + sizeof(f54->query_47.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 48 reserved */ + + /* query 49 */ + if (f54->query_47.has_query49) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_49.data, + sizeof(f54->query_49.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 50 */ + if (f54->query_49.has_query50) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_50.data, + sizeof(f54->query_50.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 51 */ + if (f54->query_50.has_query51) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_51.data, + sizeof(f54->query_51.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 53 54 */ + if (f54->query_51.has_query53_query54_ctrl198) + offset += 2; + + /* query 55 */ + if (f54->query_51.has_query55) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_55.data, + sizeof(f54->query_55.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 56 */ + if (f54->query_55.has_query56) + offset += 1; + + /* query 57 */ + if (f54->query_55.has_query57) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_57.data, + sizeof(f54->query_57.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 58 */ + if (f54->query_57.has_query58) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_58.data, + sizeof(f54->query_58.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 59 */ + if (f54->query_58.has_query59) + offset += 1; + + /* query 60 */ + if (f54->query_58.has_query60) + offset += 1; + + /* query 61 */ + if (f54->query_58.has_query61) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_61.data, + sizeof(f54->query_61.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 62 63 */ + if (f54->query_61.has_ctrl215_query62_query63) + offset += 2; + + /* query 64 */ + if (f54->query_61.has_query64) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_64.data, + sizeof(f54->query_64.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 65 */ + if (f54->query_64.has_query65) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_65.data, + sizeof(f54->query_65.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 66 */ + if (f54->query_65.has_query66_ctrl231) + offset += 1; + + /* query 67 */ + if (f54->query_65.has_query67) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_67.data, + sizeof(f54->query_67.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 68 */ + if (f54->query_67.has_query68) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_68.data, + sizeof(f54->query_68.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 68 */ + if (f54->query_68.has_query69) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_69.data, + sizeof(f54->query_69.data)); + if (retval < 0) + return retval; + offset += 1; + } + + return 0; +} + +static void test_f54_set_regs(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count, + unsigned char page) +{ + unsigned char ii; + unsigned char intr_offset; + + f54->query_base_addr = fd->query_base_addr | (page << 8); + f54->control_base_addr = fd->ctrl_base_addr | (page << 8); + f54->data_base_addr = fd->data_base_addr | (page << 8); + f54->command_base_addr = fd->cmd_base_addr | (page << 8); + + f54->intr_reg_num = (intr_count + 7) / 8; + if (f54->intr_reg_num != 0) + f54->intr_reg_num -= 1; + + f54->intr_mask = 0; + intr_offset = intr_count % 8; + for (ii = intr_offset; + ii < (fd->intr_src_count + intr_offset); + ii++) { + f54->intr_mask |= 1 << ii; + } +} + +static int test_f55_set_controls(void) +{ + unsigned char offset = 0; + + /* controls 0 1 2 */ + if (f55->query.has_sensor_assignment) + offset += 3; + + /* control 3 */ + if (f55->query.has_edge_compensation) + offset++; + + /* control 4 */ + if (f55->query.curve_compensation_mode == 0x1 || + f55->query.curve_compensation_mode == 0x2) + offset++; + + /* control 5 */ + if (f55->query.curve_compensation_mode == 0x2) + offset++; + + /* control 6 */ + if (f55->query.has_ctrl6) + offset++; + + /* control 7 */ + if (f55->query.has_alternate_transmitter_assignment) + offset++; + + /* control 8 */ + if (f55->query_3.has_ctrl8) + offset++; + + /* control 9 */ + if (f55->query_3.has_ctrl9) + offset++; + + /* control 10 */ + if (f55->query_5.has_corner_compensation) + offset++; + + /* control 11 */ + if (f55->query.curve_compensation_mode == 0x3) + offset++; + + /* control 12 */ + if (f55->query_5.has_ctrl12) + offset++; + + /* control 13 */ + if (f55->query_5.has_ctrl13) + offset++; + + /* control 14 */ + if (f55->query_5.has_ctrl14) + offset++; + + /* control 15 */ + if (f55->query_5.has_basis_function) + offset++; + + /* control 16 */ + if (f55->query_17.has_ctrl16) + offset++; + + /* control 17 */ + if (f55->query_17.has_ctrl17) + offset++; + + /* controls 18 19 */ + if (f55->query_17.has_ctrl18_ctrl19) + offset += 2; + + /* control 20 */ + if (f55->query_17.has_ctrl20) + offset++; + + /* control 21 */ + if (f55->query_17.has_ctrl21) + offset++; + + /* control 22 */ + if (f55->query_17.has_ctrl22) + offset++; + + /* control 23 */ + if (f55->query_18.has_ctrl23) + offset++; + + /* control 24 */ + if (f55->query_18.has_ctrl24) + offset++; + + /* control 25 */ + if (f55->query_18.has_ctrl25) + offset++; + + /* control 26 */ + if (f55->query_18.has_ctrl26) + offset++; + + /* control 27 */ + if (f55->query_18.has_ctrl27_query20) + offset++; + + /* control 28 */ + if (f55->query_18.has_ctrl28_query21) + offset++; + + /* control 29 */ + if (f55->query_22.has_ctrl29) + offset++; + + /* control 30 */ + if (f55->query_22.has_ctrl30) + offset++; + + /* control 31 */ + if (f55->query_22.has_ctrl31) + offset++; + + /* control 32 */ + if (f55->query_22.has_ctrl32) + offset++; + + /* controls 33 34 35 36 reserved */ + + /* control 37 */ + if (f55->query_28.has_ctrl37) + offset++; + + /* control 38 */ + if (f55->query_30.has_ctrl38) + offset++; + + /* control 39 */ + if (f55->query_30.has_ctrl39) + offset++; + + /* control 40 */ + if (f55->query_30.has_ctrl40) + offset++; + + /* control 41 */ + if (f55->query_30.has_ctrl41) + offset++; + + /* control 42 */ + if (f55->query_30.has_ctrl42) + offset++; + + /* controls 43 44 */ + if (f55->query_30.has_ctrl43_ctrl44) { + f55->afe_mux_offset = offset; + offset += 2; + } + + /* controls 45 46 */ + if (f55->query_33.has_ctrl45_ctrl46) { + f55->has_force = true; + f55->force_tx_offset = offset; + f55->force_rx_offset = offset + 1; + offset += 2; + } + + return 0; +} + +static int test_f55_set_queries(void) +{ + int retval; + unsigned char offset; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr, + f55->query.data, + sizeof(f55->query.data)); + if (retval < 0) + return retval; + + offset = sizeof(f55->query.data); + + /* query 3 */ + if (f55->query.has_single_layer_multi_touch) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_3.data, + sizeof(f55->query_3.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 4 */ + if (f55->query_3.has_ctrl9) + offset += 1; + + /* query 5 */ + if (f55->query.has_query5) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_5.data, + sizeof(f55->query_5.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* queries 6 7 */ + if (f55->query.curve_compensation_mode == 0x3) + offset += 2; + + /* query 8 */ + if (f55->query_3.has_ctrl8) + offset += 1; + + /* query 9 */ + if (f55->query_3.has_query9) + offset += 1; + + /* queries 10 11 12 13 14 15 16 */ + if (f55->query_5.has_basis_function) + offset += 7; + + /* query 17 */ + if (f55->query_5.has_query17) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_17.data, + sizeof(f55->query_17.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 18 */ + if (f55->query_17.has_query18) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_18.data, + sizeof(f55->query_18.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 19 */ + if (f55->query_18.has_query19) + offset += 1; + + /* query 20 */ + if (f55->query_18.has_ctrl27_query20) + offset += 1; + + /* query 21 */ + if (f55->query_18.has_ctrl28_query21) + offset += 1; + + /* query 22 */ + if (f55->query_18.has_query22) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_22.data, + sizeof(f55->query_22.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 23 */ + if (f55->query_22.has_query23) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_23.data, + sizeof(f55->query_23.data)); + if (retval < 0) + return retval; + offset += 1; + + f55->amp_sensor = f55->query_23.amp_sensor_enabled; + f55->size_of_column2mux = f55->query_23.size_of_column2mux; + } + + /* queries 24 25 26 27 reserved */ + + /* query 28 */ + if (f55->query_22.has_query28) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_28.data, + sizeof(f55->query_28.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 29 */ + if (f55->query_28.has_query29) + offset += 1; + + /* query 30 */ + if (f55->query_28.has_query30) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_30.data, + sizeof(f55->query_30.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* queries 31 32 */ + if (f55->query_30.has_query31_query32) + offset += 2; + + /* query 33 */ + if (f55->query_30.has_query33) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_33.data, + sizeof(f55->query_33.data)); + if (retval < 0) + return retval; + offset += 1; + + f55->extended_amp = f55->query_33.has_extended_amp_pad; + } + + return 0; +} + +static void test_f55_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char rx_electrodes; + unsigned char tx_electrodes; + struct f55_control_43 ctrl_43; + + retval = test_f55_set_queries(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 query registers\n", + __func__); + return; + } + + if (!f55->query.has_sensor_assignment) + return; + + retval = test_f55_set_controls(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up F55 control registers\n", + __func__); + return; + } + + tx_electrodes = f55->query.num_of_tx_electrodes; + rx_electrodes = f55->query.num_of_rx_electrodes; + + f55->tx_assignment = kzalloc(tx_electrodes, GFP_KERNEL); + f55->rx_assignment = kzalloc(rx_electrodes, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + SENSOR_TX_MAPPING_OFFSET, + f55->tx_assignment, + tx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 tx assignment\n", + __func__); + return; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + SENSOR_RX_MAPPING_OFFSET, + f55->rx_assignment, + rx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 rx assignment\n", + __func__); + return; + } + + f54->tx_assigned = 0; + for (ii = 0; ii < tx_electrodes; ii++) { + if (f55->tx_assignment[ii] != 0xff) + f54->tx_assigned++; + } + + f54->rx_assigned = 0; + for (ii = 0; ii < rx_electrodes; ii++) { + if (f55->rx_assignment[ii] != 0xff) + f54->rx_assigned++; + } + + if (f55->amp_sensor) { + f54->tx_assigned = f55->size_of_column2mux; + f54->rx_assigned /= 2; + } + + if (f55->extended_amp) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + f55->afe_mux_offset, + ctrl_43.data, + sizeof(ctrl_43.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 AFE mux sizes\n", + __func__); + return; + } + + f54->tx_assigned = ctrl_43.afe_l_mux_size + + ctrl_43.afe_r_mux_size; + } + + /* force mapping */ + if (f55->has_force) { + f55->force_tx_assignment = kzalloc(tx_electrodes, GFP_KERNEL); + f55->force_rx_assignment = kzalloc(rx_electrodes, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + f55->force_tx_offset, + f55->force_tx_assignment, + tx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 force tx assignment\n", + __func__); + return; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + f55->force_rx_offset, + f55->force_rx_assignment, + rx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 force rx assignment\n", + __func__); + return; + } + + for (ii = 0; ii < tx_electrodes; ii++) { + if (f55->force_tx_assignment[ii] != 0xff) + f54->tx_assigned++; + } + + for (ii = 0; ii < rx_electrodes; ii++) { + if (f55->force_rx_assignment[ii] != 0xff) + f54->rx_assigned++; + } + } +} + +static void test_f55_set_regs(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn_desc *fd, + unsigned char page) +{ + f55 = kzalloc(sizeof(*f55), GFP_KERNEL); + if (!f55) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for F55\n", + __func__); + return; + } + + f55->query_base_addr = fd->query_base_addr | (page << 8); + f55->control_base_addr = fd->ctrl_base_addr | (page << 8); + f55->data_base_addr = fd->data_base_addr | (page << 8); + f55->command_base_addr = fd->cmd_base_addr | (page << 8); +} + +static void test_f21_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char size_of_query2; + unsigned char size_of_query5; + unsigned char query_11_offset; + unsigned char ctrl_4_offset; + struct f21_query_2 *query_2 = NULL; + struct f21_query_5 *query_5 = NULL; + struct f21_query_11 *query_11 = NULL; + + query_2 = kzalloc(sizeof(*query_2), GFP_KERNEL); + if (!query_2) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_2\n", + __func__); + goto exit; + } + + query_5 = kzalloc(sizeof(*query_5), GFP_KERNEL); + if (!query_5) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_5\n", + __func__); + goto exit; + } + + query_11 = kzalloc(sizeof(*query_11), GFP_KERNEL); + if (!query_11) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_11\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 1, + &size_of_query2, + sizeof(size_of_query2)); + if (retval < 0) + goto exit; + + if (size_of_query2 > sizeof(query_2->data)) + size_of_query2 = sizeof(query_2->data); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 2, + query_2->data, + size_of_query2); + if (retval < 0) + goto exit; + + if (!query_2->query11_is_present) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No F21 force capabilities\n", + __func__); + goto exit; + } + + query_11_offset = query_2->query0_is_present + + query_2->query1_is_present + + query_2->query2_is_present + + query_2->query3_is_present + + query_2->query4_is_present + + query_2->query5_is_present + + query_2->query6_is_present + + query_2->query7_is_present + + query_2->query8_is_present + + query_2->query9_is_present + + query_2->query10_is_present; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 11, + query_11->data, + sizeof(query_11->data)); + if (retval < 0) + goto exit; + + if (!query_11->has_force_sensing_txrx_mapping) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No F21 force mapping\n", + __func__); + goto exit; + } + + f21->max_num_of_tx = query_11->max_number_of_force_txs; + f21->max_num_of_rx = query_11->max_number_of_force_rxs; + f21->max_num_of_txrx = f21->max_num_of_tx + f21->max_num_of_rx; + + f21->force_txrx_assignment = kzalloc(f21->max_num_of_txrx, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 4, + &size_of_query5, + sizeof(size_of_query5)); + if (retval < 0) + goto exit; + + if (size_of_query5 > sizeof(query_5->data)) + size_of_query5 = sizeof(query_5->data); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 5, + query_5->data, + size_of_query5); + if (retval < 0) + goto exit; + + ctrl_4_offset = query_5->ctrl0_is_present + + query_5->ctrl1_is_present + + query_5->ctrl2_is_present + + query_5->ctrl3_is_present; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->control_base_addr + ctrl_4_offset, + f21->force_txrx_assignment, + f21->max_num_of_txrx); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F21 force txrx assignment\n", + __func__); + goto exit; + } + + f21->has_force = true; + + for (ii = 0; ii < f21->max_num_of_tx; ii++) { + if (f21->force_txrx_assignment[ii] != 0xff) + f21->tx_assigned++; + } + + for (ii = f21->max_num_of_tx; ii < f21->max_num_of_txrx; ii++) { + if (f21->force_txrx_assignment[ii] != 0xff) + f21->rx_assigned++; + } + +exit: + kfree(query_2); + kfree(query_5); + kfree(query_11); +} + +static void test_f21_set_regs(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn_desc *fd, + unsigned char page) +{ + f21 = kzalloc(sizeof(*f21), GFP_KERNEL); + if (!f21) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for F21\n", + __func__); + return; + } + + f21->query_base_addr = fd->query_base_addr | (page << 8); + f21->control_base_addr = fd->ctrl_base_addr | (page << 8); + f21->data_base_addr = fd->data_base_addr | (page << 8); + f21->command_base_addr = fd->cmd_base_addr | (page << 8); +} + +static int test_scan_pdt(void) +{ + int retval; + unsigned char intr_count = 0; + unsigned char page; + unsigned short addr; + bool f54found = false; + bool f55found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (!rmi_fd.fn_number) + break; + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F54: + test_f54_set_regs(rmi4_data, + &rmi_fd, intr_count, page); + f54found = true; + break; + case SYNAPTICS_RMI4_F55: + test_f55_set_regs(rmi4_data, + &rmi_fd, page); + f55found = true; + break; + case SYNAPTICS_RMI4_F21: + test_f21_set_regs(rmi4_data, + &rmi_fd, page); + break; + default: + break; + } + + if (f54found && f55found) + goto pdt_done; + + intr_count += rmi_fd.intr_src_count; + } + } + + if (!f54found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F54\n", + __func__); + return -EINVAL; + } + +pdt_done: + return 0; +} + +static void synaptics_rmi4_test_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!f54) + return; + + if (f54->intr_mask & intr_mask) + queue_work(f54->test_report_workqueue, &f54->test_report_work); + + return; +} + +static int synaptics_rmi4_test_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (f54) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + f54 = kzalloc(sizeof(*f54), GFP_KERNEL); + if (!f54) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for F54\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + f54->rmi4_data = rmi4_data; + + f55 = NULL; + + f21 = NULL; + + retval = test_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + retval = test_set_queries(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F54 query registers\n", + __func__); + goto exit_free_mem; + } + + f54->tx_assigned = f54->query.num_of_tx_electrodes; + f54->rx_assigned = f54->query.num_of_rx_electrodes; + + retval = test_set_controls(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up F54 control registers\n", + __func__); + goto exit_free_control; + } + + test_set_data(); + + if (f55) + test_f55_init(rmi4_data); + + if (f21) + test_f21_init(rmi4_data); + + if (rmi4_data->external_afe_buttons) + f54->tx_assigned++; + + retval = test_set_sysfs(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs entries\n", + __func__); + goto exit_sysfs; + } + + f54->test_report_workqueue = + create_singlethread_workqueue("test_report_workqueue"); + INIT_WORK(&f54->test_report_work, test_report_work); + + hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + f54->watchdog.function = test_get_report_timeout; + INIT_WORK(&f54->timeout_work, test_timeout_work); + + mutex_init(&f54->status_mutex); + f54->status = STATUS_IDLE; + + return 0; + +exit_sysfs: + if (f21) + kfree(f21->force_txrx_assignment); + + if (f55) { + kfree(f55->tx_assignment); + kfree(f55->rx_assignment); + kfree(f55->force_tx_assignment); + kfree(f55->force_rx_assignment); + } + +exit_free_control: + test_free_control_mem(); + +exit_free_mem: + kfree(f21); + f21 = NULL; + kfree(f55); + f55 = NULL; + kfree(f54); + f54 = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_test_remove(struct synaptics_rmi4_data *rmi4_data) +{ + if (!f54) + goto exit; + + hrtimer_cancel(&f54->watchdog); + + cancel_work_sync(&f54->test_report_work); + flush_workqueue(f54->test_report_workqueue); + destroy_workqueue(f54->test_report_workqueue); + + test_remove_sysfs(); + + if (f21) + kfree(f21->force_txrx_assignment); + + if (f55) { + kfree(f55->tx_assignment); + kfree(f55->rx_assignment); + kfree(f55->force_tx_assignment); + kfree(f55->force_rx_assignment); + } + + test_free_control_mem(); + + if (f54->data_buffer_size) + kfree(f54->report_data); + + kfree(f21); + f21 = NULL; + + kfree(f55); + f55 = NULL; + + kfree(f54); + f54 = NULL; + +exit: + complete(&test_remove_complete); +} + +static void synaptics_rmi4_test_reset(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (!f54) { + synaptics_rmi4_test_init(rmi4_data); + return; + } + + if (f21) + kfree(f21->force_txrx_assignment); + + if (f55) { + kfree(f55->tx_assignment); + kfree(f55->rx_assignment); + kfree(f55->force_tx_assignment); + kfree(f55->force_rx_assignment); + } + + test_free_control_mem(); + + kfree(f55); + f55 = NULL; + + kfree(f21); + f21 = NULL; + + retval = test_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + retval = test_set_queries(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F54 query registers\n", + __func__); + goto exit_free_mem; + } + + f54->tx_assigned = f54->query.num_of_tx_electrodes; + f54->rx_assigned = f54->query.num_of_rx_electrodes; + + retval = test_set_controls(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up F54 control registers\n", + __func__); + goto exit_free_control; + } + + test_set_data(); + + if (f55) + test_f55_init(rmi4_data); + + if (f21) + test_f21_init(rmi4_data); + + if (rmi4_data->external_afe_buttons) + f54->tx_assigned++; + + f54->status = STATUS_IDLE; + + return; + +exit_free_control: + test_free_control_mem(); + +exit_free_mem: + hrtimer_cancel(&f54->watchdog); + + cancel_work_sync(&f54->test_report_work); + flush_workqueue(f54->test_report_workqueue); + destroy_workqueue(f54->test_report_workqueue); + + test_remove_sysfs(); + + if (f54->data_buffer_size) + kfree(f54->report_data); + + kfree(f21); + f21 = NULL; + + kfree(f55); + f55 = NULL; + + kfree(f54); + f54 = NULL; + + return; +} + +static struct synaptics_rmi4_exp_fn test_module = { + .fn_type = RMI_TEST_REPORTING, + .init = synaptics_rmi4_test_init, + .remove = synaptics_rmi4_test_remove, + .reset = synaptics_rmi4_test_reset, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_test_attn, +}; + +static int __init rmi4_test_module_init(void) +{ + synaptics_rmi4_new_function(&test_module, true); + + return 0; +} + +static void __exit rmi4_test_module_exit(void) +{ + synaptics_rmi4_new_function(&test_module, false); + + wait_for_completion(&test_remove_complete); +} + +module_init(rmi4_test_module_init); +module_exit(rmi4_test_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Test Reporting Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_video.c b/synaptics_dsx/synaptics_dsx_video.c new file mode 100644 index 0000000000..5067d85ca1 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_video.c @@ -0,0 +1,403 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS + * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. + * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION + * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED + * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES + * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' + * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. + * DOLLARS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SYSFS_FOLDER_NAME "video" + +static ssize_t video_sysfs_dcs_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t video_sysfs_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static int video_send_dcs_command(unsigned char command_opcode); + +struct f38_command { + union { + struct { + unsigned char command_opcode; + unsigned char register_access:1; + unsigned char gamma_page:1; + unsigned char f38_control1_b2__7:6; + unsigned char parameter_field_1; + unsigned char parameter_field_2; + unsigned char parameter_field_3; + unsigned char parameter_field_4; + unsigned char send_to_dcs:1; + unsigned char f38_command6_b1__7:7; + } __packed; + unsigned char data[7]; + }; +}; + +struct synaptics_rmi4_video_handle { + unsigned char param; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + struct synaptics_rmi4_data *rmi4_data; + struct kobject *sysfs_dir; +}; + +#ifdef RMI_DCS_SUSPEND_RESUME +struct dcs_command { + unsigned char command; + unsigned int wait_time; +}; + +static struct dcs_command suspend_sequence[] = { + { + .command = 0x28, + .wait_time = 200, + }, + { + .command = 0x10, + .wait_time = 200, + }, +}; + +static struct dcs_command resume_sequence[] = { + { + .command = 0x11, + .wait_time = 200, + }, + { + .command = 0x29, + .wait_time = 200, + }, +}; +#endif + +static struct device_attribute attrs[] = { + __ATTR(dcs_write, 0220, + synaptics_rmi4_show_error, + video_sysfs_dcs_write_store), + __ATTR(param, 0220, + synaptics_rmi4_show_error, + video_sysfs_param_store), +}; + +static struct synaptics_rmi4_video_handle *video; + +DECLARE_COMPLETION(video_remove_complete); + +static ssize_t video_sysfs_dcs_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + + if (kstrtouint(buf, 16, &input) != 1) + return -EINVAL; + + retval = video_send_dcs_command((unsigned char)input); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t video_sysfs_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 16, &input) != 1) + return -EINVAL; + + video->param = (unsigned char)input; + + return count; +} + +static int video_send_dcs_command(unsigned char command_opcode) +{ + int retval; + struct f38_command command; + struct synaptics_rmi4_data *rmi4_data = video->rmi4_data; + + memset(&command, 0x00, sizeof(command)); + + command.command_opcode = command_opcode; + command.parameter_field_1 = video->param; + command.send_to_dcs = 1; + + video->param = 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + video->command_base_addr, + command.data, + sizeof(command.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to send DCS command\n", + __func__); + return retval; + } + + return 0; +} + +static int video_scan_pdt(void) +{ + int retval; + unsigned char page; + unsigned short addr; + bool f38_found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = video->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (!rmi_fd.fn_number) + break; + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F38) { + f38_found = true; + goto f38_found; + } + } + } + + if (!f38_found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F38\n", + __func__); + return -EINVAL; + } + +f38_found: + video->query_base_addr = rmi_fd.query_base_addr | (page << 8); + video->control_base_addr = rmi_fd.ctrl_base_addr | (page << 8); + video->data_base_addr = rmi_fd.data_base_addr | (page << 8); + video->command_base_addr = rmi_fd.cmd_base_addr | (page << 8); + + return 0; +} + +static int synaptics_rmi4_video_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + + if (video) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + video = kzalloc(sizeof(*video), GFP_KERNEL); + if (!video) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for video\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + video->rmi4_data = rmi4_data; + + retval = video_scan_pdt(); + if (retval < 0) { + retval = 0; + goto exit_scan_pdt; + } + + video->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, + &rmi4_data->input_dev->dev.kobj); + if (!video->sysfs_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs directory\n", + __func__); + retval = -ENODEV; + goto exit_sysfs_dir; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(video->sysfs_dir, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_sysfs_attrs; + } + } + + return 0; + +exit_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr); + + kobject_put(video->sysfs_dir); + +exit_sysfs_dir: +exit_scan_pdt: + kfree(video); + video = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_video_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!video) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr); + + kobject_put(video->sysfs_dir); + + kfree(video); + video = NULL; + +exit: + complete(&video_remove_complete); +} + +static void synaptics_rmi4_video_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!video) + synaptics_rmi4_video_init(rmi4_data); +} + +#ifdef RMI_DCS_SUSPEND_RESUME +static void synaptics_rmi4_video_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char command; + unsigned char num_of_cmds; + + if (!video) + return; + + num_of_cmds = ARRAY_SIZE(suspend_sequence); + + for (ii = 0; ii < num_of_cmds; ii++) { + command = suspend_sequence[ii].command; + retval = video_send_dcs_command(command); + if (retval < 0) + return; + msleep(suspend_sequence[ii].wait_time); + } +} + +static void synaptics_rmi4_video_resume(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char command; + unsigned char num_of_cmds; + + if (!video) + return; + + num_of_cmds = ARRAY_SIZE(resume_sequence); + + for (ii = 0; ii < num_of_cmds; ii++) { + command = resume_sequence[ii].command; + retval = video_send_dcs_command(command); + if (retval < 0) + return; + msleep(resume_sequence[ii].wait_time); + } +} +#endif + +static struct synaptics_rmi4_exp_fn video_module = { + .fn_type = RMI_VIDEO, + .init = synaptics_rmi4_video_init, + .remove = synaptics_rmi4_video_remove, + .reset = synaptics_rmi4_video_reset, + .reinit = NULL, + .early_suspend = NULL, +#ifdef RMI_DCS_SUSPEND_RESUME + .suspend = synaptics_rmi4_video_suspend, + .resume = synaptics_rmi4_video_resume, +#else + .suspend = NULL, + .resume = NULL, +#endif + .late_resume = NULL, + .attn = NULL, +}; + +static int __init rmi4_video_module_init(void) +{ + synaptics_rmi4_new_function(&video_module, true); + + return 0; +} + +static void __exit rmi4_video_module_exit(void) +{ + synaptics_rmi4_new_function(&video_module, false); + + wait_for_completion(&video_remove_complete); +} + +module_init(rmi4_video_module_init); +module_exit(rmi4_video_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Video Module"); +MODULE_LICENSE("GPL v2");