From 9f8659ac9660b2bc1d708afe79ee7b30ae3ccdfc Mon Sep 17 00:00:00 2001 From: haojl2 Date: Fri, 26 Jul 2019 11:12:26 +0800 Subject: [PATCH] nova_0flash_mmi: Novatek TP 0flash driver. TP: Initial release for Moto Defender based on SVN V4.18. Change-Id: I66825b8962c6f4a187fa4eea4364015124118b40 Signed-off-by: haojl2 Reviewed-on: https://gerrit.mot.com/1412019 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hongshu Lou Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- .../touchscreen/nova_0flash_mmi/Android.mk | 14 + .../input/touchscreen/nova_0flash_mmi/Kbuild | 13 + .../touchscreen/nova_0flash_mmi/Makefile | 10 + .../touchscreen/nova_0flash_mmi/nt36xxx.c | 2193 +++++++++++++++++ .../touchscreen/nova_0flash_mmi/nt36xxx.h | 204 ++ .../nova_0flash_mmi/nt36xxx_ext_proc.c | 594 +++++ .../nova_0flash_mmi/nt36xxx_fw_update.c | 940 +++++++ .../nova_0flash_mmi/nt36xxx_mem_map.h | 257 ++ .../nova_0flash_mmi/nt36xxx_mp_ctrlram.c | 1565 ++++++++++++ .../nova_0flash_mmi/nt36xxx_mp_ctrlram.h | 456 ++++ 10 files changed, 6246 insertions(+) create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/Android.mk create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/Kbuild create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/Makefile create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.c create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.h create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_ext_proc.c create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.c create mode 100644 drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h diff --git a/drivers/input/touchscreen/nova_0flash_mmi/Android.mk b/drivers/input/touchscreen/nova_0flash_mmi/Android.mk new file mode 100644 index 000000000000..3d455f4ecdf0 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +NOVA_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +NOVA_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := nova_0flash_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(NOVA_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/nova_0flash_mmi/Kbuild b/drivers/input/touchscreen/nova_0flash_mmi/Kbuild new file mode 100644 index 000000000000..f054a1d17202 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/nova_0flash_mmi + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_NT36xxx_HOSTDL_SPI)),) + obj-m := nova_0flash_mmi.o +endif + +nova_0flash_mmi-objs += nt36xxx.o +nova_0flash_mmi-objs += nt36xxx_ext_proc.o +nova_0flash_mmi-objs += nt36xxx_fw_update.o +nova_0flash_mmi-objs += nt36xxx_mp_ctrlram.o diff --git a/drivers/input/touchscreen/nova_0flash_mmi/Makefile b/drivers/input/touchscreen/nova_0flash_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.c new file mode 100644 index 000000000000..7f5e7d7a4d3f --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.c @@ -0,0 +1,2193 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47376 $ + * $Date: 2019-07-12 09:06:29 +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 +#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" +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_ESD_PROTECT +static struct delayed_work nvt_esd_check_work; +static struct workqueue_struct *nvt_esd_check_wq; +static unsigned long irq_timer = 0; +uint8_t esd_check = false; +uint8_t esd_retry = 0; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_EXT_PROC +extern int32_t nvt_extra_proc_init(void); +extern void nvt_extra_proc_deinit(void); +#endif + +#if NVT_TOUCH_MP +extern int32_t nvt_mp_proc_init(void); +extern void nvt_mp_proc_deinit(void); +#endif + +struct nvt_ts_data *ts; + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#if defined(CONFIG_FB) +#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 + +uint32_t ENG_RST_ADDR = 0x7FFF80; +uint32_t SWRST_N8_ADDR = 0; //read from dtsi +uint32_t SPI_RD_FAST_ADDR = 0; //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_POWER, //GESTURE_WORD_C + KEY_POWER, //GESTURE_WORD_W + KEY_POWER, //GESTURE_WORD_V + KEY_POWER, //GESTURE_DOUBLE_CLICK + KEY_POWER, //GESTURE_WORD_Z + KEY_POWER, //GESTURE_WORD_M + KEY_POWER, //GESTURE_WORD_O + KEY_POWER, //GESTURE_WORD_e + KEY_POWER, //GESTURE_WORD_S + KEY_POWER, //GESTURE_SLIDE_UP + KEY_POWER, //GESTURE_SLIDE_DOWN + KEY_POWER, //GESTURE_SLIDE_LEFT + KEY_POWER, //GESTURE_SLIDE_RIGHT +}; +#endif + +#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 = 0; + +/******************************************************* +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 + DUMMY_BYTES); + + 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 -1; + } else { + return 0; + } +} + +/******************************************************* +Description: + Novatek touchscreen check FW status function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_check_fw_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 50; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(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 -1; + } else { + return 0; + } +} + +/******************************************************* +Description: + Novatek touchscreen check FW reset state function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + 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}; + 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 type is 0x%02X\n", buf[14]); + + //---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 +} + +static uint8_t nvt_fw_recovery(uint8_t *point_data) +{ + uint8_t i = 0; + uint8_t detected = true; + + /* check pattern */ + for (i=1 ; i<7 ; i++) { + if (point_data[i] != 0x77) { + detected = false; + break; + } + } + + return detected; +} + +#if NVT_TOUCH_ESD_PROTECT +void nvt_esd_check_enable(uint8_t enable) +{ + /* update interrupt timer */ + irq_timer = jiffies; + /* clear esd_retry counter, if protect function is enabled */ + esd_retry = enable ? 0 : esd_retry; + /* enable/disable esd check flag */ + esd_check = enable; +} + +static void nvt_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - irq_timer); + + //NVT_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(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 = 0; +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 POINT_DATA_CHECKSUM +static int32_t nvt_ts_point_data_checksum(uint8_t *buf, uint8_t length) +{ + uint8_t checksum = 0; + int32_t i = 0; + + // Generate checksum + for (i = 0; i < length - 1; i++) { + checksum += buf[i + 1]; + } + checksum = (~checksum + 1); + + // Compare ckecksum and dump fail data + if (checksum != buf[length]) { + NVT_ERR("i2c/spi packet checksum not match. (point_data[%d]=0x%02X, checksum=0x%02X)\n", + length, buf[length], checksum); + + for (i = 0; i < 10; i++) { + NVT_LOG("%02X %02X %02X %02X %02X %02X\n", + buf[1 + i*6], buf[2 + i*6], buf[3 + i*6], buf[4 + i*6], buf[5 + i*6], buf[6 + i*6]); + } + + NVT_LOG("%02X %02X %02X %02X %02X\n", buf[61], buf[62], buf[63], buf[64], buf[65]); + + return -1; + } + + return 0; +} +#endif /* POINT_DATA_CHECKSUM */ + +#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 + if (bTouchIsAwake == 0) { + pm_wakeup_event(&ts->input_dev->dev, 5000); + } +#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(BOOT_UPDATE_FIRMWARE_NAME); + goto XFER_ERROR; + } +#endif /* #if NVT_TOUCH_WDT_RECOVERY */ + + /* ESD protect by FW handshake */ + if (nvt_fw_recovery(point_data)) { +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(true); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + goto XFER_ERROR; + } + +#if POINT_DATA_CHECKSUM + if (POINT_DATA_LEN >= POINT_DATA_CHECKSUM_LEN) { + ret = nvt_ts_point_data_checksum(point_data, POINT_DATA_CHECKSUM_LEN); + if (ret) { + goto XFER_ERROR; + } + } +#endif /* POINT_DATA_CHECKSUM */ + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_ts_wakeup_gesture_report(input_id, point_data); + mutex_unlock(&ts->lock); + return IRQ_HANDLED; + } +#endif + + finger_cnt = 0; + + for (i = 0; i < ts->max_touch_num; i++) { + position = 1 + 6 * i; + input_id = (uint8_t)(point_data[position + 0] >> 3); + if ((input_id == 0) || (input_id > ts->max_touch_num)) + continue; + + if (((point_data[position] & 0x07) == 0x01) || ((point_data[position] & 0x07) == 0x02)) { //finger down (enter & moving) +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + input_x = (uint32_t)(point_data[position + 1] << 4) + (uint32_t) (point_data[position + 3] >> 4); + input_y = (uint32_t)(point_data[position + 2] << 4) + (uint32_t) (point_data[position + 3] & 0x0F); + if ((input_x < 0) || (input_y < 0)) + continue; + if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) + continue; + input_w = (uint32_t)(point_data[position + 4]); + if (input_w == 0) + input_w = 1; + if (i < 2) { + input_p = (uint32_t)(point_data[position + 5]) + (uint32_t)(point_data[i + 63] << 8); + if (input_p > TOUCH_FORCE_NUM) + input_p = TOUCH_FORCE_NUM; + } else { + input_p = (uint32_t)(point_data[position + 5]); + } + if (input_p == 0) + input_p = 1; + +#if MT_PROTOCOL_B + press_id[input_id - 1] = 1; + input_mt_slot(ts->input_dev, input_id - 1); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); +#else /* MT_PROTOCOL_B */ + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif /* MT_PROTOCOL_B */ + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); + +#if MT_PROTOCOL_B +#else /* MT_PROTOCOL_B */ + input_mt_sync(ts->input_dev); +#endif /* MT_PROTOCOL_B */ + + finger_cnt++; + } + } + +#if MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + if (press_id[i] != 1) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); +#else /* MT_PROTOCOL_B */ + if (finger_cnt == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(ts->input_dev); + } +#endif /* MT_PROTOCOL_B */ + +#if TOUCH_KEY_NUM > 0 + if (point_data[61] == 0xF8) { +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + for (i = 0; i < ts->max_button_num; i++) { + input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01)); + } + } else { + for (i = 0; i < ts->max_button_num; i++) { + input_report_key(ts->input_dev, touch_key_array[i], 0); + } + } +#endif + + input_sync(ts->input_dev); + +XFER_ERROR: + + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + + +/******************************************************* +Description: + Novatek touchscreen check 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 0x1F600--- + 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; +} + +/******************************************************* +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"); + + ts = kmalloc(sizeof(struct nvt_ts_data), GFP_KERNEL); + if (ts == NULL) { + 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(ts->xbuf == NULL) { + NVT_ERR("kzalloc for xbuf failed!\n"); + if (ts) { + kfree(ts); + ts = NULL; + } + return -ENOMEM; + } + + 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; + } + + //---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); + + //---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, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); +#if MT_PROTOCOL_B + // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it +#else + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); +#endif //MT_PROTOCOL_B +#endif //TOUCH_MAX_FINGER_NUM > 1 + +#if TOUCH_KEY_NUM > 0 + for (retry = 0; retry < ts->max_button_num; retry++) { + input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]); + } +#endif + +#if WAKEUP_GESTURE + for (retry = 0; retry < (sizeof(gesture_key_array) / sizeof(gesture_key_array[0])); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } +#endif + + sprintf(ts->phys, "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(14000)); +#endif + + NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT); +#if NVT_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func); + nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1); + if (!nvt_esd_check_wq) { + NVT_ERR("nvt_esd_check_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_esd_check_wq_failed; + } + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + //---set device node--- +#if NVT_TOUCH_PROC + ret = nvt_flash_proc_init(); + if (ret != 0) { + NVT_ERR("nvt flash proc init failed. ret=%d\n", ret); + goto err_flash_proc_init_failed; + } +#endif + +#if NVT_TOUCH_EXT_PROC + ret = nvt_extra_proc_init(); + if (ret != 0) { + NVT_ERR("nvt extra proc init failed. ret=%d\n", ret); + goto err_extra_proc_init_failed; + } +#endif + +#if NVT_TOUCH_MP + ret = nvt_mp_proc_init(); + if (ret != 0) { + NVT_ERR("nvt mp proc init failed. ret=%d\n", ret); + goto err_mp_proc_init_failed; + } +#endif + +#if defined(CONFIG_FB) +#ifdef _MSM_DRM_NOTIFY_H_ + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + ret = msm_drm_register_client(&ts->drm_notif); + if(ret) { + NVT_ERR("register drm_notifier failed. ret=%d\n", ret); + goto err_register_drm_notif_failed; + } +#else + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + if(ret) { + NVT_ERR("register fb_notifier failed. ret=%d\n", ret); + goto err_register_fb_notif_failed; + } +#endif +#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 + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_irq_enable(true); + + return 0; + +#if defined(CONFIG_FB) +#ifdef _MSM_DRM_NOTIFY_H_ + 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 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: +err_spi_setup: +err_ckeck_full_duplex: + spi_set_drvdata(client, 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) +#ifdef _MSM_DRM_NOTIFY_H_ + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + +#if NVT_TOUCH_MP + nvt_mp_proc_deinit(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_deinit(); +#endif +#if NVT_TOUCH_PROC + nvt_flash_proc_deinit(); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +#endif + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +#endif + +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + + nvt_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_gpio_deconfig(ts); + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + spi_set_drvdata(client, 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) +#ifdef _MSM_DRM_NOTIFY_H_ + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + +#if NVT_TOUCH_MP + nvt_mp_proc_deinit(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_deinit(); +#endif +#if NVT_TOUCH_PROC + nvt_flash_proc_deinit(); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +#endif + +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif +} + +/******************************************************* +Description: + Novatek touchscreen driver suspend function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_ts_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; +#if MT_PROTOCOL_B + uint32_t i = 0; +#endif + + if (!bTouchIsAwake) { + NVT_LOG("Touch is already suspend\n"); + return 0; + } + +#if !WAKEUP_GESTURE + nvt_irq_enable(false); +#endif + +#if NVT_TOUCH_ESD_PROTECT + NVT_LOG("cancel delayed work sync\n"); + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + bTouchIsAwake = 0; + +#if WAKEUP_GESTURE + //---write command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + CTP_SPI_WRITE(ts->client, buf, 2); + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else // WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_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"); + 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(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 + nvt_irq_enable(true); +#endif + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + bTouchIsAwake = 1; + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + + +#if defined(CONFIG_FB) +#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 == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_suspend(&ts->client->dev); + } + } else if (event == MSM_DRM_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + } + + return 0; +} +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct nvt_ts_data *ts = + container_of(self, struct nvt_ts_data, fb_notif); + + if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#endif +#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 + +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_OF + .of_match_table = nvt_match_table, +#endif + }, +}; + +/******************************************************* +Description: + Driver Install function. + +return: + Executive Outcomes. 0---succeed. not 0---failed. +********************************************************/ +static int32_t __init nvt_driver_init(void) +{ + int32_t ret = 0; + + NVT_LOG("start\n"); + + //---add 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) +{ + 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/nova_0flash_mmi/nt36xxx.h b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.h new file mode 100644 index 000000000000..93a4058e5ef4 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 46000 $ + * $Date: 2019-06-12 14:25:52 +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. + * + */ +#ifndef _LINUX_NVT_TOUCH_H +#define _LINUX_NVT_TOUCH_H + +#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 + +#define NVT_DEBUG 1 + +//---GPIO number--- +#define NVTTOUCH_RST_PIN 980 +#define NVTTOUCH_INT_PIN 943 + + +//---INT trigger mode--- +//#define IRQ_TYPE_EDGE_RISING 1 +//#define IRQ_TYPE_EDGE_FALLING 2 +#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + + +//---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 2340 +#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 0 + +//---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 BOOT_UPDATE_FIRMWARE 1 +#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin" +#define MP_UPDATE_FIRMWARE_NAME "novatek_ts_mp.bin" +#define POINT_DATA_CHECKSUM 1 +#define POINT_DATA_CHECKSUM_LEN 65 + +//---ESD Protect.--- +#define NVT_TOUCH_ESD_PROTECT 0 +#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ +#define NVT_TOUCH_WDT_RECOVERY 1 + +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) +#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 + 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; +#ifdef CONFIG_MTK_SPI + struct mt_chip_conf spi_ctrl; +#endif +#ifdef CONFIG_SPI_MT65XX + struct mtk_chip_config spi_ctrl; +#endif +}; + +#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; + +//---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/nova_0flash_mmi/nt36xxx_ext_proc.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_ext_proc.c new file mode 100644 index 000000000000..f6a3409a3211 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_ext_proc.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47376 $ + * $Date: 2019-07-12 09:06:29 +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 "nt36xxx.h" + +#if NVT_TOUCH_EXT_PROC +#define NVT_FW_VERSION "nvt_fw_version" +#define NVT_BASELINE "nvt_baseline" +#define NVT_RAW "nvt_raw" +#define NVT_DIFF "nvt_diff" + +#define BUS_TRANSFER_LENGTH 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; + +/******************************************************* +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[BUS_TRANSFER_LENGTH + 1] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = ts->x_num * ts->y_num * 2; + residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_set_page(head_addr + XDATA_SECTOR_SIZE * i); + + //---read xdata by BUS_TRANSFER_LENGTH + for (j = 0; j < (XDATA_SECTOR_SIZE / BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = BUS_TRANSFER_LENGTH * j; + CTP_SPI_READ(ts->client, buf, BUS_TRANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < BUS_TRANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + BUS_TRANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_set_page(xdata_addr + data_len - residual_len); + + //---read xdata by BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = BUS_TRANSFER_LENGTH * j; + CTP_SPI_READ(ts->client, buf, BUS_TRANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < BUS_TRANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + BUS_TRANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + xdata[i] = (int16_t)(xdata_tmp[dummy_len + i * 2] + 256 * xdata_tmp[dummy_len + i * 2 + 1]); + } + +#if TOUCH_KEY_NUM > 0 + //read button xdata : step3 + //---change xdata index--- + nvt_set_page(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, +}; + +/******************************************************* +Description: + Novatek touchscreen extra function proc. file node + initial function. + +return: + Executive outcomes. 0---succeed. -12---failed. +*******************************************************/ +int32_t nvt_extra_proc_init(void) +{ + NVT_proc_fw_version_entry = proc_create(NVT_FW_VERSION, 0444, NULL,&nvt_fw_version_fops); + if (NVT_proc_fw_version_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_FW_VERSION); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_FW_VERSION); + } + + NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL,&nvt_baseline_fops); + if (NVT_proc_baseline_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_BASELINE); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_BASELINE); + } + + NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL,&nvt_raw_fops); + if (NVT_proc_raw_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_RAW); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_RAW); + } + + NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL,&nvt_diff_fops); + if (NVT_proc_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_DIFF); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_DIFF); + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen extra function proc. file node + deinitial function. + +return: + n.a. +*******************************************************/ +void nvt_extra_proc_deinit(void) +{ + if (NVT_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_FW_VERSION, NULL); + NVT_proc_fw_version_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_FW_VERSION); + } + + if (NVT_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_BASELINE, NULL); + NVT_proc_baseline_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_BASELINE); + } + + if (NVT_proc_raw_entry != NULL) { + remove_proc_entry(NVT_RAW, NULL); + NVT_proc_raw_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_RAW); + } + + if (NVT_proc_diff_entry != NULL) { + remove_proc_entry(NVT_DIFF, NULL); + NVT_proc_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_DIFF); + } +} +#endif diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c new file mode 100644 index 000000000000..9dc7cd814811 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c @@ -0,0 +1,940 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 43459 $ + * $Date: 2019-04-17 15:28:41 +0800 (週三, 17 四月 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" + +struct timeval start, end; +const struct firmware *fw_entry = NULL; +static size_t fw_need_write_size = 0; +static uint8_t *fwbuf = NULL; + +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 -1; +} + +/******************************************************* +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), 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 = 0; +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) + sprintf(bin_map[list].name, "ILM"); + else if (list == 1) + sprintf(bin_map[list].name, "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)) { + sprintf(bin_map[list].name, "Header"); + } else { + sprintf(bin_map[list].name, "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 + sprintf(bin_map[list].name, "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 = 0; +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; + mm_segment_t org_fs; + + sprintf(file, "%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); + 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 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); + 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 "/etc/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(BOOT_UPDATE_FIRMWARE_NAME); + mutex_unlock(&ts->lock); +} +#endif /* BOOT_UPDATE_FIRMWARE */ diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h new file mode 100644 index 000000000000..2d1a03782ef1 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 43248 $ + * $Date: 2019-04-12 11:06:27 +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. + * + */ + +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 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 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 = {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/nova_0flash_mmi/nt36xxx_mp_ctrlram.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.c new file mode 100644 index 000000000000..13722ededdb6 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1565 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47376 $ + * $Date: 2019-07-12 09:06:29 +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/local/tmp/ShortTest.csv" +#define OPEN_TEST_CSV_FILE "/data/local/tmp/OpenTest.csv" +#define FW_RAWDATA_CSV_FILE "/data/local/tmp/FWMutualTest.csv" +#define FW_CC_CSV_FILE "/data/local/tmp/FWCCTest.csv" +#define NOISE_TEST_CSV_FILE "/data/local/tmp/NoiseTest.csv" + +#define nvt_mp_seq_printf(m, fmt, args...) do { \ + seq_printf(m, fmt, ##args); \ + if (!nvt_mp_test_result_printed) \ + printk(fmt, ##args); \ +} while (0) + +static uint8_t *RecordResult_Short = NULL; +static uint8_t *RecordResult_Open = NULL; +static uint8_t *RecordResult_FWMutual = NULL; +static uint8_t *RecordResult_FW_CC = NULL; +static uint8_t *RecordResult_FW_DiffMax = NULL; +static uint8_t *RecordResult_FW_DiffMin = NULL; + +static int32_t TestResult_Short = 0; +static int32_t TestResult_Open = 0; +static int32_t TestResult_FW_Rawdata = 0; +static int32_t TestResult_FWMutual = 0; +static int32_t TestResult_FW_CC = 0; +static int32_t TestResult_Noise = 0; +static int32_t TestResult_FW_DiffMax = 0; +static int32_t TestResult_FW_DiffMin = 0; + +static int32_t *RawData_Short = NULL; +static int32_t *RawData_Open = NULL; +static int32_t *RawData_Diff = NULL; +static int32_t *RawData_Diff_Min = NULL; +static int32_t *RawData_Diff_Max = NULL; +static int32_t *RawData_FWMutual = NULL; +static int32_t *RawData_FW_CC = NULL; + +static struct proc_dir_entry *NVT_proc_selftest_entry = NULL; +static int8_t nvt_mp_test_result_printed = 0; +static uint8_t fw_ver = 0; + +extern void nvt_change_mode(uint8_t mode); +extern uint8_t nvt_get_fw_pipe(void); +extern void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr); +extern void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num); +int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible); + +/******************************************************* +Description: + Novatek touchscreen allocate buffer for mp selftest. + +return: + Executive outcomes. 0---succeed. -12---Out of memory +*******************************************************/ +static int nvt_mp_buffer_init(void) +{ + size_t RecordResult_BufSize = IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE; + size_t RawData_BufSize = (IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * sizeof(int32_t); + + RecordResult_Short = (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++) { + sprintf(tmp_log + i * 7, "%5d, ", data[i]); + } + tmp_log[data_num * 7] = '\0'; + printk("%s", tmp_log); + if (tmp_log) { + kfree(tmp_log); + tmp_log = NULL; + } + + return; +} + +static void nvt_print_result_log_in_one_line(uint8_t *result, int32_t result_num) +{ + char *tmp_log = NULL; + int32_t i = 0; + + 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++) { + sprintf(tmp_log + i * 6, "0x%02X, ", result[i]); + } + tmp_log[result_num * 6] = '\0'; + printk("%s", tmp_log); + if (tmp_log) { + kfree(tmp_log); + tmp_log = NULL; + } + + return; +} + +/******************************************************* +Description: + Novatek touchscreen self-test criteria print function. + +return: + n.a. +*******************************************************/ +static void nvt_print_lmt_array(int32_t *array, int32_t x_ch, int32_t y_ch) +{ + int32_t j = 0; + + for (j = 0; j < y_ch; j++) { + nvt_print_data_log_in_one_line(array + j * x_ch, x_ch); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(array + y_ch * x_ch, Key_Channel); + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ +} + +static void nvt_print_criteria(void) +{ + NVT_LOG("++\n"); + + //---PS_Config_Lmt_Short_Rawdata--- + printk("PS_Config_Lmt_Short_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_Open_Rawdata--- + printk("PS_Config_Lmt_Open_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Open_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_Rawdata--- + printk("PS_Config_Lmt_FW_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_CC--- + printk("PS_Config_Lmt_FW_CC_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_Diff--- + printk("PS_Config_Lmt_FW_Diff_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_Diff_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_N, X_Channel, Y_Channel); + + NVT_LOG("--\n"); +} + +static int32_t nvt_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; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:++%s\n", __func__, file_path); + + for (y = 0; y < y_ch; y++) { + for (x = 0; x < x_ch; x++) { + iArrayIndex = y * x_ch + x; + } + nvt_print_data_log_in_one_line(rawdata + y * x_ch, x_ch); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + } + nvt_print_data_log_in_one_line(rawdata + y_ch * x_ch, Key_Channel); + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + 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 = 70; + + 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 -1; + } else { + return 0; + } +} + +static int8_t nvt_switch_FreqHopEnDis(uint8_t FreqHopEnDis) +{ + uint8_t buf[8] = {0}; + uint8_t retry = 0; + int8_t ret = 0; + + NVT_LOG("++\n"); + + for (retry = 0; retry < 20; retry++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(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 -1; // FAIL + } else { + return 0; // PASS + } +} + +/******************************************************* +Description: + Novatek touchscreen print self-test result function. + +return: + n.a. +*******************************************************/ +void print_selftest_result(struct seq_file *m, int32_t TestResult, uint8_t RecordResult[], int32_t rawdata[], uint8_t x_len, uint8_t y_len) +{ + int32_t i = 0; + int32_t j = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + switch (TestResult) { + case 0: + nvt_mp_seq_printf(m, " PASS!\n"); + break; + + case 1: + nvt_mp_seq_printf(m, " ERROR! Read Data FAIL!\n"); + break; + + case -1: + nvt_mp_seq_printf(m, " FAIL!\n"); + nvt_mp_seq_printf(m, "RecordResult:\n"); + for (i = 0; i < y_len; i++) { + for (j = 0; j < x_len; j++) { + iArrayIndex = i * x_len + j; + seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_result_log_in_one_line(RecordResult + i * x_len, x_len); + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_result_log_in_one_line(RecordResult + y_len * x_len, Key_Channel); + nvt_mp_seq_printf(m, "\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + nvt_mp_seq_printf(m, "ReadData:\n"); + for (i = 0; i < y_len; i++) { + for (j = 0; j < x_len; j++) { + iArrayIndex = i * x_len + j; + seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_data_log_in_one_line(rawdata + i * x_len, x_len); + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_data_log_in_one_line(rawdata + y_len * x_len, Key_Channel); + nvt_mp_seq_printf(m, "\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + break; + } + nvt_mp_seq_printf(m, "\n"); +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print show + function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_show_selftest(struct seq_file *m, void *v) +{ + NVT_LOG("++\n"); + + nvt_mp_seq_printf(m, "FW Version: %d\n\n", fw_ver); + + nvt_mp_seq_printf(m, "Short Test"); + 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 /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(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(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(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 -1; + } else { + NVT_LOG("%s. len=%d\n", name, len); + ret = of_property_read_u32_array(np, name, tmp, len); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } + + for (i = 0; i < len; i++) + array[i] = tmp[i]; + +#if NVT_DEBUG + printk("[NVT-ts] %s = ", name); + nvt_print_result_log_in_one_line(array, len); + printk("\n"); +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for u32 type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_u32(struct device_node *np, const char *name, int32_t *para) +{ + int32_t ret; + + ret = of_property_read_u32(np, name, para); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } else { +#if NVT_DEBUG + NVT_LOG("%s=%d\n", name, *para); +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for array type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_array(struct device_node *np, const char *name, int32_t *array, + int32_t size) +{ + struct property *data; + int32_t len, ret; +#if NVT_DEBUG + int32_t j = 0; +#endif + + data = of_find_property(np, name, &len); + len /= sizeof(u32); + if ((!data) || (!len) || (len < size)) { + NVT_ERR("error find %s. len=%d\n", name, len); + return -1; + } else { + NVT_LOG("%s. len=%d\n", name, len); + ret = of_property_read_u32_array(np, name, array, len); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } + +#if NVT_DEBUG + NVT_LOG("%s =\n", name); + for (j = 0; j < Y_Channel; j++) { + nvt_print_data_log_in_one_line(array + j * X_Channel, X_Channel); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(array + Y_Channel * X_Channel, Key_Channel); + printk("\n"); +#endif +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse device tree mp function. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible) +{ + struct device_node *np = root; + struct device_node *child = NULL; + + NVT_LOG("Parse mp criteria for node %s\n", node_compatible); + + /* find each MP sub-nodes */ + for_each_child_of_node(root, child) { + /* find the specified node */ + if (of_device_is_compatible(child, node_compatible)) { + NVT_LOG("found child node %s\n", node_compatible); + np = child; + break; + } + } + if (child == NULL) { + NVT_ERR("Not found compatible node %s!\n", node_compatible); + return -1; + } + + /* MP Config*/ + if (nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE)) + return -1; + + if (nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE)) + return -1; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE)) + return -1; +#endif + + if (nvt_mp_parse_u32(np, "X_Channel", &X_Channel)) + return -1; + + if (nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel)) + return -1; + + if (nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE)) + return -1; + + if (nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE)) + return -1; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE)) + return -1; +#endif + + /* MP Criteria */ + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_P", PS_Config_Lmt_Short_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_N", PS_Config_Lmt_Short_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_P", PS_Config_Lmt_Open_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_N", PS_Config_Lmt_Open_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_P", PS_Config_Lmt_FW_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_N", PS_Config_Lmt_FW_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_P", PS_Config_Lmt_FW_CC_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_N", PS_Config_Lmt_FW_CC_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_P", PS_Config_Lmt_FW_Diff_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_N", PS_Config_Lmt_FW_Diff_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame)) + return -1; + + NVT_LOG("Parse mp criteria done!\n"); + + return 0; +} +#endif /* #ifdef CONFIG_OF */ + +/******************************************************* +Description: + Novatek touchscreen MP function proc. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_mp_proc_init(void) +{ + NVT_proc_selftest_entry = proc_create("nvt_selftest", 0444, NULL, &nvt_selftest_fops); + if (NVT_proc_selftest_entry == NULL) { + NVT_ERR("create /proc/nvt_selftest Failed!\n"); + return -1; + } else { + if(nvt_mp_buffer_init()) { + NVT_ERR("Allocate mp memory failed\n"); + return -1; + } + else { + NVT_LOG("create /proc/nvt_selftest Succeeded!\n"); + } + return 0; + } +} + +/******************************************************* +Description: + Novatek touchscreen MP function proc. file node + deinitial function. + +return: + n.a. +*******************************************************/ +void nvt_mp_proc_deinit(void) +{ + nvt_mp_buffer_deinit(); + + if (NVT_proc_selftest_entry != NULL) { + remove_proc_entry("nvt_selftest", NULL); + NVT_proc_selftest_entry = NULL; + NVT_LOG("Removed /proc/%s\n", "nvt_selftest"); + } +} +#endif /* #if NVT_TOUCH_MP */ diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h new file mode 100644 index 000000000000..924a4c4a0fb1 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 46242 $ + * $Date: 2019-06-17 10:29:24 +0800 (週一, 17 六月 2019) $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#if NVT_TOUCH_MP + +static uint32_t IC_X_CFG_SIZE = 18; +static uint32_t IC_Y_CFG_SIZE = 36; +static uint32_t IC_KEY_CFG_SIZE = 4; +static uint32_t X_Channel = 18; +static uint32_t Y_Channel = 36; +static uint32_t Key_Channel = TOUCH_KEY_NUM; +static uint8_t AIN_X[40] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; +static uint8_t AIN_Y[40] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; +#if TOUCH_KEY_NUM > 0 +static uint8_t AIN_KEY[8] = {0, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +#endif /* #if TOUCH_KEY_NUM > 0 */ + +static int32_t PS_Config_Lmt_Short_Rawdata_P[40 * 40] = { + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, +#if TOUCH_KEY_NUM > 0 + 14008,14008,14008, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = { + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, +#if TOUCH_KEY_NUM > 0 + 10000,10000,10000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = { + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, +#if TOUCH_KEY_NUM > 0 + 5120,5120,5120, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = { + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, +#if TOUCH_KEY_NUM > 0 + 50,50,50, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = { + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, +#if TOUCH_KEY_NUM > 0 + 2560,2560,2560, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = { + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +#if TOUCH_KEY_NUM > 0 + 240,240,240, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = { + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, +#if TOUCH_KEY_NUM > 0 + 314,314,314, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +#if TOUCH_KEY_NUM > 0 + 0,0,0, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_P[40 * 40] = { + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, +#if TOUCH_KEY_NUM > 0 + 75,75,75, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_N[40 *40] = { + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, +#if TOUCH_KEY_NUM > 0 + -75,-75,-75, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Diff_Test_Frame = 50; + +#endif /* #if NVT_TOUCH_MP */