From 3adfcb9ef922217f6c1530469e3a575c751a1e8d Mon Sep 17 00:00:00 2001 From: Adithya R Date: Fri, 8 Jan 2021 19:20:52 +0530 Subject: [PATCH] input: touchscreen: Import NT36672C touchpanel driver Signed-off-by: azrim --- drivers/input/touchscreen/Kconfig | 2 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/nt36xxx_spi/Kconfig | 12 + .../input/touchscreen/nt36xxx_spi/Makefile | 7 + .../input/touchscreen/nt36xxx_spi/nt36xxx.c | 3367 +++++++++++++++++ .../input/touchscreen/nt36xxx_spi/nt36xxx.h | 306 ++ .../nt36xxx_spi/nt36xxx_ext_proc.c | 2293 +++++++++++ .../nt36xxx_spi/nt36xxx_fw_update.c | 1177 ++++++ .../touchscreen/nt36xxx_spi/nt36xxx_mem_map.h | 300 ++ .../nt36xxx_spi/nt36xxx_mp_ctrlram.c | 1927 ++++++++++ .../nt36xxx_spi/nt36xxx_mp_ctrlram.h | 710 ++++ 11 files changed, 10102 insertions(+) create mode 100644 drivers/input/touchscreen/nt36xxx_spi/Kconfig create mode 100644 drivers/input/touchscreen/nt36xxx_spi/Makefile create mode 100755 drivers/input/touchscreen/nt36xxx_spi/nt36xxx.c create mode 100755 drivers/input/touchscreen/nt36xxx_spi/nt36xxx.h create mode 100644 drivers/input/touchscreen/nt36xxx_spi/nt36xxx_ext_proc.c create mode 100755 drivers/input/touchscreen/nt36xxx_spi/nt36xxx_fw_update.c create mode 100644 drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mem_map.h create mode 100644 drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.c create mode 100644 drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d6030ba38063..3137b31224d4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1296,4 +1296,6 @@ config TOUCHSCREEN_RAYDIUM_CHIPSET If unsure, say N. +source "drivers/input/touchscreen/nt36xxx_spi/Kconfig" + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 21df02a44d42..34fd2f2530fe 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -109,3 +109,4 @@ obj-$(CONFIG_TOUCHSCREEN_ST) += st/ obj-$(CONFIG_TOUCHSCREEN_HIMAX_CHIPSET) += hxchipset/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM) += synaptics_tcm/ obj-$(CONFIG_TOUCHSCREEN_RAYDIUM_CHIPSET) += raydium_wt030/ +obj-$(CONFIG_TOUCHSCREEN_NT36xxx_HOSTDL_SPI) += nt36xxx_spi/ diff --git a/drivers/input/touchscreen/nt36xxx_spi/Kconfig b/drivers/input/touchscreen/nt36xxx_spi/Kconfig new file mode 100644 index 000000000000..8020a4651ad1 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/Kconfig @@ -0,0 +1,12 @@ +# +# Novatek NT36xxx touchscreen driver configuration +# +config TOUCHSCREEN_NT36xxx_HOSTDL_SPI + tristate "Novatek NT36xxx host download SPI driver" + depends on SPI + default y + help + Say Y here if you have a Novatek NT36xxx no flash touchscreen connected + to your system by SPI bus. + + If unsure, say N. diff --git a/drivers/input/touchscreen/nt36xxx_spi/Makefile b/drivers/input/touchscreen/nt36xxx_spi/Makefile new file mode 100644 index 000000000000..28b4dca21b37 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Novatek NT36xxx touchscreen driver. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_TOUCHSCREEN_NT36xxx_HOSTDL_SPI) += nt36xxx.o nt36xxx_fw_update.o nt36xxx_ext_proc.o nt36xxx_mp_ctrlram.o diff --git a/drivers/input/touchscreen/nt36xxx_spi/nt36xxx.c b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx.c new file mode 100755 index 000000000000..57a2d92e7a64 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx.c @@ -0,0 +1,3367 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 43560 $ + * $Date: 2019-04-19 11:34:19 +0800 (週五, 19 四月 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 +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_FB) +#ifdef CONFIG_DRM_MSM +#include +#endif +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#include "nt36xxx.h" +//#include +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#ifdef CHECK_TOUCH_VENDOR +extern char *saved_command_line; +static uint8_t touch_vendor_id; +#endif + +#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; +uint8_t esd_check; +uint8_t esd_retry; +#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 + +/*2019.12.06 longcheer taocheng add charger mode begin*/ +/*function description*/ +#if NVT_USB_PLUGIN +static void nvt_ts_usb_plugin_work_func(struct work_struct *work); +DECLARE_WORK(nvt_usb_plugin_work, nvt_ts_usb_plugin_work_func); +extern touchscreen_usb_plugin_data_t g_touchscreen_usb_pulgin; +#endif +/*2019.12.06 longcheer taocheng add charger mode end*/ + +struct nvt_ts_data *ts; +//static struct device *spi_geni_master_dev; + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#if defined(CONFIG_FB) +static void nvt_ts_resume_work(struct work_struct *work); +#ifdef _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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void nvt_ts_early_suspend(struct early_suspend *h); +static void nvt_ts_late_resume(struct early_suspend *h); +#endif + +#if WAKEUP_GESTURE +extern void set_lcd_reset_gpio_keep_high(bool en); +static int lct_nvt_tp_gesture_callback(bool flag); +#endif + +uint32_t ENG_RST_ADDR = 0x7FFF80; +uint32_t SWRST_N8_ADDR; //read from dtsi +uint32_t SPI_RD_FAST_ADDR; //read from dtsi + +#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_WAKEUP, //GESTURE_WORD_C + KEY_WAKEUP, //GESTURE_WORD_W + KEY_WAKEUP, //GESTURE_WORD_V + KEY_WAKEUP, //GESTURE_DOUBLE_CLICK + KEY_WAKEUP, //GESTURE_WORD_Z + KEY_WAKEUP, //GESTURE_WORD_M + KEY_WAKEUP, //GESTURE_WORD_O + KEY_WAKEUP, //GESTURE_WORD_e + KEY_WAKEUP, //GESTURE_WORD_S + KEY_WAKEUP, //GESTURE_SLIDE_UP + KEY_WAKEUP, //GESTURE_SLIDE_DOWN + KEY_WAKEUP, //GESTURE_SLIDE_LEFT + KEY_WAKEUP, //GESTURE_SLIDE_RIGHT + KEY_POWER, //FOR PALM USING ONLY +}; +#endif + +#ifdef CONFIG_MTK_SPI +const struct mt_chip_conf spi_ctrdata = { + .setuptime = 25, + .holdtime = 25, + .high_time = 5, /* 10MHz (SPI_SPEED=100M / (high_time+low_time(10ns)))*/ + .low_time = 5, + .cs_idletime = 2, + .ulthgh_thrsh = 0, + .cpol = 0, + .cpha = 0, + .rx_mlsb = 1, + .tx_mlsb = 1, + .tx_endian = 0, + .rx_endian = 0, + .com_mod = DMA_TRANSFER, + .pause = 0, + .finish_intr = 1, + .deassert = 0, + .ulthigh = 0, + .tckdly = 0, +}; +#endif + +#ifdef CONFIG_SPI_MT65XX +const struct mtk_chip_config spi_ctrdata = { + .rx_mlsb = 1, + .tx_mlsb = 1, + .cs_pol = 0, +}; +#endif + +static uint8_t bTouchIsAwake; +static uint8_t open_pocket_fail; +static uint8_t close_pocket_fail; + +#if WAKEUP_GESTURE +#define WAKEUP_OFF 4 +#define WAKEUP_ON 5 +int nvt_gesture_switch(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + NVT_LOG("Enter. type = %u, code = %u, value = %d\n", type, code, value); + if (type == EV_SYN && code == SYN_CONFIG) { + if (value == WAKEUP_OFF) + lct_nvt_tp_gesture_callback(false); + else if (value == WAKEUP_ON) + lct_nvt_tp_gesture_callback(true); + } + NVT_LOG("Exit\n"); + return 0; +} + +static int32_t nvt_ts_resume(struct device *dev); +static int32_t nvt_ts_suspend(struct device *dev); + +typedef int(*touchpanel_recovery_cb_p_t)(void); +extern int set_touchpanel_recovery_callback(touchpanel_recovery_cb_p_t cb); + +/* Fix Touch/Fingerprint wakeup crash issue */ +int nvt_ts_recovery_callback(void) +{ + if (unlikely(bTouchIsAwake)) { + NVT_ERR("touch is awake, can not to set\n"); + return -EPERM; + } + if (ts->is_gesture_mode) { + NVT_LOG("recovery touch 'Double Click' mode start\n"); + nvt_ts_resume(&ts->client->dev); + nvt_ts_suspend(&ts->client->dev); + NVT_LOG("recovery touch 'Double Click' mode end\n"); + } + return 0; +} +EXPORT_SYMBOL(nvt_ts_recovery_callback); + +#endif +/*2019.12.6 longcheer taocheng add charger mode begin*/ +/*function description*/ +#if NVT_USB_PLUGIN +void nvt_ts_usb_event_callback(void) +{ + schedule_work(&nvt_usb_plugin_work); +} + +static void nvt_ts_usb_plugin_work_func(struct work_struct *work) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + if (!bTouchIsAwake) { + NVT_ERR("tp is suspended, can not to set\n"); + return; + } + + NVT_LOG("++\n"); + mutex_lock(&ts->lock); + NVT_LOG("usb_plugged_in = %d\n", g_touchscreen_usb_pulgin.usb_plugged_in); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto exit; + } + + buf[0] = EVENT_MAP_HOST_CMD; + if (g_touchscreen_usb_pulgin.usb_plugged_in) + buf[1] = 0x53;// power plug ac on + else + buf[1] = 0x51;// power plug off + + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write pwr plug switch command fail!\n"); + goto exit; + } + +exit: + mutex_unlock(&ts->lock); + NVT_LOG("--\n"); +} +#endif +/*2019.12.6 longcheer taocheng add charger mode end*/ + +/******************************************************* +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 spi read/write core function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static inline int32_t spi_read_write(struct spi_device *client, uint8_t *buf, size_t len, NVT_SPI_RW rw) +{ + struct spi_message m; + struct spi_transfer t = { + .len = len, + }; + + memcpy(ts->xbuf, buf, len); + + switch (rw) { + case NVTREAD: + t.tx_buf = ts->xbuf; + t.rx_buf = ts->rbuf; + t.len = (len + DUMMY_BYTES); + break; + + case NVTWRITE: + 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 CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = SPI_READ_MASK(buf[0]); + + while (retries < 5) { + ret = spi_read_write(client, buf, len, NVTREAD); + 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 CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = SPI_WRITE_MASK(buf[0]); + + while (retries < 5) { + ret = spi_read_write(client, buf, len, NVTWRITE); + 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_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 CTP_SPI_WRITE(ts->client, buf, 3); +} + +/******************************************************* +Description: + Novatek touchscreen write data to specify address. + +return: + Executive outcomes. 0---succeed. -5---access fail. +*******************************************************/ +int32_t nvt_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 = CTP_SPI_WRITE(ts->client, 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 = CTP_SPI_WRITE(ts->client, 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_bld_crc_enable(void) +{ + uint8_t buf[2] = {0}; + + //---set xdata index to BLD_CRC_EN_ADDR--- + nvt_set_page(ts->mmap->BLD_CRC_EN_ADDR); + + //---read data from index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + + //---write data to index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = buf[1] | (0x01 << 7); + CTP_SPI_WRITE(ts->client, buf, 2); +} + +/******************************************************* +Description: + Novatek touchscreen clear status & enable fw crc function. + +return: + N/A. +*******************************************************/ +void nvt_fw_crc_enable(void) +{ + uint8_t buf[2] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); + + //---clear fw reset status--- + buf[0] = EVENT_MAP_RESET_COMPLETE & (0x7F); + buf[1] = 0x00; + CTP_SPI_WRITE(ts->client, buf, 2); + + //---enable fw crc--- + buf[0] = EVENT_MAP_HOST_CMD & (0x7F); + buf[1] = 0xAE; //enable fw crc command + CTP_SPI_WRITE(ts->client, buf, 2); +} + +/******************************************************* +Description: + Novatek touchscreen set boot ready function. + +return: + N/A. +*******************************************************/ +void nvt_boot_ready(void) +{ + //---write BOOT_RDY status cmds--- + nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 1); + + mdelay(5); + + if (!ts->hw_crc) { + //---write BOOT_RDY status cmds--- + nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 0); + + //---write POR_CD cmds--- + nvt_write_addr(ts->mmap->POR_CD_ADDR, 0xA0); + } +} + +/******************************************************* +Description: + Novatek touchscreen eng reset cmd + function. + +return: + n.a. +*******************************************************/ +void nvt_eng_reset(void) +{ + //---eng reset cmds to ENG_RST_ADDR--- + nvt_write_addr(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_sw_reset(void) +{ + //---software reset cmds to SWRST_N8_ADDR--- + nvt_write_addr(SWRST_N8_ADDR, 0x55); + + msleep(10); +} + +/******************************************************* +Description: + Novatek touchscreen reset MCU then into idle mode + function. + +return: + n.a. +*******************************************************/ +void nvt_sw_reset_idle(void) +{ + //---MCU idle cmds to SWRST_N8_ADDR--- + nvt_write_addr(SWRST_N8_ADDR, 0xAA); + + msleep(15); +} + +/******************************************************* +Description: + Novatek touchscreen reset MCU (boot) function. + +return: + n.a. +*******************************************************/ +void nvt_bootloader_reset(void) +{ + //---reset cmds to SWRST_N8_ADDR--- + nvt_write_addr(SWRST_N8_ADDR, 0x69); + + mdelay(5); //wait tBRST2FR after Bootload RST + + if (SPI_RD_FAST_ADDR) { + /* disable SPI_RD_FAST */ + nvt_write_addr(SPI_RD_FAST_ADDR, 0x00); + } +} + +/******************************************************* +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(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_SPI_WRITE(ts->client, buf, 2); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, 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 -EPERM; + } 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(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_SPI_READ(ts->client, 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 -EPERM; + } 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; + int32_t retry_max = (check_reset_state == RESET_STATE_INIT) ? 10 : 50; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE); + + while (1) { + //---read reset state--- + buf[0] = EVENT_MAP_RESET_COMPLETE; + buf[1] = 0x00; + CTP_SPI_READ(ts->client, buf, 6); + + if ((buf[1] >= check_reset_state) && (buf[1] <= 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, 10000); + } + + 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}; + int32_t ret = 0; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID); + + //---read project id--- + buf[0] = EVENT_MAP_PROJECTID; + buf[1] = 0x00; + buf[2] = 0x00; + CTP_SPI_READ(ts->client, buf, 3); + + ts->nvt_pid = (buf[2] << 8) + buf[1]; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); + + NVT_LOG("PID=%04X\n", ts->nvt_pid); + + return ret; +} + +/******************************************************* +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}; + uint8_t tp_info_buf[64] = {0}; + uint32_t retry_count = 0; + int32_t ret = 0; + +info_retry: + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + + //---read fw info--- + buf[0] = EVENT_MAP_FWINFO; + CTP_SPI_READ(ts->client, 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; + } + + NVT_LOG("fw_ver = 0x%02X, fw_type = 0x%02X\n", ts->fw_ver, buf[14]); + +#ifdef CHECK_TOUCH_VENDOR + switch (ts->touch_vendor_id) { + case TP_VENDOR_HUAXING: + snprintf(tp_info_buf, PAGE_SIZE, "[Vendor]huaxing,[FW]0x%02x,[IC]nt36672c\n", ts->fw_ver); + update_lct_tp_info(tp_info_buf, NULL); + break; + case TP_VENDOR_TIANMA: + snprintf(tp_info_buf, PAGE_SIZE, "[Vendor]tianma,[FW]0x%02x,[IC]nt36672c\n", ts->fw_ver); + update_lct_tp_info(tp_info_buf, NULL); + break; + } +#else + snprintf(tp_info_buf, PAGE_SIZE, "[Vendor]unknown,[FW]0x%02x,[IC]nt36672c\n", ts->fw_ver); + update_lct_tp_info(tp_info_buf, NULL); +#endif + + //---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 "NVTSPI" + +/******************************************************* +Description: + Novatek touchscreen /proc/NVTSPI 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 = NULL; + int32_t ret = 0; + int32_t retries = 0; + int8_t spi_wr = 0; + uint8_t *buf; + + if ((count > NVT_TRANSFER_LEN + 3) || (count < 3)) { + NVT_ERR("invalid transfer len!\n"); + return -EFAULT; + } + + /* allocate buffer for spi transfer */ + str = (uint8_t *)kzalloc((count), GFP_KERNEL); + if (str == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + goto kzalloc_failed; + } + + buf = (uint8_t *)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_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 */ + + spi_wr = str[0] >> 7; + memcpy(buf, str+2, ((str[0] & 0x7F) << 8) | str[1]); + + if (spi_wr == NVTWRITE) { //SPI write + while (retries < 20) { + ret = CTP_SPI_WRITE(ts->client, buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + else + 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 == NVTREAD) { //SPI read + while (retries < 20) { + ret = CTP_SPI_READ(ts->client, buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + else + 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_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/NVTSPI 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; + + //if (dev) + kfree(dev); + + return 0; +} + +static const struct file_operations nvt_flash_fops = { + .owner = THIS_MODULE, + .open = nvt_flash_open, + .release = nvt_flash_close, + .read = nvt_flash_read, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/NVTSPI 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/NVTSPI 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 int32_t nvt_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int32_t ret = 0; + +#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); + + ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &SWRST_N8_ADDR); + if (ret) { + NVT_ERR("error reading novatek,swrst-n8-addr. ret=%d\n", ret); + return ret; + } else { + NVT_LOG("SWRST_N8_ADDR=0x%06X\n", SWRST_N8_ADDR); + } + + ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &SPI_RD_FAST_ADDR); + if (ret) { + NVT_LOG("not support novatek,spi-rd-fast-addr\n"); + SPI_RD_FAST_ADDR = 0; + ret = 0; + } else { + NVT_LOG("SPI_RD_FAST_ADDR=0x%06X\n", SPI_RD_FAST_ADDR); + } + + return ret; +} +#else +static int32_t 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; + return 0; +} +#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_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_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 +} + +#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 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; +} + +static void nvt_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - irq_timer); + + //NVT_LOG("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, reload fw */ + nvt_update_firmware(ts->boot_update_firmware_name); + 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 */ + +#if NVT_TOUCH_WDT_RECOVERY +static uint8_t recovery_cnt; +static uint8_t nvt_wdt_fw_recovery(uint8_t *point_data) +{ + uint32_t recovery_cnt_max = 10; + uint8_t recovery_enable = false; + uint8_t i = 0; + + recovery_cnt++; + + /* check pattern */ + for (i = 1 ; i < 7 ; i++) { + if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) { + recovery_cnt = 0; + break; + } + } + + if (recovery_cnt > recovery_cnt_max) { + recovery_enable = true; + recovery_cnt = 0; + } + + return recovery_enable; +} +#endif /* #if NVT_TOUCH_WDT_RECOVERY */ + +#if LCT_TP_PALM_EN +/*2020.228 longcheer taocheng add for pocket mode start*/ +#define FUNCPAGE_PALM 4 +#define PACKET_PALM_ON 3 +#define PACKET_PALM_OFF 4 +int32_t nvt_check_palm(uint8_t input_id, uint8_t *data) +{ + int32_t ret = 0; + uint8_t func_type = data[2]; + uint8_t palm_state = data[3]; + uint8_t keycode = 0; + + if (get_lct_tp_palm_status()) { + if ((input_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_PALM)) { + ret = palm_state; + if (palm_state == PACKET_PALM_ON) { + NVT_LOG("get packet palm on event.\n"); + keycode = gesture_key_array[13]; + } else if (palm_state == PACKET_PALM_OFF) { + NVT_LOG("get packet palm off event.\n"); + } else { + NVT_ERR("invalid palm state %d!\n", palm_state); + ret = -1; + } + } else { + ret = 0; + } + if (keycode > 0) { + NVT_LOG("powerkey.\n"); + 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); + set_lct_tp_palm_status(false); + } + } + return ret; +} +/*2020.2.28 longcheer taocheng add for pocket mode end*/ +#endif + +#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 + DUMMY_BYTES] = {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 +#ifdef CONFIG_PM + if (ts->dev_pm_suspend && ts->is_gesture_mode) { + ret = wait_for_completion_timeout(&ts->dev_pm_suspend_completion, msecs_to_jiffies(700)); + if (!ret) { + NVT_ERR("system(spi bus) can't finished resuming procedure, skip it"); + return IRQ_HANDLED; + } + } +#endif +#endif + + mutex_lock(&ts->lock); + + ret = CTP_SPI_READ(ts->client, point_data, POINT_DATA_LEN + 1); + if (ret < 0) { + NVT_ERR("CTP_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_TOUCH_WDT_RECOVERY + /* ESD protect by WDT */ + if (nvt_wdt_fw_recovery(point_data)) { + NVT_ERR("Recover for fw reset, %02X\n", point_data[1]); + nvt_update_firmware(ts->boot_update_firmware_name); + goto XFER_ERROR; + } +#endif /* #if NVT_TOUCH_WDT_RECOVERY */ + +#if NVT_TOUCH_ESD_PROTECT + /* ESD protect by FW handshake */ + if (nvt_fw_recovery(point_data)) { + nvt_esd_check_enable(true); + goto XFER_ERROR; + } +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + input_id = (uint8_t)(point_data[1] >> 3); +#if LCT_TP_PALM_EN + if (nvt_check_palm(input_id, point_data)) { + goto XFER_ERROR; + } +#endif +#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 LCT_TP_PALM_EN + //nvt_check_palm(input_id, point_data); + //mutex_unlock(&ts->lock); + //return IRQ_HANDLED; +#endif + +#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 chip version trim function. + +return: + Executive outcomes. 0---NVT IC. -1---not NVT IC. +*******************************************************/ +static int8_t nvt_ts_check_chip_ver_trim(void) +{ + 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; + + //---Check for 5 times--- + for (retry = 5; retry > 0; retry--) { + + nvt_bootloader_reset(); + + //---set xdata index to 0x1F64E--- + nvt_set_page(0x1F64E); + + buf[0] = 0x4E; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + CTP_SPI_READ(ts->client, 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]); + + // 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; + ts->hw_crc = trim_id_table[list].hwinfo->hw_crc; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(10); + } + +out: + return ret; +} + +#if WAKEUP_GESTURE +/******************************************************* +Description: + Novatek touchscreen driver get regulator function. + +return: + Executive outcomes. 0---succeed. negative---failed +*******************************************************/ +static int32_t nvt_ts_get_regulator(bool get) +{ + int32_t ret = 0; + + NVT_LOG("get/put regulator : %d \n", get); + + if (!get) { + goto put_regulator; + } + + ts->pwr_vdd = regulator_get(&ts->client->dev, "touch_vddio"); + if (IS_ERR_OR_NULL(ts->pwr_vdd)) { + ret = PTR_ERR(ts->pwr_vdd); + NVT_ERR("Failed to get vdd regulator"); + goto put_regulator; + } else { + if (regulator_count_voltages(ts->pwr_vdd) > 0) { + ret = regulator_set_voltage(ts->pwr_vdd, + 1800000, + 1800000); + if (ret) { + NVT_ERR("vddio regulator set_vtg failed,ret=%d", ret); + goto put_regulator; + } + } + } + + ts->pwr_lab = regulator_get(&ts->client->dev, "touch_lab"); + if (IS_ERR_OR_NULL(ts->pwr_lab)) { + ret = PTR_ERR(ts->pwr_lab); + NVT_ERR("Failed to get lab regulator"); + goto put_regulator; + } + + ts->pwr_ibb = regulator_get(&ts->client->dev, "touch_ibb"); + if (IS_ERR_OR_NULL(ts->pwr_ibb)) { + ret = PTR_ERR(ts->pwr_ibb); + NVT_ERR("Failed to get ibb regulator"); + goto put_regulator; + } + + return 0; + +put_regulator: + if (ts->pwr_vdd) { + regulator_put(ts->pwr_vdd); + ts->pwr_vdd = NULL; + } + + if (ts->pwr_lab) { + regulator_put(ts->pwr_lab); + ts->pwr_lab = NULL; + } + + if (ts->pwr_ibb) { + regulator_put(ts->pwr_ibb); + ts->pwr_ibb = NULL; + } + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen driver enable regulator function. + +return: + Executive outcomes. 0---succeed. negative---failed +*******************************************************/ +static int32_t nvt_ts_enable_regulator(bool en) +{ + static bool status; + int32_t ret = 0; + + if (status == en) { + NVT_LOG("Already %s touch regulator", en?"enable":"disable"); + return 0; + } + status = en; + NVT_LOG("%s touch regulator", en?"enable":"disable"); + + if (!en) { + goto disable_ibb_regulator; + } + + if (ts->pwr_vdd) { + ret = regulator_enable(ts->pwr_vdd); + if (ret < 0) { + NVT_ERR("Failed to enable vdd regulator"); + goto exit; + } + } + + if (ts->pwr_lab) { + ret = regulator_enable(ts->pwr_lab); + if (ret < 0) { + NVT_ERR("Failed to enable lab regulator"); + goto disable_vdd_regulator; + } + } + + if (ts->pwr_ibb) { + ret = regulator_enable(ts->pwr_ibb); + if (ret < 0) { + NVT_ERR("Failed to enable ibb regulator"); + goto disable_lab_regulator; + } + } + + return 0; + +disable_ibb_regulator: + if (ts->pwr_ibb) + regulator_disable(ts->pwr_ibb); + +disable_lab_regulator: + if (ts->pwr_lab) + regulator_disable(ts->pwr_lab); + +disable_vdd_regulator: + if (ts->pwr_vdd) + regulator_disable(ts->pwr_vdd); + +exit: + return ret; +} +#endif + +#if LCT_TP_WORK_EN +static void nvt_ts_release_all_finger(void) +{ + struct input_dev *input_dev = ts->input_dev; +#if MT_PROTOCOL_B + u32 finger_count = 0; + u32 max_touches = ts->max_touch_num; +#endif + + mutex_lock(&ts->lock); +#if MT_PROTOCOL_B + 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->lock); + NVT_LOG("release all finger\n"); +} + +int lct_nvt_tp_work_callback(bool en) +{ + nvt_irq_enable(en); + if (!en) + nvt_ts_release_all_finger(); + set_lct_tp_work_status(en); + NVT_LOG("%s Touchpad\n", en?"Enable":"Disable"); + return 0; +} +#endif +/*2020.2.28 longcheer taocheng add for pocket mode start*/ +#if LCT_TP_PALM_EN +int lct_nvt_tp_palm_callback(bool en) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + if (en) { + msleep(30); + NVT_LOG("sleep 30ms"); + } else { + if (!open_pocket_fail) { + msleep(10); + NVT_LOG("sleep 10ms"); + } else { + msleep(20); + NVT_LOG("sleep 20ms"); + } + } + + if (!bTouchIsAwake) { + NVT_ERR("tp is suspended, can not to set!"); + if (!en) { + open_pocket_fail = 1; + } else { + msleep(450); + NVT_LOG("sleep 450ms"); + NVT_LOG("bTouchIsAwake=%d", bTouchIsAwake); + if (!bTouchIsAwake) { + close_pocket_fail = 0; + } else { + close_pocket_fail = 1; + } + } + return ret; + } + NVT_LOG("init write_buf[8] = {0}"); + NVT_LOG("en=%d", en); + mutex_lock(&ts->lock); + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!"); + goto exit; + } + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x74; + buf[2] = 0x00; + if (en) { + NVT_LOG("screen is not locked"); + } else { + NVT_LOG("screen is locked"); + buf[1] = 0x73; + } + ret = CTP_SPI_WRITE(ts->client, buf, 3); + if (ret < 0) { + NVT_ERR("Write palm command fail!"); + goto exit; + } + if (!en) { + open_pocket_fail = 0; + } else { + close_pocket_fail = 0; + } + //set_lct_tp_palm_status(en); + NVT_LOG("%s PALM", en ? "Disable" : "Enable"); + +exit: + mutex_unlock(&ts->lock); + return ret; + +} +#endif +/*2020.2.28 longcheer taocheng add for pocket mode end*/ + +#if LCT_TP_GRIP_AREA_EN +static int lct_tp_get_screen_angle_callback(void) +{ + uint8_t tmp[8] = {0}; + int32_t ret = -EIO; + uint8_t edge_reject_switch; + + if (!bTouchIsAwake) { + NVT_ERR("tp is suspended\n"); + return ret; + } + + NVT_LOG("++\n"); + + mutex_lock(&ts->lock); + + msleep(35); + + //--set xdata index to EVENT_BUF_ADDR --- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto out; + } + + tmp[0] = 0x5C; + tmp[1] = 0x00; + ret = CTP_SPI_READ(ts->client, tmp, 2); + if (ret < 0) { + NVT_ERR("Read edge reject switch status fail!\n"); + goto out; + } + + edge_reject_switch = ((tmp[1] >> 5) & 0x03); + switch (edge_reject_switch) { + case 1: + ret = 0; + break; + case 2: + ret = 270; + break; + case 3: + ret = 90; + break; + default: + break; + } + NVT_LOG("edge_reject_switch = %d, angle = %d\n", edge_reject_switch, ret); + +out: + mutex_unlock(&ts->lock); + NVT_LOG("--\n"); + return ret; +} + +static int lct_tp_set_screen_angle_callback(int angle) +{ + uint8_t tmp[3]; + int ret = -EIO; + + if (!bTouchIsAwake) { + NVT_ERR("tp is suspended\n"); + return ret; + } + + NVT_LOG("++\n"); + + mutex_lock(&ts->lock); + + //--set xdata index to EVENT_BUF_ADDR --- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto out; + } + + tmp[0] = EVENT_MAP_HOST_CMD; + if (angle == 90) { + tmp[1] = 0xBC; + } else if (angle == 270) { + tmp[1] = 0xBB; + } else { + tmp[1] = 0xBA; + } + ret = CTP_SPI_WRITE(ts->client, tmp, 2); + if (ret < 0) { + NVT_LOG("i2c read error!\n"); + goto out; + } + ret = 0; + +out: + mutex_unlock(&ts->lock); + NVT_LOG("--\n"); + return ret; +} +#endif + +/* 2019.12.6 longcheer taocheng add (xiaomi game mode) start */ +/*function description*/ +#ifdef CONFIG_TOUCHSCREEN_XIAOMI_TOUCHFEATURE + +static struct xiaomi_touch_interface xiaomi_touch_interfaces; + +int32_t nvt_xiaomi_read_reg(uint8_t *read_buf) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + msleep(35); + + mutex_lock(&ts->reg_lock); + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5C); + + // read reg_addr:0x21C5C + buf[0] = 0x5C; + buf[1] = 0x00; + + ret = CTP_SPI_READ(ts->client, buf, 2); + + *read_buf = ((buf[1] >> 2) & 0xFF); // 0x21C5C 的內容會讀取放在 buf[1] + NVT_LOG("read_buf = %d\n", *read_buf); + + mutex_unlock(&ts->reg_lock); + + return ret; + +} + + +int32_t nvt_xiaomi_write_reg(uint8_t write_buf_high, uint8_t write_buf_low) +{ + + int32_t ret = 0; + uint8_t buf[8] = {0}; + + NVT_LOG("write_buf[1] = 0x%x, write_buf[2] = 0x%x,\n", write_buf_high, write_buf_low); + mutex_lock(&ts->reg_lock); + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0X50); + + // Write 0x7D@offset 0x50, 0x51@offset 0x51 + buf[0] = EVENT_MAP_HOST_CMD; // write from 0x50 + buf[1] = write_buf_high; //write into 0x50 + buf[2] = write_buf_low; //write info 0x51 + + NVT_LOG("buf[0] = 0x%x, buf[1] = 0x%x, buf[2] = 0x%x\n", buf[0], buf[1], buf[2]); + ret = CTP_SPI_WRITE(ts->client, buf, 3); + + mutex_unlock(&ts->reg_lock); + + return ret; + +} + +static void nvt_init_touchmode_data(void) +{ + int i; + + NVT_LOG("%s,ENTER\n", __func__); + /* Touch Game Mode Switch */ + xiaomi_touch_interfaces.touch_mode[Touch_Game_Mode][GET_DEF_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Game_Mode][GET_MAX_VALUE] = 1; + xiaomi_touch_interfaces.touch_mode[Touch_Game_Mode][GET_MIN_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Game_Mode][SET_CUR_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Game_Mode][GET_CUR_VALUE] = 0; + + /* Active Mode */ + xiaomi_touch_interfaces.touch_mode[Touch_Active_MODE][GET_MAX_VALUE] = 1; + xiaomi_touch_interfaces.touch_mode[Touch_Active_MODE][GET_MIN_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Active_MODE][GET_DEF_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Active_MODE][SET_CUR_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Active_MODE][GET_CUR_VALUE] = 0; + + /* sensivity */ + xiaomi_touch_interfaces.touch_mode[Touch_UP_THRESHOLD][GET_MAX_VALUE] = 50; + xiaomi_touch_interfaces.touch_mode[Touch_UP_THRESHOLD][GET_MIN_VALUE] = 35; + xiaomi_touch_interfaces.touch_mode[Touch_UP_THRESHOLD][GET_DEF_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_UP_THRESHOLD][SET_CUR_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_UP_THRESHOLD][GET_CUR_VALUE] = 0; + + /* Tolerance */ + xiaomi_touch_interfaces.touch_mode[Touch_Tolerance][GET_MAX_VALUE] = 255; + xiaomi_touch_interfaces.touch_mode[Touch_Tolerance][GET_MIN_VALUE] = 64; + xiaomi_touch_interfaces.touch_mode[Touch_Tolerance][GET_DEF_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Tolerance][SET_CUR_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Tolerance][GET_CUR_VALUE] = 0; + /* edge filter orientation*/ + xiaomi_touch_interfaces.touch_mode[Touch_Panel_Orientation][GET_MAX_VALUE] = 3; + xiaomi_touch_interfaces.touch_mode[Touch_Panel_Orientation][GET_MIN_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Panel_Orientation][GET_DEF_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Panel_Orientation][SET_CUR_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Panel_Orientation][GET_CUR_VALUE] = 0; + + /* edge filter area*/ + xiaomi_touch_interfaces.touch_mode[Touch_Edge_Filter][GET_MAX_VALUE] = 3; + xiaomi_touch_interfaces.touch_mode[Touch_Edge_Filter][GET_MIN_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Edge_Filter][GET_DEF_VALUE] = 2; + xiaomi_touch_interfaces.touch_mode[Touch_Edge_Filter][SET_CUR_VALUE] = 0; + xiaomi_touch_interfaces.touch_mode[Touch_Edge_Filter][GET_CUR_VALUE] = 0; + + + for (i = 0; i < Touch_Mode_NUM; i++) { + NVT_LOG("mode:%d, set cur:%d, get cur:%d, def:%d min:%d max:%d\n", + i, + xiaomi_touch_interfaces.touch_mode[i][SET_CUR_VALUE], + xiaomi_touch_interfaces.touch_mode[i][GET_CUR_VALUE], + xiaomi_touch_interfaces.touch_mode[i][GET_DEF_VALUE], + xiaomi_touch_interfaces.touch_mode[i][GET_MIN_VALUE], + xiaomi_touch_interfaces.touch_mode[i][GET_MAX_VALUE]); + } + + return; +} + + +static int nvt_set_cur_value(int nvt_mode, int nvt_value) +{ + + uint8_t nvt_game_value[2] = {0}; + uint8_t temp_value = 0; + uint8_t reg_value = 0; + uint8_t ret = 0; + + if (nvt_mode >= Touch_Mode_NUM && nvt_mode < 0) { + NVT_ERR("%s, nvt mode is error:%d", __func__, nvt_mode); + return -EINVAL; + } else if (xiaomi_touch_interfaces.touch_mode[nvt_mode][SET_CUR_VALUE] > + xiaomi_touch_interfaces.touch_mode[nvt_mode][GET_MAX_VALUE]) { + + xiaomi_touch_interfaces.touch_mode[nvt_mode][SET_CUR_VALUE] = + xiaomi_touch_interfaces.touch_mode[nvt_mode][GET_MAX_VALUE]; + + } else if (xiaomi_touch_interfaces.touch_mode[nvt_mode][SET_CUR_VALUE] < + xiaomi_touch_interfaces.touch_mode[nvt_mode][GET_MIN_VALUE]) { + + xiaomi_touch_interfaces.touch_mode[nvt_mode][SET_CUR_VALUE] = + xiaomi_touch_interfaces.touch_mode[nvt_mode][GET_MIN_VALUE]; + } + + xiaomi_touch_interfaces.touch_mode[nvt_mode][SET_CUR_VALUE] = nvt_value; + + NVT_LOG("%s,nvt_mode:%d,nvt_vlue:%d", __func__, nvt_mode, nvt_value); + + switch (nvt_mode) { + case Touch_Game_Mode: + break; + case Touch_Active_MODE: + break; + case Touch_UP_THRESHOLD: + /* 0,1,2 = default,no hover,strong hover reject*/ + temp_value = xiaomi_touch_interfaces.touch_mode[Touch_UP_THRESHOLD][SET_CUR_VALUE]; + if (temp_value >= 0 && temp_value < 35) + reg_value = 3; + else if (temp_value > 35 && temp_value <= 40) + reg_value = 0; + else if (temp_value > 40 && temp_value <= 45) + reg_value = 1; + else if (temp_value > 45 && temp_value <= 50) + reg_value = 2; + else + reg_value = 3; + + nvt_game_value[0] = 0x71; + nvt_game_value[1] = reg_value; + break; + case Touch_Tolerance: + /* jitter 0,1,2,3,4,5 = default,weakest,weak,mediea,strong,strongest*/ + temp_value = xiaomi_touch_interfaces.touch_mode[Touch_Tolerance][SET_CUR_VALUE]; + if (temp_value >= 0 && temp_value <= 80) + reg_value = 0; + else if (temp_value > 80 && temp_value <= 150) + reg_value = 1; + else if (temp_value > 150 && temp_value <= 255) + reg_value = 2; + + nvt_game_value[0] = 0x70; + nvt_game_value[1] = reg_value; + break; + case Touch_Edge_Filter: + /* filter 0,1,2,3,4,5,6,7,8 = default,1,2,3,4,5,6,7,8 level*/ + temp_value = xiaomi_touch_interfaces.touch_mode[Touch_Edge_Filter][SET_CUR_VALUE]; + reg_value = temp_value; + + nvt_game_value[0] = 0x72; + nvt_game_value[1] = reg_value; + break; + case Touch_Panel_Orientation: + /* 0,1,2,3 = 0, 90, 180,270 */ + /* + temp_value = xiaomi_touch_interfaces.touch_mode[Touch_Panel_Orientation][SET_CUR_VALUE]; + if (temp_value == 0 || temp_value == 2) { + nvt_game_value[0] = 0xBA; + nvt_game_value[1] = 0x00; + } else if (temp_value == 3) { + nvt_game_value[0] = 0xBB; + nvt_game_value[1] = 0x00; + } else if (temp_value == 1) { + nvt_game_value[0] = 0xBC; + nvt_game_value[1] = 0x00; + } + */ + break; + default: + /* Don't support */ + break; + + }; + + NVT_LOG("0000 mode:%d, value:%d,temp_value:%d, game value:0x%x,0x%x", + nvt_mode, nvt_value, temp_value, nvt_game_value[0], nvt_game_value[1]); + + xiaomi_touch_interfaces.touch_mode[nvt_mode][GET_CUR_VALUE] = + xiaomi_touch_interfaces.touch_mode[nvt_mode][SET_CUR_VALUE]; + if (xiaomi_touch_interfaces.touch_mode[Touch_Game_Mode][SET_CUR_VALUE]) { + + ret = nvt_xiaomi_write_reg(nvt_game_value[0], nvt_game_value[1]); + if (ret < 0) { + NVT_ERR("change game mode fail"); + } + } + return 0; +} + +static int nvt_get_mode_value(int mode, int value_type) +{ + int value = -1; + + if (mode < Touch_Mode_NUM && mode >= 0) + value = xiaomi_touch_interfaces.touch_mode[mode][value_type]; + else + NVT_ERR("%s, don't support\n", __func__); + + return value; +} + +static int nvt_get_mode_all(int mode, int *value) +{ + if (mode < Touch_Mode_NUM && mode >= 0) { + value[0] = xiaomi_touch_interfaces.touch_mode[mode][GET_CUR_VALUE]; + value[1] = xiaomi_touch_interfaces.touch_mode[mode][GET_DEF_VALUE]; + value[2] = xiaomi_touch_interfaces.touch_mode[mode][GET_MIN_VALUE]; + value[3] = xiaomi_touch_interfaces.touch_mode[mode][GET_MAX_VALUE]; + } else { + NVT_ERR("%s, don't support\n", __func__); + } + NVT_LOG("%s, mode:%d, value:%d:%d:%d:%d\n", __func__, mode, value[0], + value[1], value[2], value[3]); + + return 0; +} + +static int nvt_reset_mode(int mode) +{ + int i = 0; + + NVT_LOG("enter reset mode\n"); + + if (mode < Touch_Mode_NUM && mode > 0) { + xiaomi_touch_interfaces.touch_mode[mode][SET_CUR_VALUE] = + xiaomi_touch_interfaces.touch_mode[mode][GET_DEF_VALUE]; + nvt_set_cur_value(mode, xiaomi_touch_interfaces.touch_mode[mode][SET_CUR_VALUE]); + } else if (mode == 0) { + for (i = Touch_Mode_NUM-1; i >= 0; i--) { + xiaomi_touch_interfaces.touch_mode[i][SET_CUR_VALUE] = + xiaomi_touch_interfaces.touch_mode[i][GET_DEF_VALUE]; + nvt_set_cur_value(i, xiaomi_touch_interfaces.touch_mode[mode][SET_CUR_VALUE]); + } + } else { + NVT_ERR("%s, don't support\n", __func__); + } + + NVT_ERR("%s, mode:%d\n", __func__, mode); + + return 0; +} +#endif +/* 2019.12.16 longcheer taocheng add (xiaomi game mode) end */ + +/******************************************************* +Description: + Novatek touchscreen driver probe function. + +return: + Executive outcomes. 0---succeed. negative---failed +*******************************************************/ +static int32_t nvt_ts_probe(struct spi_device *client) +{ + int32_t ret = 0; +#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE) + int32_t retry = 0; +#endif + + NVT_LOG("start\n"); + + //spi_geni_master_dev = NULL; + + ts = kzalloc(sizeof(struct nvt_ts_data), GFP_KERNEL); + if (IS_ERR_OR_NULL(ts)) { + NVT_ERR("failed to allocated memory for nvt ts data\n"); + return -ENOMEM; + } + + ts->xbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN+1), GFP_KERNEL); + if (IS_ERR_OR_NULL(ts->xbuf)) { + NVT_ERR("kzalloc for xbuf failed!\n"); + //if (ts) { + kfree(ts); + ts = NULL; + //} + return -ENOMEM; + } + +#ifdef CHECK_TOUCH_VENDOR + ts->touch_vendor_id = touch_vendor_id; + switch (ts->touch_vendor_id) { + case TP_VENDOR_HUAXING: + memcpy(ts->boot_update_firmware_name, BOOT_UPDATE_HUAXING_FIRMWARE_NAME, + sizeof(BOOT_UPDATE_HUAXING_FIRMWARE_NAME)); + memcpy(ts->mp_update_firmware_name, MP_UPDATE_HUAXING_FIRMWARE_NAME, sizeof(MP_UPDATE_HUAXING_FIRMWARE_NAME)); + break; + case TP_VENDOR_TIANMA: + memcpy(ts->boot_update_firmware_name, BOOT_UPDATE_TIANMA_FIRMWARE_NAME, + sizeof(BOOT_UPDATE_TIANMA_FIRMWARE_NAME)); + memcpy(ts->mp_update_firmware_name, MP_UPDATE_TIANMA_FIRMWARE_NAME, sizeof(MP_UPDATE_TIANMA_FIRMWARE_NAME)); + break; + default: + goto err_vendor_check; + } +#else + memcpy(ts->boot_update_firmware_name, BOOT_UPDATE_FIRMWARE_NAME, sizeof(BOOT_UPDATE_FIRMWARE_NAME)); + memcpy(ts->mp_update_firmware_name, MP_UPDATE_FIRMWARE_NAME, sizeof(MP_UPDATE_FIRMWARE_NAME)); +#endif + + 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; + } + +#ifdef CONFIG_MTK_SPI + /* old usage of MTK spi API */ + memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mt_chip_conf)); + ts->client->controller_data = (void *)&ts->spi_ctrl; +#endif + +#ifdef CONFIG_SPI_MT65XX + /* new usage of MTK spi API */ + memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mtk_chip_config)); + ts->client->controller_data = (void *)&ts->spi_ctrl; +#endif + + NVT_LOG("mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz); + + //---parse dts--- + ret = nvt_parse_dt(&client->dev); + if (ret) { + NVT_ERR("parse dt error\n"); + goto err_spi_setup; + } + + //---config regulator--- +#if WAKEUP_GESTURE + ret = nvt_ts_get_regulator(true); + if (ret < 0) { + NVT_ERR("Failed to get register\n"); + goto err_get_regulator; + } + + ret = nvt_ts_enable_regulator(false);//default disable regulator + if (ret < 0) { + NVT_ERR("Failed to enable regulator\n"); + goto err_enable_regulator; + } +#endif + + //---request and config GPIOs--- + ret = nvt_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); + mutex_init(&ts->reg_lock); + + //---eng reset before TP_RESX high + nvt_eng_reset(); + +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + // need 10ms delay after POR(power on reset) + msleep(10); + + //---check chip version trim--- + ret = nvt_ts_check_chip_ver_trim(); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + goto err_chipvertrim_failed; + } + + ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = 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 = 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 - 1, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max - 1, 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 + ts->input_dev->event = nvt_gesture_switch; + for (retry = 0; retry < (sizeof(gesture_key_array) / sizeof(gesture_key_array[0])); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } +#endif + + snprintf(ts->phys, PAGE_SIZE, "input/ts"); + ts->input_dev->name = NVT_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; + } + + //---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_SPI_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(5000)); +#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 + + //create longcheer procfs node + ret = init_lct_tp_info("[Vendor]unknown,[FW]unknown,[IC]unknown\n", NULL); + if (ret < 0) { + NVT_ERR("init_lct_tp_info Failed!\n"); + goto err_init_lct_tp_info_failed; + } else { + NVT_LOG("init_lct_tp_info Succeeded!\n"); + } + +#if WAKEUP_GESTURE + ret = init_lct_tp_gesture(lct_nvt_tp_gesture_callback); + if (ret < 0) { + NVT_ERR("init_lct_tp_gesture Failed!\n"); + goto err_init_lct_tp_gesture_failed; + } else { + NVT_LOG("init_lct_tp_gesture Succeeded!\n"); + } +#endif + +#if LCT_TP_GRIP_AREA_EN + ret = init_lct_tp_grip_area(lct_tp_set_screen_angle_callback, lct_tp_get_screen_angle_callback); + if (ret < 0) { + NVT_ERR("init_lct_tp_grip_area Failed!\n"); + goto err_init_lct_tp_grip_area_failed; + } else { + NVT_LOG("init_lct_tp_grip_area Succeeded!\n"); + } +#endif + +#if LCT_TP_WORK_EN + ret = init_lct_tp_work(lct_nvt_tp_work_callback); + if (ret < 0) { + NVT_ERR("init_lct_tp_work Failed!\n"); + goto err_init_lct_tp_work_failed; + } else { + NVT_LOG("init_lct_tp_work Succeeded!\n"); + } +#endif + +#if LCT_TP_PALM_EN + ret = init_lct_tp_palm(lct_nvt_tp_palm_callback); + if (ret < 0) { + NVT_ERR("init_lct_tp_palm Failed!"); + goto err_init_lct_tp_palm_failed; + } else { + NVT_LOG("init_lct_tp_palm Succeeded!"); + } +#endif + +#if defined(CONFIG_FB) + ts->workqueue = create_singlethread_workqueue("nvt_ts_workqueue"); + if (!ts->workqueue) { + NVT_ERR("create nvt_ts_workqueue fail"); + ret = -ENOMEM; + goto err_create_nvt_ts_workqueue_failed; + } + INIT_WORK(&ts->resume_work, nvt_ts_resume_work); +#ifdef _MSM_DRM_NOTIFY_H_ + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + ret = drm_register_client(&ts->drm_notif); + //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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = nvt_ts_early_suspend; + ts->early_suspend.resume = nvt_ts_late_resume; + ret = register_early_suspend(&ts->early_suspend); + if (ret) { + NVT_ERR("register early suspend failed. ret=%d\n", ret); + goto err_register_early_suspend_failed; + } +#endif + + /* 2019.12.16 longcheer taocheng add (xiaomi game mode) start */ + /*function description*/ + if (ts->nvt_tp_class == NULL) { +#ifdef CONFIG_TOUCHSCREEN_XIAOMI_TOUCHFEATURE + ts->nvt_tp_class = get_xiaomi_touch_class(); +#endif + if (ts->nvt_tp_class) { + ts->nvt_touch_dev = device_create(ts->nvt_tp_class, NULL, 0x38, ts, "tp_dev"); + if (IS_ERR(ts->nvt_touch_dev)) { + NVT_ERR("Failed to create device !\n"); + goto err_class_create; + } + dev_set_drvdata(ts->nvt_touch_dev, ts); +#ifdef CONFIG_TOUCHSCREEN_XIAOMI_TOUCHFEATURE + memset(&xiaomi_touch_interfaces, 0x00, sizeof(struct xiaomi_touch_interface)); + xiaomi_touch_interfaces.getModeValue = nvt_get_mode_value; + xiaomi_touch_interfaces.setModeValue = nvt_set_cur_value; + xiaomi_touch_interfaces.resetMode = nvt_reset_mode; + xiaomi_touch_interfaces.getModeAll = nvt_get_mode_all; + nvt_init_touchmode_data(); + xiaomitouch_register_modedata(&xiaomi_touch_interfaces); +#endif + } + } + /* 2019.12.16 longcheer taocheng add (xiaomi game mode) end */ + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_irq_enable(true); + +#ifdef CONFIG_PM + ts->dev_pm_suspend = false; + init_completion(&ts->dev_pm_suspend_completion); +#endif + + pm_runtime_enable(&ts->client->dev); + +//2019.12.06 longcheer taocheng add for charger mode +#if NVT_USB_PLUGIN + g_touchscreen_usb_pulgin.event_callback = nvt_ts_usb_event_callback; +#endif + + set_touchpanel_recovery_callback(nvt_ts_recovery_callback); + +#if 0 + //spi bus pm_runtime_get + spi_geni_master_dev = lct_get_spi_geni_master_dev(ts->client->master); + if (spi_geni_master_dev) { + if (pm_runtime_get(spi_geni_master_dev)) + NVT_ERR("pm_runtime_get fail!\n"); + } +#endif + + return 0; + +//2019.12.16 longcheer taocheng add (xiaomi game mode) +err_class_create: + class_destroy(ts->nvt_tp_class); + ts->nvt_tp_class = NULL; + +#if defined(CONFIG_FB) +err_create_nvt_ts_workqueue_failed: + if (ts->workqueue) + destroy_workqueue(ts->workqueue); +#ifdef _MSM_DRM_NOTIFY_H_ + if (drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); + //if (msm_drm_unregister_client(&ts->drm_notif)) + // NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +err_register_drm_notif_failed: +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +err_register_fb_notif_failed: +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +err_register_early_suspend_failed: +#endif +#if LCT_TP_WORK_EN +err_init_lct_tp_work_failed: +uninit_lct_tp_work(); +#endif + +#if LCT_TP_PALM_EN +err_init_lct_tp_palm_failed: +uninit_lct_tp_palm(); +#endif + +#if LCT_TP_GRIP_AREA_EN +err_init_lct_tp_grip_area_failed: +uninit_lct_tp_grip_area(); +#endif +#if WAKEUP_GESTURE +err_init_lct_tp_gesture_failed: +uninit_lct_tp_gesture(); +#endif +err_init_lct_tp_info_failed: +uninit_lct_tp_info(); +#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: + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + nvt_gpio_deconfig(ts); +err_gpio_config_failed: +#if WAKEUP_GESTURE + nvt_ts_enable_regulator(false); +err_enable_regulator: + nvt_ts_get_regulator(false); +err_get_regulator: +#endif +err_spi_setup: +err_ckeck_full_duplex: + spi_set_drvdata(client, NULL); +#ifdef CHECK_TOUCH_VENDOR +err_vendor_check: +#endif + //if (ts->xbuf) { + kfree(ts->xbuf); + ts->xbuf = NULL; + //} + //if (ts) { + kfree(ts); + ts = NULL; + //} + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen driver release function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_ts_remove(struct spi_device *client) +{ + NVT_LOG("Removing driver...\n"); + +#if defined(CONFIG_FB) + if (ts->workqueue) + destroy_workqueue(ts->workqueue); +#ifdef _MSM_DRM_NOTIFY_H_ + if (drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); + //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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + + //remove longcheer procfs +#if LCT_TP_WORK_EN + uninit_lct_tp_work(); +#endif + +#if LCT_TP_PALM_EN + uninit_lct_tp_palm(); +#endif + +#if LCT_TP_GRIP_AREA_EN + uninit_lct_tp_grip_area(); +#endif +#if WAKEUP_GESTURE + uninit_lct_tp_gesture(); +#endif + uninit_lct_tp_info(); + +#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 + 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); + +#if WAKEUP_GESTURE + nvt_ts_enable_regulator(false); + nvt_ts_get_regulator(false); +#endif + + nvt_gpio_deconfig(ts); + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + spi_set_drvdata(client, NULL); + + //if (ts->xbuf) { + kfree(ts->xbuf); + ts->xbuf = NULL; + //} + + //if (ts) { + kfree(ts); + ts = NULL; + //} + + return 0; +} + +static void nvt_ts_shutdown(struct spi_device *client) +{ + NVT_LOG("Shutdown driver...\n"); + + nvt_irq_enable(false); + +#if defined(CONFIG_FB) + if (ts->workqueue) + destroy_workqueue(ts->workqueue); +#ifdef _MSM_DRM_NOTIFY_H_ + if (drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); + //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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + + //remove longcheer procfs +#if LCT_TP_WORK_EN + uninit_lct_tp_work(); +#endif +#if WAKEUP_GESTURE + uninit_lct_tp_gesture(); +#endif + uninit_lct_tp_info(); + +#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 + 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 + if (!ts->is_gesture_mode) { + nvt_irq_enable(false); +#if 0 + //spi bus pm_runtime_get + if (spi_geni_master_dev) { + if (pm_runtime_put(spi_geni_master_dev)) + NVT_ERR("pm_runtime_put fail!\n"); + } +#endif + } +#else + 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 + if (ts->is_gesture_mode) { + //---write command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + CTP_SPI_WRITE(ts->client, buf, 2); + enable_irq_wake(ts->client->irq); + NVT_LOG("Enabled touch wakeup gesture\n"); + } else { + //---write command to enter "deep sleep mode"--- + //buf[0] = EVENT_MAP_HOST_CMD; + //buf[1] = 0x11; + //CTP_SPI_WRITE(ts->client, buf, 2); + NVT_LOG("power off, enter sleep mode\n"); + } +#else // WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_SPI_WRITE(ts->client, 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) +{ + if (bTouchIsAwake) { + NVT_LOG("Touch is already resume\n"); +#if NVT_TOUCH_WDT_RECOVERY + mutex_lock(&ts->lock); + nvt_update_firmware(ts->boot_update_firmware_name); + mutex_unlock(&ts->lock); +#endif /* #if NVT_TOUCH_WDT_RECOVERY */ + 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_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + if (nvt_update_firmware(ts->boot_update_firmware_name)) { + NVT_ERR("download firmware failed, ignore check fw state\n"); + } else { + nvt_check_fw_reset_state(RESET_STATE_REK); + } + +#if WAKEUP_GESTURE + if (!ts->is_gesture_mode) { + nvt_irq_enable(true); +#if 0 + //spi bus pm_runtime_get + if (spi_geni_master_dev) { + if (pm_runtime_get(spi_geni_master_dev)) + NVT_ERR("pm_runtime_get fail!\n"); + } +#endif + } +#else + 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; + NVT_LOG("bTouchIsAwake = 1\n"); + mutex_unlock(&ts->lock); + +#if LCT_TP_PALM_EN + if (open_pocket_fail) { + NVT_LOG("re-open pocket mode\n"); + lct_nvt_tp_palm_callback(false); + } +#endif + +#if WAKEUP_GESTURE + if (ts->delay_gesture) { + lct_nvt_tp_gesture_callback(!ts->is_gesture_mode); + ts->delay_gesture = false; + } +#endif + +#if LCT_TP_WORK_EN + if (!get_lct_tp_work_status()) + nvt_irq_enable(false); +#endif + +//2019.12.06 longcheer taocheng add for charger mode +#if NVT_USB_PLUGIN + if (g_touchscreen_usb_pulgin.valid && g_touchscreen_usb_pulgin.usb_plugged_in) + g_touchscreen_usb_pulgin.event_callback(); +#endif + + NVT_LOG("end\n"); + +#if LCT_TP_PALM_EN + msleep(100); + if (close_pocket_fail) { + NVT_LOG("re-close pocket mode\n"); + lct_nvt_tp_palm_callback(true); + } +#endif + + return 0; +} + +#if WAKEUP_GESTURE +int lct_nvt_tp_gesture_callback(bool flag) +{ + if (!bTouchIsAwake) { + ts->delay_gesture = true; + NVT_LOG("The gesture mode will be %s the next time you wakes up.\n", flag?"enabled":"disbaled"); + return 0; + } + if (flag) { + ts->is_gesture_mode = true; + if (nvt_ts_enable_regulator(true) < 0) + NVT_ERR("Failed to enable regulator\n"); + set_lcd_reset_gpio_keep_high(true); + NVT_LOG("enable gesture mode\n"); + } else { + ts->is_gesture_mode = false; + if (nvt_ts_enable_regulator(false) < 0) + NVT_ERR("Failed to disable regulator\n"); + set_lcd_reset_gpio_keep_high(false); + NVT_LOG("disable gesture mode\n"); + } + return 0; +} +#endif + +#if defined(CONFIG_FB) +static void nvt_ts_resume_work(struct work_struct *work) +{ + nvt_ts_resume(&ts->client->dev); +} +#ifdef _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 == DRM_EARLY_EVENT_BLANK) { + if (*blank == DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + cancel_work_sync(&ts->resume_work); + nvt_ts_suspend(&ts->client->dev); + } + } else if (event == DRM_EVENT_BLANK) { + if (*blank == DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + //nvt_ts_resume(&ts->client->dev); + queue_work(ts->workqueue, &ts->resume_work); + } + } + } + + 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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) +/******************************************************* +Description: + Novatek touchscreen driver early suspend function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_early_suspend(struct early_suspend *h) +{ + nvt_ts_suspend(ts->client, PMSG_SUSPEND); +} + +/******************************************************* +Description: + Novatek touchscreen driver late resume function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_late_resume(struct early_suspend *h) +{ + nvt_ts_resume(ts->client); +} +#endif + +#ifdef CONFIG_PM +static int nvt_pm_suspend(struct device *dev) +{ + struct nvt_ts_data *ts = dev_get_drvdata(dev); + + ts->dev_pm_suspend = true; + reinit_completion(&ts->dev_pm_suspend_completion); + NVT_LOG("pm suspend"); + + return 0; +} + +static int nvt_pm_resume(struct device *dev) +{ + struct nvt_ts_data *ts = dev_get_drvdata(dev); + + ts->dev_pm_suspend = false; + complete(&ts->dev_pm_suspend_completion); + NVT_LOG("pm resume"); + + return 0; +} + +static const struct dev_pm_ops nvt_dev_pm_ops = { + .suspend = nvt_pm_suspend, + .resume = nvt_pm_resume, +}; +#endif + +static const struct spi_device_id nvt_ts_id[] = { + { NVT_SPI_NAME, 0 }, + { } +}; + +#ifdef CONFIG_OF +static struct of_device_id nvt_match_table[] = { + { .compatible = "novatek,NVT-ts-spi",}, + { }, +}; +#endif + +static struct spi_driver nvt_spi_driver = { + .probe = nvt_ts_probe, + .remove = nvt_ts_remove, + .shutdown = nvt_ts_shutdown, + .id_table = nvt_ts_id, + .driver = { + .name = NVT_SPI_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &nvt_dev_pm_ops, +#endif +#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"); +#ifdef CHECK_TOUCH_VENDOR + //Check TP vendor + if (IS_ERR_OR_NULL(saved_command_line)) { + NVT_ERR("saved_command_line ERROR!\n"); + ret = -ENOMEM; + goto err_driver; + } else { + if (strnstr(saved_command_line, "huaxing", strlen(saved_command_line)) != NULL) { + touch_vendor_id = TP_VENDOR_HUAXING; + NVT_LOG("TP info: [Vendor]huaxing [IC]nt36672c\n"); + } else if (strnstr(saved_command_line, "tianma", strlen(saved_command_line)) != NULL) { + touch_vendor_id = TP_VENDOR_TIANMA; + NVT_LOG("TP info: [Vendor]tianma [IC]nt36672c\n"); + } else { + touch_vendor_id = TP_VENDOR_UNKNOWN; + NVT_ERR("Unknown Touch\n"); + ret = -ENODEV; + goto err_driver; + } + } + + //Check android mode + + //if (strstr(saved_command_line, "androidboot.mode=charger") != NULL) { + // NVT_LOG("androidboot.mode=charger, doesn't support touch in the charging mode!\n"); + // ret = -ENODEV; + // goto err_driver; + //} +#endif + + //---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_driver_exit(void) +{ + NVT_LOG("exit tp driver ...\n"); + spi_unregister_driver(&nvt_spi_driver); +} + +//late_initcall(nvt_driver_init); +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/nt36xxx_spi/nt36xxx.h b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx.h new file mode 100755 index 000000000000..cd9f6663c8d4 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx.h @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 43560 $ + * $Date: 2019-04-19 11:34:19 +0800 (週五, 19 四月 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 + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "nt36xxx_mem_map.h" + +#ifdef CONFIG_MTK_SPI +/* Please copy mt_spi.h file under mtk spi driver folder */ +#include "mt_spi.h" +#endif + +#ifdef CONFIG_SPI_MT65XX +#include +#endif + +// include longcheer header +#include "../lct_tp_info.h" +#include "../lct_tp_selftest.h" +#include "../lct_tp_gesture.h" +#include "../lct_tp_grip_area.h" +#include "../lct_tp_work.h" +#include "../lct_tp_palm.h" +#ifdef CONFIG_TOUCHSCREEN_XIAOMI_TOUCHFEATURE +#include "../xiaomi/xiaomi_touch.h" +#endif +#define NVT_DEBUG 1 + +//---GPIO number--- +#define NVTTOUCH_RST_PIN 87 +#define NVTTOUCH_INT_PIN 88 + + +//---INT trigger mode--- +//#define IRQ_TYPE_EDGE_RISING 1 +//#define IRQ_TYPE_EDGE_FALLING 2 +#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + + +//---SPI driver info.--- +#define NVT_SPI_NAME "NVT-ts" + +#if NVT_DEBUG +#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) pr_info("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_SPI_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 2400 +#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 1 +#if WAKEUP_GESTURE +extern const uint16_t gesture_key_array[]; +#endif +#define LCT_TP_PALM_EN 1 +#define BOOT_UPDATE_FIRMWARE 1 +#define FIRMWARE_NAME_LEN 256 +#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin" +#define BOOT_UPDATE_TIANMA_FIRMWARE_NAME "novatek_ts_tianma_fw.bin" +#define BOOT_UPDATE_HUAXING_FIRMWARE_NAME "novatek_ts_huaxing_fw.bin" +#define MP_UPDATE_FIRMWARE_NAME "novatek_ts_mp.bin" +#define MP_UPDATE_TIANMA_FIRMWARE_NAME "novatek_ts_tianma_mp.bin" +#define MP_UPDATE_HUAXING_FIRMWARE_NAME "novatek_ts_huaxing_mp.bin" + +//---ESD Protect.--- +#define NVT_TOUCH_ESD_PROTECT 0 +#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ +#define NVT_TOUCH_WDT_RECOVERY 1 +#define NVT_TOUCH_ESD_DISP_RECOVERY 1 + +//enable 'check touch vendor' feature +#define CHECK_TOUCH_VENDOR + +//enable tp work feature +#define LCT_TP_WORK_EN 1 + +//enable tp grip area feature +#define LCT_TP_GRIP_AREA_EN 1 + +/*2019.12.06 longcheer taocheng add for charger mode begin*/ +/*functions description*/ +//enable tp usb plugin feature +#define NVT_USB_PLUGIN 1 + +#if NVT_USB_PLUGIN +typedef struct touchscreen_usb_plugin_data { + bool valid; + bool usb_plugged_in; + void (*event_callback)(void); +} touchscreen_usb_plugin_data_t; +#endif +/*2019.12.06 longcheer taocheng add charger mode end*/ + +//---Touch Vendor ID--- +#define TP_VENDOR_UNKNOWN 0x00 +#define TP_VENDOR_HUAXING 0x01 +#define TP_VENDOR_TIANMA 0x02 + +/* 2019.12.16 longcheer taocheng add (xiaomi game mode) start */ +#define NVT_REG_MONITOR_MODE 0x7000 +#define NVT_REG_THDIFF 0x7100 +#define NVT_REG_SENSIVITY 0x7200 +#define NVT_REG_EDGE_FILTER_LEVEL 0xBA00 +#define NVT_REG_EDGE_FILTER_ORIENTATION 0xBC00 +/* 2019.12.16 longcheer taocheng add (xiaomi game mode) end */ + +//new qcom platform use +#define _MSM_DRM_NOTIFY_H_ + +struct nvt_ts_data { + 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_FB) + struct workqueue_struct *workqueue; + struct work_struct resume_work; +#ifdef _MSM_DRM_NOTIFY_H_ + struct notifier_block drm_notif; +#else + struct notifier_block fb_notif; +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +#ifdef CHECK_TOUCH_VENDOR + uint8_t touch_vendor_id; +#endif + uint8_t boot_update_firmware_name[FIRMWARE_NAME_LEN]; + uint8_t mp_update_firmware_name[FIRMWARE_NAME_LEN]; + 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; + uint8_t hw_crc; + uint16_t nvt_pid; + uint8_t rbuf[1025]; + uint8_t *xbuf; + struct mutex xbuf_lock; + bool irq_enabled; +#if WAKEUP_GESTURE + bool delay_gesture; + bool is_gesture_mode; +#ifdef CONFIG_PM + bool dev_pm_suspend; + struct completion dev_pm_suspend_completion; +#endif + struct regulator *pwr_vdd; /* IOVCC 1.8V */ + struct regulator *pwr_lab; /* VSP +5V */ + struct regulator *pwr_ibb; /* VSN -5V */ +#endif +#ifdef CONFIG_MTK_SPI + struct mt_chip_conf spi_ctrl; +#endif +#ifdef CONFIG_SPI_MT65XX + struct mtk_chip_config spi_ctrl; +#endif + +/*2019.12.16 longcheer taocheng add (xiaomi game mode) start*/ +#ifdef CONFIG_TOUCHSCREEN_XIAOMI_TOUCHFEATURE + u8 palm_sensor_switch; + bool palm_sensor_changed; + bool gamemode_enabled; +#endif + struct mutex reg_lock; + struct device *nvt_touch_dev; + struct class *nvt_tp_class; +/*2019.12.16 longcheer taocheng add (xiaomi game mode) end*/ +}; + +#if NVT_TOUCH_PROC +struct nvt_flash_data{ + rwlock_t lock; +}; +#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, +} SPI_EVENT_MAP; + +//---SPI READ/WRITE--- +#define SPI_WRITE_MASK(a) (a | 0x80) +#define SPI_READ_MASK(a) (a & 0x7F) + +#define DUMMY_BYTES (1) +#define NVT_TRANSFER_LEN (63*1024) + +typedef enum { + NVTWRITE = 0, + NVTREAD = 1 +} NVT_SPI_RW; + +#if NVT_TOUCH_ESD_DISP_RECOVERY +#define ILM_CRC_FLAG 0x01 +#define CRC_DONE 0x04 +#define F2C_RW_READ 0x00 +#define F2C_RW_WRITE 0x01 +#define BIT_F2C_EN 0 +#define BIT_F2C_RW 1 +#define BIT_CPU_IF_ADDR_INC 2 +#define BIT_CPU_POLLING_EN 5 +#define FFM2CPU_CTL 0x3F280 +#define F2C_LENGTH 0x3F283 +#define CPU_IF_ADDR 0x3F284 +#define FFM_ADDR 0x3F286 +#define CP_TP_CPU_REQ 0x3F291 +#define TOUCH_DATA_ADDR 0x20000 +#define DISP_OFF_ADDR 0x2800 +#endif /* NVT_TOUCH_ESD_DISP_RECOVERY */ + +//---extern structures--- +extern struct nvt_ts_data *ts; + +//---extern functions--- +int32_t CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len); +int32_t CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len); +void nvt_bootloader_reset(void); +void nvt_eng_reset(void); +void nvt_sw_reset(void); +void nvt_sw_reset_idle(void); +void nvt_boot_ready(void); +void nvt_bld_crc_enable(void); +void nvt_fw_crc_enable(void); +int32_t nvt_update_firmware(char *firmware_name); +int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state); +int32_t nvt_get_fw_info(void); +int32_t nvt_clear_fw_status(void); +int32_t nvt_check_fw_status(void); +int32_t nvt_set_page(uint32_t addr); +int32_t nvt_write_addr(uint32_t addr, uint8_t data); +#if NVT_TOUCH_ESD_PROTECT +extern void nvt_esd_check_enable(uint8_t enable); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_ext_proc.c b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_ext_proc.c new file mode 100644 index 000000000000..3a918e2ef013 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_ext_proc.c @@ -0,0 +1,2293 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 43423 $ + * $Date: 2019-04-16 19:58:23 +0800 (週二, 16 四月 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 "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 NVT_XIAOMI_CONFIG_INFO "nvt_xiaomi_config_info" +#define NVT_PF_SWITCH "nvt_pf_switch" +#define NVT_SENSITIVITY_SWITCH "nvt_sensitivity_switch" +#define NVT_ER_RANGE_SWITCH "nvt_er_range_switch" +#define NVT_MAX_POWER_SWITCH "nvt_max_power_switch" +#define NVT_EDGE_REJECT_SWITCH "nvt_edge_reject_switch" +#define NVT_POCKET_PALM_SWITCH "nvt_pocket_palm_switch" +#define NVT_CHARGER_SWITCH "nvt_charger_switch" +#define LCT_TP_DATA_DUMP "tp_data_dump" + +#define SPI_TANSFER_LENGTH 256 + +#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; +static struct proc_dir_entry *NVT_proc_xiaomi_config_info_entry; +static struct proc_dir_entry *NVT_proc_pf_switch_entry; +static struct proc_dir_entry *NVT_proc_sensitivity_switch_entry; +static struct proc_dir_entry *NVT_proc_er_range_switch_entry; +static struct proc_dir_entry *NVT_proc_max_power_switch_entry; +static struct proc_dir_entry *NVT_proc_edge_reject_switch_entry; +static struct proc_dir_entry *NVT_proc_pocket_palm_switch_entry; +static struct proc_dir_entry *NVT_proc_charger_switch_entry; +static int32_t diff_data[2048] = {0}; +static struct proc_dir_entry *LCT_proc_tp_data_dump_entry; + +// Xiaomi Config Info. +static uint8_t nvt_xiaomi_conf_info_fw_ver; +static uint8_t nvt_xiaomi_conf_info_fae_id; +static uint64_t nvt_xiaomi_conf_info_reservation; + +/******************************************************* +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(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = mode; + CTP_SPI_WRITE(ts->client, buf, 2); + + if (mode == NORMAL_MODE) { + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = HANDSHAKING_HOST_READY; + CTP_SPI_WRITE(ts->client, 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(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_SPI_READ(ts->client, 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[SPI_TANSFER_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++) { + //---read xdata by SPI_TANSFER_LENGTH + for (j = 0; j < (XDATA_SECTOR_SIZE / SPI_TANSFER_LENGTH); j++) { + //---change xdata index--- + nvt_set_page(head_addr + (XDATA_SECTOR_SIZE * i) + (SPI_TANSFER_LENGTH * j)); + + //---read data--- + buf[0] = SPI_TANSFER_LENGTH * j; + CTP_SPI_READ(ts->client, buf, SPI_TANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < SPI_TANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + SPI_TANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + SPI_TANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---read xdata by SPI_TANSFER_LENGTH + for (j = 0; j < (residual_len / SPI_TANSFER_LENGTH + 1); j++) { + //---change xdata index--- + nvt_set_page(xdata_addr + data_len - residual_len + (SPI_TANSFER_LENGTH * j)); + + //---read data--- + buf[0] = SPI_TANSFER_LENGTH * j; + CTP_SPI_READ(ts->client, buf, SPI_TANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < SPI_TANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + SPI_TANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], + //((dummy_len+data_len-residual_len) + SPI_TANSFER_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(xdata_btn_addr); + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + CTP_SPI_READ(ts->client, 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(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 file_operations nvt_fw_version_fops = { + .owner = THIS_MODULE, + .open = nvt_fw_version_open, + .read = seq_read, + .llseek = seq_lseek, + .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 file_operations nvt_baseline_fops = { + .owner = THIS_MODULE, + .open = nvt_baseline_open, + .read = seq_read, + .llseek = seq_lseek, + .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 file_operations nvt_raw_fops = { + .owner = THIS_MODULE, + .open = nvt_raw_open, + .read = seq_read, + .llseek = seq_lseek, + .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 file_operations nvt_diff_fops = { + .owner = THIS_MODULE, + .open = nvt_diff_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int nvt_xiaomi_config_info_show(struct seq_file *m, void *v) +{ + seq_printf(m, "FW version/Config version, Debug version: 0x%02X\n", nvt_xiaomi_conf_info_fw_ver); + seq_printf(m, "FAE ID: 0x%02X\n", nvt_xiaomi_conf_info_fae_id); + seq_printf(m, "Reservation byte: 0x%012llX\n", nvt_xiaomi_conf_info_reservation); + + return 0; +} + +static int32_t nvt_xiaomi_config_info_open(struct inode *inode, struct file *file) +{ + uint8_t buf[16] = {0}; + + 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 */ + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x9C); + + buf[0] = 0x9C; + CTP_SPI_READ(ts->client, buf, 9); + + nvt_xiaomi_conf_info_fw_ver = buf[1]; + nvt_xiaomi_conf_info_fae_id = buf[2]; + nvt_xiaomi_conf_info_reservation = + (((uint64_t)buf[3] << 40) | ((uint64_t)buf[4] << 32) | + ((uint64_t)buf[5] << 24) | ((uint64_t)buf[6] << 16) | + ((uint64_t)buf[7] << 8) | (uint64_t)buf[8]); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return single_open(file, nvt_xiaomi_config_info_show, NULL); +} + +static const struct file_operations nvt_xiaomi_config_info_fops = { + .owner = THIS_MODULE, + .open = nvt_xiaomi_config_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/******************************************************* +Description: + Novatek touchscreen read diff meta data function. + +return: + n.a. +*******************************************************/ +void nvt_read_diff_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[SPI_TANSFER_LENGTH + 1] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + + //---set diff_data 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 diff_data : step 1 + for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) { + //---read diff_data by SPI_TANSFER_LENGTH + for (j = 0; j < (XDATA_SECTOR_SIZE / SPI_TANSFER_LENGTH); j++) { + //---change diff_data index--- + nvt_set_page(head_addr + (XDATA_SECTOR_SIZE * i) + (SPI_TANSFER_LENGTH * j)); + + //---read data--- + buf[0] = SPI_TANSFER_LENGTH * j; + CTP_SPI_READ(ts->client, buf, SPI_TANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < SPI_TANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + SPI_TANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], + //(XDATA_SECTOR_SIZE*i + SPI_TANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i)); + } + + //read diff_data : step2 + if (residual_len != 0) { + //---read diff_data by SPI_TANSFER_LENGTH + for (j = 0; j < (residual_len / SPI_TANSFER_LENGTH + 1); j++) { + //---change diff_data index--- + nvt_set_page(xdata_addr + data_len - residual_len + (SPI_TANSFER_LENGTH * j)); + + //---read data--- + buf[0] = SPI_TANSFER_LENGTH * j; + CTP_SPI_READ(ts->client, buf, SPI_TANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < SPI_TANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + SPI_TANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], + //((dummy_len+data_len-residual_len) + SPI_TANSFER_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++) { + diff_data[i] = (int16_t)(xdata_tmp[dummy_len + i * 2] + 256 * xdata_tmp[dummy_len + i * 2 + 1]); + } + +#if TOUCH_KEY_NUM > 0 + //read button diff_data : step3 + //---change diff_data index--- + nvt_set_page(xdata_btn_addr); + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + CTP_SPI_READ(ts->client, buf, (TOUCH_KEY_NUM * 2 + 1)); + + //---2bytes-to-1data--- + for (i = 0; i < TOUCH_KEY_NUM; i++) { + diff_data[ts->x_num * ts->y_num + i] = (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]); + } +#endif + + //---set diff_data index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); +} + +/******************************************************* +Description: + Novatek touchscreen tp_data_dump sequence print show + function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_tp_data_dump_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + int32_t j = 0; + + seq_printf(m, "\nRAW DATA\n"); +#if 0 //0 + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) { + seq_printf(m, "%6d", xdata[ts->x_num * i + j]); + } + seq_puts(m, "\n"); + } +#endif +#if 1 //90 + for (i = 0; i < ts->x_num; i++) { + for (j = 0; j < ts->y_num; j++) { + seq_printf(m, "%6d", xdata[ts->x_num * j + (ts->x_num - 1 - i)]); + } + seq_puts(m, "\n"); + } +#endif +#if 0 //180 + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) { + seq_printf(m, "%6d", xdata[ts->x_num * (ts->y_num - 1 - i) + j]); + } + seq_puts(m, "\n"); + } +#endif +#if 0 //270 + for (i = 0; i < ts->x_num; i++) { + for (j = 0; j < ts->y_num; j++) { + seq_printf(m, "%6d", xdata[ts->x_num * (ts->y_num - 1 - j) + i]); + } + seq_puts(m, "\n"); + } +#endif + + +#if TOUCH_KEY_NUM > 0 + for (i = 0; i < TOUCH_KEY_NUM; i++) { + seq_printf(m, "%6d", xdata[ts->x_num * ts->y_num + i]); + } + seq_puts(m, "\n"); +#endif + + seq_printf(m, "\nDIFF DATA\n"); +#if 0 //0 + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) { + seq_printf(m, "%6d", diff_data[ts->x_num * i + j]); + } + seq_puts(m, "\n"); + } +#endif +#if 1 //90 + for (i = 0; i < ts->x_num; i++) { + for (j = 0; j < ts->y_num; j++) { + seq_printf(m, "%6d", diff_data[ts->x_num * j + (ts->x_num - 1 - i)]); + } + seq_puts(m, "\n"); + } +#endif +#if 0 //180 + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) { + seq_printf(m, "%6d", diff_data[ts->x_num * (ts->y_num - 1 - i) + j]); + } + seq_puts(m, "\n"); + } +#endif +#if 0 //270 + for (i = 0; i < ts->x_num; i++) { + for (j = 0; j < ts->y_num; j++) { + seq_printf(m, "%6d", diff_data[ts->x_num * (ts->y_num - 1 - j) + i]); + } + seq_puts(m, "\n"); + } +#endif + +#if TOUCH_KEY_NUM > 0 + for (i = 0; i < TOUCH_KEY_NUM; i++) { + seq_printf(m, "%6d", diff_data[ts->x_num * ts->y_num + i]); + } + seq_puts(m, "\n"); +#endif + + return 0; +} + +const struct seq_operations lct_tp_data_dump_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_tp_data_dump_show +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/tp_data_dump open function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t lct_tp_data_dump_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); + + if (nvt_get_fw_pipe() == 0) + nvt_read_diff_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_read_diff_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, &lct_tp_data_dump_seq_ops); +} + +static const struct file_operations lct_tp_data_dump_fops = { + .owner = THIS_MODULE, + .open = lct_tp_data_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +/*2019.12.10 longcheer taocheng add for charger mode & other node start*/ +/*function description*/ +int32_t nvt_set_pf_switch(uint8_t pf_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set pf switch: %d\n", pf_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_pf_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x70; + buf[2] = pf_switch; + ret = CTP_SPI_WRITE(ts->client, buf, 3); + if (ret < 0) { + NVT_ERR("Write pf switch command fail!\n"); + goto nvt_set_pf_switch_out; + } + +nvt_set_pf_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_pf_switch(uint8_t *pf_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5D); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_pf_switch_out; + } + + buf[0] = 0x5D; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read pf switch status fail!\n"); + goto nvt_get_pf_switch_out; + } + + *pf_switch = (buf[1] & 0x03); + NVT_LOG("pf_switch = %d\n", *pf_switch); + +nvt_get_pf_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_pf_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t pf_switch; + char tmp_buf[64]; + + NVT_LOG("++\n"); + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_pf_switch(&pf_switch); + + mutex_unlock(&ts->lock); +//2019.12.21 longcheer taocheng edit for enable pf_proc + cnt = snprintf(tmp_buf, PAGE_SIZE, "pf_switch: %d\n", pf_switch); + if (copy_to_user(buf, tmp_buf, sizeof(tmp_buf))) { + NVT_ERR("copy_to_user() error!\n"); + return -EFAULT; + } + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_pf_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t pf_switch; + char *tmp_buf; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value! count = %zu\n", count); + ret = -EINVAL; + goto out; + } + + /*2019.12.21 longcheer taocheng add for enable pf_proc start*/ + /*function description*/ + tmp_buf = kzalloc(count, GFP_KERNEL); + if (!tmp_buf) { + NVT_ERR("Allocate tmp_buf fail!\n"); + ret = -ENOMEM; + goto out; + } + if (copy_from_user(tmp_buf, buf, count)) { + NVT_ERR("copy_from_user() error!\n"); + ret = -EFAULT; + goto out; + } + /*2019.12.21 longcheer taocheng add for enable pf_proc end*/ + + ret = sscanf(tmp_buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value! ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if (tmp < 0 || tmp > 2) { + NVT_ERR("Invalid value! tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + pf_switch = (uint8_t)tmp; + NVT_LOG("pf_switch = %d\n", pf_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_pf_switch(pf_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + kfree(tmp_buf); + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_pf_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_pf_switch_proc_read, + .write = nvt_pf_switch_proc_write, +}; + +int32_t nvt_set_sensitivity_switch(uint8_t sensitivity_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set sensitivity switch: %d\n", sensitivity_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_sensitivity_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x71; + buf[2] = sensitivity_switch; + ret = CTP_SPI_WRITE(ts->client, buf, 3); + if (ret < 0) { + NVT_ERR("Write sensitivity switch command fail!\n"); + goto nvt_set_sensitivity_switch_out; + } + +nvt_set_sensitivity_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_sensitivity_switch(uint8_t *sensitivity_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5D); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_sensitivity_switch_out; + } + + buf[0] = 0x5D; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read sensitivity switch status fail!\n"); + goto nvt_get_sensitivity_switch_out; + } + + *sensitivity_switch = ((buf[1] >> 2) & 0x03); + NVT_LOG("sensitivity_switch = %d\n", *sensitivity_switch); + +nvt_get_sensitivity_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_sensitivity_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t sensitivity_switch; + char tmp_buf[64]; + + NVT_LOG("++\n"); + + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_sensitivity_switch(&sensitivity_switch); + + mutex_unlock(&ts->lock); + + cnt = snprintf(tmp_buf, PAGE_SIZE, "sensitivity_switch: %d\n", sensitivity_switch); + if (copy_to_user(buf, tmp_buf, sizeof(tmp_buf))) { + NVT_ERR("copy_to_user() error!\n"); + return -EFAULT; + } + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_sensitivity_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t sensitivity_switch; + char *tmp_buf; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value! count = %zu\n", count); + ret = -EINVAL; + goto out; + } + /*2019.12.21 longcheer taocheng add for enable sensitivity_proc start*/ + /*function description*/ + tmp_buf = kzalloc(count, GFP_KERNEL); + if (!tmp_buf) { + NVT_ERR("Allocate tmp_buf fail!\n"); + ret = -ENOMEM; + goto out; + } + if (copy_from_user(tmp_buf, buf, count)) { + NVT_ERR("copy_from_user() error!\n"); + ret = -EFAULT; + goto out; + } + /*2019.12.21 longcheer taocheng add for enable sensitivity_proc end*/ + + ret = sscanf(tmp_buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value! ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if (tmp < 0 || tmp > 3) { + NVT_ERR("Invalid value! tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + sensitivity_switch = (uint8_t)tmp; + NVT_LOG("sensitivity_switch = %d\n", sensitivity_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_sensitivity_switch(sensitivity_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + //2019.12.21 longcheer taocheng add for enable sensitivity_proc + kfree(tmp_buf); + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_sensitivity_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_sensitivity_switch_proc_read, + .write = nvt_sensitivity_switch_proc_write, +}; + +int32_t nvt_set_er_range_switch(uint8_t er_range_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set er range switch: %d\n", er_range_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_er_range_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x72; + buf[2] = er_range_switch; + ret = CTP_SPI_WRITE(ts->client, buf, 3); + if (ret < 0) { + NVT_ERR("Write er range switch command fail!\n"); + goto nvt_set_er_range_switch_out; + } + +nvt_set_er_range_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_er_range_switch(uint8_t *er_range_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5D); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_er_range_switch_out; + } + + buf[0] = 0x5D; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read er range switch status fail!\n"); + goto nvt_get_er_range_switch_out; + } + + *er_range_switch = ((buf[1] >> 4) & 0x03); + NVT_LOG("er_range_switch = %d\n", *er_range_switch); + +nvt_get_er_range_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_er_range_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t er_range_switch; + char tmp_buf[64]; + + NVT_LOG("++\n"); + + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_er_range_switch(&er_range_switch); + + mutex_unlock(&ts->lock); + + cnt = snprintf(tmp_buf, PAGE_SIZE, "er_range_switch: %d\n", er_range_switch); + if (copy_to_user(buf, tmp_buf, sizeof(tmp_buf))) { + NVT_ERR("copy_to_user() error!\n"); + return -EFAULT; + } + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_er_range_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t er_range_switch; + char *tmp_buf; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value! count = %zu\n", count); + ret = -EINVAL; + goto out; + } + + /*2019.12.21 longcheer taocheng add for enable er_range_proc start*/ + /*function description*/ + tmp_buf = kzalloc(count, GFP_KERNEL); + if (!tmp_buf) { + NVT_ERR("Allocate tmp_buf fail!\n"); + ret = -ENOMEM; + goto out; + } + if (copy_from_user(tmp_buf, buf, count)) { + NVT_ERR("copy_from_user() error!\n"); + ret = -EFAULT; + goto out; + } + /*2019.12.21 longcheer taocheng add for enable er_range_proc end*/ + ret = sscanf(tmp_buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value! ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if (tmp < 0 || tmp > 3) { + NVT_ERR("Invalid value! tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + er_range_switch = (uint8_t)tmp; + NVT_LOG("er_range_switch = %d\n", er_range_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_er_range_switch(er_range_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + kfree(tmp_buf); + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_er_range_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_er_range_switch_proc_read, + .write = nvt_er_range_switch_proc_write, +}; + +int32_t nvt_set_max_power_switch(uint8_t max_power_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set max power switch: %d\n", max_power_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_max_power_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + if (max_power_switch == 1) { + buf[1] = 0x75; + } else if (max_power_switch == 0) { + buf[1] = 0x76; + } else { + NVT_ERR("Invalid max power switch: %d\n", max_power_switch); + ret = -EINVAL; + goto nvt_set_max_power_switch_out; + } + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write max power switch command fail!\n"); + goto nvt_set_max_power_switch_out; + } + +nvt_set_max_power_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_max_power_switch(uint8_t *max_power_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5D); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_max_power_switch_out; + } + + buf[0] = 0x5D; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read max power switch status fail!\n"); + goto nvt_get_max_power_switch_out; + } + + *max_power_switch = ((buf[1] >> 7) & 0x01); + NVT_LOG("max_power_switch = %d\n", *max_power_switch); + +nvt_get_max_power_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_max_power_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t max_power_switch; + + NVT_LOG("++\n"); + + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_max_power_switch(&max_power_switch); + + mutex_unlock(&ts->lock); + + cnt = snprintf(buf, PAGE_SIZE - len, "max_power_switch: %d\n", max_power_switch); + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_max_power_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t max_power_switch; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value! count = %zu\n", count); + ret = -EINVAL; + goto out; + } + + ret = sscanf(buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value! ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if (tmp < 0 || tmp > 1) { + NVT_ERR("Invalid value! tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + max_power_switch = (uint8_t)tmp; + NVT_LOG("max_power_switch = %d\n", max_power_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_max_power_switch(max_power_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_max_power_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_max_power_switch_proc_read, + .write = nvt_max_power_switch_proc_write, +}; + +int32_t nvt_set_edge_reject_switch(uint8_t edge_reject_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set edge reject switch: %d\n", edge_reject_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_edge_reject_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + if (edge_reject_switch == 1) { + // vertical + buf[1] = 0xBA; + } else if (edge_reject_switch == 2) { + // left up + buf[1] = 0xBB; + } else if (edge_reject_switch == 3) { + // righ up + buf[1] = 0xBC; + } else { + NVT_ERR("Invalid value! edge_reject_switch = %d\n", edge_reject_switch); + ret = -EINVAL; + goto nvt_set_edge_reject_switch_out; + } + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write edge reject switch command fail!\n"); + goto nvt_set_edge_reject_switch_out; + } + +nvt_set_edge_reject_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_edge_reject_switch(uint8_t *edge_reject_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5C); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_edge_reject_switch_out; + } + + buf[0] = 0x5C; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read edge reject switch status fail!\n"); + goto nvt_get_edge_reject_switch_out; + } + + *edge_reject_switch = ((buf[1] >> 5) & 0x03); + NVT_LOG("edge_reject_switch = %d\n", *edge_reject_switch); + +nvt_get_edge_reject_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_edge_reject_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t edge_reject_switch; + + NVT_LOG("++\n"); + + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_edge_reject_switch(&edge_reject_switch); + + mutex_unlock(&ts->lock); + + cnt = snprintf(buf, PAGE_SIZE - len, "edge_reject_switch: %d\n", edge_reject_switch); + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_edge_reject_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t edge_reject_switch; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value! count = %zu\n", count); + ret = -EINVAL; + goto out; + } + + ret = sscanf(buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value! ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if (tmp < 1 || tmp > 3) { + NVT_ERR("Invalid value! tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + edge_reject_switch = (uint8_t)tmp; + NVT_LOG("edge_reject_switch = %d\n", edge_reject_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_edge_reject_switch(edge_reject_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_edge_reject_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_edge_reject_switch_proc_read, + .write = nvt_edge_reject_switch_proc_write, +}; + +int32_t nvt_set_pocket_palm_switch(uint8_t pocket_palm_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set pocket palm switch: %d\n", pocket_palm_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_pocket_palm_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + if (pocket_palm_switch == 0) { + // pocket palm disable + buf[1] = 0x74; + } else if (pocket_palm_switch == 1) { + // pocket palm enable + buf[1] = 0x73; + } else { + NVT_ERR("Invalid value! pocket_palm_switch = %d\n", pocket_palm_switch); + ret = -EINVAL; + goto nvt_set_pocket_palm_switch_out; + } + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write pocket palm switch command fail!\n"); + goto nvt_set_pocket_palm_switch_out; + } + +nvt_set_pocket_palm_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_pocket_palm_switch(uint8_t *pocket_palm_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5D); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_pocket_palm_switch_out; + } + + buf[0] = 0x5D; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read pocket palm switch status fail!\n"); + goto nvt_get_pocket_palm_switch_out; + } + + *pocket_palm_switch = ((buf[1] >> 6) & 0x01); + NVT_LOG("pocket_palm_switch = %d\n", *pocket_palm_switch); + +nvt_get_pocket_palm_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_pocket_palm_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t pocket_palm_switch; + char tmp_buf[64]; + + NVT_LOG("++\n"); + + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_pocket_palm_switch(&pocket_palm_switch); + + mutex_unlock(&ts->lock); +//2020.01.02 longcheer taocheng edit for enable pocket_proc + cnt = snprintf(tmp_buf, PAGE_SIZE, "pocket_palm_switch: %d\n", pocket_palm_switch); + if (copy_to_user(buf, tmp_buf, sizeof(tmp_buf))) { + NVT_LOG("copy_to_user() error!\n"); + return -EFAULT; + } + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_pocket_palm_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t pocket_palm_switch; + char *tmp_buf; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value!, count = %zu\n", count); + ret = -EINVAL; + goto out; + } + /*2020.01.02 longcheer taocheng add for enable pocket_palm_proc start*/ + /*function description*/ + tmp_buf = kzalloc(count, GFP_KERNEL); + if (!tmp_buf) { + NVT_ERR("Allocate tmp_buf fail!\n"); + ret = -ENOMEM; + goto out; + } + if (copy_from_user(tmp_buf, buf, count)) { + NVT_ERR("copy_from_user() error!\n"); + ret = -EFAULT; + goto out; + } + /*2020.01.02 longcheer taocheng add for enable pocket_palm_proc end*/ + + ret = sscanf(tmp_buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value!, ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if ((tmp < 0) || (tmp > 1)) { + NVT_ERR("Invalid value!, tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + pocket_palm_switch = (uint8_t)tmp; + NVT_LOG("pocket_palm_switch = %d\n", pocket_palm_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_pocket_palm_switch(pocket_palm_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + kfree(tmp_buf); + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_pocket_palm_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_pocket_palm_switch_proc_read, + .write = nvt_pocket_palm_switch_proc_write, +}; + +int32_t nvt_set_charger_switch(uint8_t charger_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + NVT_LOG("set charger switch: %d\n", charger_switch); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_charger_switch_out; + } + + buf[0] = EVENT_MAP_HOST_CMD; + if (charger_switch == 0) { + // charger off + buf[1] = 0x51; + } else if (charger_switch == 1) { + // charger on + buf[1] = 0x53; + } else { + NVT_ERR("Invalid value! charger_switch = %d\n", charger_switch); + ret = -EINVAL; + goto nvt_set_charger_switch_out; + } + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write charger switch command fail!\n"); + goto nvt_set_charger_switch_out; + } + +nvt_set_charger_switch_out: + NVT_LOG("--\n"); + return ret; +} + +int32_t nvt_get_charger_switch(uint8_t *charger_switch) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("++\n"); + + msleep(35); + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | 0x5D); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_get_charger_switch_out; + } + + buf[0] = 0x5C; + buf[1] = 0x00; + ret = CTP_SPI_READ(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Read charger switch status fail!\n"); + goto nvt_get_charger_switch_out; + } + + *charger_switch = ((buf[1] >> 2) & 0x01); + NVT_LOG("charger_switch = %d\n", *charger_switch); + +nvt_get_charger_switch_out: + NVT_LOG("--\n"); + return ret; +} + +static ssize_t nvt_charger_switch_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + static int finished; + int32_t cnt = 0; + int32_t len = 0; + uint8_t charger_switch; + char tmp_buf[64]; + + NVT_LOG("++\n"); + + /* + * We return 0 to indicate end of file, that we have + * no more information. Otherwise, processes will + * continue to read from us in an endless loop. + */ + if (finished) { + NVT_LOG("read END\n"); + finished = 0; + return 0; + } + finished = 1; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_get_charger_switch(&charger_switch); + + mutex_unlock(&ts->lock); + + cnt = snprintf(tmp_buf, PAGE_SIZE, "charger_switch: %d\n", charger_switch); + if (copy_to_user(buf, tmp_buf, sizeof(tmp_buf))) { + NVT_ERR("copy_to_user() error!\n"); + return -EFAULT; + } + buf += cnt; + len += cnt; + + NVT_LOG("--\n"); + return len; +} + +static ssize_t nvt_charger_switch_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + int32_t ret; + int32_t tmp; + uint8_t charger_switch; + char *tmp_buf; + + NVT_LOG("++\n"); + + if (count == 0 || count > 2) { + NVT_ERR("Invalid value!, count = %zu\n", count); + ret = -EINVAL; + goto out; + } + + tmp_buf = kzalloc(count, GFP_KERNEL); + if (!tmp_buf) { + NVT_ERR("Allocate tmp_buf fail!\n"); + ret = -ENOMEM; + goto out; + } + if (copy_from_user(tmp_buf, buf, count)) { + NVT_ERR("copy_from_user() error!\n"); + ret = -EFAULT; + goto out; + } + ret = sscanf(tmp_buf, "%d", &tmp); + if (ret != 1) { + NVT_ERR("Invalid value!, ret = %d\n", ret); + ret = -EINVAL; + goto out; + } + if ((tmp < 0) || (tmp > 1)) { + NVT_ERR("Invalid value!, tmp = %d\n", tmp); + ret = -EINVAL; + goto out; + } + charger_switch = (uint8_t)tmp; + NVT_LOG("charger_switch = %d\n", charger_switch); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_set_charger_switch(charger_switch); + + mutex_unlock(&ts->lock); + + ret = count; +out: + kfree(tmp_buf); + NVT_LOG("--\n"); + return ret; +} + +static const struct file_operations nvt_charger_switch_fops = { + .owner = THIS_MODULE, + .read = nvt_charger_switch_proc_read, + .write = nvt_charger_switch_proc_write, +}; +/*2019.12.10 longcheer taocheng add for charger mode & other nodes end*/ + +/******************************************************* +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); + } + + NVT_proc_xiaomi_config_info_entry = proc_create(NVT_XIAOMI_CONFIG_INFO, 0444, NULL, &nvt_xiaomi_config_info_fops); + if (NVT_proc_xiaomi_config_info_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_XIAOMI_CONFIG_INFO); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_XIAOMI_CONFIG_INFO); + } +/*2019.12.10 longcheer taocheng add for creating nodes begin*/ +/*function description*/ + NVT_proc_pf_switch_entry = proc_create(NVT_PF_SWITCH, 0666, NULL, &nvt_pf_switch_fops); + if (NVT_proc_pf_switch_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_PF_SWITCH); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_PF_SWITCH); + } + + NVT_proc_sensitivity_switch_entry = proc_create(NVT_SENSITIVITY_SWITCH, 0666, NULL, &nvt_sensitivity_switch_fops); + if (NVT_proc_sensitivity_switch_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SENSITIVITY_SWITCH); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_SENSITIVITY_SWITCH); + } + + NVT_proc_er_range_switch_entry = proc_create(NVT_ER_RANGE_SWITCH, 0666, NULL, &nvt_er_range_switch_fops); + if (NVT_proc_er_range_switch_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_ER_RANGE_SWITCH); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_ER_RANGE_SWITCH); + } + + NVT_proc_max_power_switch_entry = proc_create(NVT_MAX_POWER_SWITCH, 0666, NULL, &nvt_max_power_switch_fops); + if (NVT_proc_max_power_switch_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_MAX_POWER_SWITCH); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_MAX_POWER_SWITCH); + } + + NVT_proc_edge_reject_switch_entry = proc_create(NVT_EDGE_REJECT_SWITCH, 0666, NULL, &nvt_edge_reject_switch_fops); + if (NVT_proc_edge_reject_switch_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_EDGE_REJECT_SWITCH); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_EDGE_REJECT_SWITCH); + } + + NVT_proc_pocket_palm_switch_entry = proc_create(NVT_POCKET_PALM_SWITCH, 0666, NULL, &nvt_pocket_palm_switch_fops); + if (NVT_proc_pocket_palm_switch_entry == NULL) { + NVT_ERR("create proc/nvt_pocket_palm_switch Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_pocket_palm_switch Succeeded!\n"); + } + + NVT_proc_charger_switch_entry = proc_create(NVT_CHARGER_SWITCH, 0666, NULL, &nvt_charger_switch_fops); + if (NVT_proc_charger_switch_entry == NULL) { + NVT_ERR("create proc/nvt_charger_switch Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_charger_switch Succeeded!\n"); + } + + LCT_proc_tp_data_dump_entry = proc_create(LCT_TP_DATA_DUMP, 0444, NULL, &lct_tp_data_dump_fops); + if (LCT_proc_tp_data_dump_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", LCT_TP_DATA_DUMP); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", LCT_TP_DATA_DUMP); + } + return 0; +} +/*2019.12.10 longcheer taocheng add for creating nodes end*/ + +/******************************************************* +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); + } + + if (NVT_proc_xiaomi_config_info_entry != NULL) { + remove_proc_entry(NVT_XIAOMI_CONFIG_INFO, NULL); + NVT_proc_xiaomi_config_info_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_XIAOMI_CONFIG_INFO); + } +/*2019.12.10 longcheer taocheng add for charger extra proc begin*/ +/*function description*/ + if (NVT_proc_pf_switch_entry != NULL) { + remove_proc_entry(NVT_PF_SWITCH, NULL); + NVT_proc_pf_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_PF_SWITCH); + } + + if (NVT_proc_sensitivity_switch_entry != NULL) { + remove_proc_entry(NVT_SENSITIVITY_SWITCH, NULL); + NVT_proc_sensitivity_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SENSITIVITY_SWITCH); + } + + if (NVT_proc_er_range_switch_entry != NULL) { + remove_proc_entry(NVT_ER_RANGE_SWITCH, NULL); + NVT_proc_er_range_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_ER_RANGE_SWITCH); + } + + if (NVT_proc_max_power_switch_entry != NULL) { + remove_proc_entry(NVT_MAX_POWER_SWITCH, NULL); + NVT_proc_max_power_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_MAX_POWER_SWITCH); + } + + if (NVT_proc_edge_reject_switch_entry != NULL) { + remove_proc_entry(NVT_EDGE_REJECT_SWITCH, NULL); + NVT_proc_edge_reject_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_EDGE_REJECT_SWITCH); + } + + if (NVT_proc_pocket_palm_switch_entry != NULL) { + remove_proc_entry(NVT_POCKET_PALM_SWITCH, NULL); + NVT_proc_pocket_palm_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_POCKET_PALM_SWITCH); + } + + if (NVT_proc_charger_switch_entry != NULL) { + remove_proc_entry(NVT_CHARGER_SWITCH, NULL); + NVT_proc_charger_switch_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_CHARGER_SWITCH); + } +/*2019.12.10 longcheer taocheng add for charger extra proc end*/ + if (LCT_proc_tp_data_dump_entry != NULL) { + remove_proc_entry(LCT_TP_DATA_DUMP, NULL); + LCT_proc_tp_data_dump_entry = NULL; + NVT_LOG("Removed /proc/%s\n", LCT_TP_DATA_DUMP); + } +} +#endif diff --git a/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_fw_update.c b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_fw_update.c new file mode 100755 index 000000000000..1c7beddc6411 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_fw_update.c @@ -0,0 +1,1177 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 43560 $ + * $Date: 2019-04-19 11:34:19 +0800 (週五, 19 四月 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 "nt36xxx.h" + +#if BOOT_UPDATE_FIRMWARE + +#define SIZE_4KB 4096 +#define FLASH_SECTOR_SIZE SIZE_4KB +#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) + +#define NVT_DUMP_PARTITION (0) +#define NVT_DUMP_PARTITION_LEN (1024) +#define NVT_DUMP_PARTITION_PATH "/data/local/tmp" + +//2019.12.06 longcheer taocheng add for charger mode +#if NVT_USB_PLUGIN +extern touchscreen_usb_plugin_data_t g_touchscreen_usb_pulgin; +#endif + +static struct timeval start, end; +const struct firmware *fw_entry; +static size_t fw_need_write_size; +static uint8_t *fwbuf; + +struct nvt_ts_bin_map { + char name[12]; + uint32_t BIN_addr; + uint32_t SRAM_addr; + uint32_t size; + uint32_t crc; +}; + +static struct nvt_ts_bin_map *bin_map; + +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 (strncmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], + "NVT", NVT_FLASH_END_FLAG_LEN) == 0) { + fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx), NVT end flag\n", fw_need_write_size, fw_need_write_size); + return 0; + } + + /* check if there is end flag "MOD" at the end of this sector */ + if (strncmp(&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), MOD end flag\n", fw_need_write_size, fw_need_write_size); + return 0; + } + } + + NVT_ERR("end flag \"NVT\" \"MOD\" not found!\n"); + return -EPERM; +} + +/******************************************************* +Description: + Novatek touchscreen init variable and allocate buffer +for download firmware function. + +return: + n.a. +*******************************************************/ +static int32_t nvt_download_init(void) +{ + /* allocate buffer for transfer firmware */ + //NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_TRANSFER_LEN); + + if (fwbuf == NULL) { + fwbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN + 1 + DUMMY_BYTES), GFP_KERNEL); + if (fwbuf == NULL) { + NVT_ERR("kzalloc for fwbuf failed!\n"); + return -ENOMEM; + } + } + + 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 partition; +static uint8_t ilm_dlm_num = 2; +static int32_t nvt_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; + + /* Find the header size */ + end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24); + 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 + */ + partition = 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, ilm_dlm_num, ovly_sec_num, info_sec_num, partition); + + /* allocated memory for header info */ + bin_map = (struct nvt_ts_bin_map *)kzalloc((partition+1) * sizeof(struct nvt_ts_bin_map), GFP_KERNEL); + if (bin_map == NULL) { + NVT_ERR("kzalloc for bin_map failed!\n"); + return -ENOMEM; + } + + for (list = 0; list < partition; list++) { + /* + * [1] parsing ILM & DLM header info + * BIN_addr : SRAM_addr : size (12-bytes) + * crc located at 0x18 & 0x1C + */ + if (list < 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 larger than bin size!\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, PAGE_SIZE, "ILM"); + else if (list == 1) + snprintf(bin_map[list].name, PAGE_SIZE, "DLM"); + } + + /* + * [2] parsing others header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if ((list >= ilm_dlm_num) && (list < (ilm_dlm_num + info_sec_num))) { + /* others partition located at 0x30 offset */ + pos = 0x30 + (0x10 * (list - ilm_dlm_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 larger than bin size!\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 == 0) && (bin_map[list].size != 0)) { + snprintf(bin_map[list].name, PAGE_SIZE, "Header"); + } else { + snprintf(bin_map[list].name, PAGE_SIZE, "Info-%d", (list - ilm_dlm_num)); + } + } + + /* + * [3] parsing overlay section header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if (list >= (ilm_dlm_num + info_sec_num)) { + /* overlay info located at DLM (list = 1) start addr */ + pos = bin_map[1].BIN_addr + (0x10 * (list - 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 larger than bin size!\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, PAGE_SIZE, "Overlay-%d", (list - 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 update_firmware_release(void) +{ + if (fw_entry) { + release_firmware(fw_entry); + } + + fw_entry = NULL; +} + +/******************************************************* +Description: + Novatek touchscreen request update firmware function. + +return: + Executive outcomes. 0---succeed. -1,-22---failed. +*******************************************************/ +static int32_t update_firmware_request(char *filename) +{ + uint8_t retry = 0; + int32_t ret = 0; + + if (NULL == filename) { + return -ENOENT; + } + + while (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); + goto request_fail; + } + + // 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"); + ret = -EINVAL; + goto invalid; + } + + // 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)); + ret = -ENOEXEC; + goto invalid; + } + + /* BIN Header Parser */ + ret = nvt_bin_header_parser(fw_entry->data, fw_entry->size); + if (ret) { + NVT_ERR("bin header parser failed\n"); + goto invalid; + } else { + break; + } + +invalid: + update_firmware_release(); + if (!IS_ERR_OR_NULL(bin_map)) { + kfree(bin_map); + bin_map = NULL; + } + +request_fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + break; + } + } + + return ret; +} + +#if NVT_DUMP_PARTITION +/******************************************************* +Description: + Novatek touchscreen dump flash partition function. + +return: + n.a. +*******************************************************/ +loff_t file_offset; +static int32_t nvt_read_ram_and_save_file(uint32_t addr, uint16_t len, char *name) +{ + char file[256] = ""; + uint8_t *fbufp = NULL; + int32_t ret = 0; + struct file *fp = NULL; + struct filename *vts_name; + mm_segment_t org_fs; + + snprintf(file, PAGE_SIZE, "%s/dump_%s.bin", NVT_DUMP_PARTITION_PATH, name); + NVT_LOG("Dump [%s] from 0x%08X to 0x%08X\n", file, addr, addr+len); + + fbufp = (uint8_t *)kzalloc(len+1, GFP_KERNEL); + if (fbufp == NULL) { + NVT_ERR("kzalloc for fbufp failed!\n"); + ret = -ENOMEM; + goto alloc_buf_fail; + } + + org_fs = get_fs(); + set_fs(KERNEL_DS); + vts_name = getname_kernel(file); + fp = file_open_name(vts_name, O_RDWR | O_CREAT, 0644); + //fp = filp_open(file, O_RDWR | O_CREAT, 0644); + if (fp == NULL || IS_ERR(fp)) { + ret = -ENOMEM; + NVT_ERR("open file failed\n"); + goto open_file_fail; + } + + /* SPI read */ + //---set xdata index to addr--- + nvt_set_page(addr); + + fbufp[0] = addr & 0x7F; //offset + CTP_SPI_READ(ts->client, fbufp, len+1); + + /* Write to file */ + ret = vfs_write(fp, (char __user *)fbufp+1, len, &file_offset); + if (ret != len) { + NVT_ERR("write file failed\n"); + goto open_file_fail; + } else { + ret = 0; + } + +open_file_fail: + set_fs(org_fs); + if (!IS_ERR_OR_NULL(fp)) { + filp_close(fp, NULL); + fp = NULL; + } + + if (!IS_ERR_OR_NULL(fbufp)) { + kfree(fbufp); + fbufp = NULL; + } +alloc_buf_fail: + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen nvt_dump_partition function to dump + each partition for debug. + +return: + n.a. +*******************************************************/ +static int32_t nvt_dump_partition(void) +{ + uint32_t list = 0; + char *name; + uint32_t SRAM_addr, size; + uint32_t i = 0; + uint16_t len = 0; + int32_t count = 0; + int32_t ret = 0; + + if (NVT_DUMP_PARTITION_LEN >= sizeof(ts->rbuf)) { + NVT_ERR("dump len %d is larger than buffer size %ld\n", + NVT_DUMP_PARTITION_LEN, sizeof(ts->rbuf)); + return -EINVAL; + } else if (NVT_DUMP_PARTITION_LEN >= NVT_TRANSFER_LEN) { + NVT_ERR("dump len %d is larger than NVT_TRANSFER_LEN\n", NVT_DUMP_PARTITION_LEN); + return -EINVAL; + } + + if (bin_map == NULL) { + NVT_ERR("bin_map is NULL\n"); + return -ENOMEM; + } + + memset(fwbuf, 0, (NVT_DUMP_PARTITION_LEN+1)); + + for (list = 0; list < partition; list++) { + /* initialize variable */ + SRAM_addr = bin_map[list].SRAM_addr; + size = bin_map[list].size; + name = bin_map[list].name; + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!size) + continue; + else + size = size + 1; + + /* write data to SRAM */ + if (size % NVT_DUMP_PARTITION_LEN) + count = (size / NVT_DUMP_PARTITION_LEN) + 1; + else + count = (size / NVT_DUMP_PARTITION_LEN); + + for (i = 0 ; i < count ; i++) { + len = (size < NVT_DUMP_PARTITION_LEN) ? size : NVT_DUMP_PARTITION_LEN; + + /* dump for debug download firmware */ + ret = nvt_read_ram_and_save_file(SRAM_addr, len, name); + if (ret < 0) { + NVT_ERR("nvt_read_ram_and_save_file failed, ret = %d\n", ret); + goto out; + } + + SRAM_addr += NVT_DUMP_PARTITION_LEN; + size -= NVT_DUMP_PARTITION_LEN; + } + + file_offset = 0; + } + +out: + return ret; +} +#endif /* NVT_DUMP_PARTITION */ + +/******************************************************* +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_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_TRANSFER_LEN) + count = (size / NVT_TRANSFER_LEN) + 1; + else + count = (size / NVT_TRANSFER_LEN); + + for (i = 0 ; i < count ; i++) { + len = (size < NVT_TRANSFER_LEN) ? size : NVT_TRANSFER_LEN; + + //---set xdata index to start address of SRAM--- + ret = nvt_set_page(SRAM_addr); + if (ret) { + NVT_ERR("set page failed, ret = %d\n", ret); + return ret; + } + + //---write data into SRAM--- + fwbuf[0] = SRAM_addr & 0x7F; //offset + memcpy(fwbuf+1, &fwdata[BIN_addr], len); //payload + ret = CTP_SPI_WRITE(ts->client, fwbuf, len+1); + if (ret) { + NVT_ERR("write to sram failed, ret = %d\n", ret); + return ret; + } + + SRAM_addr += NVT_TRANSFER_LEN; + BIN_addr += NVT_TRANSFER_LEN; + size -= NVT_TRANSFER_LEN; + } + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen nvt_write_firmware function to write +firmware into each partition. + +return: + n.a. +*******************************************************/ +static int32_t nvt_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(fwbuf, 0, (NVT_TRANSFER_LEN+1)); + + for (list = 0; list < partition; list++) { + /* initialize variable */ + SRAM_addr = bin_map[list].SRAM_addr; + size = bin_map[list].size; + BIN_addr = bin_map[list].BIN_addr; + name = 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_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_check_fw_checksum(void) +{ + uint32_t fw_checksum = 0; + uint32_t len = partition*4; + uint32_t list = 0; + int32_t ret = 0; + + memset(fwbuf, 0, (len+1)); + + //---set xdata index to checksum--- + nvt_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); + + /* read checksum */ + fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F; + ret = CTP_SPI_READ(ts->client, fwbuf, len+1); + if (ret) { + NVT_ERR("Read fw checksum failed\n"); + return ret; + } + + /* + * Compare each checksum from fw + * ILM + DLM + Overlay + Info + * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + for (list = 0; list < partition; list++) { + fw_checksum = byte_to_word(&fwbuf[1+list*4]); + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!bin_map[list].size) + continue; + + if (bin_map[list].crc != fw_checksum) { + NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n", + list, 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_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) +{ + /* write destination address */ + nvt_set_page(DES_ADDR); + fwbuf[0] = DES_ADDR & 0x7F; + fwbuf[1] = (SRAM_ADDR) & 0xFF; + fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF; + fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF; + CTP_SPI_WRITE(ts->client, fwbuf, 4); + + /* write length */ + //nvt_set_page(LENGTH_ADDR); + fwbuf[0] = LENGTH_ADDR & 0x7F; + fwbuf[1] = (size) & 0xFF; + fwbuf[2] = (size >> 8) & 0xFF; + fwbuf[3] = (size >> 16) & 0x01; + if (ts->hw_crc == 1) { + CTP_SPI_WRITE(ts->client, fwbuf, 3); + } else if (ts->hw_crc > 1) { + CTP_SPI_WRITE(ts->client, fwbuf, 4); + } + + /* write golden dlm checksum */ + //nvt_set_page(G_CHECKSUM_ADDR); + fwbuf[0] = G_CHECKSUM_ADDR & 0x7F; + fwbuf[1] = (crc) & 0xFF; + fwbuf[2] = (crc >> 8) & 0xFF; + fwbuf[3] = (crc >> 16) & 0xFF; + fwbuf[4] = (crc >> 24) & 0xFF; + CTP_SPI_WRITE(ts->client, fwbuf, 5); + + return; +} + +/******************************************************* +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_set_bld_hw_crc(void) +{ + /* [0] ILM */ + /* write register bank */ + nvt_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, bin_map[0].SRAM_addr, + ts->mmap->ILM_LENGTH_ADDR, bin_map[0].size, + ts->mmap->G_ILM_CHECKSUM_ADDR, bin_map[0].crc); + + /* [1] DLM */ + /* write register bank */ + nvt_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, bin_map[1].SRAM_addr, + ts->mmap->DLM_LENGTH_ADDR, bin_map[1].size, + ts->mmap->G_DLM_CHECKSUM_ADDR, 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_read_bld_hw_crc(void) +{ + uint8_t buf[8] = {0}; + uint32_t g_crc = 0, r_crc = 0; + + /* CRC Flag */ + nvt_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR); + buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F; + buf[1] = 0x00; + CTP_SPI_READ(ts->client, 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_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; + CTP_SPI_READ(ts->client, buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_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; + CTP_SPI_READ(ts->client, 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", + bin_map[0].crc, g_crc, r_crc); + + /* DLM CRC */ + nvt_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; + CTP_SPI_READ(ts->client, buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_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; + CTP_SPI_READ(ts->client, 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", + bin_map[1].crc, g_crc, r_crc); + + return; +} + +#if NVT_TOUCH_ESD_DISP_RECOVERY +static int32_t nvt_check_crc_done_ilm_err(void) +{ + uint8_t buf[8] = {0}; + + nvt_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR); + buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F; + buf[1] = 0x00; + CTP_SPI_READ(ts->client, buf, 2); + + NVT_ERR("CRC DONE, ILM DLM FLAG = 0x%02X\n", buf[1]); + if (((buf[1] & ILM_CRC_FLAG) && (buf[1] & CRC_DONE)) || + (buf[1] == 0xFE) || + ((buf[1] & CRC_DONE) == 0x00)) { + return 1; + } else { + return 0; + } +} + +static int32_t nvt_f2c_read_write(uint8_t F2C_RW, uint32_t DDIC_REG_ADDR, uint16_t len, uint8_t *data) +{ + uint8_t buf[8] = {0}; + uint8_t retry = 0; + uint8_t f2c_control = 0; + uint32_t f2c_retry = 0; + uint32_t retry_max = 5; + int32_t ret = 0; + + nvt_sw_reset_idle(); + + //Setp1: Set REG CPU_IF_ADDR[15:0] + nvt_set_page(CPU_IF_ADDR); + buf[0] = CPU_IF_ADDR & 0x7F; + buf[1] = (DDIC_REG_ADDR) & 0xFF; + buf[2] = (DDIC_REG_ADDR >> 8) & 0xFF; + CTP_SPI_WRITE(ts->client, buf, 3); + + + //Step2: Set REG FFM_ADDR[15:0] + nvt_set_page(FFM_ADDR); + buf[0] = FFM_ADDR & 0x7F; + buf[1] = (TOUCH_DATA_ADDR) & 0xFF; + buf[2] = (TOUCH_DATA_ADDR >> 8) & 0xFF; + buf[3] = 0x00; + if (ts->hw_crc > 1) { + CTP_SPI_WRITE(ts->client, buf, 4); + } else { + CTP_SPI_WRITE(ts->client, buf, 3); + } + + //Step3: Write Data to TOUCH_DATA_ADDR + nvt_write_addr(TOUCH_DATA_ADDR, *data); + + //Step4: Set REG F2C_LENGT[H7:0] + nvt_write_addr(F2C_LENGTH, len); + + //Enable CP_TP_CPU_REQ + nvt_write_addr(CP_TP_CPU_REQ, 0x01); + +nvt_f2c_retry: + //Step5: Set REG CPU_Polling_En, F2C_RW, CPU_IF_ADDR_INC, F2C_EN + nvt_set_page(FFM2CPU_CTL); + buf[0] = FFM2CPU_CTL & 0x7F; + buf[1] = 0xFF; + ret = CTP_SPI_READ(ts->client, buf, 1 + len);//1 is AddrL + if (ret) { + NVT_ERR("Read FFM2CPU control failed!\n"); + return ret; + } + + f2c_control = buf[1] | + (0x01 << BIT_F2C_EN) | + (0x01 << BIT_CPU_IF_ADDR_INC) | + (0x01 << BIT_CPU_POLLING_EN); + + if (F2C_RW == F2C_RW_READ) { + f2c_control = f2c_control & (~(1 << BIT_F2C_RW)); + } else if (F2C_RW == F2C_RW_WRITE) { + f2c_control = f2c_control | (1 << BIT_F2C_RW); + } + + nvt_write_addr(FFM2CPU_CTL, f2c_control); + + //Step6: wait F2C_EN = 0 + retry = 0; + while (1) { + nvt_set_page(FFM2CPU_CTL); + buf[0] = FFM2CPU_CTL & 0x7F; + buf[1] = 0xFF; + buf[2] = 0xFF; + ret = CTP_SPI_READ(ts->client, buf, 3); + if (ret) { + NVT_ERR("Read FFM2CPU control failed!\n"); + return ret; + } + + if ((buf[1] & 0x01) == 0x00) + break; + + usleep_range(1000, 1000); + retry++; + + if (unlikely(retry > 40)) { + NVT_ERR("Wait F2C_EN = 0 failed! retry = %d\n", retry); + return -EIO; + } + } + + //Step7: Check REG TH_CPU_CHK status (1: Success, 0: Fail), if 0, can Retry Step5. + if (((buf[2] & 0x04) >> 2) != 0x01) { + f2c_retry++; + if (f2c_retry <= retry_max) { + goto nvt_f2c_retry; + } else { + NVT_ERR("check TH_CPU_CHK failed!, buf[1]=0x%02X, buf[2]=0x%02X, f2c_retry = %d\n", + buf[1], buf[2], f2c_retry); + return -EIO; + } + } + + if (F2C_RW == F2C_RW_READ) { + nvt_set_page(TOUCH_DATA_ADDR); + buf[0] = TOUCH_DATA_ADDR & 0x7F; + buf[1] = 0xFF; + ret = CTP_SPI_READ(ts->client, buf, 1 + len);//1 is AddrL + if (ret) { + NVT_ERR("Read data failed!\n"); + return ret; + } + *data = buf[1]; + } + + return ret; +} + +static int32_t nvt_f2c_disp_off(void) +{ + uint8_t data = 0x00; + + return nvt_f2c_read_write(F2C_RW_WRITE, DISP_OFF_ADDR, 1, &data); +} +#endif /* NVT_TOUCH_ESD_DISP_RECOVERY */ + +/******************************************************* +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_download_firmware_hw_crc(void) +{ + uint8_t retry = 0; + int32_t ret = 0; + + do_gettimeofday(&start); + + while (1) { + /* bootloader reset to reset MCU */ + nvt_bootloader_reset(); + + /* Start to write firmware process */ + ret = nvt_write_firmware(fw_entry->data, 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 ilm & dlm reg bank */ + nvt_set_bld_hw_crc(); + + /* enable hw bld crc function */ + nvt_bld_crc_enable(); + + /* clear fw reset status & enable fw crc check */ + nvt_fw_crc_enable(); + + /* Set Boot Ready Bit */ + nvt_boot_ready(); + + ret = nvt_check_fw_reset_state(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_read_bld_hw_crc(); +#if NVT_TOUCH_ESD_DISP_RECOVERY + if (nvt_check_crc_done_ilm_err() || nvt_check_crc_done_ilm_err() || nvt_check_crc_done_ilm_err()) { + NVT_ERR("set display off to trigger display esd recovery.\n"); + nvt_f2c_disp_off(); + } +#endif /* #if NVT_TOUCH_ESD_DISP_RECOVERY */ + break; + } + } + + do_gettimeofday(&end); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen Download_Firmware function. It's +complete download firmware flow. + +return: + n.a. +*******************************************************/ +static int32_t nvt_download_firmware(void) +{ + uint8_t retry = 0; + int32_t ret = 0; + + do_gettimeofday(&start); + + while (1) { + /* + * Send eng reset cmd before download FW + * Keep TP_RESX low when send eng reset cmd + */ +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 0); + mdelay(1); //wait 1ms +#endif + nvt_eng_reset(); +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); + mdelay(10); //wait tRT2BRST after TP_RST +#endif + nvt_bootloader_reset(); + + /* clear fw reset status */ + nvt_write_addr(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE, 0x00); + + /* Start to write firmware process */ + ret = nvt_write_firmware(fw_entry->data, 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_boot_ready(); + + ret = nvt_check_fw_reset_state(RESET_STATE_INIT); + if (ret) { + NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); + goto fail; + } + + /* check fw checksum result */ + ret = nvt_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; + } + } + + do_gettimeofday(&end); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen update firmware main function. + +return: + n.a. +*******************************************************/ +int32_t nvt_update_firmware(char *firmware_name) +{ + int32_t ret = 0; + + // request bin file in "/vendor/firmware" + ret = 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_download_init(); + if (ret) { + NVT_ERR("Download Init failed. (%d)\n", ret); + goto download_fail; + } + + /* download firmware process */ + if (ts->hw_crc) + ret = nvt_download_firmware_hw_crc(); + else + ret = nvt_download_firmware(); + if (ret) { + NVT_ERR("Download Firmware failed. (%d)\n", ret); + goto download_fail; + } + + NVT_LOG("Update firmware success! <%ld us>\n", + (end.tv_sec - start.tv_sec)*1000000L + (end.tv_usec - start.tv_usec)); + + /* Get FW Info */ + ret = nvt_get_fw_info(); + if (ret) { + NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret); + } + +download_fail: + if (!IS_ERR_OR_NULL(bin_map)) { + kfree(bin_map); + bin_map = NULL; + } + + update_firmware_release(); +request_firmware_fail: + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen update firmware when booting + function. + +return: + n.a. +*******************************************************/ +void Boot_Update_Firmware(struct work_struct *work) +{ + mutex_lock(&ts->lock); + nvt_update_firmware(ts->boot_update_firmware_name); + mutex_unlock(&ts->lock); +//2019.12.06 longcheer taocheng add for charger mode +#if NVT_USB_PLUGIN + if (!IS_ERR_OR_NULL(g_touchscreen_usb_pulgin.event_callback)) + g_touchscreen_usb_pulgin.valid = true; + if (g_touchscreen_usb_pulgin.valid && g_touchscreen_usb_pulgin.usb_plugged_in) + g_touchscreen_usb_pulgin.event_callback(); +#endif +} +#endif /* BOOT_UPDATE_FIRMWARE */ diff --git a/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mem_map.h b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mem_map.h new file mode 100644 index 000000000000..69c61cf21454 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mem_map.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 43560 $ + * $Date: 2019-04-19 11:34:19 +0800 (週五, 19 四月 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. + * + */ + +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; + /* Phase 2 Host Download */ + uint32_t BOOT_RDY_ADDR; + uint32_t POR_CD_ADDR; + /* 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_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, + /* 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_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, + /* 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_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, + /* 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_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, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +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, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +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 = {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} +}; diff --git a/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.c b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.c new file mode 100644 index 000000000000..4ec27e1256bf --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1927 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 41918 $ + * $Date: 2019-03-12 15:17:45 +0800 (週二, 12 三月 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/vendor/touchpad/NVT_ShortTest.csv" +#define OPEN_TEST_CSV_FILE "/data/vendor/touchpad/NVT_OpenTest.csv" +#define FW_RAWDATA_CSV_FILE "/data/vendor/touchpad/NVT_FWMutualTest.csv" +#define FW_CC_CSV_FILE "/data/vendor/touchpad/NVT_FWCCTest.csv" +#define NOISE_TEST_CSV_FILE "/data/vendor/touchpad/NVT_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; +static uint8_t *RecordResult_Open; +static uint8_t *RecordResult_FWMutual; +static uint8_t *RecordResult_FW_CC; +static uint8_t *RecordResult_FW_DiffMax; +static uint8_t *RecordResult_FW_DiffMin; + +static int32_t TestResult_Short; +static int32_t TestResult_Open; +static int32_t TestResult_FW_Rawdata; +static int32_t TestResult_FWMutual; +static int32_t TestResult_FW_CC; +static int32_t TestResult_Noise; +static int32_t TestResult_FW_DiffMax; +static int32_t TestResult_FW_DiffMin; + +static int32_t *RawData_Short; +static int32_t *RawData_Open; +static int32_t *RawData_Diff; +static int32_t *RawData_Diff_Min; +static int32_t *RawData_Diff_Max; +static int32_t *RawData_FWMutual; +static int32_t *RawData_FW_CC; + +static struct proc_dir_entry *NVT_proc_selftest_entry; +static int8_t nvt_mp_test_result_printed; +static uint8_t fw_ver; + +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 = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short) { + NVT_ERR("kzalloc for RecordResult_Short failed!\n"); + return -ENOMEM; + } + + RecordResult_Open = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Open) { + NVT_ERR("kzalloc for RecordResult_Open failed!\n"); + return -ENOMEM; + } + + RecordResult_FWMutual = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FWMutual) { + NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC) { + NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMax = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMax) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMin = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMin) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n"); + return -ENOMEM; + } + + RawData_Short = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short) { + NVT_ERR("kzalloc for RawData_Short failed!\n"); + return -ENOMEM; + } + + RawData_Open = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Open) { + NVT_ERR("kzalloc for RawData_Open failed!\n"); + return -ENOMEM; + } + + RawData_Diff = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff) { + NVT_ERR("kzalloc for RawData_Diff failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Min = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Min) { + NVT_ERR("kzalloc for RawData_Diff_Min failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Max = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Max) { + NVT_ERR("kzalloc for RawData_Diff_Max failed!\n"); + return -ENOMEM; + } + + RawData_FWMutual = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FWMutual) { + NVT_ERR("kzalloc for RawData_FWMutual failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC = (int32_t *)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; + + tmp_log = (char *)kzalloc(data_num * 7 + 1, 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, PAGE_SIZE, "%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; + + tmp_log = (char *)kzalloc(result_num * 6 + 1, 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, PAGE_SIZE, "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_save_rawdata_to_csv(int32_t *rawdata, uint8_t x_ch, uint8_t y_ch, const char *file_path, uint32_t offset) +{ + int32_t x = 0; + int32_t y = 0; + int32_t iArrayIndex = 0; + struct file *fp = NULL; + char *fbufp = NULL; + mm_segment_t org_fs; + int32_t write_ret = 0; + uint32_t output_len = 0; + loff_t pos = 0; + struct filename *vts_name; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; + int32_t keydata_output_offset = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:++\n", __func__); + fbufp = (char *)kzalloc(8192, GFP_KERNEL); + if (!fbufp) { + NVT_ERR("kzalloc for fbufp failed!\n"); + return -ENOMEM; + } + + for (y = 0; y < y_ch; y++) { + for (x = 0; x < x_ch; x++) { + iArrayIndex = y * x_ch + x; + snprintf(fbufp + iArrayIndex * 7 + y * 2, PAGE_SIZE, "%5d, ", rawdata[iArrayIndex]); + } + nvt_print_data_log_in_one_line(rawdata + y * x_ch, x_ch); + printk("\n"); + snprintf(fbufp + (iArrayIndex + 1) * 7 + y * 2, PAGE_SIZE, "\r\n"); + } +#if TOUCH_KEY_NUM > 0 + keydata_output_offset = y_ch * x_ch * 7 + y_ch * 2; + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + snprintf(fbufp + keydata_output_offset + k * 7, PAGE_SIZE, "%5d, ", rawdata[iArrayIndex]); + } + nvt_print_data_log_in_one_line(rawdata + y_ch * x_ch, Key_Channel); + printk("\n"); + snprintf(fbufp + y_ch * x_ch * 7 + y_ch * 2 + Key_Channel * 7, PAGE_SIZE, "\r\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + org_fs = get_fs(); + set_fs(KERNEL_DS); + vts_name = getname_kernel(file_path); + fp = file_open_name(vts_name, O_RDWR | O_CREAT, 0644); + //fp = filp_open(file_path, O_RDWR | O_CREAT, 0644); + if (fp == NULL || IS_ERR(fp)) { + NVT_ERR("open %s failed\n", file_path); + set_fs(org_fs); + //if (fbufp) { + kfree(fbufp); + fbufp = NULL; + //} + return -EPERM; + } + +#if TOUCH_KEY_NUM > 0 + output_len = y_ch * x_ch * 7 + y_ch * 2 + Key_Channel * 7 + 2; +#else + output_len = y_ch * x_ch * 7 + y_ch * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + pos = offset; + write_ret = vfs_write(fp, (char __user *)fbufp, output_len, &pos); + if (write_ret <= 0) { + NVT_ERR("write %s failed\n", file_path); + set_fs(org_fs); + if (fp) { + filp_close(fp, NULL); + fp = NULL; + } + //if (fbufp) { + kfree(fbufp); + fbufp = NULL; + //} + return -EPERM; + } + + set_fs(org_fs); + if (fp) { + filp_close(fp, NULL); + fp = NULL; + } + //if (fbufp) { + kfree(fbufp); + fbufp = NULL; + //} + + printk("%s:--\n", __func__); + + return 0; +} + +static int32_t nvt_polling_hand_shake_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 250; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(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_SPI_READ(ts->client, 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(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_SPI_READ(ts->client, 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 -EPERM; + } 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(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---switch FreqHopEnDis--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = FreqHopEnDis; + CTP_SPI_WRITE(ts->client, buf, 2); + + msleep(35); + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, 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 */ + + printk("%s:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_RAWDATA_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 */ + + printk("%s:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_CC_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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(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_SPI_WRITE(ts->client, 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; + uint32_t rawdata_diff_min_offset = 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); + + printk("%s:RawData_Diff_Max:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Diff_Max, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + +#if TOUCH_KEY_NUM > 0 + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_Diff_Min:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Diff_Min, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, rawdata_diff_min_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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(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_SPI_WRITE(ts->client, buf, 5); +} + +static void nvt_enable_short_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(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_SPI_WRITE(ts->client, 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 = (uint8_t *)kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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(raw_pipe_addr + y * IC_X_CFG_SIZE * 2); + + buf[0] = (uint8_t)((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) & 0xFF); + CTP_SPI_READ(ts->client, 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(raw_btn_pipe_addr); + + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_SPI_READ(ts->client, 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); + + + printk("%s:RawData_Open\n", __func__); + // Save RawData to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, OPEN_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 = (uint8_t *)kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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(raw_pipe_addr + y * X_Channel * 2); + + buf[0] = (uint8_t)((raw_pipe_addr + y * X_Channel * 2) & 0xFF); + CTP_SPI_READ(ts->client, 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(raw_btn_pipe_addr); + + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_SPI_READ(ts->client, 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); + + printk("%s:RawData_Short\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, SHORT_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 == false) { + return -EPERM; // 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"); + if ((TestResult_Short == 0) || (TestResult_Short == 1)) { + print_selftest_result(m, TestResult_Short, RecordResult_Short, + RawData_Short, X_Channel, Y_Channel); + } else { // TestResult_Short is -1 + 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 print self-test result on log function. + +return: + n.a. +*******************************************************/ +static void print_selftest_result_on_log(int32_t TestResult, + uint8_t RecordResult[], int32_t rawdata[], uint8_t x_len, uint8_t y_len) +{ + int32_t i = 0; + + //--- Print Record Result --- + for (i = 0; i < y_len; i++) { + nvt_print_result_log_in_one_line(RecordResult + i * x_len, x_len); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_result_log_in_one_line(RecordResult + y_len * x_len, Key_Channel); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + //--- Print RawData --- + for (i = 0; i < y_len; i++) { + nvt_print_data_log_in_one_line(rawdata + i * x_len, x_len); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(rawdata + y_len * x_len, Key_Channel); +#endif /* #if TOUCH_KEY_NUM > 0 */ +} + + +/******************************************************* +Description: + Novatek touchscreen /proc/tp_selftest callback function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int lct_nvt_tp_selftest_callback(unsigned char cmd) +{ + struct device_node *np = ts->client->dev.of_node; + unsigned char mpcriteria[32] = {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"); + + switch (cmd) { + case TP_SELFTEST_CMD_LONGCHEER_MMI: + NVT_LOG("touch selftest command: mmi\n"); + break; + case TP_SELFTEST_CMD_XIAOMI_I2C: + NVT_LOG("touch selftest command: i2c\n"); + NVT_LOG("--\n"); + return 2;//PASS + case TP_SELFTEST_CMD_XIAOMI_OPEN: + NVT_LOG("touch selftest command: open\n"); + break; + case TP_SELFTEST_CMD_XIAOMI_SHORT: + NVT_LOG("touch selftest command: short\n"); + break; + default: + NVT_LOG("touch selftest command: unknown\n"); + NVT_LOG("--\n"); + return 0;//Unknown + } + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + //---Download MP FW--- + nvt_update_firmware(ts->mp_update_firmware_name); + + 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)) { + //---Download Normal FW--- + nvt_update_firmware(ts->boot_update_firmware_name); + 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); + } + + //---Download Normal FW--- + nvt_update_firmware(ts->boot_update_firmware_name); + + mutex_unlock(&ts->lock); + + //Short Test Result + print_selftest_result_on_log(TestResult_Short, + RecordResult_Short, RawData_Short, X_Channel, Y_Channel); + + //Open Test Result + print_selftest_result_on_log(TestResult_Open, + RecordResult_Open, RawData_Open, X_Channel, Y_Channel); + + //FW Rawdata Test Result + if ((TestResult_FW_Rawdata == 0) || (TestResult_FW_Rawdata == 1)) { + print_selftest_result_on_log(TestResult_FWMutual, + RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel); + } else { // TestResult_FW_Rawdata is -1 + if (TestResult_FWMutual == -1) { //FW Mutual + print_selftest_result_on_log(TestResult_FWMutual, + RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel); + } + if (TestResult_FW_CC == -1) { //FW CC + print_selftest_result_on_log(TestResult_FW_CC, + RecordResult_FW_CC, RawData_FW_CC, X_Channel, Y_Channel); + } + } + + //Noise Test Result + if ((TestResult_Noise == 0) || (TestResult_Noise == 1)) { + print_selftest_result_on_log(TestResult_FW_DiffMax, + RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel); + } else { // TestResult_Noise is -1 + if (TestResult_FW_DiffMax == -1) { //FW Diff Max + print_selftest_result_on_log(TestResult_FW_DiffMax, + RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel); + } + if (TestResult_FW_DiffMin == -1) { //FW Diff Min + print_selftest_result_on_log(TestResult_FW_DiffMin, + RecordResult_FW_DiffMin, RawData_Diff_Min, X_Channel, Y_Channel); + } + } + + NVT_LOG("TestResult_Short=%d, TestResult_Open=%d, TestResult_FW_Rawdata=%d, TestResult_Noise=%d\n", + TestResult_Short, TestResult_Open, TestResult_FW_Rawdata, TestResult_Noise); + NVT_LOG("--\n"); + if (!TestResult_Short && !TestResult_Open && !TestResult_FW_Rawdata && !TestResult_Noise) + return 2;//PASS + else + return 1;//FAIL +} + +/******************************************************* +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[32] = {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 */ + + //---Download MP FW--- + nvt_update_firmware(ts->mp_update_firmware_name); + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + NVT_ERR("get fw info failed!\n"); + return -EAGAIN; + } + + /* 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)) { + //---Download Normal FW--- + nvt_update_firmware(ts->boot_update_firmware_name); + 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); + } + + //---Download Normal FW--- + nvt_update_firmware(ts->boot_update_firmware_name); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + nvt_mp_test_result_printed = 0; + + return seq_open(file, &nvt_selftest_seq_ops); +} + +static const struct file_operations nvt_selftest_fops = { + .owner = THIS_MODULE, + .open = nvt_selftest_open, + .read = seq_read, + .llseek = seq_lseek, + .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 -EPERM; + } 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 -EPERM; + } + + 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 -EPERM; + } 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 -EPERM; + } 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 -EPERM; + } + +#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 -EPERM; + } + + /* MP Config*/ + if (nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE)) + return -EPERM; + + if (nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE)) + return -EPERM; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE)) + return -EPERM; +#endif + + if (nvt_mp_parse_u32(np, "X_Channel", &X_Channel)) + return -EPERM; + + if (nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel)) + return -EPERM; + + if (nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE)) + return -EPERM; + + if (nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE)) + return -EPERM; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE)) + return -EPERM; +#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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + if (nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame)) + return -EPERM; + + 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) +{ + //longcheer touch selftest + lct_tp_selftest_init(lct_nvt_tp_selftest_callback); + + //novatek touch selftest + 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 -EPERM; + } else { + if (nvt_mp_buffer_init()) { + NVT_ERR("Allocate mp memory failed\n"); + return -EPERM; + } 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/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.h b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.h new file mode 100644 index 000000000000..aa0ecffb4c41 --- /dev/null +++ b/drivers/input/touchscreen/nt36xxx_spi/nt36xxx_mp_ctrlram.h @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * Copyright (C) 2020 XiaoMi, Inc. + * $Revision: 43145 $ + * $Date: 2019-04-10 17:46:18 +0800 (週三, 10 四月 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 = 16; +static uint32_t IC_Y_CFG_SIZE = 36; +static uint32_t IC_KEY_CFG_SIZE = 4; +static uint32_t X_Channel = 16; +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 }; +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, +#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, +#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] = { + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, + 4096, 4096, 4096, 4096, +#if TOUCH_KEY_NUM > 0 + 4096, 4096, 4096, +#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, +#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] = { + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, + 1920, 1920, 1920, 1920, +#if TOUCH_KEY_NUM > 0 + 1920, 1920, 1920, +#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, +#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, +#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] = { + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, +#if TOUCH_KEY_NUM > 0 + 30, 30, 30, +#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, +#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, +#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 */